Android知识点总结(四)进程间通信

进程间通讯方式

Android进程通讯方式有很多,如Socket、ContentProvider、共享文件(这种方式的缺点是不支持并发写,同时需要手动操作IO)、AIDL、Messenger(底层实现也是AIDL)等。

Messenger实现

Messenger是一种轻量级的IPC方案,可以在不同进程中传递Message对象。不适用与并发服务场景。下面是其常用方法源码

    /**
     * Create a new Messenger pointing to the given Handler.  Any Message
     * objects sent through this Messenger will appear in the Handler as if
     * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
     * been called directly.
     */
    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
    
     /**
     * Create a Messenger from a raw IBinder, which had previously been
     * retrieved with {@link #getBinder}.
     */
    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    } 
    
    /**
     * Send a Message to this Messenger's Handler.
     */
    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }
    
    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

它的实现要比直接使用AIDL简单的多:

1. 服务端进程
首先,在服务端创建一个Service,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回Messenger底层的Binder即可。
2. 客户端进程
客户端进程,首先绑定服务端的Service,然后利用服务端返回的IBinder的代理对象创建一个Messenger,通过这个Messenger对象就可以向服务端发送消息了。消息类型为Message对象,跨进程发送要使用Bundle传送,否则可能会崩溃:Can't marshal non-Parcelable objects across processes.
服务端代码如下:

public class MyService extends Service {
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg != null) {
                switch (msg.what) {
                    case MSG:
                        Log.d("from customer: ", msg.getData().getString("msg"));
                        break;
                    default:
                        break;
                }
            }
            return false;
        }
    });
    private Messenger mMessenger = new Messenger(handler);

    public MyService() {
    }

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

客户端代码如下

public class MainActivity extends AppCompatActivity {
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger  mMessenger = new Messenger(service);
            Message obtain = Message.obtain(null, MyUtils.MSG);
            Bundle bundle = new Bundle();
            bundle.putString("msg", "hello, i am customer");
            obtain.setData(bundle);
            try {
                mMessenger.send(obtain);
            } 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);
        findViewById(R.id.tv1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               Intent intent = new Intent(this, MyService.class);
        	   bindService(intent, serviceConnection,Context.BIND_AUTO_CREATE);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);
    }
}

另外,注意给远程Service设置进程:

<service
       android:name=".service.MyService"
       android:enabled="true"
       android:exported="true"
       android:process=":scy"
       />

最后查看服务端日志的时候,注意在AS的Logcat上切换到服务进程查看:

04-01 16:08:08.244 16677-16677/com.scy.component.arithmetictest:scy D/
from customer:: hello, i am customer

到这里实现了单向的Client -> Service的跨进程通信,服务端要想实现回复,只需稍作修改就行:
首先,在客户端要再创建一个Messenger和Handler对象,用于接收消息,在发送消息前将这个Messenger对象赋值给Message的replyTo属性即可。
Android知识点总结(四)进程间通信
然后,在服务端的Handler里取出消息后,紧接着用Message的replyTo属性获取到客户端的Messenger对象并用其发送消息即可。
Android知识点总结(四)进程间通信
如果要查看日志,记得切换进程。
Android知识点总结(四)进程间通信

Message有多种创建方式,建议使用Message.obain系列进行创建,相对于直接new Message()的创建方式可以有效避免对象的重复创建
Message的obj字段在Android2.2版本以后支持实现Parcelable接口的对象进行跨进程传递,但是不支持非系统内置即自定义的Parcelable对象。所以跨进程通信尽量使用Bundle方式传递
Messenger是以串行方式处理消息的,即便大量的消息同时发送到服务端,服务端仍然只能一个个处理,所以Messenger不适用于并发请求,对于高并发场景可以使用AIDL来实现跨进程通信
Messenger的作用主要是为了传递消息,如果需要跨进程调用服务端的方法,Messenger就无法做到了,AIDL可以做到