天天看點

Android進階之Service遠端通信AIDLService的遠端通信AIDL

Service的遠端通信AIDL

先解釋下什麼是AIDL,AIDL (Android Interface Definition Language)是一種IDL 語言,用于生成可以在Android裝置上兩個程序之間進行程序間通信(IPC)的代碼。如果在一個程序中(例如Activity)要調用另一個程序中(例如Service)對象的操作,就可以使用AIDL生成可序列化的參數。

AIDL IPC機制是面向接口的、輕量級的。通過AIDL定義的接口可以實作伺服器端與用戶端的IPC通信。

使用AIDL實作IPC(程序間通訊)

說明:

以下代碼我用的是最新版的SDK,android6.0 api

建立.aidl檔案

// IAppServiceRemoteBinder.aidl
package me.dengfengdecao.android.servicedemo;

// Declare any non-default types here with import statements

interface IAppServiceRemoteBinder {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void syncData(String data);
}
           

其中 syncData方法用來和用戶端同步資料,測試用的。

實作AIDL接口

在RemoteService中的onBind方法中傳回aidl接口中Stub内部抽象類的實作,這裡使用了匿名對象:

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "onBind");
    return new IAppServiceRemoteBinder.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d(TAG, "basicTypes: ");
        }

        // data:從用戶端同步過來的資料
        @Override
        public void syncData(String data) throws RemoteException {
            Log.d(TAG, "syncData: 要同步的資料:" + data);
            defaultdata += data;
        }
    };

}
           

檢視自定義的aidl接口可知Stub為aidl的抽象内部類,并且它繼承自Binder類和實作我們自定義的aidl接口。

/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements me.dengfengdecao.android.servicedemo.IAppServiceRemoteBinder
           

這個内部抽象類是必須要實作的。

注意:

服務端AIDL的任何修改都必須的同步到所有的用戶端,否則用戶端調用服務端得接口可能會導緻程式異常(因為此時用戶端此時可能會調用到服務端已不再支援的接口)。

這樣,onBind方法就傳回了一個Stub類的對象,該對象為service提供了RPC接口。該對象會被傳遞到實作了ServiceConnection接口的類的public void onServiceConnected(ComponentName name, IBinder service)方法中。

實作ADIL接口時需要注意一下幾點:

  • 不能保證所有對aidl接口的調用都在主線程中執行,是以必須考慮多線程調用的情況,也就是必須考慮線程安全。
  • 預設IPC調用是同步的。如果已知IPC服務端會花費很多毫秒才能完成,那就不要在Activity或View線程中調用,否則會引起應用程式挂起(Android可能會顯示“應用程式未響應”對話框),可以試着在獨立的線程中調用。
  • 不會将異常傳回給調用方

下一步中該對象将會向用戶端公開,這樣用戶端就可以通過該對象與該service進行互動了。

向用戶端暴露接口

在完成了接口的實作後需要向用戶端暴露接口了,也就是釋出服務,實作的方法是繼承 Service,然後實作以Service.onBind(Intent)傳回一個實作了接口的類對象。

下面的代碼片斷表示了暴露IRemoteService接口給用戶端的方式。

@Override
public IBinder onBind(Intent intent) {
    Log.d(TAG, "onBind");
    return new IAppServiceRemoteBinder.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d(TAG, "basicTypes: ");
        }

        // data:從用戶端同步過來的資料
        @Override
        public void syncData(String data) throws RemoteException {
            Log.d(TAG, "syncData: 要同步的資料:" + data);
            defaultdata += data;
        }
    };

}
           

即在繼承了Service的類中的onBind方法中傳回aidl接口的Binder對象。

然後在manifest中注冊遠端服務:

<service
        android:name=".RemoteService"
        android:process=":remote"
        >
        <intent-filter>
            <action android:name="me.dengfengdecao.action.REMOTE_SERVICE"/>
        </intent-filter>
 </service>

android:process=":remote",指定建立一個遠端服務在新的程序中。
           

注意,最後要将遠端服務端的aidl copy到用戶端,包括完整的包名。

現在,如果用戶端(ClientActivity)調用bindService()來連接配接該服務端(RemoteService) ,用戶端的onServiceConnected()回調函數将會獲得從服務端(RemoteService )的onBind()傳回的Binder對象。

ClientActivity清單(為了易讀,删除了view代碼):

public class ClientActivity extends AppCompatActivity implements View.OnClickListener, ServiceConnection {

    private static final String TAG = "ClientActivity";

    // 遠端服務的AIDL
    private IAppServiceRemoteBinder mRemoteBinder;

    private Intent mIntent;

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

        // 隐式intent啟動遠端服務
        mIntent = new Intent();
        mIntent.setAction("me.dengfengdecao.action.REMOTE_SERVICE");

    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "onServiceConnected");
        // 接收從服務端傳遞過來的IBinder對象
        mRemoteBinder = IAppServiceRemoteBinder.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "onServiceDisconnected: ");

    }
}
           

ok,現在啟動ClientActivity如下:

Android進階之Service遠端通信AIDLService的遠端通信AIDL

綁定到遠端服務,日志如下:

Android進階之Service遠端通信AIDLService的遠端通信AIDL

可以看到,從遠端服務那裡接收到了IBinder對象。

切換到遠端端,日志如下:

Android進階之Service遠端通信AIDLService的遠端通信AIDL

輸入要同步到遠端端的資料,日志如下:

Android進階之Service遠端通信AIDLService的遠端通信AIDL

切換回ClientActivity 解綁服務,日志如下:

Android進階之Service遠端通信AIDLService的遠端通信AIDL

可以看到,和遠端服務斷開了關聯,切換到系統正在運作的app:

Android進階之Service遠端通信AIDLService的遠端通信AIDL

可以确定,确實和遠端服務斷開了連接配接。

如果對遠端傳輸對象感興趣請看官方文檔:

http://developer.android.com/intl/zh-cn/guide/components/aidl.html#PassingObjects

還可以和郭霖前輩的這篇博文一起學習:

http://blog.csdn.net/guolin_blog/article/details/9797169

OK,如果有任何問題請指出,謝謝!

部分内容參考:

http://www.cnblogs.com/hibraincol/archive/2011/09/06/2169325.html