Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 中AIDL的使用與理解

Android 中AIDL的使用與理解

編輯:關於Android編程

AIDL的使用:

最常見的aidl的使用就是Service的跨進程通信了,那麼我們就寫一個Activity和Service的跨進程通信吧。
首先,我們就在AS裡面新建一個aidl文件(ps:現在AS建aidl不要求和java包名相同了):

package aidl;
interface IMyInterface {
    String getInfor(String s);
}

可以看到,在這裡面我們就一個方法getInfor(String s),接受一個字符串參數,然後返回一個字符串,恩,相當的簡單。

接著你sync project一下就可以在app/generated/source/aidl/debug/aidl裡面發現由aidl文件生成的java文件了。點進去一看,可能你也被這個貌似’龐大’的類給嚇住了,那現在我們就先不管它了。

然後就看看Service:

public class MyService extends Service { 

public final static String TAG = "MyService";

private IBinder binder = new IMyInterface.Stub() {
        @Override       
        public String getInfor(String s) throws RemoteException { 
            Log.i(TAG, s); 
            return "我是 Service 返回的字符串"; 
       }
    };
@Overrid
public void onCreate() {
    super.onCreate(); 
    Log.i(TAG, "onCreat");    
}       
@Override    
public IBinder onBind(Intent intent) { 
    return binder;  
    }
}

這裡我們寫了一個Service,看一下也比較簡單。先new了一IMyInterface.Stub()並把它向上轉型成了IBinder,最後在onBind方法中返回回去。可能你注意到了,在IMyInterface.Stub()的內部我們重寫getInfor(String s) 方法,沒錯這就是我們 aidl文件中定義的接口。
對了,因為我們希望看到的是跨進程通信,所以我們把MyService定義成新啟動一個進程:

定義為啟動在新進程中,只需要在AndroidMainfest.xml中聲明是加上一個process屬性即可,不過這裡有兩個地方值得注意:
1.組件默認的進程名就是包名;
2.定義新的進程名的時候需要以包的形式(eg: com.xu.aidl)。

接著,我們繼續向下看:

public class MainActivity extends AppCompatActivity {
    public final static String TAG = "MainActivity";
    private IMyInterface myInterface;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myInterface = IMyInterface.Stub.asInterface(service);
            Log.i(TAG, "連接Service 成功");
            try {
                String s = myInterface.getInfor("我是Activity傳來的字符串");
                Log.i(TAG, "從Service得到的字符串:" + s);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "連接Service失敗");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startAndBindService();
    }
    private void startAndBindService() {
        Intent service = new Intent(MainActivity.this, MyService.class);
        //startService(service);
        bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

由於主要是Service和Activity間的通信,所以為了讓代碼整潔就沒有寫UI了。

在onCreate(Bundle savedInstanceState)中,我們調用了自己定義的一個方法startAndBindService(),這個方法裡面我們生成了一個Intent,然後 bindService了這個Intent傳入了三個參數分別是Intent、ServiceConnection、Flag。

Intent我們就不用說了,我們看看後面兩個參數:
在Activity中,我們new了一個ServiceConnection並實現了他的兩個方法onServiceConnected、onServiceDisconnected。在onServiceConnected中我們通過IMyInterface.Stub.asInterface(service)把傳來的IBinder轉換成了我們定義的IMyInterface。然後我們調用了getInfor方法,傳遞了個字符串和獲取從MyService傳來的字符串,並且打印了Log。

對於我們傳的Context.BIND_AUTO_CREATE的意思就是說:如果綁定Service的時候,Service還沒有被創建,就創建它。當然,這裡還有很多Flag,我也不一一說了。

然後,我們的編碼就完成了,開始運行測試一下把!

這裡寫圖片描述

這裡寫圖片描述vcHLztLDx8/r0qq1xL3hufujrMv50tSjrNK7uPbTw2FpZGzKtc/WtcS/5734s8zNqNDFvs3V4tH5zeqzycHLoaO1sci7ztLDx7XEZGVtbyjV4rTOsrvE3Ma0tO3ByynW0KOsZ2V0SW5mb3LDu9PQyM66zsLfvK2jrMTj0rK/ydLUvNPSu9Cpwt+8rcil1rTQ0NK70Km4tNTTtcSy2df3oaM8L3A+DQo8aDIgaWQ9"aidl的理解">AIDL的理解:

現在我們會使用aidl了,那我們還不去掀開它神秘的面紗。

Service中的IBinder
還記得我們在MyService中利用new IMyInterface.Stub()向上轉型成了IBinder然後在onBind方法中返回的。那我們就看看IMyInterface.Stub吧:

public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
}

可以看到,Stub是IMyInterface中的一個靜態抽象類,繼承了Binder,並且實現了IMyInterface接口。這也就解釋了我們定義IMyInterface.Stub的時候為什麼需要實現IMyInterface中的方法了,也說明了為什麼我們可以把IMyInterface.Stub向上轉型成IBinder了。

Activity中的IMyInterface
在Activity中,通過ServiceConnection連接MyService並成功回調onServiceConnected中我們把傳回來的IBinder通過IMyInterface.Stub.asInterface(service)轉換成為IMyInterface,那就來看看這裡是如何轉換的吧:

public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {

..........

public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    //檢查Binder是不是在當前進程
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
        return ((aidl.IMyInterface) iin);
    }
    return new aidl.IMyInterface.Stub.Proxy(obj);
}
}

首先,我們因該明白的是,傳回來的IBinder就是我們在Service的onBind( )方法所return的IBinder,然後我們調用Stub中的靜態方法asInterface並把返回來的IBinder當參數傳進去。
在asInterface方法中,首先判斷了傳進來的IBinder是不是null,如果為null就返回一個null;接著就判斷傳進來的IBinder是不是就在當前進程裡面,如果是的話就直接返回IMyInterface,不是的話就返回IMyInterface.Stub.Proxy(obj)。這裡我覺得需要明白的是:直接返回的IMyInterface是實現了定義的接口方法getInfor的。因為在IMyInterface.Stub中所實現的。當然如果是在同一進程中,那麼我們調用IMyInterface的方法時就是在本地調用方法,直接調用就可以了。

如果沒在同一進程,就會返回IMyInterface.Stub.Proxy(obj):

 private static class Proxy implements aidl.IMyInterface {
        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 java.lang.String getInfor(java.lang.String s) 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(s);
               //傳送數據到遠程的
                mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
                _reply.readException();
              //接受從遠端傳回的數據
                _result = _reply.readString();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        } 
   }
    static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

在Proxy中,我們首先把Service連接成功返回的IBinder它的內部變量mRemote,這裡在提一下,這裡得IBinder還是是MyService中onBind所返回的。然後,當我們調用IMyInterface的方法的時候,其實就是調用的Proxy的方法了,這也是為什麼這個類叫做Porxy的原因了。

當調用IMyInterface.getInfor(String s) ,我們就看Proxy中的getInfor,先獲取了兩個Parcel對象 _data、_data,從變量名就可以看出,一個是傳送數據的,另一個則是接受返回數據的。接著,向_data中寫入了DESCRIPTOR(也就是這個類的全名),再寫入了方法參數。然後就到了最重要的一步了,

mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
這裡我們調用了IBinder的transact方法,來把數據傳給遠端的服務器。然後在我們遠程的MyService中,裡面的Stub中就會回調onTransact()(因為你把數據傳個遠程的服務,遠端的服務收到數據也就回調了)

注意:這裡是在遠程的服務裡調用的。

@Overridepublic 
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_getInfor: {
            data.enforceInterface(DESCRIPTOR);
            java.lang.String _arg0;
           //取出參數
            _arg0 = data.readString();
           // 遠程服務調用自己本地實現的方法獲取返回值
            java.lang.String _result = this.getInfor(_arg0);
            reply.writeNoException();
           //寫入返回值
            reply.writeString(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

onTransact方法是在Stub的內部實現的。

先看一下它的四個參數:
code:每個方法都有一個int類型的數字用來區分(後面中的swicth),在我們例子中也就是我們Proxy中的Stub.TRANSACTION_getInfor。
data:傳過來的數據,其中包含我們的參數,以及類的描述。
reply:傳回的數據,我們要寫入是否發生了Exception,以及返回值
flags:該方法是否有返回值 ,0表示有返回值。

調用onTransact就表示有數據傳來,首先就會通過swicth判斷是哪個方法,然後取出方法參數,調用本地實現的方法獲取返回值,寫入返回值到reply。最後,返回true,才會把數據發送出去,發揮false就不會把結果返回給Activity了。這裡也就是說,只有返回true,我們Proxy中才能接受從遠端傳回的數據。

       //傳送數據到遠程的
        mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
        _reply.readException();
       //接受從遠端傳回的數據
        _result = _reply.readString();

注意:Service也是把數據發送出來,讓客戶端接受的。

Service發出了數據,客戶端接收到了,就會一層一層返回去。所以,當我們簡單的調用IMyInterface的getInfor時候,先是Proxy的transact發送出數據,然後服務端的onTransact接受並處理傳來的數據,再把處理得到的數據寫入返回值並發送給客戶端,客戶端讀取值後就成為調用方法的返回值返回了。

然後AIDL的基本原理就是這樣了,看明白了AIDL,才發現原來AIDL不過就是幫我們生成了那些數據寫入,傳送,讀取的方法而已。所以,我們自己寫一個不要AIDL的跨進程通信也是挺簡單的,快動手試試吧!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved