本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發布
“Android進程通信”,乍一聽感覺好深奧的東西。到底什么是進程通信呢?舉個栗子:現在我手機有兩個應用程序,其中一個我們暫且叫它客戶端,它的功能是實現兩個數相加,即當你在界面中輸入兩個數,點一下計算的按鈕,就會得到兩個數相加的結果。第二個應用程序我們暫且叫它服務端,它是用來存放客戶端的具體邏輯的,即兩個數相加的具體計算過程在這個應用程序中。我們在客戶端中輸入兩個數,然后將這兩個數傳到服務端中,服務端經過計算把兩個數的相加結果再傳回客戶端。這樣兩個應用程序便實現了通信。
Android實現進程間的通信有四種方式,分別對應于Android中的四大組件。即Activity、Broadcast、ContentProvider、Service。其中Activity可以通過Intent訪問其他進程的Activity,Broadcast可以給Android系統中所有的應用程序發送廣播,需要跨進程通信的應用程序可以監聽這些廣播,ContentProvider可以向其他應用程序共享數據,以及允許其他應用程序對其數據進行增刪改查操作。最后便是本文的重點,通過Service利用AIDL進行通信。
AIDL(Android Interface Definition Language)是一種接口定義語言,由于Android的每個進程都運行在獨立的虛擬機中,所以進程之間通信會比較麻煩。我們可以利用AIDL將一個進程的數據拆分成Android系統可識別的數據單元,然后系統再重新將數據單元合成傳遞給另一個進程。這樣就實現了進程間的通信。
那么我們該如何使用AIDL呢?既然AIDL是一種接口定義語言,自然我們就得先定義好接口。具體的步驟如下:
1.創建.aidl文件
2.實現接口
3.將接口暴露給客戶端
接下來我們來通過本文開頭這個例子來具體講講如何通過以上三步就可以實現進程間的通信。
第一步:創建.aidl文件
如上圖,新建一個Android工程,然后添加一個模塊。其中一個aidlclient作為客戶端,另一個app為服務端。先在app文件夾下建立一個與Java文件同級的文件夾命名為“aidl”,再在這個文件夾下新建一個與該模塊同名的包,包下新建一個aidl接口。注意其擴展名為aidl。在該文件中我們便可以定義自己的接口,在上例中我們可以這樣定義:
package com.example.administrator.aidldemo;
interface IMyAidlInterface {
int add(int num1,int num2);
}
注:文件必須聲明包名,且要和該服務端模塊的包名相同。接口方法中的入參支持基本數據類型,除short外。因為其無法被序列化
然后我們在客戶端aidlclient模塊下新建一個與服務端中一模一樣的aidl文件夾,注意客戶端中的aidl文件夾下的內容必須保證和服務端的一致,包括包名和具體的aidl文件。
然后我們編譯一下Android Studio,在兩個模塊的
build-->generated-->source
下生成一個aidl文件夾,且文件夾下的目錄如下圖所示則表示我們的aidl文件已經創建成功
第二步:實現接口
在我們的服務端的模塊的Java文件夾下新建一個服務如下圖:
該服務類中的代碼如下
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;
}
};
}
其中如下代碼,我們通過調用編譯生成的IMyAidlInterface的Stub方法實現我們之前定義的接口,即完成了我們的第二步實現接口
private IBinder iBinder = new IMyAidlInterface.Stub(){
@Override
public int add(int num1, int num2) throws RemoteException {
return num1+num2;
}
};
注:別忘了在AndroidManifest文件中注冊該Service
<service android:name=".IRemoteService"
android:process=":remote"
android:exported="true">
</service>
第三步:將接口暴露給客戶端
public IBinder onBind(Intent intent) {
return iBinder;
}
我們只要在IRomoteService中的onBind中返回我們實現好的接口。這樣一旦客戶端綁定該服務就會執行onBind方法從而得到已實現好的接口。客戶端得到該接口就可以調用接口中的add
方法來實現加法的運算。
接下來我們來看一下調用的具體過程:
首先我們將客戶端的界面先布置好:
<?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="遠程計算"
android:gravity="center"
android:textSize="24sp" />
</LinearLayout>
界面非常簡單在兩個EditText中輸入兩個數點擊按鈕,然后顯示計算結果。
接下來我們來看一下該界面的具體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;
//綁定服務回調
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//服務綁定成功后調用,獲取服務端的接口,這里的service就是服務端onBind返
//回的iBinder即已實現的接口
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
//解除綁定時調用,清空接口,防止內容溢出
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);
}
//按鈕點擊事件
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+"");
}
//綁定服務
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();
//當活動銷毀時解除綁定
unbindService(conn);
}
}
其實上面的代碼還是很好理解的,當啟動客戶端,將會執行bindService方法,去綁定服務端的遠程服務,一旦綁定成功,就會回調conn 中的onServiceConnected方法。在方法中,我們獲取到了服務端實現好的接口。即服務端將該實現好的接口暴漏給了客戶端。
然后當我們輸入兩個數,點擊按鈕時,就可以調用我們剛得到的接口的add
方法,來實現兩個加數的相加運算。
這樣我們就完成整個例子的編碼,接下來我們就來運行下。
先運行服務端的模塊,再運行客戶端的。
好了,這篇先就講這么多,下一篇Android進程通信(IPC)之AIDL對象傳遞主要講解了如何在AIDL中傳遞對象。