版权声明:本文为博主原创文章,未经博主允许不得转载。 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
即可,我们使用一个 “包名” + “服务名” 的形式显示声明即可。