android程序間通訊Binder(一)
最近在看程序間通訊方面的東西,在這裡粗略的的記錄一下自己看到的東西。
在android中實現程序間通訊用的都是Binder機制,Binder:貼上劑,個人感覺很形象,將各個程序貼上起來,實現程序之間資料的互動,拒絕了程序間的老死不相往來。本來程序就是互斥的,為的就是保持程序的純淨和安全,避免被其它程序惡意篡改資料。但是又不能不做程序間的互動,因此,Binder就來起作用了。
我暫時對Binder的底層實現沒有去看,從其它地方盜了一張圖,說明了Server、Client、ServiceManager、Binder之間的關係
下面是我自己畫的一張他們之間的關係圖(寫的字賊醜,請不要在意這些細節)
流程分析:
1、Service生成時(可能用詞不太恰當),Service在new的時候,需要重寫onBind方法,在這裡需要返回一個IBinder物件
2、根據原始碼ServiceManager.addService(String name, IBinder service),最後會在/dev/binder,Binder驅動(核心)中建立一個Binder的引用
3、將這個Binder引用(包含一些其他的資料)發給ServiceManager
4、SMGR接到之後,從資料中讀取到Service的名字和引用,並新增到一張表中
------------------Service在這裡就算建立成功了,接下來就是Client來使用Service
5、Client來bindService,其實就是給SMGR傳送訊息,我要繫結哪個哪個Service
6,SMGR從table中進行查詢,查到了Client請求的Service,將Binder的引用(注意是Binder的引用)返回給Client。
這樣Client就持有了Service的Binder的引用,這樣,Client這邊對Binder的引用的操作,就會轉嫁到實際Binder實體中。
----結束。
僅僅從這裡來看,並不是很明白,我們來通過程式碼來see一下這個過程是怎麼做的。(底子有限,我只能從上層來分析這塊的內容,底層怎麼做的,暫時還看不明白)
根據android的設定,我們需要一個AIDL檔案來串聯起來 Server 和 Client
ITest.aidl // 我在這裡把包名也寫出來了,包名在後面會用到的,在這裡先mark一下
package com.example.lenovo.testbinder;
import com.example.lenovo.bean.Student;
interface ITest {
int getAge(int num);
String getName();
Student getStudent();
}
Service端:我們new了一個Binder,需要繼承ITest.Stub,並且重寫其中的幾個方法
TestBinder.java
public class TestBinder extends ITest.Stub {
@Override
public int getAge(int num) throws RemoteException {
return 3 + 10;
}
@Override
public String getName() throws RemoteException {
return "test string";
}
@Override
public Student getStudent() throws RemoteException {
return new Student("zhangsan", 8);
}
public static IBinder instance() {
return new TestBinder();
}
}
接下來就是Service的內容了,(Service的註冊,manifest檔案什麼的我就不寫了)
MainService.java
public class MainService extends Service {
private static final String TAG = "MainService";
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind");
return TestBinder.instance();
}
}
在這裡,onBind中就把TestBinder物件返回回去了。
-------------------底層Kernel中怎麼做的,我暫時不清楚,就簡單的這樣理解:在/dev/binder驅動中,有了一個TestBinder的應用,該引用指向了具體的TestBinder實體類,對這個引用做的操作(getName(),getAge(),getStudent())都會操作到實體類中。
Client端:------------------在這裡就是另外一個應用了,它使用的是另外一個程序,
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
ITest mTestBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initService();
}
private void initService() {
Intent intent = new Intent("com.example.lenovo.TEST");
intent.setPackage("com.example.lenovo.testbinder");
Context appContext = getApplicationContext();
appContext.startService(intent);
bindService(intent, mServiceConnection, 0);
}
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 藉助AIDL檔案可以直接呼叫aidl中的方法,aidl中生成的內容其實和下面的內容一樣
mTestBinder = ITest.Stub.asInterface(service);
try {
Log.e(TAG, "" + mTestBinder.getName());
Log.e(TAG, "" + mTestBinder.getAge(3));
Log.e(TAG, "" + mTestBinder.getStudent());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
這裡去綁定了MainService,並指定繫結結束之後返回IBinder物件,在這裡返回的IBinder就是binder驅動中的TestBinder的引用,ServiceManager中就是去之前提到的table裡面去查詢的,查到了對應的IBinder,就將這個IBinder的引用返回回來。
這樣,在Client端就可以使用服務端的TestBinder了,並且得到返回值。
以上就是android提供的aidl的使用,簡化了很多的步驟,只需要:服務端,繼承ITest.Stub,並實現ITest.aidl中的方法。客戶端,繫結服務,使用ITest.Stub.asInteterface(sevice)就可以得到ITest的binder物件,接下來就是資料傳遞操作。
下一篇文章,我們來看看aidl檔案,在android studio中到底是怎麼搞的,怎麼就可以直接使用,連上了Server和Client。