IPC简介
Android IPC机制是 Interprocess-communication的简称,android系统默认情况下是一个程序默认一个进程实例,不同进程间的数据都是互相独立,但是google提供了可以对不同进程间通信的机制,这就是IPC机制。android平台提供了 AIDL和Messenger来针对不同进程间通信的方式。
使用场景不同的进程间的通信
比如一个应用可能一些特殊原因,一个进程的内存不够用这个时候可能就会多开一个进程来获取系统更大的内存,四大组件都支持独立进程执行,通过在清单文件中配置 process来实现。这个时候不同进程要通信就要通过aidl来实现了。 或者是不同应用间的通信也需要用到IPC机制。
AIDL实现方式
aidl是(Android Interface Definition Language)的简称是一种接口描述语言,用来定义进程间通信的接口。使用aidl首先需要知道那个程序需要暴露什么接口和数据给别人使用,可以把定义的一端叫做服务端就像web服务器端一样提供给别人访问。有了服务端后,其他应用就可以根据提供的接口来访问服务器了。
服务端的AIDL定义
android studio ide提供了生成aidl的快捷方式,这里创建了IMyService.aidl和Student.aidl。IMyService就是提供接口给其他应用访问,Student是映射Student实体类。
IMyService.aidl
/
package com.jw.code;
// Declare any non-default types here with import statements
import com.jw.code.Student;
interface IMyService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
List<Student> getStudent();
void addStudent(in Student student);
}
aidl中支持的参数类型为:
1.基本类型(int,long,char,boolean等),String,CharSequence,List,Map,
2.另外,接口中的参数除了aidl支持的类型,其他类型必须标识其方向:到底是输入还是输出抑或两者兼之,用in,out或者inout来表示,上面的代码我们用in标记,因为它是输入型参数
Student.aidl
package com.jw.code;
// Declare any non-default types here with import statements
parcelable Student;
build 一下可以看到ide自动生成了对应的.java类
public interface IMyService extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jw.code.IMyService {
private static final java.lang.String DESCRIPTOR = "com.jw.code.IMyService";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.jw.code.IMyService interface,
* generating a proxy if needed.
*/
public static com.jw.code.IMyService asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.jw.code.IMyService))) {
return ((com.jw.code.IMyService) iin);
}
return new com.jw.code.IMyService.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getStudent: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jw.code.Student> _result = this.getStudent();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addStudent: {
data.enforceInterface(DESCRIPTOR);
com.jw.code.Student _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jw.code.Student.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addStudent(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.jw.code.IMyService {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public java.util.List<com.jw.code.Student> getStudent() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.jw.code.Student> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.jw.code.Student.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addStudent(com.jw.code.Student student) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((student != null)) {
_data.writeInt(1);
student.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public java.util.List<com.jw.code.Student> getStudent() throws android.os.RemoteException;
public void addStudent(com.jw.code.Student student) throws android.os.RemoteException;
}
可以看到自动实现了定义的接口
在java包下面需要创建一个实现Parcelable接口的实体类,IPC通信传递的class必须是可以序列化的。
public class Student implements Parcelable {
public static final int SEX_MALE = 1;
public static final int SEX_FEMALE = 2;
public int sno;
public String name;
public int sex;
public int age;
public Student() {
}
public static final Parcelable.Creator<Student> CREATOR = new
Parcelable.Creator<Student>() {
public Student createFromParcel(Parcel in) {
return new Student(in);
}
public Student[] newArray(int size) {
return new Student[size];
}
};
private Student(Parcel in) {
readFromParcel(in);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(sno);
dest.writeString(name);
dest.writeInt(sex);
dest.writeInt(age);
}
public void readFromParcel(Parcel in) {
sno = in.readInt();
name = in.readString();
sex = in.readInt();
age = in.readInt();
}
@Override
public String toString() {
return String.format(Locale.ENGLISH, "Student[ %d, %s, %d, %d ]", sno, name, sex, age);
}
}
创建服务端的Service类
public class MyServer extends Service {
private final static String TAG = "MyService";
private static final String PACKAGE_SAYHI = "com.example.test";
private NotificationManager mNotificationManager;
private boolean mCanRun = true;
private List<Student> mStudents = new ArrayList<Student>();
@Override
public void onCreate() {
Thread thr = new Thread(null, new ServiceWorker(), "BackgroundService");
thr.start();
synchronized (mStudents) {
for (int i = 1; i < 6; i++) {
Student student = new Student();
student.name = "student#" + i;
student.age = i * 5;
mStudents.add(student);
}
}
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, String.format("on bind,intent = %s", intent.toString()));
displayNotificationMessage("服务已启动");
return mBinder;
}
//这里实现了aidl中的抽象函数
private final IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public List<Student> getStudent() throws RemoteException {
synchronized (mStudents) {
return mStudents;
}
}
@Override
public void addStudent(Student student) throws RemoteException {
synchronized (mStudents) {
if (!mStudents.contains(student)) {
mStudents.add(student);
}
}
}
//在这里可以做权限认证,return false意味着客户端的调用就会失败,比如下面,只允许包名为com.example.test的客户端通过,
//其他apk将无法完成调用过程
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String packageName = null;
String[] packages = MyServer.this.getPackageManager().
getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0) {
packageName = packages[0];
}
Log.d(TAG, "onTransact: " + packageName);
if (!PACKAGE_SAYHI.equals(packageName)) {
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
mCanRun = false;
super.onDestroy();
}
private void displayNotificationMessage(String message) {
//Notification notification = new Notification(R.drawable.icon, message, System
// .currentTimeMillis());
//notification.flags = Notification.FLAG_AUTO_CANCEL;
//notification.defaults |= Notification.DEFAULT_ALL;
//PendingIntent contentIntent = PendingIntent
// .getActivity(this, 0, new Intent(this, MyActivity.class), 0);
//notification.setLatestEventInfo(this, "我的通知", message, contentIntent);
//mNotificationManager.notify(R.id.app_notification_id + 1, notification);
}
class ServiceWorker implements Runnable {
long counter = 0;
@Override
public void run() {
// do background processing here.....
while (mCanRun) {
Log.d("scott", "" + counter);
counter++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
onTransact在这里可以做权限认证,return false意味着客户端的调用就会失败,比如下面,只允许包名为com.example.test的客户端通过,其他apk将无法完成调用过程onBind提供定义的Stub,返回给远端应用使用就是靠这个bind来操作接口。
xml注册service
<service android:name=".MyServer">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.test.aidl"></action>
</intent-filter>
</service>
远端应用调用
如果其他应用想使用这套接口,就必须把定义好的aidl和实体类复制一份到自己项目中。
绑定服务setAction setPackage设置目标参数
@Override
public void onClick(View v) {
Intent intentService = new Intent();
intentService.setAction(ACTION_BIND_SERVICE);
intentService.setPackage("com.example.administrator.myapplication");
MainActivity.this.bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);
}
获取连接&调用接口,正确的话可以看到成功的调用了远程应用的接口。
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
mIMyService = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通过服务端onBind方法返回的binder对象得到IMyService的实例,得到实例就可以调用它的方法了
mIMyService = IMyService.Stub.asInterface(service);
try {
Student student = mIMyService.getStudent().get(0);
showDialog(student.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
public void showDialog(String message) {
new AlertDialog.Builder(MainActivity.this).setTitle("scott").setMessage(message)
.setPositiveButton("确定", null).show();
}
Messenager调用
Messenager是一个简化版的通信方式客户端不需要复制服务端的那一套aidl文件,但是它内部也是通过Hander+Message+aidl来实现通信的。
服务端返回Messenger
private Handler mHandler = new Handler() {
@Override
public void dispatchMessage(Message msg) {
String string = (String) msg.obj;
Log.i("jinweiaaa", "msg " + string);
}
};
把创建的Messenger返回给绑定者
@Override
public IBinder onBind(Intent intent) {
mMessenger = new Messenger(mHandler);
return mMessenger.getBinder();
}
客户端定义Messenager,根据远程Binder创建一个Messenger。
Messenger mService = null;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
}
和服务器进行通信,发送一个msg给服务端。
public void onClick(View v) {
Message obtain = Message.obtain(null, 1, 0, 0);
//obtain.obj = "IPC msg send succ";
try {
mService.send(obtain);
} catch (RemoteException e) {
e.printStackTrace();
}
}
Messenger与AIDL的比较
首先,在实现的难度上,肯定是Messenger要简单的多——至少不需要写AIDL文件了(虽然如果认真的究其本质,会发现它的底层实现还是AIDL)。另外,使用Messenger还有一个显著的好处是它会把所有的请求排入队列,因此你几乎可以不用担心多线程可能会带来的问题。
但是这样说来,难道AIDL进行IPC就一无是处了么?当然不是,如果是那样的话它早就被淘汰了。一方面是如果项目中有并发处理问题的需求,或者会有大量的并发请求,这个时候Messenger就不适用了——它的特性让它只能串行的解决请求。另外,我们在使用Messenger的时候只能通过Message来传递信息实现交互,但是在有些时候也许我们需要直接跨进程调用服务端的方法,这个时候又怎么办呢?只能使用AIDL。
所以,这两种IPC方式各有各的优点和缺点,具体使用哪种就看具体的需要了——当然,能使用简单的就尽量使用简单的吧。