IPC機制(一)了解AIDL
AIDL了解
AIDL這門語言的目的就是為了實作行程間通信,在Android系統中,每個行程都運行在一塊獨立的記憶體中,在其中完成自己的各項活動,與其他行程都分隔開來,可是有時候我們又有應用間進行互動的需求,比較傳遞資料或者任務委托等,AIDL就是為了滿足這種需求而誕生的,通過AIDL,可以在一個行程中獲取另一個行程的資料和呼叫其暴露出來的方法,從而滿足行程間通信的需求 ,
aidl主要分為服務端與客戶端兩方面:
服務端
服務端首先創建一個Service監聽客戶端的連接請求,創建一個aidl檔案,在service中實作,并暴露給客戶端,
客戶端
客戶端主要系結服務端的Service,將Binder物件轉成aidl型別,就可以呼叫服務端的實作方法,
AIDL支持的資料型別 :
1.基本資料型別: int、long、float、double、boolean等(不包含short:aidl不支持short,因為Parcel沒有辦法對short進行序列化,也就沒辦法通過aidl將short型別在客戶端與服務端進行傳遞)
2.String,CharSequence
3.實作了Parcelable介面的資料型別
4.List 型別,List承載的資料必須是AIDL支持的型別,或者是其它宣告的AIDL物件
5.Map型別,Map承載的資料必須是AIDL支持的型別,或者是其它宣告的AIDL物件
AIDL檔案可以分為兩類,一類用來宣告實作了Parcelable介面的資料型別,以供其他AIDL檔案使用那些非默認支持的資料型別,還有一類是用來定義介面方法,宣告要暴露哪些介面給客戶端呼叫,定向Tag就是用來標注這些方法的引數值
定向Tag定向Tag表示在跨行程通信中資料的流向,用于標注方法的引數值,分為 in、out、inout 三種,其中 in 表示資料只能由客戶端流向服務端, out 表示資料只能由服務端流向客戶端,而 inout 則表示資料可在服務端與客戶端之間雙向流通,此外,如果AIDL方法介面的引數值型別是:基本資料型別、String、CharSequence或者其他AIDL檔案定義的方法介面,那么這些引數值的定向 Tag 默認是且只能是 in,所以除了這些型別外,其他引數值都需要明確標注使用哪種定向Tag,
明確導包在AIDL檔案中需要明確標明參考到的資料型別所在的包名,即使兩個檔案處在同個包名下
代碼較亂,kotlin和java都有…多多包涵
服務端代碼
aidl介面創建

創建Aidl檔案
// IMyAidlInterface.aidl
package com.example.aidl;
parcelable Student;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getName();
Student getStudent();
void setName( String name);
void setStudent(in Student stu);
void setOutName( out Student stu);
void setInOutName(inout Student stu);
void setDefaultName( String name);
}
創建Student類(需要實作Parcelable介面)
package com.example.aidl
import android.os.Parcel
import android.os.Parcelable
/**
* Created by Android Studio
* Date: 2020/11/2
* Time: 11:09
*/
class Student() : Parcelable {
var name: String? = null
var old = 0
constructor(parcel: Parcel) : this() {
name = parcel.readString()
old = parcel.readInt()
}
fun readFromParcel(`in`: Parcel) {
name = `in`.readString()
old = `in`.readInt()
}
override fun toString(): String {
return "name=" + name + "-------" + "old=" + old
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeInt(old)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Student> {
override fun createFromParcel(parcel: Parcel): Student {
return Student(parcel)
}
override fun newArray(size: Int): Array<Student?> {
return arrayOfNulls(size)
}
}
}
readFromParcel()方法需要自己加進去,否則轉換會報錯,當然,也要有默認構造器,否則后續可能會出現錯誤,
rebuild工程
更改aidl檔案需要重新build一下,系統自動重新生成新的IMyAidlInterface,以及內部抽象Stub類,內部類Proxy等,
創建服務端Service:
package com.example.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.example.aidl.IMyAidlInterface;
import com.example.aidl.Student;
public class MyService extends Service {
private static final String TAG = "MyService";
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
if (intent != null) {
Log.e(TAG, "onBind this hash code = " + this.hashCode());
Log.e(TAG, "intent from = " + intent.getStringExtra("from"));
}
return myBinder;
}
public static MyBinder myBinder = new MyBinder();
static class MyBinder extends IMyAidlInterface.Stub {
String name;
Student student;
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) throws RemoteException {
this.name = name;
}
@Override
public Student getStudent() throws RemoteException {
if (student != null) {
Log.e(TAG, "getStudent: " + this.student.toString());
} else {
Log.e(TAG, "getStudent: ==null");
}
return student;
}
@Override
public void setStudent(Student stu) throws RemoteException {
Log.e(TAG, "studentIn: 服務端收到引數屬性"+stu.toString() );
this.student = stu;
stu.setName("service in change");
Log.e(TAG, "studentIn: " + stu.hashCode());
}
@Override
public void setOutName(Student stu) throws RemoteException {
Log.e(TAG, "studentOut: 服務端收到引數屬性"+stu.toString() );
student = stu;
stu.setName("service out change");
Log.e(TAG, "studentOut: " + stu.hashCode());
}
@Override
public void setInOutName(Student stu) throws RemoteException {
Log.e(TAG, "studentInOut: 服務端收到引數屬性"+stu.toString() );
student = stu;
stu.setName("service InOut change");
Log.e(TAG, "studentInOut: " + stu.hashCode());
}
@Override
public void setDefaultName(String name) throws RemoteException {
this.name = name;
}
}
}
宣告service 的action,由于跨行程,需要隱式啟動服務,
<service
android:name="com.example.service.MyService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.service.MyService" />
</intent-filter>
</service>
啟動服務
服務端啟動服務或者系結服務都可以
(最開始以為是必須的,后面好像發現不啟動是不是也行?有點怪,畢竟通訊嘛,都啟動吧,哈哈),
ser = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
interfaceA = IMyAidlInterface.Stub.asInterface(service)
}
}
var intent = Intent(this, MyService::class.java);
intent.putExtra("from", "AidlTestActivity")
bindService(intent, ser, Context.BIND_AUTO_CREATE)
客戶端代碼
將aidl檔案以及序列化類粘貼至服務端(保持包名相同)

系結服務,連接遠程服務端
ser = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("TAGwcz", "onServiceConnected: ");
interfaceA = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent = new Intent();
intent.setAction("com.example.service.MyService");
intent.setPackage("com.example.aidlservice");
intent.putExtra("from", "MainActivity");
bindService(intent, ser, Context.BIND_AUTO_CREATE);
aidl_change1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
studentOut = new Student("client out Student", 1);
Log.e(TAG, "onClick:before studentOut.hashCode()=="+studentOut.hashCode() );
interfaceA.setOutName(studentOut);
Log.e(TAG, "onClick:after studentOut.hashCode()=="+studentOut.hashCode() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
aidl_change2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
studentInOut = new Student("client inout Student", 2);
Log.e(TAG, "onClick:before studentInOut.hashCode()=="+studentInOut.hashCode() );
interfaceA.setInOutName(studentInOut);
Log.e(TAG, "onClick:after studentInOut.hashCode()=="+studentInOut.hashCode() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
aidl_change3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
studentIn = new Student("client in Student", 3);
Log.e(TAG, "onClick:before studentIn.hashCode()=="+studentIn.hashCode() );
interfaceA.setStudent(studentIn);
Log.e(TAG, "onClick:after studentIn.hashCode()=="+studentIn.hashCode() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
這樣就可以雙向通信了,
aidl小例子
IPC機制之了解AIDL(二)了解定向TAG
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/205017.html
標籤:其他
