Android 进程通信 (IPC) 之 Messenger 的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/afei__/article/details/83386759

一、简介

Messenger 是 Android 提供的一种轻量级的进程间通信方案。它的本质是对 AIDL 的封装,所以使用起来也更加容易了。
Messenger 是指 “信使” 的意思,即它是一个进程与进程间的信使,就像是一个快递员在两个进程间传递消息(Message)。因此,它的使用也和 Message 息息相关。

二、服务端

我们创建一个 Service 作为服务端,它是一个独立的进程,并负责接收和发送消息给客户端。

package com.afei.myapplication;

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 android.support.annotation.Nullable;
import android.util.Log;

public class RemoteService extends Service {

    public static final String TAG = "RemoteService";

    private Messenger mMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x001:
                    // get msg from client
                    Log.d(TAG, "收到来自客户端的消息:" + ((Bundle) msg.obj).getString("str"));
                    Messenger messenger = msg.replyTo;

                    // reply client
                    Message replyMsg = Message.obtain();
                    replyMsg.what = 0x001;
                    Bundle bundle = new Bundle();
                    bundle.putString("str", "I am service!");
                    replyMsg.obj = bundle;
                    try {
                        messenger.send(replyMsg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    });

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

这里比较重要的一步就是在 onBind 方法中,我们返回 mMessenger 对象的 Binder,然后系统会帮我们完成进程与进程间的绑定工作。
由于 Service 将是一个独立的进程,因此我们还需要在 AndroidManifest.xml 文件中添加:

        <service
            android:name=".RemoteService"
            android:process=":remote" />

通过设置 android:process=":remote" 可以将这个组件声明为单独的一个进程。

三、客户端

我们需要在客户端完成服务的绑定,以及发送和接收来自服务端的消息。

package com.afei.myapplication;

import android.app.Activity;
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.util.Log;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    private Messenger mReplyMessenger  = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x001:
                    // get msg from service
                    Log.d(TAG, "收到来自服务端的消息:" + ((Bundle) msg.obj).getString("str"));
                    break;
                default:
                    break;
            }
        }
    });

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // send msg to service
            Messenger messenger = new Messenger(service);
            Message msg = Message.obtain();
            msg.what = 0x001;
            Bundle bundle = new Bundle();
            bundle.putString("str", "I am client!");
            msg.obj = bundle;
            // 在这里声明使用哪个 Messenger 来接收服务端返回的信息
            msg.replyTo = mReplyMessenger;
            try {
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, RemoteService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); // 绑定服务
    }

    @Override
    protected void onDestroy() {
        unbindService(mServiceConnection); // 解除绑定
        super.onDestroy();
    }

}

执行结果为:

10-25 13:18:06.058 14433-14433/com.afei.myapplication:remote D/RemoteService: 收到来自客户端的消息:I am client!
10-25 13:18:06.060 14403-14403/com.afei.myapplication D/MainActivity: 收到来自服务端的消息:I am service!

可以看到,消息都有正常的接收到了,并且它们的进程 id 也不一致。

四、注意事项

1. Message 的 obj 必须是可序列化的

即我们传递的对象必须是实现了 Parcelable 接口的,如若不然,则会报出如下错误:

    --------- beginning of crash
10-25 13:23:06.615 14612-14612/com.afei.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.afei.myapplication, PID: 14612
    java.lang.RuntimeException: Can't marshal non-Parcelable objects across processes.
        at android.os.Message.writeToParcel(Message.java:587)
        at android.os.IMessenger$Stub$Proxy.send(IMessenger.java:84)
        at android.os.Messenger.send(Messenger.java:57)
        at com.afei.myapplication.MainActivity$2.onServiceConnected(MainActivity.java:47)
        at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1652)
        at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1681)
        at android.os.Handler.handleCallback(Handler.java:790)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6494)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

2. 启动外部服务

本例中的 Service 和我们的客户端其实还是在一个工程下的,之前也有人问到,假如两者在不同的工程里,那么怎么办呢?

其实很简单的,Intent 本身就是可以启动外部应用或服务的。修改我们绑定服务的 Intent 即可,我们使用一个 “包名” + “服务名” 的形式显示声明即可。

猜你喜欢

转载自blog.csdn.net/afei__/article/details/83386759