在Android中,下层是Linux的核,但上层的java做的framework把这一切封装的密不透风。以消息处理为例,在MFC中,我们可以用PreTranslateMessage等东东自由处理消息,在C#中,Anders Hejlsberg老大说了,他为我们通向底层开了一扇“救生窗”,但很遗憾,在Android中,这扇窗户也被关闭了(至少我现在没发现...)。
在Android中,你想处理一些消息(比如:Keydown之类的...),你必须寻找Activity为你提供的一些重载函数(比如 onKeyDown之类的...)或者是各式各样的listener(比如OnKeyDownListner之类的...)。这样做的好处是显而易见的, 越多的自由就会有越多的危险和越多的晦涩,条条框框画好了,用起来省心看起来省脑,这是一个设计良好的framework应该提供的享受。对于我目前的工 程而言,我没有什么BT的需求在当前API下做不到的,google的设计ms还是很nice的。
但世界是残酷的,有的时候我们还是必须有机制提供消息的分发和处理的,因为有的工作是不能通过直接调用来同步处理的,同时也不能通过Activity中内 嵌的消息分发和接口设定来做到,比如说事件的定时触法,异步的循环事件的处理,高耗时的工作等等。在Android中,它提供了一些蛮有意思的方式来做这 件事情(不好意思,我见不多识不广,我没见过类似玩法,有见过的提个醒 && 嘴下超生^_^),它有一个android.os.Handler的类,这个类接受一个Looper参数,顾名思义,这是一个封装过的,表征消息循环的 类。默认情况下,Handler接受的是当前线程下的消息循环实例,也就是说一个消息循环可以被当前线程中的多个对象来分发, 来处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理...)。在实例化一个 handlerInstance之后,你可以通过sendMessage等消息发送机制来发送消息,通过重载handleMessage等函数来分发消 息。但是!该handlerInstance能够接受到的消息,只有通过handlerInstance.obtainMessage构造出来的消息(这 种说法是不确切的,你也可以手动new一个Message,然后配置成该handlerInstance可以处理的,我没有跟进去分析其识别机制,有兴趣 的自己玩吧^_^)。也就是说A, B, C, D都可以来处理同一线程内的消息分发,但各自都只能处理属于自己的那一份消息, 这抹杀了B想偷偷进入A领地,越俎代庖做一些非份之事的可能(从理论上看,B还是有可能把消息伪装的和A他们家的一样,我没有尝试挑战一下google的 智商,有BT需求的自行研究^_^)。这样做,不但兼顾了灵活性,也确保了安全性,用起来也会简单,我的地盘我做主,不用当心伤及无辜,左拥右抱是一件很 开心的事情。。。
很显然,消息发送者不局限于自己线程,否者只能做一些定时,延时之类的事情,岂不十分无趣。在实例化Handler的时候,Looper可以是任意线程 的,只要有Handler的指针,任何线程也都可以sendMessage(这种构造方式也很有意思,你可以在A线程里面传B线程的Looper来构造 Handler,也可以在B线程里构造,这给内存管理的方法带来很大的变数...)。但有条规则肯定是不能破坏的,就是非UI线程,是不能触碰UI类的。 在不同平台上有很多解决方式(如果你有多的不能再多的兴趣,可以看一下很久很久以前我写的一个,不SB不要钱)。我特意好好跟了一下android中的AsyncQueryHandler类,来了解google官方的解决方案。
AsyncQueryHandler是Handler的子类,文档上说,如果处理ContentProvider相关的内容,不用需要自行定义一套东西, 而可以简单的使用async方式。我想指代的就应该是AsyncQueryHandler类。该类是一个典型的模板类,为ContentProvider 的增删改查提供了很好的接口,提供了一个解决架构,final了一些方法,置空了一些方法。通过派生,实例化一些方法(不是每个对 ContentProvider的处理多需要全部做增删改查,我想这也是该类默认置空一些方法而不是抽象一些方法的原因),来达到这个目的。在内部,该类 隐藏了多线程处理的细节,当你使用时,你会感觉异常便利。以query为例,你可以这么来用:
queryHandler = new AsyncQueryHandler(this.getContentResolver()){ // 传入的是一个ContentResolver实例,所以必须在OnCreate后实例化该Handler类
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
// 在这里你可以获得一个cursor和你传入的附加的token和cookie。
// 该方法在当前线程下(如果传入的是默认的Looper话),可以自由设定UI信息
}
};
// 调用时只需要调用startQuery(int token, Object cookie, ContentURI uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)函数即可:
queryHandler.startQuery(token, cookie, uri, projection, selection, selectionArgs, sortBy);
还是习惯性跑题了,其实,我是想通过我对这个类的无数次Debugger跟进,说说它的多线程异步处理的解决策略的。他的基本策略如下:
2. 获得该消息循环的指针,用它做参数实例化另一个Handler类,该类为内部类。至此,就有了两个线程,各自有一个Handler来处理消息。
3. 当调用onXXX的时候,在XXX函数内部会将请求封装成一个内部的参数类,将其作为消息的参数,将此消息发送至另一个线程。
4. 在该线程的Handler中,接受该消息,并分析传入的参数,用初始化时传入的ContentResolver进行XXX操作,并返回Cursor或其他返回值。
5. 构造一个消息,将上述返回值以及其他相关内容绑定在该消息上,发送回主线程。
6. 主线程默认的AsyncQueryHandler类的handleMessage方法(可自定义,但由于都是内部类,基本没有意义...)会分析该消息,并转发给对应的onXXXComplete方法。
7. 用户重写的onXXXComplete方法开始工作。
所以建议,如果你有什么非ContentProvider操作,却需要异步多线程执行的话,模拟一套,是个不错的策略,当然,具体情况具体分析,生搬硬套是学不好马列主义的。。。