Android跨进程IPC通信Messenger

简介

Messenger可以翻译为信使,Android官方API解释如下:

它持有一个Handler的引用,以便其它Messenger发送消息给它。该类允许不同进程之间通过消息进行通信,在一个进程中服务端使用Handler创建一个Messenger,在另一个进程中客户端持有这个Messenger就可以与服务端通信了。这个类的底层是对Binder的一个简单封装,Binder是用来执行通信的。

Messenger是一种轻量级的IPC方案,它的底层实现时AIDL,因此它也是对AIDL跨进程操作进行的一个简单封装,如果使用Messenger,就不需要用开发者自己再定义一套AIDL。

一般我们通过以下步骤就可以使用Messenger进行跨进程通信了。

  • 在Service中定义一个Handler,在Handler中处理从客户端发送过来的消息;
  • 通过传入上一步骤中创建的Handler引用创建一个Messenger;
  • 通过Messenger对象使用getBinder()方法返回给客户端一个IBinder类型的对象,这个返回的IBinder类型的对象就是Service中的onBind方法所需要的返回值;
  • 客户端通过使用ServiceConnection中的IBinder构造一个Messenger对象;
  • 客户端通过构造的Messenger对象就可以同服务端进行跨进程通信了。

上述步骤仅仅是客户端向服务端发送消息,但是如何实现双向通信呢,服务端也可以根据客户端信息进行回复。在服务端的Handler中handleMessage()方法中的msg对象有一个replyTo属性,该属性返回的就是一个Messenger对象,这时候我们就可以根据replyTo获取的Messenger对象发送一个新的Message对象到客户端了。同样的将初始的客户端向服务端发送信息的时候,我们可以设置一个接收服务端信息的Messenger对象,在客户端发送Message对象的时候,将Message对象的replyTo对象设置为客户端的一个Messenger对象,这样当服务端发送消息的时候就可以通过Messenger对象中Handler处理消息了。

接下来通过一个示例演示如何通过Messenger进行跨进程通信,在进行跨进程设置的时候,可以通过Service的 android:process=":remote" 进行设置,这样在一个应用中该Service就和主进程处于不同的进程中了,可是本文为了更好的演示效果,我们将服务端和客户端分别使用两个不同包名的不同应用,也就是将服务端和客户端是两个不同的APK。

Messenger示例

下面是服务端Service注册的清单文件:

 
 
 
 

接下来是几个常量设置:

public class Constant {
 
 //标记客户端发送的信息
    public static final int MSG_CLIENT = 1001;
 
 //标记服务端发送的信息
    public static final int MSG_SERVER = 1002;
 
 //标记获取信息的key
    public static final String MSG_KEY = "msg_key";
}

MessengerService代码如下:

public class MessengerService extends Service {
    
    private static final String TAG = "MessengerService";
 
    //接收客户端消息
    private Messenger messenger = new Messenger(new ServerHandler());
 
 //发送消息到客户端
    private Messenger replyMessenger;
 
    private class ServerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (Constant.MSG_CLIENT == msg.what) {
                String result = msg.getData().getString(Constant.MSG_KEY);
                Log.d(TAG, "server==>" + result);
 
 //将接收的客户端信息进行再次封装后告诉客户端
                Message message = Message.obtain();
                message.what = Constant.MSG_SERVER;
                Bundle bundle = new Bundle();
                bundle.putString(Constant.MSG_KEY, "server got " + result);
                message.setData(bundle);
 
                replyMessenger = msg.replyTo;
                try {
                    replyMessenger.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
 
 //通过Messenger获取IBinder
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}

MessengerActivity代码如下:

public class MessengerActivity extends AppCompatActivity {
 
    private static final String TAG = "MessengerActivity";
    private TextView textView;
 
 //标记服务是否绑定成功
    private boolean isConnected;
 
 //从服务端获取的Messenger
    private Messenger messenger;
 
 //拼接服务端返回的信息
    private StringBuffer sb = new StringBuffer();
 
 //接收服务端信息客户端自己的Messenger
    private Messenger clientMessenger = new Messenger(new Handler() {
        public void handleMessage(Message msg) {
            if (Constant.MSG_SERVER == msg.what) {
                String result = msg.getData().getString(Constant.MSG_KEY);
                Log.d(TAG, "client==>" + result);
                sb.append(result + "n");
                textView.setText(sb.toString());
            }
        }
    });
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        initData();
    }
 
 //按钮点击后的方法
    public void startSend(View view) {
        if (isConnected) {
            Message msg = Message.obtain();
            msg.what = Constant.MSG_CLIENT;
            Bundle bundle = new Bundle();
            bundle.putString(Constant.MSG_KEY, new Random().nextInt(10) + "");
            msg.setData(bundle);
            msg.replyTo = clientMessenger;
            try {
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
 
    private void initView() {
        textView = findViewById(R.id.textView);
    }
 
 //隐式启动Service,由于Service位于不同的进程中所以必须使用隐式启动
    private void initData() {
        Intent intent = new Intent();
        intent.setAction("com.sunny.server.MessengerService");
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }
 
    private ServiceConnection connection = new ServiceConnection() {
 
        public void onServiceConnected(ComponentName name, IBinder service) {
            messenger = new Messenger(service);
            isConnected = true;
        }
 
        public void onServiceDisconnected(ComponentName name) {
            messenger = null;
            isConnected = false;
        }
    };
 
 //当Activity处于不可见时解绑Service
    @Override
    protected void onStop() {
        super.onStop();
        if (isConnected) {
            unbindService(connection);
            isConnected = false;
        }
    }
 
}

下面是运行结果的截图

Messenger源码简要分析

在文章开始部分也说明了一下,Messenger也是基于AIDL实现的,首先我们看一下Server服务端代码中使用的Messenger相关方法。

public final class Messenger implements Parcelable {
 
    private final IMessenger mTarget;
 
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
 
    public IBinder getBinder() {
        return mTarget.asBinder();
    }
 
 //...
}

Messenger是一个final类修饰的,也就是说它没有子类,里面存放了一个IMessenger类型的属性mTarget。IMessenger这里就是一个以来AIDL生成的一个类文件,在AIDL文件生成的IMessenger的Java文件中mTarget.asBinder()方法返回的实际上就是Stub对象,Stub继承自Binder类,也即是IBinder类型的一个对象。Messenger中send()方法调动了mTarget的send()方法,而mTarget的send()对应着一个AIDL中定义的接口方法,只是这里传参是一个Message类型的对象,这些是服务端对应的方法,只是这里不必开发者自己手动编写AIDL文件了,Android SDK中内置了IMessenger对应的AIDL文件,该文件位于 frameworks/base/core/java/android/os/IMessenger.aidl

在构造方法中看到通过Handler获取了一个MessengerImpl类型的实例,而MessengerImpl就是跟常规的AIDL在服务端类似继承自XXX.Stub类,然后实现AIDL接口文件中定义的方法。这里可以看到最终send()方法调用了Handler的sendMessage()方法,所以我们才可以在Handler的handleMessage()方法中处理消息。

final IMessenger getIMessenger() {
 synchronized (mQueue) {
 if (mMessenger != null) {
 return mMessenger;
 }
 mMessenger = new MessengerImpl();
 return mMessenger;
 }
}
 
private final class MessengerImpl extends IMessenger.Stub {
 public void send(Message msg) {
 msg.sendingUid = Binder.getCallingUid();
 Handler.this.sendMessage(msg);
 }
}

接下来看一下Client客户端如何获取一个Messenger对象,如果使用AIDL自动生成的类,在客户端与服务端通信时首先要借助ServiceConnection中回调方法onServiceConnected(),在该方法中我们直接通过类似IRemoteService.Stub.asInterface(binder)就可以获取到AID对应的类了,接下来就可以执行跨进程方法调用了。在Messenger中有一个构造方法,也是借助于ServiceConnection中onServiceConnected()方法,通过传入一个IBinder类型的对象返回一个Messenger类型的示例。

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

通过上面简单介绍可以看出,Messenger有两个构造方法,其中一个对应了服务端处理消息的Messenger,另外一个对应客户端Messenger,这样就可以在客户端通过构造的Messenger对象调用send()方法发送消息Message到服务端了。这样只是构成了客户端可以发送消息到服务端的单向通信,进行双向通信还要借助于消息体Message另外一个属性replyTo,客户单发送一个消息Message到服务端,服务端通过Message的replyTo属性获取到一个Messenger对象,就可以再次发送处理过的消息到客户端了,客户单也可以通过相似的逻辑将replyTo赋值给另外一个传入Handler构造的Messenger,这样客户端也可以接受到服务端的消息了,就达到了双向通信的目的。

接下来看一下Message是如何通过序列化和反序列化将Messenger写入和读取的

public void writeToParcel(Parcel dest, int flags) {
 ...
 dest.writeLong(when);
 dest.writeBundle(data);
 Messenger.writeMessengerOrNullToParcel(replyTo, dest);
 dest.writeInt(sendingUid);
}
 
private void readFromParcel(Parcel source) {
 what = source.readInt();
 arg1 = source.readInt();
 arg2 = source.readInt();
 if (source.readInt() != 0) {
 obj = source.readParcelable(getClass().getClassLoader());
 }
 when = source.readLong();
 data = source.readBundle();
 replyTo = Messenger.readMessengerOrNullFromParcel(source);
 sendingUid = source.readInt();
}

Message在进行序列化和反序列化时分别调用了Messenger的对应的序列化和反序列化方法。

public static void writeMessengerOrNullToParcel(Messenger messenger,
 Parcel out) {
 out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
 : null);
}
public static Messenger readMessengerOrNullFromParcel(Parcel in) {
 IBinder b = in.readStrongBinder();
 return b != null ? new Messenger(b) : null;
}

从Messenger的源码中可以发现,在进行序列化和反序列化的时候实际上写入的是Binder,Messenger只是屏蔽了底层上对AIDL操作的繁琐,方便用户方便进行进程间通信。后续文章再继续介绍AIDL以及Binder进行进程间通信,实际上AIDL也是为了对Binder进行操作的方便才进行的抽象,从进程间通信的复杂程度来讲Messenger->AIDL->Binder是递增的。

其它

java.lang.RuntimeException: Can’t marshal non-Parcelable objects across processes

在使用Messenger跨进程通信的时候可能会抛出上面这种异常,主要是由于在使用Binder传递数据的时候必须实现Parcelable接口,否则无法在两个应用之间进行通信。所以在本文的示例中进行传值的时候使用的是Bundle,因为Bundle实现了Parcelable接口。

Default activity not found

在运行服务端示例的时候,由于服务端示例中只有一个Service,所以在Android Studio上面会提示 Default activity not found ,这时候我们需要点击运行按钮下拉菜单中的Edit Configurations,将Launch Options选项设置为Nothing,然后服务端代码就可以运行了。

另外由于Messenger会将所有Service请求入队列,所以它不支持多线程通信。如果你要支持多线程,那么请使用AIDL。

责编内容来自:SUNNY空间 (源链) | 更多关于

阅读提示:酷辣虫无法对本内容的真实性提供任何保证,请自行验证并承担相关的风险与后果!
本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 移动开发 » Android跨进程IPC通信Messenger

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录