對于AIDL的學(xué)習(xí),這些也只能說是我在學(xué)習(xí)中的理解,有理解不到位或者錯的地方也歡迎指正。
1.AIDL的簡單介紹
AIDL的目的就是實(shí)現(xiàn)進(jìn)程之間的通信,尤其是在涉及多進(jìn)程并發(fā)情況下的進(jìn)程間通信??梢詫idl理解為兩個進(jìn)程之間的橋梁,并制定規(guī)則,使其傳輸特定數(shù)據(jù)。
1.AIDL支持的數(shù)據(jù)類型有:
- 基本數(shù)據(jù)類型(int、long、char、boolean、double),定向 tag 默認(rèn)且只能是 in
- string和charSequence,定向 tag 默認(rèn)且只能是 in
- list:只支持arraylist,以及里面的所有元素必須被aidl支持
- map:只支持hashmap
- parcelable:所有實(shí)現(xiàn)parcelable接口的對象
- aidl:所有aidl接口本身也可以在aidl文件中使用
需要注意的是:在aidl中聲明其他數(shù)據(jù)類型時,需要在前面加in(輸入型參數(shù))、out(輸出型參數(shù))、inout(輸入輸出型參數(shù))
需要注意的是:除aidl所支持的數(shù)據(jù)類型外,如果要使用則必須導(dǎo)包,就算目標(biāo)文件與當(dāng)前文件在同一包下。
比如:
編寫了兩個文件,一個叫做 Book.java ,另一個叫做 BookManager.aidl,它們都在 com.xxx.aidldemo 包下 ,現(xiàn)在要在 .aidl 文件里使用 Book 對象,那么就必須在 .aidl 文件里面寫上 import com.xxx.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一個包下。
2.定向tag:
表示在跨進(jìn)程通信中數(shù)據(jù)的流向(流向是針對客戶端的對象而言)?
in(輸入型參數(shù)):客戶端——>服務(wù)器
out(輸出型參數(shù)):服務(wù)器——>客戶端
inout(輸入輸出型參數(shù)):雙向流通.
3.如果不包含非默認(rèn)支持的數(shù)據(jù)類型,只需要編寫一個AIDL文件,如果包含,通常需要寫 n+1 個AIDL文件( n 為非默認(rèn)支持的數(shù)據(jù)類型的種類數(shù))
4.非默認(rèn)支持?jǐn)?shù)據(jù)類型的序列化操作
????????1.?使數(shù)據(jù)類實(shí)現(xiàn)Parcelable接口
例:數(shù)據(jù)從客戶端到服務(wù)端,可以在客戶端對這個對象進(jìn)行序列化(通常為實(shí)現(xiàn)Parcelable接口),將其中數(shù)據(jù)轉(zhuǎn)化為序列化流,并傳輸?shù)椒?wù)端內(nèi)存中,再在服務(wù)端對這個數(shù)據(jù)進(jìn)行反序列化操作,從而還原數(shù)據(jù)。
? ? ? ? 2.?建立一個類,書寫其成員變量,建立getter和setter并添加一個無參構(gòu)造。令這個類implements Parcelable 。
需要注意的是:默認(rèn)生成的模板類對象只支持為in的定向tag。因?yàn)槟J(rèn)生成的類中只有writeToParcel() 方法,如果要實(shí)現(xiàn)為 out 或者 inout 的定向 tag 的話,需要實(shí)現(xiàn)readFromParcel() 方法。
5.書寫AIDL文件?
新建AIDL文件,AIDL文件大致分為兩類:
一類是用來定義parcelable對象,以供其他AIDL文件使用AIDL中非默認(rèn)支持的數(shù)據(jù)類型。
// Book.aidl
//用于引入了一個序列化對象Book,供其他的AIDL文件使用
//注意:Book.aidl與Book.java的包名應(yīng)當(dāng)是一樣的
package com.example.ipcclient;
//注意parcelable是小寫
parcelable Book;
一類是用來定義方法接口,以供系統(tǒng)完成跨進(jìn)程通信。
可理解為通信接口文件,需服務(wù)器端及客戶端各一個定義,需內(nèi)容相同。
// BookManager.aidl
package com.example.ipcclient;
//導(dǎo)入所需要使用的非默認(rèn)支持?jǐn)?shù)據(jù)類型的包
import com.lypeer.ipcclient.Book;
interface BookManager {
//所有的返回值前都不需要加任何東西,不管是什么數(shù)據(jù)類型
List<Book> getBooks();
Book getBook();
int getBookCount();
//傳參時除了Java基本類型以及String,CharSequence之外的類型
//都需要在前面加上定向tag,具體加什么按需而定
void setBookPrice(in Book book , int price)
void setBookName(in Book book , String name)
void addBookIn(in Book book);
void addBookOut(out Book book);
void addBookInout(inout Book book);
}
?
2.AIDL傳遞簡單數(shù)據(jù)
1.服務(wù)端
? ? ? ? a.創(chuàng)建AIDL文件?
????????IPerson.aidl
// IPerson.aidl
package com.example.aidlserver;
import com.example.aidlserver.AIDLService;
interface IPerson {
String queryPerson(int num);
}
創(chuàng)建完后進(jìn)行build,生成IPerson.java文件
? ? ? ? ?b.自定義Service類
? ? ? ? ? ? ? ? i.繼承Service類,定義一個PersonQueryBinder類用來繼承IPerson.Stub類,實(shí)現(xiàn)了IPerson接口和IBinder接口。
? ? ? ? ? ? ? ? ii.實(shí)例化自定義的Stub類,重寫onBind方法,返回binder對象。
????????AIDLService.java
package com.example.aidlserver;
public class AIDLService extends Service {
//實(shí)例化自定義的Stub類
private IBinder binder = new PersonQueryBinder();
private String[] names = {"第一個","第二個","第三個"};
private String query(int num)
{
if(num > 0 && num < 6){
return names[num - 1];
}
return null;
}
@Override
public IBinder onBind(Intent intent) {
//重寫onBind方法,返回binder對象。
// return new PersonQueryBinder();
return binder;
}
//定義一個PersonQueryBinder類用來繼承IPerson.Stub類,實(shí)現(xiàn)了IPerson接口。
private final class PersonQueryBinder extends IPerson.Stub {
public String queryPerson(int num) throws RemoteException {
return query(num);
}
}
}
? ? ? ? c.在AndroidManifest.xml文件中注冊Service
<service android:name=".AIDLService"
android:exported="true">
<intent-filter>
<action android:name="任意名即可,如:LXYaidl" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
2.客戶端
需要將服務(wù)端的aidl文件直接復(fù)制過來(包括其中的包),客戶端的內(nèi)容直接在Activity中完成即可。
流程如下:
????????a.自定義的PersonConnection類實(shí)現(xiàn)ServiceConnection接口
? ? ? ? b.以PersonConnection對象作為參數(shù),調(diào)用bindService綁定遠(yuǎn)程Service
? ? ? ? c.綁定遠(yuǎn)程Service的ServiceConnection并不能直接獲取Service的onBind( )方法
????????????????返回的IBinder對象,只能返回onBind()方法所返回的代理對象,所需處理如下:
iPerson = IPerson.Stub.asInterface(service);
代碼:MainActivity.java
package com.example.aidlclient;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText edit_num;
private Button btn_query;
private TextView txt_name;
private IPerson iPerson;
private PersonConnection conn = new PersonConnection();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//控件綁定
edit_num = findViewById(R.id.edit_num);
btn_query = findViewById(R.id.btn_query);
txt_name = findViewById(R.id.txt_name);
//綁定遠(yuǎn)程Service
Intent service = new Intent("LXY_aidl");
service.setPackage("com.example.aidlserver");
//以PersonConnection對象作為參數(shù),調(diào)用bindService綁定遠(yuǎn)程Service
bindService(service, conn, BIND_AUTO_CREATE);
btn_query.setOnClickListener(this);
}
@Override
public void onClick(View v) {
String number = edit_num.getText().toString();
int num = Integer.valueOf(number);
try {
txt_name.setText(iPerson.queryPerson(num));
} catch (RemoteException e) {
e.printStackTrace();
}
edit_num.setText("");
}
//自定義的PersonConnection類實(shí)現(xiàn)ServiceConnection接口
private final class PersonConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
iPerson = IPerson.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName name) {
iPerson = null;
}
}
}
3.啟動步驟及可能出現(xiàn)的bug
? ? ? ? 啟動步驟為:先啟動服務(wù)端,再啟動客戶端
這個地方可能出現(xiàn)的bug如下:?
- Android?AIDL客戶端調(diào)用服務(wù)端方法報錯空指針,即找不到那個方法。
報錯為:java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String com.example.aidlserver.ILanguage.queryLanguage(int)' on a null object reference
?解決方法:
????????1.在客戶端清單文件中配置服務(wù)端service的包路徑
????????2.服務(wù)端的onBind方法中,返回值不可為空,返回的值為binder對象;或自定義的繼承了IPerson.Stub類的類的new?方法。
- 服務(wù)端和客戶端AIDL文件所在包路徑不一致,報錯異常為:?
java.lang.SecurityException: Binder invocation to an incorrect interface
解決方法:修改服務(wù)端service對應(yīng)的完整包名,客戶端應(yīng)與服務(wù)端的一致。
服務(wù)端:
?客戶端:
3.AIDL傳遞復(fù)雜數(shù)據(jù)
1.基礎(chǔ)操作:
? ? ? ? 1.將writeToParcel和readFromPacel方法寫入方法,將對象寫入到包裹(parcel)中。
需要注意的是:寫入順序與讀取順序需要相同
????????2.在該類中添加一個名為CREATOR的static final屬性 改屬性需要實(shí)現(xiàn):android.os.Parcelable.Creator接口
????????3.從接口中寫兩個方法:
? ? ? ? a.createFromParcel(Parcel source)方法:實(shí)現(xiàn)從source創(chuàng)建出JavaBean實(shí)例
????????b.newArray(int size):創(chuàng)建一個數(shù)組。
????????4.定向tag
2.服務(wù)端
代碼:Step1:創(chuàng)建aidl文件實(shí)現(xiàn)接口.
1.Person.aidl
// Person.aidl
package com.example.aidl_p;
parcelable Person;
2. Salary.aidl
// Salary.aidl
package com.example.aidl_p;
parcelable Salary;
代碼:Step2:分別建立類,需實(shí)現(xiàn)Parcelable接口,重寫對應(yīng)的方法
1.Person.java
package com.example.aidl_p;
public class Person implements Parcelable {
private Integer id;
private String name;
public Person(Integer id,String name){
this.id = id;
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
protected Person(Parcel in) {
}
//寫入數(shù)據(jù)到Parcel中的方法
@Override
public void writeToParcel(Parcel dest, int flags) {
//把對象所包含的數(shù)據(jù)寫入到parcel中
dest.writeInt(id);
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
//必須提供一個名為CREATOR的static final屬性 該屬性需要實(shí)現(xiàn)
//android.os.Parcelable.Creator<T>接口
public static final Creator<Person> CREATOR = new Creator<Person>() {
//從Parcel中讀取數(shù)據(jù),返回Person對象
@Override
public Person createFromParcel(Parcel in) {
return new Person(in.readInt(),in.readString());
}
@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
//因?yàn)榧先〕鲈氐臅r候是根據(jù)Person對象來取得
//需要重寫hashCode()和equals()方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +((name == null)? 0 : name.hashCode());
return result;
}
//hashCode 自動生成
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id) &&
Objects.equals(name, person.name);
}
}
2.Salary.java?
package com.example.aidl_p;
public class Salary implements Parcelable {
private String type;
private Integer salary;
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
protected Salary(Parcel in) {
}
public Salary(String type,Integer salary){
this.type = type;
this.salary = salary;
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Salary> CREATOR = new Creator<Salary>() {
//從Parcel中讀取數(shù)據(jù),返回Person對象
@Override
public Salary createFromParcel(Parcel in) {
return new Salary(in.readString(),in.readInt());
}
@Override
public Salary[] newArray(int size) {
return new Salary[size];
}
};
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(type);
parcel.writeInt(salary);
}
public String toString(){
return "工作:" + type + " 薪水: " + salary;
}
}
3.ISalary.aidl??獲取工資信息的方法
// ISalary.aidl
package com.example.aidl_p;
//無論在不在同一個包中都需要導(dǎo)包
import com.example.aidl_p.Salary;
import com.example.aidl_p.Person;
interface ISalary {
//定義一個Person對象作為傳入?yún)?shù)
//接口中定義方法時,需要制定新參的傳遞模式,這里是傳入,所以前面有一個in
//獲取工資信息
Salary getMsg(in Person owner);
}
4.AidlService.java?
定義一個SalaryBinder類繼承Stub,從而實(shí)現(xiàn)ISalary和IBinder接口;定義一個存儲信息的Map集合,
重新onBind方法,返回SalaryBinder類的對象實(shí)例
5.注冊Service:
<service android:name=".AidlService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.AIDLService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
3.客戶端
代碼:Step1:將服務(wù)端的aidl復(fù)制過來。
代碼:Step2:定義一個ServciceConnection對象,重寫對應(yīng)方法,和普通數(shù)據(jù)的類似,接著bindService,然后再Button的點(diǎn)擊事件中獲取Salary對象并顯示。
MainActivity.java
package com.example.aidl_kehu;
public class MainActivity extends AppCompatActivity {
private ISalary salaryService;
private Button btnquery;
private EditText editname;
private TextView textshow;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//返回的是代理對象,要調(diào)用這個方法
salaryService = ISalary.Stub.asInterface(iBinder); }
@Override
public void onServiceDisconnected(ComponentName componentName) {
salaryService = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnquery = findViewById(R.id.btnquery);
editname = findViewById(R.id.editname);
textshow = findViewById(R.id.textshow);
ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
actionBar.hide();
}
Intent service = new Intent("android.intent.action.AIDLService");
service.setPackage("com.example.aidl_p");
bindService(service,connection, BIND_AUTO_CREATE);
btnquery.setOnClickListener(new View.OnClickListener() {
@SuppressLint("SetTextI18n")
@Override
public void onClick(View view) {
try {
String name = editname.getText().toString();
Salary salary = salaryService.getMsg(new Person(1,name));
textshow.setText(name+salary.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
this.unbindService(connection);
}
}
4.AIDL總結(jié)?
1.服務(wù)端
? ? ? ? a.創(chuàng)建aidl,創(chuàng)建同名類,在aidl中寫parcelable接口。并創(chuàng)建aidl實(shí)現(xiàn)接口方法,且需要導(dǎo)包。
????????b.?在類中生成set、get方法,創(chuàng)建對象,writeToParcel中把對象寫進(jìn)去,在createFromParcel中返回對象,創(chuàng)建toString() 方法用于輸出。
? ? ? ? c.在Service中實(shí)現(xiàn)功能文章來源:http://www.zghlxwxcb.cn/news/detail-831555.html
2.客戶端
? ? ? ? a.復(fù)制服務(wù)端的aidl和類,在MainActivity中實(shí)現(xiàn)功能文章來源地址http://www.zghlxwxcb.cn/news/detail-831555.html
到了這里,關(guān)于Android:AIDL簡單介紹+傳遞簡單數(shù)據(jù)+傳遞復(fù)雜數(shù)據(jù)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!