这篇我们讲下如何使用service实现进程间通信 。通常讲到这里大家都会想到aidl,其实不然,aidl只是其中的一种方式,并且使用起来,我个人感觉太不灵活了,相对Messenger更加灵活。
整个过程我们还是借助于 Activity 跨进程通信 中使用的项目
1.AIDL 跨进程通讯
aidl 是个简写,全拼android interface difine language(android自定义接口语言)
服务端:
1.1先从新建开始,选中main 右键选择aidl file 新建,会在main 下生成aidl文件夹,我的新建默认名字叫
IMyAidlInterface,也就是IMyAidlInterface.aidl文件。这个就是我们要定义的接口类,默认会有
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);接口,也就是默认支持六种基本数据类型
先写两个基本数据类型,再自定义一个自定义接口
package com.example.intentmode;
import com.example.intentmode.Person;
// Declare any non-default types here with import statements
interface IRemoteService {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getPid();
String getName(int id);
Person getPerson(int id);
}
先别着急,
1.2 Person getPerson(int id);是会报错的。现在定义Person类,在和IRemoteService类平级目录下新建Person类,并且必须实现Parcelable接口,为啥Serializable 接口不行呢,在android 中都使用Parcelable接口因为Serializable的序列化效率相对较低。
Person 如下:
public class Person implements Parcelable {
private String name;
private int age;
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;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.age);
}
public Person() {
}
protected Person(Parcel in) {
this.name = in.readString();
this.age = in.readInt();
}
public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel source) {
return new Person(source);
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}
1.3 新建Person.aidl类 :
// Person.aidl
package com.example.intentmode;
// Declare any non-default types here with import statements
parcelable Person;
1.4 新建服务类 ServiceAIDL ,继承自Service
public class ServiceAIDL extends Service {
private String [] names={"吕布","关羽","赵子龙","张飞"};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IMyAidlInterface.Stub mBinder=new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
System.out.println("Thread: " + Thread.currentThread().getName());
System.out.println("basicTypes aDouble: " + aDouble +" anInt: " + anInt+" aBoolean " + aBoolean+" aString " + aString);
}
@Override
public int getPid() throws RemoteException {
System.out.println("Thread: " + Thread.currentThread().getName());
System.out.println("RemoteService getPid ");
return android.os.Process.myPid();
}
@Override
public String getName(int id) throws RemoteException {
return names[id];
}
@Override
public Person getPerson(int id) throws RemoteException {
Person person=new Person();
try {
person.setAge(20);
person.setName(names[id]);
} catch (Exception e) {
e.printStackTrace();
}
return person;
}
};
}
在这里我们定义了接口的返回数据,用于客户端调用时返回想要的数据,其实叫他服务端另一端客户端是有问题的,他们是可以相互调用的,我们暂且这么叫吧。这里有的朋友可能会报attempting to use incompatible return type ,这是因为使用自定义类型要在接口类中导入包名,
package com.example.csdnactivity;
// Declare any non-default types here with import statements
//这个要手动导入下
import com.example.csdnactivity.Person;
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getPid();
String getName(int id);
Person getPerson(int id);
}
1.5 注册service 并且设置标签 action
<service android:name=".ServiceAIDL">
<intent-filter>
<action android:name="com.example.intentmode"/>
</intent-filter>
</service>
需要注意的是,aidl写完后需要重新构建下,会在build --> generated--> source --> aidl --> debug--> 接口类
如果没有,有可能是代码有错误了。
服务端结束,下面看客户端
客户端:
1.1 将服务端的aidl文件夹粘贴到客户端的main文件下。
1.2 绑定客户端service
//服务端设置的action
intent.setAction("com.example.intentmode");
//服务端的包名
intent.setPackage("com.example.csdnactivity");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
package com.example.csdnactivity1;
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.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.example.csdnactivity.IMyAidlInterface;
import com.example.csdnactivity.Person;
/**
* Created by LCT
* Time:2018/12/24 13:49.
* Annotation:
*/
public class AIDLActivity extends Activity implements View.OnClickListener {
private static final String TAG = "AIDLActivity";
/**
* 获取远aidl程数据
*/
private Button mGetData;
IMyAidlInterface iMyAidlInterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.aidl_activity);
initView();
bindService();
}
private void bindService() {
Intent intent = new Intent();
//服务端设置的action
intent.setAction("com.example.intentmode");
//服务端的包名
intent.setPackage("com.example.csdnactivity");
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* IBinder 转为IRemoteService接口
*/
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void initView() {
mGetData = (Button) findViewById(R.id.get_Data);
mGetData.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
default:
break;
case R.id.get_Data:
/**
* 通过iMyAidlInterface 获取数据
*/
try {
if (iMyAidlInterface != null) {
String name = iMyAidlInterface.getName(0);
iMyAidlInterface.basicTypes(12, 1223, true, 12.2f, 12.3, "有梦就要去追,加油!");
Toast.makeText(this, name, Toast.LENGTH_SHORT).show();
Log.d(TAG, "onClick: name:"+ name);
Person person = iMyAidlInterface.getPerson(0);
String str=person.getName() + "_" +person.getAge();
Log.d(TAG, "onClick: person:"+ str);
}
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
客户端代码较少,现在可以运行服务端,如果你使用的是studio 使用了默人新建的build.gradle 文件,那么现在项目可能运行不起来,会提示你:错误:找不到符号符号:类 Person 我用的3.2的编辑器,即使这样还是不太智能需要手动写点东西
在build.gradle 添加如下代码
sourceSets {
main {
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/aidl']
resources.srcDirs = ['src/main/java', 'src/main/aidl']
aidl.srcDirs = ['src/main/aidl']
res.srcDirs = ['src/main/res']
assets.srcDirs = ['src/main/assets']
}
}
这样问题就解决了。
现在可以运行服务端,然后启动客户端,获取数据了。
1.Messenger跨进程通讯
原理与实现
这个相对aidl相对比较简单,并不是代码实现少,而是比较好理解,首先在服务端会有一个Messenger 它绑定了一个Hander ,客户端同样有一个Messenger 同样绑定了一个Hander ,然后客户端绑定服务通过service onBind返回服务端的Messenger对象,然后将客户端的Messenger Message.replyTo=cMessenger
到服务端的messenger中,然后发送消息,服务端在Hander收到消息,得到客户端的Messenger ,这样就各自持有对方messenger对象,互相发送消息了。接着我们看看具体实现。
服务端
1.1 新建Messenger对象
Messenger有两个构造函数分别是
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
handler用于绑定Messenger,接收消息。
IBinder用于将服务端的IBinder构造为Messenger对象
@Override
public void onCreate() {
super.onCreate();
sMessenger=new Messenger(handler);
}
1.2获取Messenger 的IBinder通过onBind返回
public IBinder onBind(Intent intent) {
return sMessenger.getBinder();
}
1.3Handler 接收数据处理,将收到的客户端Messenger保存,并处理相关逻辑
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg!=null) {
Log.d(TAG, "handleMessage: 收到客户端发来的消息");
/**
* 保存客户端Messenger,用于向客户端发送消息
*/
if (msg.replyTo!=null) {
cMessenger=msg.replyTo;
}
Bundle bundle=msg.getData();
String ms;
if (bundle !=null) {
ms=bundle.getString("serviceData");
}else {
Log.d(TAG, "handleMessage: null bundle");
return;
}
if (ms==null) {
Toast.makeText(ServiceMessenger.this,"客户端收到空消息" ,Toast.LENGTH_LONG).show();
return;
}
/**
* 如果收到消息1 就往客户端发送一条消息
*/
if (ms.equals("1")) {
Message message=Message.obtain();
Bundle bundle1=new Bundle();
bundle1.putString("clientData","2");
message.setData(bundle1);
try {
cMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}else {
Toast.makeText(ServiceMessenger.this,"收到客户端发来的消息"+ms,Toast.LENGTH_LONG).show();
}
}
}
};
服务端的完整代码
public class ServiceMessenger extends Service {
private static final String TAG = "ServiceMessenger";
/**
* 服务端Messenger
*/
Messenger sMessenger;
/**
* 客户端 Messenger
*/
Messenger cMessenger=null;
@Override
public void onCreate() {
super.onCreate();
sMessenger=new Messenger(handler);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return sMessenger.getBinder();
}
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg!=null) {
Log.d(TAG, "handleMessage: 收到客户端发来的消息");
/**
* 保存客户端Messenger,用于向客户端发送消息
*/
if (msg.replyTo!=null) {
cMessenger=msg.replyTo;
}
Bundle bundle=msg.getData();
String ms;
if (bundle !=null) {
ms=bundle.getString("serviceData");
}else {
Log.d(TAG, "handleMessage: null bundle");
return;
}
if (ms==null) {
Toast.makeText(ServiceMessenger.this,"客户端收到空消息" ,Toast.LENGTH_LONG).show();
return;
}
/**
* 如果收到消息1 就往客户端发送一条消息
*/
if (ms.equals("1")) {
Message message=Message.obtain();
Bundle bundle1=new Bundle();
bundle1.putString("clientData","2");
message.setData(bundle1);
try {
cMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}else {
Toast.makeText(ServiceMessenger.this,"收到客户端发来的消息"+ms,Toast.LENGTH_LONG).show();
}
}
}
};
}
1.4.注册service 并设置action,action用来客户端绑定服务时使用
<service android:name=".ServiceMessenger">
<intent-filter>
<action android:name="com.android.serviceMessenger.action" />
</intent-filter>
</service>
服务端结束。
客户端
1.1 创建客户端Messenger ,绑定handler,用于接收消息
cMessenger = new Messenger(handler);
1.2 绑定服务并监听绑定状态,获取服务端的Messenger对象。
private void bindMessengerService() {
Intent intent = new Intent();
//服务端设置的action
intent.setAction("com.android.serviceMessenger.action");
//服务端的包名
intent.setPackage("com.example.csdnactivity");
bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);
}
获取服务端Messenger,并将客户端的Messenger发送到服务端
private ServiceConnection connMessenger = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 得到服务端的messenger
*/
sMessenger = new Messenger(service);
if (sMessenger == null) {
Log.d(TAG, "onServiceConnected: messenger is null");
return;
}
/**
* 发送客户端的messenger到服务端
*/
Message message = Message.obtain();
message.replyTo = cMessenger;
try {
sMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
1.3 发送消息到服务端
String str = mSendDataToServiceEdit.getText().toString();
if (str != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("serviceData",str);
message.setData(bundle);
try {
sMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
完整代码类
public class MessengerActivity extends Activity implements View.OnClickListener {
private static final String TAG = "MessengerActivity";
/**
* 输入发送的信息 通过Messenger 发送
*/
private EditText mSendDataToService;
/**
* SENd
*/
private Button mSendData;
/**
* 服务端 Messenger
*/
Messenger sMessenger;
/**
* 客户端 Messenger
*/
Messenger cMessenger;
/**
* messenger
*/
private Button mMessengerButton;
/**
* 输入发送的信息 通过Messenger 发送
*/
private EditText mSendDataToServiceEdit;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messenger_activity);
initView();
initMessenger();
}
private void initMessenger() {
cMessenger = new Messenger(handler);
bindMessengerService();
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
if (bundle != null) {
String ms = bundle.getString("clientData");
if (ms.equals("2")) {
Toast.makeText(MessengerActivity.this, "收到服务端发来的数据了" + ms + "", Toast.LENGTH_LONG).show();
}
}
}
;
};
private void bindMessengerService() {
Intent intent = new Intent();
//服务端设置的action
intent.setAction("com.android.serviceMessenger.action");
//服务端的包名
intent.setPackage("com.example.csdnactivity");
bindService(intent, connMessenger, Context.BIND_AUTO_CREATE);
}
private ServiceConnection connMessenger = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
/**
* 得到服务端的messenger
*/
sMessenger = new Messenger(service);
if (sMessenger == null) {
Log.d(TAG, "onServiceConnected: messenger is null");
return;
}
/**
* 发送客户端的messenger到服务端
*/
Message message = Message.obtain();
message.replyTo = cMessenger;
try {
sMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void initView() {
mSendDataToServiceEdit = (EditText) findViewById(R.id.send_data_to_service_edit);
mSendDataToServiceEdit.setOnClickListener(this);
mSendData = (Button) findViewById(R.id.send_data);
mSendData.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
default:
break;
case R.id.send_data:
String str = mSendDataToServiceEdit.getText().toString();
if (str != null) {
Message message = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("serviceData",str);
message.setData(bundle);
try {
sMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
}
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/send_data_to_service_edit"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_toLeftOf="@+id/send_data"
android:hint="输入发送的信息 通过Messenger 发送"
android:textSize="14sp" />
<Button
android:id="@+id/send_data"
android:layout_width="100dp"
android:layout_height="50dp"
android:layout_alignParentRight="true"
android:text="SENd" />
</RelativeLayout>
</LinearLayout>
客户端完毕。这里注意一点,在调试时记得将两端都启动起来,客户端可以向服务端通过输入框发送消息,服务端收到后会toast出来,如果发送的数据是1,那么服务端会给客户端发送一条消息内容为 2,客户端会toast出来,整个service 跨进程通信就这么多了。
service 跨进程通信源码