Android IPC程式間通訊之AIDL和Messenger的使用
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方式各有各的優點和缺點,具體使用哪種就看具體的需要了——當然,能使用簡單的就儘量使用簡單的吧。
相關文章
- android IPC 通訊(上)-sharedUserId&&MessengerAndroidMessenger
- Android IPC 之AIDLAndroidAI
- Android程式間通訊,AIDL工作原理AndroidAI
- Android多程式之Messenger的使用AndroidMessenger
- Android 多程式之Messenger的使用AndroidMessenger
- IPC-程式間通訊
- 程式間通訊——XSI IPC之訊息佇列佇列
- 微服務的程式間通訊(IPC)微服務
- Android 程式之間通訊Android
- Aidl程式間通訊詳細介紹AI
- linux程式間通訊(IPC)小結Linux
- [Android]你不知道的Android程式化(5)--程式通訊Messenger框架AndroidMessenger框架
- [Android]你不知道的Android程式化(4)--程式通訊AIDL框架AndroidAI框架
- android AIDL程式間通訊(只介紹了簡單資料型別)AndroidAI資料型別
- linux環境程式設計(3): 使用POSIX IPC完成程式間通訊Linux程式設計
- IPC(InterProcess Communication):程序間通訊
- Flutter與android之間的通訊FlutterAndroid
- 餓了麼開源專案Hermes:新穎巧妙易用的Android程式間通訊IPC框架Android框架
- 藉助 AIDL 理解 Android Binder 機制——AIDL 的使用和原理分析AIAndroid
- Android程式間通訊詳解Android
- 4-AIII–Service跨程式通訊:aidlAI
- 程式間的八種通訊方式----共享記憶體是最快的 IPC 方式記憶體
- Android AIDL使用詳解AndroidAI
- Linux 下的程式間通訊:使用管道和訊息佇列Linux佇列
- linux 程式間通訊之管道Linux
- linux 程式間通訊之FIFOLinux
- Linux程式之間如何通訊?Linux
- 實現不同程式之間的通訊
- Android程式間通訊(複習筆記)Android筆記
- Java 和 Python 之間的 Socket 通訊JavaPython
- Tcp, WebSocket 和 http 之間的通訊TCPWebHTTP
- web前端技術分享Electron之IPC 通訊Web前端
- web前端培訓分享Electron之IPC 通訊Web前端
- Android開發之執行緒間通訊Android執行緒
- 一篇看懂Android與Flutter之間的通訊AndroidFlutter
- Electron實戰之程式間通訊
- python 之 併發程式設計(守護程式、互斥鎖、IPC通訊機制)Python程式設計
- 程式間的通訊
- Webview獨立程式並通過AIDL實現資料通訊WebViewAI