繼使用Intent實(shí)現(xiàn)IPC后源祈,這篇文章來學(xué)習(xí)以下使用AIDL實(shí)現(xiàn)IPC,用Intent實(shí)現(xiàn)的方法可以查看上一篇稳衬,安卓實(shí)現(xiàn)IPC(二)—— Intent。一樣的配方坐漏,一樣的套路薄疚,我們還是通過一個(gè)demo來學(xué)習(xí),先上效果圖 :
從動(dòng)畫演示來看仙畦,是從一個(gè)應(yīng)用跳轉(zhuǎn)到另一個(gè)應(yīng)用输涕,在當(dāng)中進(jìn)行了一些操作,顯示了一些數(shù)據(jù)慨畸。
這其中AndroidIPC_Demo作為AIDL中Service端莱坎,提供了數(shù)據(jù)及操作數(shù)據(jù)的方法,OtherApplication作為Client端寸士,調(diào)用服務(wù)端的方法和數(shù)據(jù)檐什,并且展示出來∪蹩ǎ客戶端還有兩個(gè)方法bindService和unbindService乃正,因?yàn)锳IDL的服務(wù)端和客戶端是通過Service組件連接的,所以這就需要我們的客戶端去綁定服務(wù)婶博,就有了這兩個(gè)方法瓮具。
具體的細(xì)節(jié)在代碼中來看,因?yàn)檫@其中涉及到包名一致凡人,引包的一些操作名党,所以先放上服務(wù)端的目錄結(jié)構(gòu) :
首先,建立一個(gè)實(shí)體類Book:
public class Book implements Parcelable {
private String name;
private int price;
protected Book(Parcel in) {
name = in.readString();
price = in.readInt();
}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(price);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
AIDL能傳遞一些基礎(chǔ)數(shù)據(jù)類型挠轴,有int传睹、long、boolean岸晦、float欧啤、double、String等启上,此外如果想傳遞一些自定義類型的數(shù)據(jù)邢隧,那么需要這個(gè)數(shù)據(jù)類實(shí)現(xiàn)序列化。建立好實(shí)現(xiàn)了序列化的Book類后冈在,在當(dāng)前包下創(chuàng)建一個(gè)同名的aidl文件府框,即Book.aidl文件:
//包名和Book.java的包名一樣
package com.example.computer_ren.androidipc_demo;
//Book類是自定義實(shí)體類,實(shí)現(xiàn)Parceable序列化接口,這里需要注意parcelable的首字母p是小寫
parcelable Book;
然后創(chuàng)建一個(gè)aidl文件迫靖,創(chuàng)建方法見截圖:
選中main目錄院峡,創(chuàng)建的aidl文件和java是同級(jí)的,創(chuàng)建一個(gè)BookManager.aidl文件:
// BookManager.aidl
package com.example.computer_ren.androidipc_demo;
//引入Book.java的包
import com.example.computer_ren.androidipc_demo.Book;
// Declare any non-default types here with import statements
interface BookManager {
/**
* 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);
//自己定義的方法 獲取Book列表
List<Book> getBooks();
//自定義的方法 增加Book
void addBook(in Book book);
}
將剛才創(chuàng)建的Book.aidl文件移到aidl文件目錄下系宜,具體結(jié)構(gòu)見目錄結(jié)構(gòu)圖照激,至此 重新編譯一下工程,會(huì)自動(dòng)生成一個(gè)中間文件BookManager.java盹牧,這里面包含一些我們后面需要調(diào)用的方法俩垃。
至此aidl文件就編寫完畢,接著在java包下創(chuàng)建一個(gè)service汰寓,即AIDLService:
public class AIDLService extends Service {
private List<Book> mBooks = new ArrayList<>();
@Override
public void onCreate() {
super.onCreate();
//設(shè)置數(shù)據(jù)源
mBooks.add(new Book("第一行代碼",39));
mBooks.add(new Book("第二行代碼",49));
mBooks.add(new Book("Android開發(fā)藝術(shù)探索",59));
}
/**
* 通過中間類BookManager的方法Stub()方法獲取Binder對(duì)象 在onBind()方法中返回這個(gè)對(duì)象 方便客戶端拿到Binder對(duì)象
* 實(shí)現(xiàn)自定義的兩個(gè)方法 獲取書的列表和添加書
*/
private Binder mBinder = new BookManager.Stub() {
@Override
public List<Book> getBooks() throws RemoteException {
return mBooks;
}
@Override
public void addBook(Book book) throws RemoteException {
mBooks.add(book);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("TAG", "onUnbind: " );
return super.onUnbind(intent);
}
}
這里用到是安卓系統(tǒng)提供的Service組件口柳,在onCreate()周期中初始化數(shù)據(jù),然后獲取到Binder對(duì)象并實(shí)現(xiàn)接口中定義的方法有滑,因?yàn)檫@里需要用到Binder對(duì)象跃闹,所以在onBind()方法中返回獲取到的Binder對(duì)象。
接著在AndroidManifest.xml中注冊(cè)Service:
<service android:name=".AIDLService">
<intent-filter>
<action android:name="com.example.computer_ren.androidipc_demo.AIDLService"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
至此服務(wù)端的工作已經(jīng)完成毛好,下面是客戶端的部分:
將服務(wù)端aidl文件原封不動(dòng)的復(fù)制過來望艺,目錄結(jié)構(gòu)和服務(wù)端一樣,把需要傳遞的自定義實(shí)體類也復(fù)制過來肌访,這里需要注意包名也要和服務(wù)端一致找默,具體目錄結(jié)構(gòu)如下:
其中紅框內(nèi)的結(jié)構(gòu)需要注意 ,特別是包名要與服務(wù)端一致吼驶,否則編譯時(shí)會(huì)報(bào)錯(cuò)惩激。
下面是MainActivity(從服務(wù)端跳轉(zhuǎn)過來的頁面 )的代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button bindServiceBtn;
private Button unbindServiceBtn;
private Button addBookBtn;
private Button getBooksBtn;
private TextView showBooksTv;
private BookManager mBookManager = null;
private boolean isBind = false;//綁定服務(wù)的標(biāo)志符
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
bindServiceBtn = findViewById(R.id.btn_bind);
unbindServiceBtn = findViewById(R.id.btn_unbind);
addBookBtn = findViewById(R.id.btn_add_book);
getBooksBtn = findViewById(R.id.btn_get_book);
showBooksTv = findViewById(R.id.tv_show_books);
bindServiceBtn.setOnClickListener(this);
unbindServiceBtn.setOnClickListener(this);
addBookBtn.setOnClickListener(this);
getBooksBtn.setOnClickListener(this);
}
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通過Service中的onBinder()方法返回的Binder對(duì)象獲取到中間類(BookManager.java)對(duì)象
mBookManager = BookManager.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "綁定服務(wù)成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("TAG", "onServiceDisconnected");
}
};
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_bind://綁定服務(wù)
bindService();
break;
case R.id.btn_unbind://解綁服務(wù)
unbindService();
break;
case R.id.btn_add_book://增加書
addBook();
break;
case R.id.btn_get_book://獲取書列表
getBooks();
break;
}
}
/**
* 解綁服務(wù)
*/
private void unbindService() {
if (isBind) {
unbindService(mServiceConnection);
mBookManager = null;
Toast.makeText(this, "成功解綁服務(wù)", Toast.LENGTH_SHORT).show();
isBind = false;
}
}
/**
* 獲取書列表
*/
private void getBooks() {
if (!isBind) {
Toast.makeText(this, "請(qǐng)先綁定服務(wù)", Toast.LENGTH_SHORT).show();
return;
}
try {
List<Book> books = mBookManager.getBooks();
showBooksTv.setText(books.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 增加書
*/
private void addBook() {
if (!isBind) {
Toast.makeText(this, "請(qǐng)先綁定服務(wù)", Toast.LENGTH_SHORT).show();
return;
}
try {
mBookManager.addBook(new Book("西游記"+System.currentTimeMillis(),69));
Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 綁定服務(wù)
*/
private void bindService() {
if (!isBind) {
//隱式啟動(dòng)遠(yuǎn)程服務(wù)
Intent intent = new Intent();
intent.setAction("com.example.computer_ren.androidipc_demo.AIDLService");
intent.setPackage("com.example.computer_ren.androidipc_demo");
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
isBind = true;
}
}
}
這部分代碼主要說三個(gè)點(diǎn):
1、用intent隱式啟動(dòng)遠(yuǎn)程服務(wù)蟹演,調(diào)用方法bindService()綁定服務(wù)风钻;
2、在綁定服務(wù)的時(shí)候需要傳入一個(gè)ServiceConnection對(duì)象轨帜,在創(chuàng)建這個(gè)對(duì)象的同時(shí)我們可以獲取到從Service的onBind()方法中返回的Binder對(duì)象魄咕;
3衩椒、拿到了Binder對(duì)象蚌父,我們就可以通過aidl文件生成的中間對(duì)象調(diào)用方法Stub和asInterface方法拿到中間類對(duì)象。
通過以上方法拿到中間類對(duì)象后毛萌,就能調(diào)用我們?cè)诜?wù)端自定義的方法了苟弛,也就實(shí)現(xiàn)了進(jìn)程間通信。
其余都是一些簡單的業(yè)務(wù)代碼阁将,代碼中也給了詳細(xì)的注釋膏秫,這里就不做介紹了。
好了做盅,以上就是用AIDL實(shí)現(xiàn)IPC的整個(gè)過程缤削,過程有點(diǎn)復(fù)雜窘哈,所以這里做個(gè)總結(jié):
服務(wù)端:
1、傳遞的數(shù)據(jù)類型為自定義時(shí)亭敢,需要實(shí)現(xiàn)序列化接口滚婉,并且還需要?jiǎng)?chuàng)建一個(gè)同名的aidl文件;
2帅刀、再創(chuàng)建一個(gè)aidl接口文件让腹,在里面定義一些方法,供客戶端調(diào)用扣溺,重新編譯一下工程會(huì)自動(dòng)生成一個(gè)對(duì)應(yīng)的中間類骇窍;
3、在Service組件中通過aidl生成的中間類的Stub()方法獲得Binder對(duì)象锥余,并且實(shí)現(xiàn)接口中自定義的方法腹纳,Binder對(duì)象在onBind()方法中返回供客戶端調(diào)用;
4哈恰、在AndroidManifest.xml中注冊(cè)service只估。
客戶端:
1、將服務(wù)端的aidl文件和需要在進(jìn)程間傳遞的實(shí)體類原封不動(dòng)的復(fù)制過來着绷,這里需要注意實(shí)體類的包名也要與服務(wù)端一致蛔钙;
2、通過Intent隱式啟動(dòng)遠(yuǎn)程服務(wù)并綁定服務(wù)荠医;
3吁脱、用ServiceConnection這個(gè)類來獲取遠(yuǎn)程服務(wù)中Binder對(duì)象,然后調(diào)用中間類的方法Stub和asInterface()將Binder對(duì)象傳進(jìn)去獲得中間類對(duì)象彬向,得到中間類對(duì)象就能調(diào)用自己定義的接口方法了
對(duì)AIDL的學(xué)習(xí)到此為止兼贡。基于AIDL還有一種方法也能實(shí)現(xiàn)進(jìn)程間通信娃胆,那就是Messenger遍希,它是對(duì)AIDL的封裝,但存在一定的局限性:
Messenger存在的局限性:
1里烦、Messenger 是以串行的方式處理接受的消息凿蒜,即使有大量的消息同時(shí)到達(dá),也只能一個(gè)個(gè)處理胁黑,所以 Messenger 不適合用于處理大量的并發(fā)請(qǐng)求废封;
2、Messenger是用Hander來發(fā)送消息丧蘸,傳遞的數(shù)據(jù)放在Message這個(gè)類中漂洋,所以只能實(shí)現(xiàn)兩個(gè)進(jìn)程間的數(shù)據(jù)通信,而無法實(shí)現(xiàn)一個(gè)進(jìn)程調(diào)用另一個(gè)進(jìn)程的方法 。
Messenger是對(duì)AIDL的封裝刽漂,所以開發(fā)成本相對(duì)較低演训,在不考慮上面提到的局限時(shí),可以考慮用Messenger來實(shí)現(xiàn)IPC贝咙,這其中的選擇根據(jù)開發(fā)業(yè)務(wù)而定仇祭。
學(xué)習(xí)了AIDL,下一篇將學(xué)習(xí)用Broadcast組件來實(shí)現(xiàn)IPC颈畸。