Android Messenger 进程间通信

前面我们介绍了 进程通信 AIDL 的学习和使用,这篇文章来介绍下 Android 中另一种 IPC 方式:Messenger。

Messenger“信使”,顾名思义,通过它可以在不同的进程中传递Message对象,在Message中放入我们要传递的内容,就可以实现进程间通信了。Messenger是一种轻量级的IPC 方案。

Messenger 有两个构造函数:

  • 以 Handler 为参数
  • 以 Binder 为参数
private final IMessenger mTarget;
public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);    //和前面的 AIDL 很相似吧
}

不管是 IMessenger 还是 Stub.asInterface,这种使用方法都表明底层就是 AIDL
实现 Messenger 有如下步骤:

客户端进程:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private TextView receiveText;
    private EditText input;
    /**
     * 客户端的 Messenger
     */
    private Messenger mClientMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg!=null && msg.arg1 == Constants.MSG_FROM_SERVER){
                if (msg.getData() == null){
                    return;
                }
                String content = (String) msg.getData().get(Constants.MSG_CONTENT);
                receiveText.setText(content);
            }
        }
    });

    /** 服务端的 Messenger */
    private Messenger mServerClient;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mServerClient = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mServerClient = null;
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        bindMessengerService();
    }

    private void bindMessengerService() {
        /** 如下方式调用另外一个进程的 service ,但是这个service必须在当前工程清单文件中声明 */
        Intent intent = new Intent(this,MessengerService.class);
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    private void initView(){
        input = (EditText) findViewById(R.id.inputText);
        Button sendBtn = (Button) findViewById(R.id.sendBtn);
        receiveText = (TextView) findViewById(R.id.receiveText);
        sendBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.sendBtn){
            String msgContent = input.getText().toString();
            msgContent = TextUtils.isEmpty(msgContent) ? "默认消息" : msgContent;

            Message message = Message.obtain();
            message.arg1 = Constants.MSG_FROM_CLIENT;
            Bundle bundle = new Bundle();
            bundle.putString(Constants.MSG_CONTENT, msgContent);
            message.setData(bundle);
            message.replyTo = mClientMessenger;     //指定回信人是客户端定义的

            try {
                mServerClient.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 服务端的 Client,通过客户端的 ServiceConnection 绑定成功之后,通过new Messenger(service);传入service ,拿到客户端服务的对象 mServerClient

  • initView 中初始化界面,重要的就算点击按钮之后,通过服务端的 Messenger 发送消息这块了。

 Message message = Message.obtain();
 message.arg1 = Constants.MSG_FROM_CLIENT;  // 设置标记
 Bundle bundle = new Bundle(); 
 bundle.putString(Constants.MSG_CONTENT, msgContent);  // 传递的消息内容
 message.setData(bundle);
 message.replyTo = mClientMessenger;     //指定回信人是客户端定义的

最后通过mServerClient.send(message);将消息发送到服务端。

  • bindMessengerService()中请在工程中配置一下对应的 service

让这个 service运行在单独的进程中 。

服务端进程:

public class MessengerService extends Service {

    private Messenger mMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(final Message msg) {
            if (msg != null && msg.arg1 == Constants.MSG_FROM_CLIENT) {
                if (msg.getData() == null) {
                    return;
                }
                String content = (String) msg.getData().get(Constants.MSG_CONTENT);  //接收客户端的消息

                //回复消息给客户端
                Message replyMsg = Message.obtain();
                replyMsg.arg1 = Constants.MSG_FROM_SERVER;
                Bundle bundle = new Bundle();
                bundle.putString(Constants.MSG_CONTENT, "收到啦,你不就是说:"+content);
                replyMsg.setData(bundle);
                Messenger serverClient = msg.replyTo;
                try {
                    serverClient.send(replyMsg);     //回信
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

可以看到Handler来处理客户端发送的消息,从消息中取出客户端的文本信息,而mMessenger 是一个 Messenger 的对象,它和handler关联起来,在onBind 方法中返回IBinder 对象,可以看到这里 是服务端来处理客户端的消息。代码比较简单易懂。

最后实现效果,app启动后 有两个进程,一个是 主工程4631 ,一个是 服务端4669
进程描述
最后实现效果
Android Messenger 进程间通信_第1张图片

当然我们可以把客户端和服务端和上次讲的 进程通信 AIDL 的学习和使用 一样,搞成两个app,这样的话看起来更加真实。
在客户端这里绑定的时候要使用

 private void bindMessengerService() {
        Intent intent = new Intent();
        //5.0 以后要求显式调用 Service,所以我们无法通过 action 或者 filter 的形式调用 Service
        intent.setComponent(new ComponentName("com.zx.serviceclient","com.zx.serviceclient.MessengerService"));
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

把对应 com.zx.serviceclient下的服务进行绑定,指明绑定的具体服务所在包名和服务类名,同时需要在服务端的Module 中启动对应的service,并在清单文件中声明。这样就和上一篇文章中讲到的效果一样了,有两个app,客户端 和 服务端。

文章参考 :https://blog.csdn.net/u011240877/article/details/72836178

你可能感兴趣的:(android)