1. 程式人生 > >Android進階之路——AIDL

Android進階之路——AIDL

一、AIDL
AIDL(Android Interface Definition Language)android介面定義語言,它可以用於讓某個service與多個應用程式元件之間進行跨程序通訊,從而可以實現多個應用程式共享一個service的功能。官方的文件是這樣解釋AIDL的:
Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
第一句最重要,“只有當你允許來自不同的客戶端訪問你的服務並且需要處理多執行緒問題時你才必須使用AIDL”,其他情況你都可以選擇其他方法,如使用messager,也能跨程序通訊。可見AIDL是處理多執行緒、多客戶端併發訪問的,而Messager是單執行緒處理。
二、AIDL的使用


首先需要建立一個AIDL檔案,在這個檔案中定義好Activity需要與Service進行通訊的方法。首先看一下Demo工程的結構圖
這裡寫圖片描述

這裡寫圖片描述
然後再來看看實現,首先定義AIDL介面,客戶端和服務端都要定義,並且要在同一個包中。我在這個Demo中定義了兩個介面檔案,一個是服務端要實現的方法,如果這個方法是非同步執行的,那麼就需要回調,但是server是在另一個程序,我們怎麼去呼叫呢?現在先不細說,這裡的另一個介面就是用來回調的。

package com.test.server;
import com.test.server.ITaskCallback;
interface ITaskBinder {
       void registerCallback(ITaskCallback cb);
void unregisterCallback(ITaskCallback cb); }
package com.test.server;
interface ITaskCallback {
    void actionPerformed(int actionId);
}

編譯後發現目錄結構build/generated/source/aidl/debug/com.text.server/有相應的.java檔案。
這裡寫圖片描述
然後修改MyService中的程式碼,在其中實現我們剛剛定義的好的一個介面。

public class MyService extends Service {
private static final String TAG = "tag"; @Override public void onCreate() { printf("service create"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { printf("service start id=" + startId); callback(startId); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent t) { printf("service on bind"); return mBinder; } @Override public void onDestroy() { printf("service on destroy"); super.onDestroy(); } @Override public boolean onUnbind(Intent intent) { printf("service on unbind"); return super.onUnbind(intent); } public void onRebind(Intent intent) { printf("service on rebind"); super.onRebind(intent); } private void printf(String str) { Log.v(TAG, "###################------ " + str + "------"); } void callback(int val) { printf("val: " + val + ""); final int N = mCallbacks.beginBroadcast(); printf(N + ""); for (int i = 0; i < N; i++) { try { mCallbacks.getBroadcastItem(i).actionPerformed(val); } catch (RemoteException e) { } } mCallbacks.finishBroadcast(); } private final ITaskBinder.Stub mBinder = new ITaskBinder.Stub() { @Override public void registerCallback(ITaskCallback cb) throws RemoteException { if (cb != null) mCallbacks.register(cb); } @Override public void unregisterCallback(ITaskCallback cb) throws RemoteException { if (cb != null) mCallbacks.unregister(cb); } }; final RemoteCallbackList<ITaskCallback> mCallbacks = new RemoteCallbackList<ITaskCallback>(); }

這樣就完成了服務端的程式碼,大概解釋一下程式碼,首先實現了一個介面的兩個方法,在這個方法中可以實現一下邏輯(當然也可以新建一個執行緒去實現,這樣可以更好的體現回撥),然後在onBind()方法中將MyAIDLService.Stub的實現返回。這裡為什麼可以這樣寫呢?因為Stub其實就是Binder的子類,所以在onBind()方法中可以直接返回Stub的實現。在最後一行,可以看到系統提供的回撥的類,系統也是通過廣播來實現不同程序之間的訊息通知。在onStartCommand()方法中呼叫了callback()方法,來通知其他程序。
然後看客戶端的程式碼,客戶端的程式碼僅實現一個activity的類。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "aidltest";
    private Button btnOk;
    private Button btnCancel, btnStart;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.activity_main);

        btnOk = (Button) findViewById(R.id.btn_ok);
        btnCancel = (Button) findViewById(R.id.btn_cancel);
        btnStart = (Button) findViewById(R.id.start);
        btnCancel.setEnabled(false);


        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Bundle args = new Bundle();
                Intent intent = new Intent("com.test.server.MyService");
                intent.putExtras(args);
                startService(intent);
//                bindService(intent, mConnection, BIND_AUTO_CREATE);
                btnCancel.setEnabled(true);
            }
        });
        btnOk.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                onOkClick();
            }
        });

        btnCancel.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                onCancelClick();
            }
        });
    }

    void onOkClick() {
        printf("send intent to start");
        Bundle args = new Bundle();
        Intent intent = new Intent("com.test.server.MyService");
        intent.putExtras(args);
//        startService(intent);
        bindService(intent, mConnection, BIND_AUTO_CREATE);
        btnCancel.setEnabled(true);
    }

    void onCancelClick() {

        printf("send intent to stop");
        //unbindService(mConnection);
        Intent intent = new Intent("com.cmcc.demo.IMyService");
        stopService(intent);
        btnCancel.setEnabled(false);
    }

    private void printf(String str) {
        Log.v(TAG, "###################------ " + str + "------");
    }

    ITaskBinder mService;

    private ServiceConnection mConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = ITaskBinder.Stub.asInterface(service);
            try {
                mService.registerCallback(mCallback);
                printf("stopRunningTask: " + mService.stopRunningTask() + "");
            } catch (RemoteException e) {

            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mService = null;
        }
    };

    private ITaskCallback mCallback = new ITaskCallback.Stub() {

        public void actionPerformed(int id) {
            printf("callback id=" + id);
        }
    };

}

我們只是修改了ServiceConnection中的程式碼。可以看到,這裡首先使用了MyAIDLService.Stub.asInterface()方法將傳入的IBinder物件傳換成了ITaskBinder物件,接下來就可以呼叫在ITaskBinder.aidl檔案中定義的所有介面了。
這樣這個Demo就完成了,執行程式,兩個程式間就可以有資料的互動了。
三、AIDL小結
1、AIDL (Android Interface Definition Language )
2、AIDL 適用於 程序間通訊,並且與Service端多個執行緒併發的情況,如果只是單個執行緒 可以使用 Messenger ,如果不需要IPC 可以使用Binder。
3、AIDL語法:基礎資料型別都可以適用,List Map等有限適用。static field 不適用。
4、 客戶端和服務端的AIDL介面檔案所在的包必須相同
5、 需要一個Service類的配合