IPC 之 Messenger 的使用
一、概述
Messenger 是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,对 AIDL 进行了封装,方便了对它的使用。Messenger 一次只处理一个请求,所以在服务端不用考虑线程同步的问题。下面给出一张 Messenger 的工作原理图来了解一下其工作原理:
根据上面原理图,要通过 Messenger 进行进程间通信,在 Cilent 和 Server 端扮演重要角色的有 Handler 、Message 、Messenger 。Message 和 Messenger 是实现 Parcelable 接口的,所以可以在进程间进行传递。Message 是我们所要传递信息的载体,Messenger 提供了 传递的渠道,而 Handler 是 最终的信息接受和处理中心。当然本身 Handler 是无法进行接受消息的,还是由在创建 Messenger 时,Messenger 持有了 Handler 的对象 ,在 Messenger 内部调用了 Handler 的 handleMessage 方法,让其去处理 Message 。下面我们就开始通过代码来实现:
二、编码实现
示例主要由 客户端 和 服务端 两个独立 App 进行 IPC ,传输的类型为自定义类型,所以它一定实现了 Parcelable 接口。
1. 服务端
服务端工程目录:
根据目录,来看 User :
package com.sl.messengerclient; import android.os.Parcel; import android.os.Parcelable; public class User implements Parcelable { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } protected User(Parcel in) { name = in.readString(); age = in.readInt(); } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } @Override public String toString() { return "[ name = " + name + " , age = " + age + "]"; } }
服务MessengerService 的编码实现:
package com.sl.messengerserver; import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import com.sl.messengerclient.User; import java.lang.ref.WeakReference; import java.util.ArrayList; public class MessengerService extends Service { private static final String USER_LIST = "user_list"; private static final int ADD_USER_REQUEST = 0x001; private static final int USER_LIST_REQUEST = 0x0002; private static final String ADD_USER = "add_user"; public ArrayList<User> mUserList; private static class MessengerHandler extends Handler { WeakReference<MessengerService> wrService; MessengerHandler(MessengerService messengerService) { wrService = new WeakReference<>(messengerService); } @Override public void handleMessage(Message msg) { switch (msg.what) { case ADD_USER_REQUEST: Bundle bundle = msg.getData(); bundle.setClassLoader(Thread.currentThread().getContextClassLoader()); User user = bundle.getParcelable(ADD_USER); wrService.get().mUserList.add(user); Message message = Message.obtain(null, ADD_USER_REQUEST); try { msg.replyTo.send(message); } catch (RemoteException e) { e.printStackTrace(); } break; case USER_LIST_REQUEST: Message userMsg = Message.obtain(null, USER_LIST_REQUEST); Bundle data = new Bundle(); data.putParcelableArrayList(USER_LIST, wrService.get().mUserList); userMsg.setData(data); try { msg.replyTo.send(userMsg); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } } private Messenger mMessenger = new Messenger(new MessengerHandler(this)); @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } @Override public void onCreate() { super.onCreate(); mUserList = new ArrayList<>(); } }
服务端这里有一点需要特别注意,使用 Messenger 传递自定义对象的时候,在接收端(不论是服务端或者客户端)拿到传递过来的自定义对象,示例里的 User 。都需要在获取之前添加代码中标红的那句代码:
bundle.setClassLoader(Thread.currentThread().getContextClassLoader());
不添加这句的后果就是会出现 ClassNotFoundException ,为什么会出现这样的错误?Bundle 里本身存储了一个 类加载器,这个类加载器是发送方的,但是类加载器本身没有实现 Parcelable 接口 ,所以传递不过来。这里设置当前为当前类加载器,就能找到我们自定义的对象类。
服务端记得在 AndroidManifest.xml 中申明 ,在 Activity 中启动,服务端就这样了。
2. 客户端
客户端工程目录:
服务端的 User 是从 这里的 User 拷贝过去的,所以看上面的就可以了。直接看 MessengerClientActivity :
package com.sl.messengerclient; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import java.lang.ref.WeakReference; import java.util.ArrayList; public class MessengerClientActivity extends AppCompatActivity { private static final String PACKAGE_REMOTE_SERVICE = "com.sl.messengerserver"; private static final String NAME_REMOTE_SERVICE = "com.sl.messengerserver.MessengerService"; private static final String USER_LIST = "user_list"; private static final int ADD_USER_REQUEST = 0x001; private static final int USER_LIST_REQUEST = 0x0002; private static final String ADD_USER = "add_user"; private Messenger mService; public LinearLayout bookContainer; private Messenger mGetRelyMessenger = new Messenger(new MessengerHandler(this)); private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bookContainer = findViewById(R.id.container); Intent intent = new Intent(); ComponentName componentName = new ComponentName(PACKAGE_REMOTE_SERVICE, NAME_REMOTE_SERVICE); intent.setComponent(componentName); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } public void sendAddUserMsg(View view) { if (mService == null) { Toast.makeText(this, "服务器未连接", Toast.LENGTH_LONG).show(); return; } Message message = Message.obtain(null, ADD_USER_REQUEST); Bundle data = new Bundle(); EditText etUserName = findViewById(R.id.user_name); EditText etUserAge = findViewById(R.id.user_age); data.putParcelable(ADD_USER, new User(etUserName.getText().toString(), Integer.valueOf(etUserAge.getText().toString()))); message.setData(data); message.replyTo = mGetRelyMessenger; try { mService.send(message); } catch (RemoteException e) { e.printStackTrace(); } } public void sendGetUserListMsg(View view) { if (mService == null) { Toast.makeText(this, "服务器未连接", Toast.LENGTH_LONG).show(); return; } Message message = Message.obtain(null, USER_LIST_REQUEST); message.replyTo = mGetRelyMessenger; try { mService.send(message); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConnection); } private static class MessengerHandler extends Handler { WeakReference<MessengerClientActivity> wrContext; MessengerHandler(MessengerClientActivity mainActivity) { wrContext = new WeakReference<>(mainActivity); } @Override public void handleMessage(Message msg) { switch (msg.what) { case USER_LIST_REQUEST: Bundle bundle = msg.getData(); bundle.setClassLoader(Thread.currentThread().getContextClassLoader()); ArrayList<User> users = bundle.getParcelableArrayList(USER_LIST); showBooks(users, wrContext); break; case ADD_USER_REQUEST: Toast.makeText(wrContext.get().getApplicationContext(), "添加用户成功", Toast.LENGTH_SHORT).show(); break; default: super.handleMessage(msg); } } private void showBooks(ArrayList<User> users, WeakReference<MessengerClientActivity> wrContext) { wrContext.get().bookContainer.removeAllViews(); if (users != null && users.size() > 0) { for (User user : users) { LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); TextView textView = new TextView(wrContext.get()); params.gravity = Gravity.CENTER_HORIZONTAL; textView.setGravity(Gravity.CENTER); params.topMargin = 10; textView.setLayoutParams(params); textView.requestLayout(); textView.setText(user.toString()); wrContext.get().bookContainer.addView(textView); } } } } }
看一下布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <EditText android:id="@+id/user_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:layout_marginTop="50dp" android:hint="@string/user_name" /> <EditText android:id="@+id/user_age" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:layout_marginTop="20dp" android:hint="@string/user_age" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="15dp" android:onClick="sendAddUserMsg" android:padding="10dp" android:text="@string/btn_1" /> <LinearLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="15dp" android:onClick="sendGetUserListMsg" android:padding="10dp" android:text="@string/btn_2" /> </LinearLayout>
3. 运行效果:
启动服务端,然后运行客户端查看结果:
三、总结
Messenger 使用起来确实比 AIDL 方便的多,不需要创建 AIDL 文件。但是它是对 AIDL 的封装,是一个轻量型的 IPC 方案,所以 AIDL 需要注意的它也需要注意。我们后续会对比每种IPC方案,来让我们选择合适的方案。AIDL 的具体使用,具体可以查看这篇:IPC之 AIDL 的使用 。