本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布
“Android進(jìn)程通信”镶摘,乍一聽感覺好深?yuàn)W的東西。到底什么是進(jìn)程通信呢渴析?舉個(gè)栗子:現(xiàn)在我手機(jī)有兩個(gè)應(yīng)用程序梭稚,其中一個(gè)我們暫且叫它客戶端,它的功能是實(shí)現(xiàn)兩個(gè)數(shù)相加怨规,即當(dāng)你在界面中輸入兩個(gè)數(shù)陌宿,點(diǎn)一下計(jì)算的按鈕,就會(huì)得到兩個(gè)數(shù)相加的結(jié)果波丰。第二個(gè)應(yīng)用程序我們暫且叫它服務(wù)端壳坪,它是用來存放客戶端的具體邏輯的,即兩個(gè)數(shù)相加的具體計(jì)算過程在這個(gè)應(yīng)用程序中掰烟。我們?cè)诳蛻舳酥休斎雰蓚€(gè)數(shù)爽蝴,然后將這兩個(gè)數(shù)傳到服務(wù)端中,服務(wù)端經(jīng)過計(jì)算把兩個(gè)數(shù)的相加結(jié)果再傳回客戶端纫骑。這樣兩個(gè)應(yīng)用程序便實(shí)現(xiàn)了通信蝎亚。
Android實(shí)現(xiàn)進(jìn)程間的通信有四種方式,分別對(duì)應(yīng)于Android中的四大組件惧磺。即Activity颖对、Broadcast、ContentProvider磨隘、Service缤底。其中Activity可以通過Intent訪問其他進(jìn)程的Activity,Broadcast可以給Android系統(tǒng)中所有的應(yīng)用程序發(fā)送廣播,需要跨進(jìn)程通信的應(yīng)用程序可以監(jiān)聽這些廣播番捂,ContentProvider可以向其他應(yīng)用程序共享數(shù)據(jù)个唧,以及允許其他應(yīng)用程序?qū)ζ鋽?shù)據(jù)進(jìn)行增刪改查操作。最后便是本文的重點(diǎn)设预,通過Service利用AIDL進(jìn)行通信徙歼。
AIDL(Android Interface Definition Language)是一種接口定義語(yǔ)言,由于Android的每個(gè)進(jìn)程都運(yùn)行在獨(dú)立的虛擬機(jī)中,所以進(jìn)程之間通信會(huì)比較麻煩魄梯。我們可以利用AIDL將一個(gè)進(jìn)程的數(shù)據(jù)拆分成Android系統(tǒng)可識(shí)別的數(shù)據(jù)單元桨螺,然后系統(tǒng)再重新將數(shù)據(jù)單元合成傳遞給另一個(gè)進(jìn)程。這樣就實(shí)現(xiàn)了進(jìn)程間的通信酿秸。
那么我們?cè)撊绾问褂肁IDL呢灭翔?既然AIDL是一種接口定義語(yǔ)言,自然我們就得先定義好接口辣苏。具體的步驟如下:
1.創(chuàng)建.aidl文件
2.實(shí)現(xiàn)接口
3.將接口暴露給客戶端
接下來我們來通過本文開頭這個(gè)例子來具體講講如何通過以上三步就可以實(shí)現(xiàn)進(jìn)程間的通信肝箱。
第一步:創(chuàng)建.aidl文件
如上圖,新建一個(gè)Android工程稀蟋,然后添加一個(gè)模塊煌张。其中一個(gè)aidlclient作為客戶端,另一個(gè)app為服務(wù)端退客。先在app文件夾下建立一個(gè)與Java文件同級(jí)的文件夾命名為“aidl”骏融,再在這個(gè)文件夾下新建一個(gè)與該模塊同名的包尉尾,包下新建一個(gè)aidl接口妓肢。注意其擴(kuò)展名為aidl蔽莱。在該文件中我們便可以定義自己的接口砌溺,在上例中我們可以這樣定義:
package com.example.administrator.aidldemo;
interface IMyAidlInterface {
int add(int num1,int num2);
}
注:文件必須聲明包名套媚,且要和該服務(wù)端模塊的包名相同慈鸠。接口方法中的入?yún)⒅С只緮?shù)據(jù)類型逝段,除short外私股。因?yàn)槠錈o法被序列化
然后我們?cè)诳蛻舳薬idlclient模塊下新建一個(gè)與服務(wù)端中一模一樣的aidl文件夾刷允,注意客戶端中的aidl文件夾下的內(nèi)容必須保證和服務(wù)端的一致冤留,包括包名和具體的aidl文件。
然后我們編譯一下Android Studio树灶,在兩個(gè)模塊的
build-->generated-->source
下生成一個(gè)aidl文件夾纤怒,且文件夾下的目錄如下圖所示則表示我們的aidl文件已經(jīng)創(chuàng)建成功
第二步:實(shí)現(xiàn)接口
在我們的服務(wù)端的模塊的Java文件夾下新建一個(gè)服務(wù)如下圖:
該服務(wù)類中的代碼如下
package com.example.administrator.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
public class IRemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub(){
@Override
public int add(int num1, int num2) throws RemoteException {
return num1+num2;
}
};
}
其中如下代碼,我們通過調(diào)用編譯生成的IMyAidlInterface的Stub方法實(shí)現(xiàn)我們之前定義的接口天通,即完成了我們的第二步實(shí)現(xiàn)接口
private IBinder iBinder = new IMyAidlInterface.Stub(){
@Override
public int add(int num1, int num2) throws RemoteException {
return num1+num2;
}
};
注:別忘了在AndroidManifest文件中注冊(cè)該Service
<service android:name=".IRemoteService"
android:process=":remote"
android:exported="true">
</service>
第三步:將接口暴露給客戶端
public IBinder onBind(Intent intent) {
return iBinder;
}
我們只要在IRomoteService中的onBind中返回我們實(shí)現(xiàn)好的接口泊窘。這樣一旦客戶端綁定該服務(wù)就會(huì)執(zhí)行onBind方法從而得到已實(shí)現(xiàn)好的接口∠窈客戶端得到該接口就可以調(diào)用接口中的add
方法來實(shí)現(xiàn)加法的運(yùn)算烘豹。
接下來我們來看一下調(diào)用的具體過程:
首先我們將客戶端的界面先布置好:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:gravity="center|right"
android:id="@+id/et_num1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="+"
android:textSize="24sp"
android:layout_gravity="center"
android:gravity="right|center"/>
<EditText
android:gravity="center|right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/et_num2"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="="
android:textSize="24sp"
android:layout_gravity="center"
android:gravity="right|center"/>
<EditText
android:gravity="center|right"
android:editable="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/edit_show_result"
android:textSize="24sp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btn_count"
android:text="遠(yuǎn)程計(jì)算"
android:gravity="center"
android:textSize="24sp" />
</LinearLayout>
界面非常簡(jiǎn)單在兩個(gè)EditText中輸入兩個(gè)數(shù)點(diǎn)擊按鈕,然后顯示計(jì)算結(jié)果诺祸。
接下來我們來看一下該界面的具體Java代碼:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText et_num1;
private EditText et_num2;
private EditText edit_show_result;
private Button btn_count;
private int mNum1;
private int mNum2;
private int mTotal;
private IMyAidlInterface iMyAidlInterface;
//綁定服務(wù)回調(diào)
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//服務(wù)綁定成功后調(diào)用携悯,獲取服務(wù)端的接口,這里的service就是服務(wù)端onBind返
//回的iBinder即已實(shí)現(xiàn)的接口
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//解除綁定時(shí)調(diào)用筷笨,清空接口憔鬼,防止內(nèi)容溢出
iMyAidlInterface = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService();
initView();
}
//初始化界面
private void initView(){
et_num1 = (EditText)findViewById(R.id.et_num1);
et_num2 = (EditText)findViewById(R.id.et_num2);
edit_show_result = (EditText)findViewById(R.id.edit_show_result);
btn_count = (Button)findViewById(R.id.btn_count);
btn_count.setOnClickListener(this);
}
//按鈕點(diǎn)擊事件
private void handleBtnClickEvent(){
mNum1 = Integer.parseInt(et_num1.getText().toString());
mNum2 = Integer.parseInt(et_num2.getText().toString());
try {
mTotal = iMyAidlInterface.add(mNum1,mNum2);
} catch (RemoteException e) {
e.printStackTrace();
}
edit_show_result.setText(mTotal+"");
}
//綁定服務(wù)
private void bindService(){
Intent intent = new Intent();
intent.setComponent(newComponentName("com.example.administrator.aidldemo",
"com.example.administrator.aidldemo.IRemoteService"));
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
@Override
public void onClick(View v) {
handleBtnClickEvent();
}
protected void onDestroy(){
super.onDestroy();
//當(dāng)活動(dòng)銷毀時(shí)解除綁定
unbindService(conn);
}
}
其實(shí)上面的代碼還是很好理解的龟劲,當(dāng)啟動(dòng)客戶端,將會(huì)執(zhí)行bindService方法轴或,去綁定服務(wù)端的遠(yuǎn)程服務(wù)昌跌,一旦綁定成功,就會(huì)回調(diào)conn 中的onServiceConnected方法照雁。在方法中避矢,我們獲取到了服務(wù)端實(shí)現(xiàn)好的接口。即服務(wù)端將該實(shí)現(xiàn)好的接口暴漏給了客戶端囊榜。
然后當(dāng)我們輸入兩個(gè)數(shù),點(diǎn)擊按鈕時(shí)亥宿,就可以調(diào)用我們剛得到的接口的add
方法卸勺,來實(shí)現(xiàn)兩個(gè)加數(shù)的相加運(yùn)算。
這樣我們就完成整個(gè)例子的編碼烫扼,接下來我們就來運(yùn)行下曙求。
先運(yùn)行服務(wù)端的模塊,再運(yùn)行客戶端的映企。
好了悟狱,這篇先就講這么多,下一篇Android進(jìn)程通信(IPC)之AIDL對(duì)象傳遞主要講解了如何在AIDL中傳遞對(duì)象堰氓。