Android的一些基本常识

1.Message

1.1 主要成员

public final class Message implements Parcelable{
    public int what;//user-defined message code
    public int arg1;
    public int arg2;
    public Object obj;//arbitrary object to send to recipient
    /**
      *一个可选的Messenger对象,表示这个消息的回复要发给谁,AsynChannel类就
      * 用到了这个成员
    **/
    public Messenger replyTo;
    Handler target;//处理这个消息的Handler
    public static Message obtain();
    public static Message obtain(Message orig);
    public static Message obtain(Handler h) ;
    public static Message obtain(Handler h, int what);
    public static Message obtain(Handler h, int what, Object obj) ;
    public static Message obtain(Handler h, int what, int arg1, int arg2);
    public static Message obtain(Handler h, int what, 
            int arg1, int arg2, Object obj);
            
     public void sendToTarget();
}

1.2 说明

Message类定义了消息结构体,里面包含了arg1,arg2用以携带基本的信息,这样大部分情况下可以不用分配内存。如果arg1,arg2满足不了要求,还可以使用obj和setData来传递更丰富的信息。
通常情况下,使用obtain()函数或者Handler::obtainMessage来构造Message类实例

2.Hanlder

2.1Handler类的作用

Hanlder类,主要用来实现消息处理函数。
简单地说,Looper是一个循环,它有一个Message que,会不停地读Message que,读出来的message,就调用改message对应的Handler(也就是message的target成员变量)的handleMessage或者message的callback进行处理。这个Looper通常运行在一个Thread上,但是Thread缺省并不带Looper,一般调用Looper.prepare()和Looper.loop()将循环运行起来。调用Handler.::sendMessage主要做两件事,一是将该Handler的值赋给message.target,而是将message enque到Looper的Message que中。下面一个简单例子展示这种关系。

class LoopThread extends Thread{
    public Handler mHandler;
    public void run(){
        Looper.prepare();
        
        mHandler =new Handler(){
            public void handleMessage(Message msg){
                //process incoming message
            }
        };
        Looper.loop();
    }
}

//somewhere
LoopThread thread=new LooperThead();
thread.run()

//somewhere
mHandler.sendMessage(msg)

2.2 HandlerThread

为了简化Hanlder,Loop,Thread之间的处理,Android定义了一个HanlderThread类,派生自Thread,特别的是,默认就带有一个looper

Class SampleHandler extends Handler{
    SampleHandler(android.os.Looper looper){
        super(looper);
    }
    public void handlerMessage(Message msg){
        //handler incoming mssage
    }
}

HandlerThread sampleThread = new HandlerThread("SampleService");
sampleThread.start();
mSampleHandler = new SampleHandler(sampleThread.getLooper());


//somewhere
mSampleHandler.sendMessage(msg);

2.3 官方文档对Handler的说明

Handler允许你发送和处理和thread的MessageQue关联的消息和Runnable objects。每个Handler实例都同一个单独的Thread和那个Thread的MessageQue关联。当你创建了一个Handler,它就绑定到创建它的thread和thread的MessageQue上,它deliver messages和Runnables到那个MessageQue,当它们从MessageQue取出时执行他们。
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
schedule messages通过post,postAtTime,postDelayed,sendEmptyMessage,sendMessage,sendMessageAtTime,sendMessageDelayed等方法来完成。
其中,post系列的函数用来schedule Runnables。而sendMessage系列的函数用来enqueue message ,该message可以包含a bundle of data,传递给handleMessage。

2.4 其它

Handler类还声明了一个成员变量
IMessenger mMessenger;
并提供了接口
final IMessenger getIMessenger() 
通过这个可以将可跨进程传递的IMessager同进程内部的Handler联系起来,Messenger类利用了这个实现了跨进程通信。

3.Messenger

3.1 介绍

Messenger类是Handler的一个索引,但是它通过Binder的一个简单封装,实现了Handler无法实现的进程间通信。
public final class Messenger implements Parcelable{
    private final IMessenger mTarget;
    
    /**
     * 创建一个指向target的Messenger,任何发给这个Messenger的
     * 消息就会像Handler::sendMessage一样,被target这个Handler
     * 处理
     * */
    public Messenger(Handler target);
    /**
     * 通过一个raw IBinder来创建Messenger,比如client端连接service的时候,
     * 通过onServiceConnected获取到IBinder对象,就可以构建出service的
     * Messenger,从而可以像service发送消息
     * */
    public Messenger(IBinder target);
    
    /*注意因为是跨进程通信,要catch exception*/
    public void send(Message message) throws RemoteException;
    
    /**
     * 获取messenger用来同对应Handler通信的IBinder对象,比如service在onBind
     * 接口可以调用这个接口返回IBinder对象给client,也就是client在
     * onServiceConnected获取到的IBinder
     * */
    public IBinder getBinder();
    
}

3.2 实例

下面以一个实例展示如何利用Messenger来实现client和bound service之间的通信,可以简单分为如下几个步骤
  • Service实现一个Handler来处理消息
  • Service利用这个Handler来创建Messenger
  • Service在onBind接口中,利用Messenger::getBinder()将IBinder对象返回
  • Client在ServiceConnection::onServiceConnect中接收到这个IBinder对象,利用这个IBinder对象,构建出远端Messenger
  • client端利用这个远端Messenger向service发送消息(Messenger::send)
  • sercie在Handler里面处理消息

public class MessengerService extends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;
    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
         return mMessenger.getBinder();
    }
}

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what' value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

几点讨论
  • 这种方式client只能向service发送消息,不能像LocalBinder或者AIDL那样通过client直接调用远端service的method
  • 因为MessageQue是串行处理的,这种方式一次只能处理一个请求,不像aidl可以同时处理多个请求,相应地也没有multi-thread的问题
  • 上个例子中,这种通信是单向的,只能client向service发送消息,如果想service也可以主动向client发消息,可以设置message的replyTo,也可以使用AsyncChannel。其实AsyncChannel就是利用利用两个Messenger来实现双工通信。
  • 因为是Remote Process Call,所以Messenger::send调用要注意catch exception

3.3 补充

Messenger这种进程间通信的本质就是获得另一端Handler对应的Messenger,所以除了在Service::onBind中返回IBinder对象外,只要能获取Messenger对象就可以,比如结合AIDL,可以利用AIDL的函数调用来获得远端的Messenger.

4.AIDL

4.1 什么是AIDL

AIDL(Android Interface Definition Language)用来帮助实现跨进程通信。仅在需要从不同的应用通过IPC同时访问service(需要处理multi-thread)的情况下使用AIDL通信,如果不需要跨进程,可以使用LocalBinder,如果不需要同时处理请求(多线程),可以使用messenger。AIDL通常用于实现Bounding Service。

4.2 AIDL的实现

4.2.1 Service端

  1. 创建.aidl文件xxx.aidl
  2. 实现aidl文件中的interface
    Android SDK工具会根据第一步的.aidl,自动生成xxx.java,在这个xxx里面有个.Stub抽象类,这个.Stub抽象类派生自Binder,负责实现aidl中的接口。所以你要做的工作就是继承这个抽象类,实现它。
    那个自动生成的抽象类.stub
    public static abstract class Stub extends Binder implements DataService;
  3. 将interface expose给client
    在Service::onBind中返回你实现的.stub类的实例
实例:
DataService.aidl
Interface DataService{
    double getData(String arg);
}

实现接口并expose给client
public class AIDLExampleService extends Service{
    private final DataService.Stub mBinder = new DataService.Stub(){
        public double getData(String arg) {
            /*implement your operation,it should be thread safe*/
            return 0;
        }
    }
    /*expose the interface*/
    public IBinder onBind(Intent intent){
        return mBinder;
    }
}

4.2.2 Client端

  1. include .aidl文件到src目录
  2. 声明一个IBinder接口实例
  3. 实现ServiceConnection
  4. 调用context.bindService,传入ServiceConnection实现
  5. 在ServiceConnection::onServiceConnected()接口中接收IBinder实例,调用xxx.Stub.asInterface()接口进行类型转换
  6. 利用IBinder接口实例来调用远端方法,注意catch RemoteException
  7. 调用unbindService disconnect
实例
public class AIDLExampleActivity extends Activity{
    private Button button;//按钮触发调用
    
    private DataService mService=null;
    private boolean mBound;
    
    private ServiceConnection = new ServiceConnection(){
        public void onServiceDisconnected(ComponentName name){
            mService=null;
            mBound=false;
        }
        public void onServiceConnected(ComponentName name,IBinder service){
            mBound=true;
            mService=DataService.stub.asInterface(service);
        }
    }
    protected void onCreate(Bundle savedInstanceState){
        //onCreate的一些normal implementation
        
        //bind service
        Intent intent=new Intent("***.DataService");
        bindService(intent,connection,BIND_AUTO_CREATE);
        
        button.setOnClickListener(new View.onClickListener(){
           public void onClick(View v){
               if(mBound && mService!=null){
                   double result=mService.getData("a");
                   /**desplay in ui***/
               }
           } 
        });
    }
    
}

4.3 其他

4.3.1 关于AIDL interface在哪个线程执行

  • 如果是本地Process发起的调用,就在发起调用的那个线程执行
  • 如果是从其它Process发起的调用,会从本进程的线程池中选一个线程执行。也就是说,同时可能产生多个调用,因此AIDL interface的实现必须保证线程安全

4.3.2 通过IPC传递Objects

  • 让你要传递的类实现Parcelable接口(实现writeToParcel和CREATOR)
  • 创建一个aidl文件,声明这个Parcellable class











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值