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

      项目源码

  • 相关阅读:
    由asp的一个错误,看语言的不同:asp & java
    chrome 显示图片遇到的问题,与 淘宝图片服务器 缓存 有关系
    asp 调用 vb(activex dll) ,参数传递(传引用)需要注意
    asp <----> vb(com,dll) <---> c 来回的调用,生命在于折腾
    HTTP/1.1 100 Continue
    java 线程栈 & java.lang.StackOverflowError
    硬盘分区的意义
    硬盘性能 & 文件碎片的一些思考
    Java并发——ReentrantLock类源码阅读
    使用Windows的NAT功能
  • 原文地址:https://www.cnblogs.com/aimqqroad-13/p/8964072.html
Copyright © 2020-2023  润新知