1. 程式人生 > >Android IBinder機制簡單介紹

Android IBinder機制簡單介紹

原理簡介

我們都知道android 是通過IBinder來實現IPC(Inter Process Communication)程序間通訊的。。。


借用一下:


1. Client、Server和Service Manager實現在使用者空間中,Binder驅動程式實現在核心空間中
2. Binder驅動程式和Service Manager在Android平臺中已經實現,開發者只需要在使用者空間實現自己的Client和Server
3. Binder驅動程式提供裝置檔案/dev/binder與使用者空間互動,Client、Server和Service Manager通過open和ioctl檔案操作函式與Binder驅動程式進行通訊
4. Client和Server之間的程序間通訊通過Binder驅動程式間接實現

5. Service Manager是一個守護程序,用來管理Server,並向Client提供查詢Server介面的能力

AIDL的使用

aidl是 Android Interface definition language的縮寫,它是一種android內部程序通訊介面的描述語言,通過它我們可以定義程序間的通訊介面

ITestService.aidl檔案

package test.aidl;
interface ITestService {
    void start();
}
注意在client端和server端中的aidl檔案的的包名必須是一樣的,此處必須是test.aidl
自動生成ITestService類,其中有Stub子類
        public static test.aidl.ITestService asInterface(android.os.IBinder obj) {
            ......
        }
Stub的asInterface方法返回了ITestService物件

lbb.demo.first程序中的服務,充當Server角色

package lbb.demo.first;
public class RemoteService extends Service {
    private TestService mITestService;

    public class TestService extends ITestService.Stub {
        public void start() {
            Log.d("LiaBin", "RemoteService start");
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //例項化Binder
        mITestService = new TestService();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mITestService;
    }
}

        <service android:name=".RemoteService">
            <intent-filter>
                <action android:name="android.intent.action.MyFirstService"/>
                <category android:name="android.intent.category.default"/>
            </intent-filter>
        </service>

lbb.demo.two程序充當Client角色
package lbb.demo.two;
public class MainActivity extends AppCompatActivity {

    private static final String REMOT_SERVICE_ACTION = "android.intent.action.MyFirstService";

    private ITestService testService;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            testService = ITestService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        bindService(new Intent(REMOT_SERVICE_ACTION), connection, BIND_AUTO_CREATE);
    }


    @OnClick(R.id.but)
    void onClick() {
        try {
            testService.start();              
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }
}
此時lbb.demo.two程序點選button最終會執行start方法,activity通過bindService獲得了遠端服務的IBinder引用。。
怎麼獲得的,現在也不是很清楚,但是肯定跟上面說的Binder驅動程式和Service Manager守護程序了脫不了關係的。。

另一方面:有沒有發現,同一個程序中的activity和service的通訊,其實也差不多。。。。只是不需要定義aidl檔案而已,因為如果在service中定義一個IBinder實現類,那麼
在activity中就能訪問得到該IBindler物件了。。所以不需要aidl檔案。。如果硬要使用aidl檔案定義的介面,此時也是沒有任何問題的。。。
但是如果service和activity在不同的程序,是沒法訪問到service中定義的IBindler,那麼只能通過共享使用一個aidl檔案了
,來達到共享IBinder進行通訊。。
    private MyBinder myBinder = new MyBinder();
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return myBinder;
    }

    public class MyBinder extends Binder{
        public void start(){
            Log.d("LiaBin","MyBinder start");
        }
    }

    MyBinder binder;
    private ServiceConnection conn = new ServiceConnection() {       
        @Override
        public void onServiceDisconnected(ComponentName name) {         
        }
       
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binder = (MyBinder)service;
        }
    };
    binder.start();
Binder類預設實現了IBinder介面

activity和service通訊能不能用handler,當然不能。因為service沒法獲取activity中的handler引用,msg跟handler是一一對應的。就是說使用service中的handler傳送訊息,
只能service中的handler接收得到。。handler只是用來執行緒間的通訊,因為其它一個執行緒時候,可以把main執行緒中的handler傳遞過去。所以實現了通訊。。但是啟動service,是沒法把handler傳過去的。。

本質上不管是startactivity,startservice,bindservice其實都是通過AMS服務來排程的。。AMS(ActivityManagerService)屬於系統程序,
應用程序跟AMS其實是通過IBinder通訊的
,以後會分析下AMS,這裡簡單提一下。。


IBinder機制在framework層的使用

TelephonyManager telManager = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
telManager.dial("110");//實際這是錯的,因為dial方法在TelephonyManager類中被隱藏了,你是看不到滴,為了演示,呼叫dial方法。。

可以看到其實context.getSystemService獲取的並不是系統服務。。只是一個簡單的物件而已。。

ContextImpl.java

    @Override
    public Object getSystemService(String name) {
        ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
        return fetcher == null ? null : fetcher.getService(this);
    }
        registerService(TELEPHONY_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return new TelephonyManager(ctx.getOuterContext());
                }});

所以this.getSystemService(Context.TELEPHONY_SERVICE);返回的是TelephonyManager物件。


TelephonyManager.java

    /** @hide */
    @SystemApi
    public void dial(String number) {
        try {
            getITelephony().dial(number);
        } catch (RemoteException e) {
            Log.e(TAG, "Error calling ITelephony#dial", e);
        }
    }
    private ITelephony getITelephony() {
        return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
    }
	
    public static final String TELEPHONY_SERVICE = "phone";

可以看到此時真正執行的是ITelephony的dial方法。。可以看到“phone”型別的服務返回的一個IBinder

ServiceManager.getService獲取的才是真正的系統服務

public class PhoneInterfaceManager extends ITelephony.Stub {
    private PhoneInterfaceManager(PhoneGlobals app, Phone phone) {
		.......
        publish();
    }

    private void publish() {
        if (DBG) log("publish: " + this);
        ServiceManager.addService("phone", this);
    }
	
	public void dial(String number) {
        dialForSubscriber(getPreferredVoiceSubscription(), number);
    }
	public void dialForSubscriber(int subId, String number) {
        if (DBG) log("dial: " + number);
        String url = createTelUrl(number);
        if (url == null) {
            return;
        }
        // PENDING: should we just silently fail if phone is offhook or ringing?
        PhoneConstants.State state = mCM.getState(subId);
        if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
            Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            mApp.startActivity(intent);
        }
    }
}
可以看到,建構函式呼叫publish,然後註冊了“phone"這個服務。。ServiceManager.addService("phone", this);

然後dial方法,實際上走的是PhoneInterfaceManager類的dial方法 Intent  intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));  此時呼叫撥號介面進行撥號

PhoneInterfaceManager實現了ITelephony.Stub,所有最後來看看ITelephony.aidl檔案

ITelephony.aidl檔案

interface ITelephony {
    /**
     * Dial a number. This doesn't place the call. It displays
     * the Dialer screen.
     * @param number the number to be dialed. If null, this
     * would display the Dialer screen with no number pre-filled.
     */
    void dial(String number);
    .......
 }

最後:此時可以看到PhoneInterfaceManager可以當作是Server角色,TelephonyManager充當是Client角色。。。
TelephonyManager是公開的,我們程式碼中可以訪問得到,其實只是一個門面,系統暴露給我們的介面(TelephonyManager更多的用到使用它來查詢當前通話狀態啊等功能),真正還是通過IBinder機制去呼叫系統服務PhoneInterfaceManager去具體的實現。。PhoneInterfaceManager是隱藏的,我們看不到。。。


總結一下:

不管是AIDL還是在framework中通過ServiceManager進行跨程序呼叫其實,都是先把IBinder拿到,只有拿到這個兩個程序才能夠通訊

ServiceManager.getService(Context.TELEPHONY_SERVICE)得到的是IBinder

bindservice中的ServiceConnection得到的也是IBinder