本文Demo见:https://github.com/w1374720640/IPCThroughServices
结合Demo阅读本文效果更好
利用Service进行进程间通信有两种方式,分别是Messenger和AIDL,Messenger底层是基于AIDL的封装,使用更加简洁高效,无需考虑并发问题,只能串行通信,有并发需求的只能用AIDL,不能用Messenger,一般情况下使用Messenger即可满足日常需求。Messenger和AIDL跨进程通信只能传递基本数据类型及实现Parcelable接口的类。
通常提供服务的进程称为服务端,获取服务的称为客户端,客户端通过bindService()
的方式绑定服务端,获取IBinder的实例。本文创建了两个项目,包名分别为com.example.service
和com.example.client
,对应服务端和客户端,下文不再重复说明。
Messenger
原理
查看Messenger源码可以发现,Messenger包含一个IMessenger的成员变量mTarget,通过mTarget可以向Handler传递Message消息。获取mTarget对象有两种方式,一种是在构造器中利用Handler获取mTarget的实例:
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
另一种方法是在构造器中通过IBinder对象获取mTarget的实例:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
注:可能有的同学发现Android sdk中没有IMessenger类,显示红色字体,那是因为IMessenger是一个AIDL文件,完整路径为
android.os.IMessenger.aidl
,只有一个抽象方法void send(in Message msg);
(暂时忽略Message前面的in),AIDL文件编译后会生成同名的Java文件,Android sdk中不包含AIDL文件及编译后生成的临时文件,所以系统找不到IMessenger类,下文会详细介绍相关知识。
服务端创建Messenger对象用第一个构造器,客户端绑定服务端时用第二个构造器获取Messenger对象,两个进程间传递的对象为Messenger对象中的mTarget变量,通过mTarget对象可以跨进程发送Message给Handler:
//Messenger.java
public void send(Message message) throws RemoteException {
mTarget.send(message);
}
使用方法
模拟客户端从服务端获取一个100以内的随机数。
在服务端新建一个RemoteMessengerService继承Service,并在AndroidManifest.xml中注册隐式启动方式
<service android:name=".RemoteMessengerService">
<intent-filter>
<action android:name="com.example.service.RemoteMessengerService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
未指定隐式启动的要加属性android:exported="true"
表示可以被其他进程启动,添加<intent-filter>
标签后默认为true。
在RemoteMessengerService中创建Handler对象mRemoteHandler,调用Messenger的第一个构造器,利用mRemoteHandler创建Messenger对象:
Messenger mRemoteMessenger = new Messenger(mRemoteHandler);
重写Service的onBind方法,返回Messenger中的mTarget变量:
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mRemoteMessenger.getBinder();
}
在客户端新建一个MessengerActivity,启动时绑定服务端:
Intent intent = new Intent();
intent.setAction("com.example.service.RemoteMessengerService");
//Android 5.0以上需要设置包名
intent.setPackage("com.example.service");
bindService(intent,mConnection,BIND_AUTO_CREATE);
绑定成功后根据服务端返回的IBinder对象调用Messenger的第二个构造器,创建Messenger对象,通过Messenger对象可以向服务端发送消息:
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteMessenger = new Messenger(service);
isConnect = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
isConnect = false;
}
};
向服务端发送消息:
Message message = Message.obtain();
message.what = 0;
try {
mRemoteMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
然后在服务端注册的Handler就可以接收到客户端发送的Message了。
一个简单的进程间通信就基本完成了,这时只能由客户端向服务端发送消息,服务端无法向客户端传递数据,要解决这个问题,需要在客户端新建一个Handler及Messenger,在发送消息时将Messenger对象传递给Message的replyTo变量,服务端的Handler收到客户端的Message后,获取replyTo变量,通过获取到的Messenger对象向客户端发送消息。
修改后的客户端代码:
//MessengerActivity.java
Handler mClientHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
Log.d(TAG,"Client receive message:" + msg.arg1);
break;
default:
break;
}
}
};
private Messenger mClientMessenger = new Messenger(mClientHandler);
private void sendMessage(){
Log.d(TAG,"Client sendMessage()");
if (!isConnect) return;
Message message = Message.obtain();
message.what = 0;
// 将客户端的Messenger对象传递到服务端,
// 不设置则只能单向通信,服务端无法向客户端传递信息
message.replyTo = mClientMessenger;
try {
// 向服务端发送消息
mRemoteMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
修改后的服务端代码:
//RemoteMessengerService.java
Handler mRemoteHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d(TAG,"Service receive client message,msg.what=" + msg.what);
switch (msg.what){
case 0:
Message message = Message.obtain();
message.what = 0;
Random random = new Random();//获取随机数
message.arg1 = random.nextInt(100);
// msg的replyTo变量是客户端生成的Messenger对象
// 如果为空则不能由服务端向客户端传递消息,只能单向通信
Messenger mClientMessenger = msg.replyTo;
if (mClientMessenger == null) return;
try {
// 向客户端回传信息
mClientMessenger.send(message);
Log.d(TAG,"Service reply client message,random Num is:" + message.arg1);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
};
先运行服务端,再运行客户端,客户端向服务端发送消息后Log如下(注意包名):
AIDL
在Android studio中AIDL文件位于app/src/main/aidl
目录下,客户端与服务端AIDL文件相同,包名为服务端包名,只能传递基本数据类型及实现Parcelable接口的类。
AIDL文件实际为模板文件,用于生成复杂但套路固定的Java文件,生成的Java文件位于app/build/generated/source/aidl/debug/<packagemane>
目录下,通过Java文件实现IPC通信,生成的Java文件单独使用具有同等效果,有兴趣的可以看看具体的实现,这里不过多讲解。
使用方式
模拟客户端通过用户ID查询用户信息、向服务端添加用户、服务端调用客户端方法实现双向通信。
新建Person类:先设置Person类的成员变量,然后实现Parcelable接口,在类名上alt + enter两次即可快捷生成Parcelable模板代码(Parcelable接口具体使用方式自行google),如下所示:
/*
若Person.java文件放在aidl目录下,需要在app/build.gradle的android标签中添加
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
*/
public class Person implements Parcelable{
private int id;
private String name;
private int age;
private String phone;
public Person(){
}
protected Person(Parcel in) {
id = in.readInt();
name = in.readString();
age = in.readInt();
phone = in.readString();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
dest.writeInt(age);
dest.writeString(phone);
}
/**
* 实现Parcelable接口时不会自动创建此方法,
* 但如果aidl文件中Person类添加了out或inout标签时必须手动实现此方法
*/
public void readFromParcel(Parcel dest) {
//注意,此处的读值顺序应当是和writeToParcel()方法中一致的
id = dest.readInt();
name = dest.readString();
age = dest.readInt();
phone = dest.readString();
}
@Override
public String toString() {
return "\"id=" + id + ",name=" + name + ",age=" + age + ",phone=" + phone + "\"";
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getPhone() { return phone; }
public void setPhone(String phone) { this.phone = phone; }
}
需要注意的是,模板代码并没有生成readFromParcel()
方法,需要我们按照writeToParcel()
的写入顺序依次读取参数,若未实现readFromParcel()
方法,则aidl中只能用in
修饰Person类,不能用out
或inout
修饰,如上文中提到的IMessenger唯一抽象方法void send(in Message msg);
。那三个修饰符有什么区别呢?以IMessenger的send方法为例:
- 如果用
in
修饰,那msg对象为原对象的副本,msg值的变化不会影响原对象。 - 如果用
out
修饰,无论msg传入的值是什么,都会在方法内部创建一个新的对象,方法执行结束会将新对象写入原msg对象,也就是说,无论你输入什么都忽视,结束后再把你的值改掉(够霸道的)。 - 如果用
inout
修饰,则会复制输入对象的值,方法执行完后再写入原对象。 -
in
修饰的对象执行效率最高,也最常用,out
和inout
因为需要回写,效率较低,尽量少用。 - 传递基本数据类型、String、aidl文件不用也不能添加标签,默认为
in
。
创建AIDL文件:在服务端app/src/main
目录下新建aidl/com/example/service
文件夹,然后鼠标点击File>new>AIDL>AIDL File,输入文件名,创建RemoteInterface.aidl文件:
// RemoteAidlInterface.aidl
package com.example.service;
//即使在同一个包下,也需要手动导入类
import com.example.service.ClientCallback;
import com.example.service.Person;
//编译后生成的Java文件在app/build/generated/source/aidl/dubug/<packagename>目录下
//服务端创建,客户端调用
interface RemoteInterface {
// 根据用户ID获取用户信息
Person getPersonById(int id);
// 添加用户,此处用in修饰
void addPerson(in Person person);
// 向服务端注册监听
void registClientCallback(ClientCallback callback);
// 取消注册
void unRegistClientCallback(ClientCallback callback);
}
ClientCallback.aidl文件如下:
// ClientAidlCallback.aidl
package com.example.service;
//客户端向服务端注册,客户端创建,服务端调用
interface ClientCallback {
// 启动客户端
void start();
// 停止客户端
void stop();
}
如果想在进程间传递对象,除了需要实现Parcelable接口外,还需要创建一个aidl文件申明该类可用于进程间通信:
// Person.aidl
package com.example.service;
//注意!不是用interface开头,用parcelable开头,p小写,表示Person类可以进行进程间通信
//用interface开头会生成同名的Java文件,用parcelable开头不会,只有一个Person.java文件
parcelable Person;
创建完成,clean一下工程,如果编译报错说明aidl文件编写有问题,比如是否正确导包(即使包名相同也需要手动导包),parcelable是否是小写,传递序列化对象前必须加in|out|inout
标记,重命名文件时其他地方不会自动替换新文件名,总之,编写aidl文件时基本没有任何提示,编译不通过肯定是你aidl文件写的有问题。编译通过后检查一下在app/build/generated/source/aidl/dubug/<packagename>
目录下是否生成相应Java文件。
复制aidl目录下文件夹及所有文件到客户端相同目录下,编译。
aidl目录如下图:
编写服务端及客户端代码
在服务端新建RemoteAidlService继承Service,在AndroidManifest.xml文件中注册:
<service android:name=".RemoteAidlService">
<intent-filter>
<action android:name="com.example.service.RemoteAidlService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
创建RemoteInterface.Stub对象,RemoteInterface.Stub类是RemoteInterface.aidl文件编译后生成的静态内部类,创建RemoteInterface.Stub对象需要实现RemoteInterface.aidl中定义的抽象方法,重写onBind()方法,返回创建的RemoteInterface.Stub对象:
public class RemoteAidlService extends Service {
private static final String TAG = "AidlTest";
private static final int START_ALL_CLIENT = 0;
private static final int STOP_ALL_CLIENT = 1;
// 模拟服务端存储客户端传递的数据
private List<Person> mPersonList = new ArrayList<>();
// 一个服务端可以对应多个客户端,即包含多个ClientCallback对象,
// 使用RemoteCallbackList可以在客户端意外断开连接时移除ClientCallback,防止DeadObjectException
private RemoteCallbackList<ClientCallback> mCallbackList = new RemoteCallbackList<>();
// 通过修改值确定是否在regist后start客户端,默认不启动
private boolean isAutoStartAfterRegist = false;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mRemoteInterface;
}
@Override
public void onDestroy() {
super.onDestroy();
// 服务结束是注意移除所有数据
mCallbackList.kill();
}
/**
* RemoteInterface.Stub为Android根据aidl文件生成的实现类,
* 实现了RemoteInterface接口,间接实现了IBinder接口,
* 客户端绑定时将mRemoteInterface对象返回给客户端,
* 在服务端定义,在客户端调用
*/
private RemoteInterface.Stub mRemoteInterface = new RemoteInterface.Stub() {
@Override
public Person getPersonById(int id) throws RemoteException {
// 返回固定值
Person person = new Person();
person.setId(id);
person.setName("小红");
person.setAge(18);
person.setPhone("120");
Log.d(TAG, "Service getPersonById()");
return person;
}
@Override
public void addPerson(Person person) throws RemoteException {
mPersonList.add(person);
Log.d(TAG, "Service addPerson(),person="
+ (person == null ? null : person.toString()));
}
@Override
public void registClientCallback(ClientCallback callback) throws RemoteException {
// 向服务端注册回调
mCallbackList.register(callback);
Log.d(TAG, "Service registClientCallback()");
if (isAutoStartAfterRegist) {
mHandler.sendEmptyMessageDelayed(START_ALL_CLIENT, 3 * 1000);
}
}
@Override
public void unRegistClientCallback(ClientCallback callback) throws RemoteException {
// 服务端取消注册回调
mCallbackList.unregister(callback);
Log.d(TAG, "Service unRegistClientCallback()");
}
};
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case START_ALL_CLIENT:
startAllClient();
mHandler.sendEmptyMessageDelayed(STOP_ALL_CLIENT, 3 * 1000);
break;
case STOP_ALL_CLIENT:
stopAllClient();
break;
}
}
};
/**
* 调用所有客户端的start()方法
*/
public void startAllClient() {
Log.d(TAG, "Service startAllClient()");
// 从列表中取数据时先调用beginBroadcast()方法获取总数,循环取出数据后finishBroadcast()
int size = mCallbackList.beginBroadcast();
for (int i = 0;i < size;i++){
try {
mCallbackList.getBroadcastItem(i).start();
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbackList.finishBroadcast();
}
/**
* 调用所有客户端的stop()方法
*/
public void stopAllClient() {
Log.d(TAG, "Service stopAllClient()");
int size = mCallbackList.beginBroadcast();
for (int i = 0;i < size;i++){
try {
mCallbackList.getBroadcastItem(i).stop();
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbackList.finishBroadcast();
}
}
客户端创建AidlActivity,里边有四个按钮,分别对应RemoteInterface.aidl定义的四个方法:getPersonById()、addPerson()、registClientCallback()、unRegistClientCallback(),点击按钮调用相应方法。同时实现了ClientCallback.Stub类,向服务端注册后服务端可以调用客户端相应方法。
public class AidlActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "AidlTest";
private boolean isConnect;
// 服务端的RemoteInterface对象,绑定服务时创建
private RemoteInterface mRemoteInterface = null;
// 客户端的ClientCallback对象
// 在服务端注册后服务端可以调用客户端方法
private ClientCallback.Stub mClientCallback = new ClientCallback.Stub() {
@Override
public void start() throws RemoteException {
Log.d(TAG, "Client start");
}
@Override
public void stop() throws RemoteException {
Log.d(TAG, "Client stop");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
findViewById(R.id.bt_get).setOnClickListener(this);
findViewById(R.id.bt_add).setOnClickListener(this);
findViewById(R.id.bt_regist).setOnClickListener(this);
findViewById(R.id.bt_unregist).setOnClickListener(this);
connectService();
}
ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConnect = true;
// 绑定服务后从服务端获取RemoteInterface对象
mRemoteInterface = RemoteInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
isConnect = false;
// 与服务端意外断开时自动重连
connectService();
}
};
private void connectService() {
Intent intent = new Intent();
intent.setAction("com.example.service.RemoteAidlService");
// Android 5.0以上需要设置包名
intent.setPackage("com.example.service");
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
private void disConnectService() {
unbindService(mConnection);
isConnect = false;
}
@Override
public void onClick(View v) {
if (!isConnect) return;
switch (v.getId()) {
case R.id.bt_get:
Person person = getPerson(10);
Log.d(TAG, "Client getPerson return, person=" +
(person == null ? null : person.toString()));
break;
case R.id.bt_add:
Person person1 = new Person();
person1.setId(100);
person1.setName("小花");
person1.setAge(16);
person1.setPhone("110");
addPerson(person1);
break;
case R.id.bt_regist:
registCallback();
break;
case R.id.bt_unregist:
unRegistCallback();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
disConnectService();
}
/**
* 从服务端获取数据
*/
private Person getPerson(int id) {
Log.d(TAG,"Client getPerson()");
if (!isConnect) return null;
Person person = null;
try {
person = mRemoteInterface.getPersonById(id);
} catch (RemoteException e) {
e.printStackTrace();
}
return person;
}
/**
* 向服务端添加数据
*/
private void addPerson(Person person) {
Log.d(TAG,"Client addPerson()");
if (!isConnect) return;
try {
mRemoteInterface.addPerson(person);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 向服务端注册回调,注册后服务端才能调用客户端方法
*/
private void registCallback() {
Log.d(TAG,"Client registCallback()");
if (!isConnect) return;
try {
mRemoteInterface.registClientCallback(mClientCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 取消注册
*/
private void unRegistCallback() {
Log.d(TAG,"Client unRegistCallback()");
if (!isConnect) return;
try {
mRemoteInterface.unRegistClientCallback(mClientCallback);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
先运行服务端,再运行客户端,分别点击客户端的四个按钮,观察Log输出如下
将的RemoteAidlService的isAutoStartAfterRegist
属性改为true后点击注册按钮,Log如下,注册后3秒自动调用客户端的start()方法,再3秒后调用客户端的stop()方法。
结语
Demo见顶部链接,文章参考: