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

  项目源码

 

posted @ 2018-04-27 19:23  Spiderman.L  阅读(524)  评论(0编辑  收藏  举报