Binder的使用方法和原始碼解析

鍵盤舞者113發表於2017-04-01

1.BInder驅動機制說明

首先Binder是用於程式間通訊,這個我們都知道,但是它是基於Binder驅動來完成程式間通訊的。Binder驅動是一個驅動程式,而這個驅動程式存在於Linux核心層,Linux核心層又可以說是作業系統層,是程式的管理者。

一個程式A要呼叫程式B的一個方法,這個程式A就需要獲取程式B的Binder代理物件,這個BInder代理物件就是程式B的BInder本地物件。當然這個Binder本地物件需要我們在程式B去實現在Service裡面,實現的同時還要實現程式A想要呼叫方法。而在程式A裡也要實現Binder代理物件。 程式A呼叫Binder代理物件時會通知Binder驅動,Binder驅動又會去呼叫程式B實現的Binder本地物件到實現的方法。

這裡要注意為何BInder驅動能夠知道程式A和程式B的所在,首先程式也可以看做記憶體裡的一個地址,然後無論是BInder代理物件和Binder本地物件都會實現IBinder介面,都會在ServiceManager裡註冊地址通過所在的Service的名字(比如:Action)註冊的,這樣一來雙方通過像是訪問Service就可以訪問對方的IBinder實現類。首先要注意無論是註冊還是傳遞資料、回撥資料都是通過Binder驅動實現,具體Binder驅動如何實現我們不需要知道,我們所能做的就是實現BInder本地物件和Binder代理物件。

2.BInder的使用方法

Server端負責實現Binder本地物件,Client負責實現Binder代理物件,實現Client端要呼叫Server完成兩個不同計算函式。這裡既然要呼叫兩個函式,我們就要教兩者學會如何分辨,就像使用handler一樣,通過數字來分辨,畢竟佔用位元組少。

mPlusBinder.transact(0x110, _data, _reply, 0);

因為呼叫函式會有返回值,所以我們在使用Binder代理物件呼叫函式時需要首先傳輸資料過去,然後在接收資料。

                _data.writeInterfaceToken("CalcPlusService");
                _data.writeInt(36);
                _data.writeInt(12);
                mPlusBinder.transact(0x111, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();

同時Server端需要首先接收函式,進過計算再返回值

                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = _arg0 * _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);

完整例項程式碼如下

BinderServer的程式碼

public class CalcPlusService extends Service
{
    private static final String DESCRIPTOR = "CalcPlusService";
    private static final String TAG = "CalcPlusService";

    public void onCreate()
    {
        Log.e(TAG, "onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Log.e(TAG, "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    public IBinder onBind(Intent t)
    {
        Log.e(TAG, "onBind");
        return mBinder;
    }

    public void onDestroy()
    {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
    }

    public boolean onUnbind(Intent intent)
    {
        Log.e(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    public void onRebind(Intent intent)
    {
        Log.e(TAG, "onRebind");
        super.onRebind(intent);
    }

    private MyBinder mBinder = new MyBinder();

    private class MyBinder extends Binder
    {
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply,
                                     int flags) throws RemoteException
        {
            switch (code)
            {
                case 0x110:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = _arg0 * _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case 0x111:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = _arg0 / _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

    };

}  


<service android:name="com.example.zhy_binder.CalcPlusService" >
    <intent-filter>
        <action android:name="com.zhy.aidl.calcplus" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

BinderClient

public class MainActivity extends Activity
{

    private IBinder mPlusBinder;
    private ServiceConnection mServiceConnPlus = new ServiceConnection()
    {
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
            Log.e("client", "mServiceConnPlus onServiceDisconnected");
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {

            Log.e("client", " mServiceConnPlus onServiceConnected");
            mPlusBinder = service;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    public void bindService(View view)
    {
        Intent intentPlus = new Intent();
        intentPlus.setAction("com.zhy.aidl.calcplus");
        boolean plus = bindService(intentPlus, mServiceConnPlus,
                Context.BIND_AUTO_CREATE);
        Log.e("plus", plus + "");
    }

    public void unbindService(View view)
    {
        unbindService(mServiceConnPlus);
    }

    public void mulInvoked(View view)
    {

        if (mPlusBinder == null)
        {
            Toast.makeText(this, "未連線服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();
        } else
        {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            int _result;
            try
            {
                _data.writeInterfaceToken("CalcPlusService");
                _data.writeInt(50);
                _data.writeInt(12);
                mPlusBinder.transact(0x110, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
                Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();

            } catch (RemoteException e)
            {
                e.printStackTrace();
            } finally
            {
                _reply.recycle();
                _data.recycle();
            }
        }

    }

    public void divInvoked(View view)
    {

        if (mPlusBinder == null)
        {
            Toast.makeText(this, "未連線服務端或服務端被異常殺死", Toast.LENGTH_SHORT).show();
        } else
        {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            int _result;
            try
            {
                _data.writeInterfaceToken("CalcPlusService");
                _data.writeInt(36);
                _data.writeInt(12);
                mPlusBinder.transact(0x111, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
                Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();

            } catch (RemoteException e)
            {
                e.printStackTrace();
            } finally
            {
                _reply.recycle();
                _data.recycle();
            }
        }

    }
}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="bindService"
        android:text="BindService" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="unbindService"
        android:text="UnbindService" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="mulInvoked"
        android:text="50*12" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="divInvoked"
        android:text="36/12" />

</LinearLayout>  

相關文章