Android開發之進程間通信AIDL的探究和學習

轉載請註明出處:http://blog.csdn.net/li0978/article/details/52075558

AIDL (Android Interface Definition Language) 顧名思義就是android接口定義語言,用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼。

我們知道android應用程序語言的許多思想來自java,而且又基於linux內核,android系統的中每一個應用程序都是一個虛擬機,android在定義之初便規定進程內的線程之間是可以通信的,而進程與進程之間是封閉的,這樣做的目的是爲了保證每個應用程序內部數據的穩定性和安全性,也不至於一個程序掛掉而另外一個程序也掛掉。但是非要程序與程序之間進行通信怎麼辦呢,這種需求又很常見,因此google便定義了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.

以上英文是官方文檔,我們根據文檔說明可以得到以下信息:

1.條件:有IPC、有多線程、有多個應用程序。方式:AIDL。

2.條件:沒有IPC、沒有多線程、有多個應用程序。方式:Binder。

3.條件:有IPC、沒有多線程。方式:Messenger。

補充:如果使用AIDL,在此之前一定要理解服務,因爲一般是在服務中實現aidl中的方法的。

所以針對進程間通信,我們要視情況而取合適的方式去解決。這樣纔會不耽誤功夫。

下面我就以一個計算器的例子來看AIDL是如何使用的,在這裏我要建兩個應用程序。一個作爲客戶端,一個作爲服務端。客戶端用於顯示輸入的數據和結果,服務端用於數據的計算。


1.創建服務端,並建立添加aidl文件。androidstudio與eclipse建立aidl稍有不同,eclipse中sdk中沒有集成相關快速建立aidl文件的插件,需要我們手動新建aidl包,手動添加aidl文件,androidstudio中可直接在相應model右鍵菜單新建文件下尋找到創建aidl文件。

// IMyAidlInterface.aidl
package com.example.aidltest;

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

interface IMyAidlInterface {
    /**
     * 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);
}
默認添加aidl格式中有一個basicTypes()方法,這個方法只是告訴你aidl支持哪些參數傳入,除了支持這幾種類型外,還支持序列化parcelable的傳遞。另外說明一下,我們知道aidl從表面上看是隻是一個接口文件,後綴名是.aidl,這在編譯的過程中adt是不認的,只有編譯成.java的java文件才能使用。eclipse中支持自動編譯。但是androidstudio中是不支持自動編譯的,所以我們要手動編譯一下(Rebuild)。

// IMyAidlInterface.aidl
package com.example.aidltest;

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

interface IMyAidlInterface {

   //計算兩個int數值的和;
   int add(int num1,int num2);
}
編譯過後,我們就開一個service,在service中去實現aidl中的方法,別忘了配置文件中註冊此服務。

public class IMyService extends Service {

    /**
     * 當客戶端綁定到該服務的時候,執行此方法。
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    private IBinder iBinder = new IMyAidlInterface.Stub() {
        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.e("服務端","收到了客戶端的請求,輸入的參數是:"+num1+"和"+num2);
            return num1+num2;
        }
    };
}
2.創建客戶端
因爲AIDL作爲客戶端和服務端通信的中間橋樑,所以我們必須保證客戶端和服務端的AIDL一模一樣,所以我在創建客戶端後將服務端的AIDL所在的包和類全部拷貝到客戶端來。

界面這裏就不提了,放張圖:

客戶端的邏輯處理:

a.先綁定服務端的service。

b.拿到服務端的service。

c.調用服務端的service,並獲取結果,顯示結果。

d.客戶端退出時結束服務端service的綁定。

public class MainActivity extends AppCompatActivity {
    private EditText num1_et,num2_et;
    private Button result_bt;
    private TextView result_tv;

    IMyAidlInterface iMyAidlInterface;

    private ServiceConnection conn = new ServiceConnection() {

        //綁定遠程服務的時候
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //拿到了遠程的服務
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        //斷開遠程服務的時候
        @Override
        public void onServiceDisconnected(ComponentName name) {
            //回收資源
            iMyAidlInterface = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        result_bt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String num1 = num1_et.getText().toString().trim();
                String num2 = num2_et.getText().toString().trim();
                if (TextUtils.isEmpty(num1)||TextUtils.isEmpty(num2)){
                    Toast.makeText(MainActivity.this,"參數不能爲空",Toast.LENGTH_SHORT).show();
                }else {
                    try {
                        //調用遠程服務獲取結果
                        int res = iMyAidlInterface.add(Integer.valueOf(num1),Integer.valueOf(num2));
                        result_tv.setText(res+"");
                    } catch (RemoteException e) {
                        e.printStackTrace();
                        Toast.makeText(MainActivity.this,"獲取結果失敗,檢查參數格式是否正確。",Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });

        //綁定服務端的service
        bindService();
    }

    private void initView() {
        num1_et = (EditText) findViewById(R.id.num1_et);
        num2_et = (EditText) findViewById(R.id.num2_et);
        result_bt = (Button) findViewById(R.id.result_bt);
        result_tv = (TextView) findViewById(R.id.result_tv);
    }

    private void bindService() {
        //綁定服務端的服務,由於服務端和客戶端不在一個進程,所以我們需要直接指定服務端的包名和類名,注意類名要寫全。
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.example.aidltest","com.example.aidltest.IMyService"));
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //斷開服務端的service
        unbindService(conn);
    }
}
3.驗證AIDL,查看進程間通信是否成功。

先後開啓服務端和客戶端,輸入相關參數。

我們服務端控制檯中能夠獲取服務端service接受到的兩個參數:


客戶端界面展示結果:



分析AIDL文件編譯後的java文件(本質):

package com.example.aidltest;
// Declare any non-default types here with import statements

public interface IMyAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.example.aidltest.IMyAidlInterface interface,
 * generating a proxy if needed.
 */
public static com.example.aidltest.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidltest.IMyAidlInterface))) {
return ((com.example.aidltest.IMyAidlInterface)iin);
}
return new com.example.aidltest.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_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.aidltest.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;
}
//計算兩個int數值的和;

@Override public int add(int num1, int num2) 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(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
//計算兩個int數值的和;

public int add(int num1, int num2) throws android.os.RemoteException;

}
AIDL文件從表面上看是一個接口,編譯過後形成java文件還是一個接口。這個文件看起來很繁雜,我們將其相關代碼合併一下,從整體來看就一個抽象類Stub和一個將要實現的方法add。這個add兩個作用:一個就是在服務端service裏面進行實現,另一個就是客戶端進行傳值。接着看抽象類Stub,繼承於系統的Binder卻又實現了aidl接口中方法,其構造方法就是客戶端綁定服務端service時返回的Binder。內部asInterface()方法目的就是客戶端根據上一步返回的Bandler從而拿到服務端的service(通過內部類Proxy獲取)。接下來onTransact()方法目的就是服務端service實現add()方法是對參數的限制和結果接收。


demo下載鏈接:http://download.csdn.net/detail/li0978/9591199











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