Android 內核 - 01 Binder

Binder 概述

Binder是一種架構,一種進程間通信(IPC)的機制,它工作在Linux內核層面,屬於驅動層的一個模塊。
但它其實並不是與硬件打交道,是上層消息轉發的一個通道。工作在內核層效率更高。

pic_1


Binder的應用


在這種架構中有三個模塊,服務端接口,Binder驅動,客戶端接口。

服務端接口:是Binder的一個對象,向客戶端提供服務,通過重載onTransact()接口來實現業務。

Binder驅動:當創建一個服務Binder對象後,它同時會在Binder驅動中創建一個Binder的mRemote對象。
                      當客戶端需要Binder服務時,都是通過mRemote對象來獲得的。這是一種代理方式。

客戶端接口:客戶端需要獲取服務,必須獲取遠程服務的代理的引用。客戶端實現自己的業務時可以獲得服務端支持。




一個簡單的Binder應用的實例


實例分爲兩部分 Server App 和 Client App,是兩個獨立的App程序。
Client會綁定一個Server的Service,將當前時間傳遞給Server,Server端收到後,再將它的時間添加在後面
返回給Client端。

Server 端
一個普通的Service類,其中包含了一個Binder子類。其中 Binder子類重載了onTransact函數用於處理Client端過來的請求。

public class SampleService extends Service
{
    private static final String TAG = "BINDServer";
    private static final String DESCRIPTOR = "TESTING";  
    public ServiceBinder mBinder = new ServiceBinder();
    private static int m_CallingTimes = 0;

    @Override
    public void onCreate() 
    {
        Log.d(TAG, "SampleService::onCreate()");
        super.onCreate();
    }

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

    @Override
    public void onDestroy() 
    {
        Log.d(TAG, "SampleService::onDestroy()");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent arg0) 
    {
        return mBinder;
    }

    public class ServiceBinder extends Binder 
    {
        protected boolean onTransact(int code, Parcel data, Parcel reply,  
                int flags) throws RemoteException  
        {
            Log.d(TAG, "ServiceBinder::onTransact() code=" + code);
            String strResult = "";
            switch (code)  
            {
                case 0x01:  
                {  
                    data.enforceInterface(DESCRIPTOR);  
                    String _arg0;  
                    _arg0 = data.readString();
                    strResult = getInfo(_arg0, "===>");
                    reply.writeNoException();
                    reply.writeString(strResult);
                    Log.d(TAG, "ServiceBinder::onTransact() result=" + strResult);
                    return true;  
                }  
                default:
                    break;
            }

            return super.onTransact(code, data, reply, flags);  
        }

        public String getInfo(String strRecv, String strCode)
        {
            Date date = new Date();
            String strResult = new String(date.toString());
            strResult = m_CallingTimes + "::" + strRecv + strCode + strResult;

            return strResult;
        }
    }
}


Client 端
一個普通的Activity,它取得當前時間,並將當前時間的字符串打包發給Server端。

1)  它通過bindService函數取得了Server端Binder的對象。
    public void bindService(View view)
    {
        Log.d(TAG, "bindService");
        Intent intent = new Intent();  
        intent.setAction("com.example.bindersample.TestInterface");  

        boolean bindFlag = bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);  
        Log.e(TAG, "bindService() = " + bindFlag);
    }
Clinet端函數中通過setAction指定 <intent-filter> = com.example.bindersample.TestInterface 

這個Inter-filter是定義在Server端的AndroidManifest.xml中,定義了這個action android:name,系統就知道如何查找了
        <service android:name="SampleService">
            <intent-filter>
                <action android:name="com.example.bindersample.TestInterface" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>            
        </service>

2)  當Client取得了Binder對象後,可以調用它的 transact() 接口,來使用Service的服務了
boolean android.os.IBinder.transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException

關於參數的簡單說明
int code:可以通過code告訴Server端提供不同類型的服務
Parcel data:傳遞給Server端的參數
Parcel reply:從Server端返回的參數
int flags:0 表示當前的操作是一個普通的RPC,如果是1 (ONE_WAY),則表示是一個one way 的RPC。

public class MainActivity extends Activity 
{
    private static final String TAG = "BinderClient";
    private IBinder mBinder;  
    private ServiceConnection mServiceConn = new ServiceConnection()  
    {  
        @Override  
        public void onServiceDisconnected(ComponentName name)  
        {  
            Log.d(TAG, "mServiceConn onServiceDisconnected");  
        }  

        @Override  
        public void onServiceConnected(ComponentName name, IBinder service)  
        {  
            Log.d(TAG, "mServiceConn onServiceConnected");            
            mBinder = service;  
        }  
    };  

    public void bindService(View view)
    {
        Log.d(TAG, "bindService");
        Intent intent = new Intent();  
        intent.setAction("com.example.bindersample.TestInterface");  

        boolean bindFlag = bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);  
        Log.e(TAG, "bindService() = " + bindFlag);
    }

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

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

    public void testClick(View view)
    {
        if (mBinder == null)  
        {  
            Log.d(TAG, "testClick() mBinder is NULL"); 
        }
        else  
        {  
            android.os.Parcel _data = android.os.Parcel.obtain();  
            android.os.Parcel _reply = android.os.Parcel.obtain();  
            String _result;
            try  
            {
                Date date = new Date();

                _data.writeInterfaceToken("TESTING");  
                _data.writeString(date.toString());  

                mBinder.transact(0x01, _data, _reply, 0);  
                _reply.readException();  
                _result = _reply.readString();  
                Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();  
            }
            catch (RemoteException e)  
            {  
                e.printStackTrace();  
            } 
            finally  
            {
                _reply.recycle();  
                _data.recycle();  
            }  
        }  
    }  
}


Binder運行的基本流程


  • 獲取Binder對象

1)  Server端的Service需要被啓動,並且Server端的Binder對象實現了public IBinder onBind(Intent arg0)  接口
      可以將它的Binder對象提供給外部。

2)  Client端需要調用bindService接口,來獲取Server端的Binder對象的引用。
      public void bindService(View view)
      {
          Log.d(TAG, "bindService");
          Intent intent = new Intent();  
          intent.setAction("com.example.bindersample.TestInterface");  
          boolean bindFlag = bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);  
          Log.e(TAG, "bindService() = " + bindFlag);
      }

public boolean bindService (Intent serviceServiceConnection conn, int flags) 

Intent service:指定Client端需要的Service, 系統通過intent.setAction("com.example.bindersample.TestInterface");  
                          中的名字會找到指定的Service。
而Client端指定的這個Action name是在Server端的AndroidManifest.xml中定義的。
        <service android:name="SampleService">
            <intent-filter>
                <action android:name="com.example.bindersample.TestInterface" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>            
        </service>

ServiceConnection conn:它的兩個接口是由系統進行回調的,主要是當Service 連接後和斷開後會被執行。
    private ServiceConnection mServiceConn = new ServiceConnection()  
    {  
        @Override  
        public void onServiceDisconnected(ComponentName name)  
        {   }  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service)  
        {   }  
    };  

在實例程序中,當Client與Server連接後,在onServiceConnected接口中可以取得Server端Binder的引用。

pic_2


  • Client 與 Server交互

1)  Client 組織好數據包裹後,調用 Binder引用的transact方法
2)  他會通知 Binder Driver有消息需要傳遞了,Binder會阻塞當前的Client線程
3)  Binder Driver會找到Server的遠程對象,並將Client的數據包裹傳遞給Server
4)  Server收到通知,在onTransact中可以對包裹進行處理,如果需要也可以將處理後的數據再返回Client端。
5)  Server處理完成後,通知Binder Driver,Binder Driver會使客戶端線程得以返回,並繼續以運行。

pic_3

關於 android.os.Parcel


它是Server端和Client端用來傳遞數據的包裹
1)  獲得包裹  android.os.Parcel.obtain();  

Retrieve a new Parcel object from the pool. 使用pool的方式效率一定是比創建/釋放的方式要高許多。


2)  接口名字 android.os.Parcel.enforceInterface(String interfaceName) 和 
          android.os.Parcel.writeInterfaceToken(String interfaceName)

可以通過這兩個接口來爲包裹設置名字,以保證他們在同一個Session中。


3)  拆包裹  android.os.Parcel.readString();
這裏當然不光是String,還可以readInt,readDouble等等,可以根據封包的順序來拆包。
注意: 關於 readException();  

API的解釋 :

Special function for reading an exception result from the header of a parcelto be used after receiving the result of a transaction.  This will throw the exception for you if it had been written to the Parcel,  otherwise return and let you read the normal result data from the Parcel.



4)  封包裹 android.os.Parcel.writeString();
這裏當然不光是String,還可以Int,Double等等,與拆包對應。





發佈了235 篇原創文章 · 獲贊 31 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章