Android进阶笔记:Messenger源码详解

  1. 云栖社区>
  2. 博客>
  3. 正文

Android进阶笔记:Messenger源码详解

技术小胖子 2017-11-09 16:29:00 浏览732
展开阅读全文

Messenger可以理解为一个是用于发送消息的一个类用法也很多,这里主要分析一下再跨进程的情况下Messenger的实现流程与源码分析。相信结合前面两篇关于aidl解析文章能够更好的对aidl有一个认识。(Android进阶笔记:AIDL内部实现详解 (一)Android进阶笔记:AIDL内部实现详解 (二)

用法说明

先来看一下Messenger在跨进程通讯时的使用方法,代码如下:

Service的代码

//用来传递Messenger中IMessenger
public class ServerService extends Service {
    public static final String TAG = "ServerService";
    private Messenger messenger;

    @Override
    public void onCreate() {
        super.onCreate();
        //创建一个Messenger对象
        messenger = new Messenger(new MessengerHandler());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回IMessenger
        return messenger.getBinder();
    }
}
//用于创建Messenger的Handler
class MessengerHandler extends Handler {
    public static final String TAG = "ServerService";

    @Override
    public void handleMessage(Message msg) {
        if (msg.what == 10001) {
            String data = msg.getData().getString("data");
            data = data == null ? "null" : data;
            Log.e(TAG, "handleMessage: get msg from client = (" + data + ")");
        }
    }
}

上面就是Service的代码,分析一下其实总共做了3步:

  1. 定义了一个MessengerHandler的内部类并且实现了handleMessage的内部回调;
  2. 创建了一个Messenger的对象,在Messenger的构造函数中传入刚刚创建的handler实例;
  3. 在Service的onBind方法中回调messenger的getBinder()方法;

Activity的代码

public class MainActivity extends AppCompatActivity {
    private ServiceConnection serviceConnection;
    private Messenger messenger;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                Log.e("MainActivity", "onServiceConnected: connection success !!!");
                //用返回的Ibinder对象来构造一个Messenger实例
                messenger = new Messenger(service);

                //创建一个msg
                Message msg = new Message();
                msg.what = 10001;
                Bundle bundle = new Bundle();
                bundle.putString("data", "hello Server");
                msg.setData(bundle);
                try {
                //调用messenger的send方法
                    messenger.send(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                messenger = null;
            }
        };
        bindService(new Intent("com.coder_f.messengerdemo.ServerService"), serviceConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

Activity中做的工作其实也不复杂也是3步:

  1. 通过绑定Service来获得IBinder对象;
  2. 通过IBinder对象重新构造一个Messenger;
  3. 通过Messenger的send方法来发送消息;

以上就是Messenger的使用方法。好了现在就可以根据上面的使用方法来看看Messenger内部到底是怎么来运作的。

源码解析

首先先来看看参数为Handler的Messenger的构造函数

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

很简单,就是保存传入Handler的getIMessenger()方法返回的东西。那接下来就去看看getIMessenger()方法返回的是什么东西。

final IMessenger getIMessenger() {
   synchronized (mQueue) {
        if (mMessenger != null) {
           return mMessenger;
        }
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}

也不复杂,无非就是判断一下mMessenger是不是空然后返回一下。那mMessenger到底是什么呢。看看它的构造函数new MessengerImpl()代码如下:

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

看到这里相信已经摸的差不多了,这个结构就是aidl的结构。这里猜一下应该就明白了,源码里面肯定有定义了一个IMessage.aidl,而且里面还声明了一个send的方法。而这个seng的方法在Handler里面被实现了,具体就是通过Handler来发送一条消息。那么可以得到结论,最后mTarget获得的其实就是一个IMessenger.Stub的实例,里面已经实现了接口中的方法(send(Message msg))

事实证明确实如此,源码的位置:platform\frameworks\base\core\java\android\os\IMessenger.aidl

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

上面只是对构造函数的源码进行了分析,但是其实已经把Messenger的结构摸的八九不离十了;

好了既然构造函数分析的差不多了,根据流程下一步应该是通过messenger.getBinder()方法取出一个Ibinder对象通过Service来返回给Activity。那么接下来再来看看messenger.getBinder()方法:

    public IBinder getBinder() {
        return mTarget.asBinder();
    }

很清楚,调用了IInterface(mTarget就是IMessenger.Stub的实例继承了IMessenger,而IMessenger继承了IInterface)的asBInder方法返回了一个Binder(这里简单的理解其实就是返回了它自己,因为stub内部类也继承了Binder)。

service这边的代码只有这些,那根据上面的使用方法,继续来看Activity这边的代码吧。 
Activity这边也有一个构造函数,参数是一个IBinder对象,这个构造函数的源码如下:

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

很清楚返回的其实就是一个proxy类也就是一个binder驱动(不明白的可以看之前的博客)。 
然后接下来就是调用了Messenger的send方法;那继续再来看看这个send方法的源码是怎么样的。

public void send(Message message) throws RemoteException {
        mTarget.send(message);
}

其实就是调用了刚刚proxy的send方法吧message当参数传进去。这里面的逻辑其实就是通过这个proxy类中的IBinder对象来远程调用service中已经实现的send方法。 
一目了然Messenger就是一个典型的aidl的例子。

总结

  1. Server端就是Handler里面实现的MessengerImpl内部类,然后在Service里面被实例化了。而这个aidl也是只有一个方法(send(Message)),就是通过当前Handler来发送一个消息。

  2. Client端就是通过Service返回过来的IBinder类来获取一个proxy对象,通过proxy对象远程调用send方法来完成通讯。

补充:如果要实现Service那边处理完消息返回给activity的话只要在activity里面也创建一个Messenger,然后把这个Messenger通过Message赋值给参数message.replyTo传过去就好了,同样Service就可以通过这个参数里面的Messenger来发送消息给activity通过activity里面handler来处理消息来完成双向通讯。

注意:这里有一点就是如果不是跨进的的话Service和Activity都运行在主线程,那么Service中用于处理消息的Handler里面不能执行耗时的工作,不然会导致ActivityUI界面卡住,因为Handler是创建在Service线程(主线程)用的是主线程的Looper。如果是跨进程的话Activity这边的主线程就不会卡住(Service所在的线程会卡住)。因为在普通的aidl在proxy调用的时候(其实就是调用IBinder.transact方法时)会挂起当前线程因此在Service端执行耗时操作时activity的UI线程会卡住。而messenger和普通的aidl不同之处在于它又添加了一个Handler,而这个Handler是运行在Service所在线程(默认为Service所在进程的主线程)而真正的Messenger.send方法只执行了一个Handler的sengmessage方法(这个方法是运行在底层binder的工作线程中,只要在这个线程中不执行耗时操作调用方所在的线程就不会被挂起太久)。因此不会卡住(Service线程可能会卡住)。这一点我感觉IntentService的实现非常的相似。



    本文转自 一点点征服   博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/8418914.html,如需转载请自行联系原作者


网友评论

登录后评论
0/500
评论
技术小胖子
+ 关注