一、概述

  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 的使用

  项目源码

最新文章

  1. 大数据之sqoopCDH 备份
  2. C#之XMAL与WPF
  3. TSQL的连乘
  4. git 创建branch分支
  5. PHP empty函数判断0返回真还是假?
  6. 类 .xml
  7. Python之格式化输出讲解
  8. MYSQL 5.7 新增150多个新功能
  9. 微信移动客户端内部浏览器分享到朋友圈,QQ空间代码
  10. C#设置textboox只能输入数字`
  11. popen()函数详解
  12. HBase海量数据存储
  13. JSP :使用&lt;%@include%&gt;报Duplicate local variable path 错误
  14. emq知识点
  15. Java Magic. Part 4: sun.misc.Unsafe
  16. java 子类不能继承父类的static方法
  17. 【SSH】——Struts2中的动态方法调用(二)
  18. [转]MYSQL5.7版本sql_mode=only_full_group_by问题
  19. 委托delegate与Dictionary实现action选择器
  20. Django admin有用的自定义功能

热门文章

  1. make是如何工作的
  2. Oracle之数据库的增删改查和格式的修改
  3. javax.servlet.ServletException: Servlet.init() for servlet springmvc threw exception
  4. JS使用onscroll、scrollTop实现图片懒加载
  5. oracle查询所有初始化参数(含隐含参数)
  6. Getting started with Kaggle -- Kaggle Competitions
  7. openvpn server setup
  8. metasploit(MSF)对windows的ms17-010漏洞利用
  9. http请求记录
  10. 前端基础小标签5 H5的一些新标签属性