1. 程式人生 > >Android Binder機制完全解析

Android Binder機制完全解析

概述

之前我寫過一篇文章Android Service全面解析,簡單實現瞭如何通過AIDL實現Service的跨程序通訊(IPC),其實是通過Binder機制來實現的,本文我們就重點來看看Binder機制的原理。

Binder可以提供系統中任何程式都可以訪問的全域性服務。這個功能當然是任何系統都應該提供的,下面我們簡單看一下Android的Binder的框架:
Android Binder框架分為伺服器介面Binder驅動、以及客戶端介面;簡單想一下,需要提供一個全域性服務,那麼全域性服務那端即是伺服器介面,任何程式即客戶端介面,它們之間通過一個Binder驅動訪問。

  • 伺服器介面:實際上是Binder類的物件,該物件一旦建立,內部則會啟動一個隱藏執行緒,接收Binder驅動傳送的訊息,收到訊息後,會執行Binder物件中的onTransact()函式,並按照該函式的引數執行不同的伺服器端程式碼。
  • Binder驅動:該物件也為Binder類的例項,客戶端通過該物件訪問遠端服務。
  • 客戶端介面:獲得Binder驅動,呼叫其transact()傳送訊息至伺服器。

例項實現

如果你覺得上面的描述太抽象了,沒關係,下面我們通過一個具體的例子來看看Binder機制的原理。例子我仍然使用上一篇文章的例子,不過之前我是使用Eclipse建立工程,今天我們使用Studio來建立專案,效果都是一樣的。

(1)Studio建立兩個Module,app代表客戶端程式,binder_server代表伺服器端程式。
這裡寫圖片描述

(2)建立aidl檔案
在app目錄上右鍵,NEW->AIDL->AIDL File,建立一個aidl檔案(IMyAidlInterface.aidl),同時必須要指明包名,包名必須和java目錄下的包名一致。此aidl檔案會預設生成到aidl目錄下,aidl目錄和java目錄同級別。
這裡寫圖片描述


IMyAidlInterface.aidl檔案內容:

interface IMyAidlInterface {
    int plus(int a, int b);
    String toUpperCase(String str);
}

build一下,會自動生成IMyAidlInterface.java檔案,不同於Eclipse的gen目錄,studio下的java檔案目錄為:
*(專案名)\app\build\generated\source\aidl\debug\com\hx\binder\IMyAidlInterface.java
關於IMyAidlInterface.java檔案內容,我們後面會具體分析,這裡先省略。

(3)將aidl檔案連同目錄一起拷貝到伺服器端
這裡寫圖片描述

(4)伺服器端新建服務MyRemoteService

public class MyRemoteService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        MainActivity.showlog("onCreate()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        MainActivity.showlog("onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        MainActivity.showlog("onDestroy()");
    }

    @Override
    public IBinder onBind(Intent intent) {
        MainActivity.showlog("onBind()");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        MainActivity.showlog("onUnbind()");
        return super.onUnbind(intent);
    }

    IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public String toUpperCase(String str) throws RemoteException {
            if (str != null) {
                return str.toUpperCase();
            }
            return null;
        }

        @Override
        public int plus(int a, int b) throws RemoteException {
            return a + b;
        }
    };
}

在Manifest中進行註冊:

<service android:name=".MyRemoteService"
         android:exported="true">
     <intent-filter>
           <action android:name="com.hx.action.remoteService" />
     </intent-filter>
</service>

(4)編寫客戶端程式碼

public class MainActivity extends Activity implements View.OnClickListener {

    private Button bindService;
    private Button unbindService;
    private Button plus;
    private Button toUpperCase;
    private IMyAidlInterface myAIDLInterface;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            myAIDLInterface = null;
            Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);
            Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        plus = (Button) findViewById(R.id.plus);
        toUpperCase = (Button) findViewById(R.id.toUpperCase);
        //button點選事件
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
        plus.setOnClickListener(this);
        toUpperCase.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bind_service:
                Intent intent = new Intent("com.hx.action.remoteService");
                //5.0以上安卓裝置,service intent必須為顯式指出
                Intent eintent = new Intent(getExplicitIntent(this,intent));
                bindService(eintent, connection, Context.BIND_AUTO_CREATE);
//              bindService(intent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_service:
                if(myAIDLInterface != null){
                    unbindService(connection);
                }
                break;
            case R.id.plus:
                if (myAIDLInterface != null) {
                    try {
                        int result = myAIDLInterface.plus(13, 19);
                        Toast.makeText(this, result + "", Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.toUpperCase:
                if (myAIDLInterface != null) {
                    try {
                        String upperStr = myAIDLInterface.toUpperCase("hello aidl service");
                        Toast.makeText(this, upperStr + "", Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }

    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }
        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);
        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);
        // Set the component to be explicit
        explicitIntent.setComponent(component);
        return explicitIntent;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical">

    <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind service" />

    <Button
        android:id="@+id/unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind service" />

    <Button
        android:id="@+id/plus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="13 + 19" />

    <Button
        android:id="@+id/toUpperCase"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="hello aidl service" />
</LinearLayout>

執行程式,看效果:
這裡寫圖片描述

我們首先點選BIND SERVICE按鈕,繫結服務,會彈出“onServiceConnected”的Toast,說明服務繫結成功,獲取到了伺服器端的Binder驅動。
服務端Log:
這裡寫圖片描述
然後分別點選13+19和hello aidl service按鈕,可以通過Binder驅動呼叫服務端的程式碼並返回正確的計算結果。
最後點選UNBIND SERVICE按鈕,我們的期望是彈出“onServiceDisconnected”的Toast,解除繫結,實際上呢?很遺憾沒有彈出。
服務端Log:
這裡寫圖片描述
由於我們當前只有一個客戶端綁定了此Service,所以Service呼叫了onUnbind和onDestory。當我們繼續點選13+19按鈕,發現依然可以正確執行得到結果,也就是說即使onUnbind被呼叫,連線也是不會斷開的,那麼什麼時候會斷開連線呢?
即當服務端被異常終止的時候,比如我們現在在手機的正在執行的程式中找到該服務,並強行停止它:
這裡寫圖片描述
可以看到這時彈出了“onServiceDisconnected”的Toast,說明連線被斷開。之後再次點選13+19按鈕,則會彈出Toast提示“伺服器被異常殺死,請重新繫結服務端”。

原理分析

還記得我們上面根據aidl檔案生成的Java檔案嗎?我們來看看它的結構吧:

public interface IMyAidlInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.hx.binder.IMyAidlInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.hx.binder.IMyAidlInterface interface,
         * generating a proxy if needed.
         */
        public static com.hx.binder.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.hx.binder.IMyAidlInterface))) {
                return ((com.hx.binder.IMyAidlInterface) iin);
            }
            return new com.hx.binder.IMyAidlInterface.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_plus: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.plus(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_toUpperCase: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.toUpperCase(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.hx.binder.IMyAidlInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public int plus(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(str);
                    mRemote.transact(Stub.TRANSACTION_toUpperCase, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_plus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_toUpperCase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int plus(int a, int b) throws android.os.RemoteException;

    public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException;
}

程式碼比較長,但思路還是比較清晰的,IMyAidlInterface.java檔案包含兩個靜態內部類—StubProxy(其中Proxy是Stub的內部類)。

public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface

其中Stub是個抽象類,它繼承了Binder,並實現了IMyAidlInterface介面。Stub提供了幾個方法:asInterfaceasBinderonTransact,但並沒有實現IMyAidlInterface介面的方法,所以需要交給Stub的實現類去實現。

private static class Proxy implements com.hx.binder.IMyAidlInterface

Proxy是Stub的內部類,也實現了IMyAidlInterface介面。並提供了幾個方法:asBindergetInterfaceDescriptor,並實現了IMyAidlInterface介面的方法plustoUpperCase

接下來看看服務端和客戶端是如何和這個檔案建立關聯的吧。
服務端:

IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
        @Override
        public String toUpperCase(String str) throws RemoteException {
            if (str != null) {
                return str.toUpperCase();
            }
            return null;
        }

        @Override
        public int plus(int a, int b) throws RemoteException {
            return a + b;
        }
    };

可以看到我們服務端提供的服務是由IMyAidlInterface.Stub來執行的,上面分析過,Stub這個類是Binder的子類,是不是符合我們文章開頭所說的服務端其實是一個Binder類的例項。而且mBinder實現了IMyAidlInterface介面的方法。
接下來看Stub的onTransact()方法:

public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_plus: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.plus(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_toUpperCase: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.toUpperCase(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

文章開頭也說到服務端的Binder例項會根據客戶端依靠Binder驅動發來的訊息,執行onTransact方法,然後由其引數決定執行服務端的程式碼。
可以看到onTransact有四個引數:code , data ,replay , flags

  • code:是一個整形的唯一標識,用於區分執行哪個方法,客戶端會傳遞此引數,告訴服務端執行哪個方法
  • data:客戶端傳遞過來的引數
  • reply:伺服器返回回去的值
  • flags:標明是否有返回值,0為有(雙向),1為沒有(單向)

我們仔細看case TRANSACTION_plus中的程式碼:

data.enforceInterface(DESCRIPTOR); 

與客戶端的writeInterfaceToken對應,標識遠端服務的名稱

int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();

接下來分別讀取了客戶端傳入的兩個引數

int _result = this.plus(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);

然後執行this.plus,即我們服務端實現的plus方法;返回result由reply寫回。
toUpperCase同理,可以看到服務端通過AIDL生成的Stub類,封裝了服務端本來需要寫的程式碼。

客戶端
客戶端主要通過ServiceConnected與服務端連線

private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnected = false;
            Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            isConnected = true;
            myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);
            Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();
        }
    };

其實這個onServiceConnected中的IBinder例項,其實就是我們文章開頭所說的Binder驅動,也是一個Binder例項。
在IMyAidlInterface.Stub.asInterface中最終呼叫了:

return new com.hx.binder.IMyAidlInterface.Stub.Proxy(obj);

這個Proxy例項傳入了我們的Binder驅動,並且封裝了我們呼叫服務端的程式碼,文章開頭說,客戶端會通過Binder驅動的transact()方法呼叫服務端程式碼。
直接看Proxy中的plus方法

            public int plus(int a, int b) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(a);
                    _data.writeInt(b);
                    mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

首先宣告兩個Parcel物件,一個用於傳遞資料,一個使用者接收返回的資料

_data.writeInterfaceToken(DESCRIPTOR);

與伺服器端的enforceInterfac對應

_data.writeInt(a);
_data.writeInt(b);

寫入需要傳遞的引數

mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);

終於看到了我們的transact方法,第一個對應服務端的code,_data,_reply分別對應服務端的data,reply,0表示是雙向的

_reply.readException();
_result = _reply.readInt();

最後讀出我們服務端返回的資料,然後return。可以看到和服務端的onTransact基本是一行一行對應的。

注意:

  • 當客戶端呼叫transact方法發起RPC(遠端過程呼叫)請求後,當前執行緒會掛起,等待伺服器端的返回結果。所以如果一個遠端方法很耗時,那麼不能再UI執行緒中呼叫此遠端方法。
  • 服務端的Binder方法(onTransact)執行在Binder執行緒池中,所以Binder方法不管是否耗時,都應該採取同步機制,因為它已經執行在一個執行緒中了。

到此,我們已經通過AIDL生成的程式碼解釋了Android Binder框架的工作原理。Service的作用其實就是為我們建立Binder驅動,即服務端與客戶端連線的橋樑。
AIDL其實通過我們寫的aidl檔案,幫助我們生成了一個介面,一個Stub類用於服務端,一個Proxy類用於客戶端呼叫。

這裡寫圖片描述

不依賴AIDL實現IPC通訊

那麼我們是否可以不通過寫aidl檔案來實現遠端的通訊呢?下面向大家展示如何完全不依賴AIDL來實現客戶端與服務端的通訊。
服務端程式碼:

public class MyRemoteService extends Service {
    private static final String DESCRIPTOR = "MyRemoteService";
    private static final int TRANSACTION_plus = 0x110;
    private static final int TRANSACTION_toUpperCase = 0x111;

    @Override
    public void onCreate() {
        super.onCreate();
        MainActivity.showlog("onCreate()");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        MainActivity.showlog("onStartCommand()");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        MainActivity.showlog("onDestroy()");
    }

    @Override
    public IBinder onBind(Intent intent) {
        MainActivity.showlog("onBind()");
        return mBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        MainActivity.showlog("onUnbind()");
        return super.onUnbind(intent);
    }

    private MyBinder mBinder = new MyBinder();
    private class MyBinder extends Binder {
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_plus: {
                    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 TRANSACTION_toUpperCase: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = _arg0.toUpperCase();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

    };
}
<service android:name=".MyRemoteService"
         android:exported="true">
      <intent-filter>
           <action android:name="com.hx.action.remoteService" />
      </intent-filter>
</service>

客戶端程式碼:

public class MainActivity extends Activity implements View.OnClickListener {
    private Button bindService;
    private Button unbindService;
    private Button plus;
    private Button toUpperCase;
    private IBinder myBinder;

    private static final String DESCRIPTOR = "MyRemoteService";
    private static final int TRANSACTION_plus = 0x110;
    private static final int TRANSACTION_toUpperCase = 0x111;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
            myBinder = null;
            Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = service;
            Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        plus = (Button) findViewById(R.id.plus);
        toUpperCase = (Button) findViewById(R.id.toUpperCase);
        //button點選事件
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
        plus.setOnClickListener(this);
        toUpperCase.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bind_service:
                Intent intent = new Intent("com.hx.action.remoteService");
                //5.0以上安卓裝置,service intent必須為顯式指出
                Intent eintent = new Intent(getExplicitIntent(this,intent));
                bindService(eintent, connection, Context.BIND_AUTO_CREATE);
//              bindService(intent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_service:
                if(myBinder != null){
                    unbindService(connection);
                }
                break;
            case R.id.plus:
                if (myBinder != null) {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    int _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        _data.writeInt(78);
                        _data.writeInt(95);
                        myBinder.transact(TRANSACTION_plus, _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();
                    }
                } else {
                    Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.toUpperCase:
                if (myBinder != null) {
                    android.os.Parcel _data = android.os.Parcel.obtain();
                    android.os.Parcel _reply = android.os.Parcel.obtain();
                    java.lang.String _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        _data.writeString("my new programe");
                        myBinder.transact(TRANSACTION_toUpperCase, _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();
                    }
                } else {
                    Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
    }

    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }
        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);
        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);
        // Set the component to be explicit
        explicitIntent.setComponent(component);
        return explicitIntent;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical">

    <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="bind service" />

    <Button
        android:id="@+id/unbind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="unbind service" />

    <Button
        android:id="@+id/plus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="78 + 95" />

    <Button
        android:id="@+id/toUpperCase"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAllCaps="false"
        android:text="my new programe" />
</LinearLayout>

這裡我們並沒有寫aidl檔案,而是將aidl生成的java檔案的處理邏輯移動到我們的服務端和客戶端來實現。同樣會達到和上面一樣的效果。
這裡寫圖片描述

IPC傳遞自定義型別Bean

不過還有一點需要說明的是,由於這是在不同的程序之間傳遞資料(引數和返回值),Android對這類