1. 程式人生 > >android程序間通訊Binder(一)

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。