編輯:關於Android編程
AIDL是Android實現IPC的一種重要的方式,理解它的原理對理解Android進程間通信有很大的幫助。AIDL的定義,已經有很多介紹的文章了,這裡就不做詳解了。我們直接從實例入手來分析AIDL實現原理。
AIDL的使用
首先需要定義AIDL接口IMyService.aidl:
// IMyService.aidl package com.chuck.aidldemo; // Declare any non-default types here with import statements interface IMyService { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ String getValue(); }
定義了getValue()方法,返回String。我們知道定義好aidl文件後,IDE在編譯項目時會自動幫我們生成一個文件IMyService.java文件,稍後會詳細介紹。
接下來需要定義一個service,這裡定義MyService.java
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); Log.e("myService","onCreate" ); } @Nullable @Override public IBinder onBind(Intent intent) { return ims; } IMyService.Stub ims=new IMyService.Stub() { @Override public String getValue() throws RemoteException { return "hello AIDL"; } }; @Override public void onDestroy() { super.onDestroy(); Log.e("myService","onDestroy"); } }
重寫onBind方法,返回IMyService.Stub對象ims,IMyService.Stub是定義在IMyService.java中,其實現了IBinder,所以這裡可以在OnBinder中作為返回對象。同時在AIDL中定義getValue方法的真正實現,就是在這裡。我們僅僅是返回一個”hello AIDL”字符串。
為了實現跨進程,我們還需要在AndroidMenifast.xml文件中設置process=”:Remote”,這樣service就和client不在同一個進程中了:
最後在定義client,為了方便我們就直接在MainActivity.java實現了:
public class MainActivity extends AppCompatActivity { private IMyService ims; private String text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onClick(View view) { if (view.getId() == R.id.btn_bind) { Intent intent = new Intent(this, MyService.class); bindService(intent, sc, BIND_AUTO_CREATE); }else if (view.getId()==R.id.btn_unbind){ unbindService(sc); } } private ServiceConnection sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ims = IMyService.Stub.asInterface(service); try { text = ims.getValue(); Log.e("text",text); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; }
這和平時bindService使用是一樣的,需要一個ServiceConnection實例,將該實例作為bindService參數傳入。這樣client就可以啟動並綁定MyService了。
一般我們使用AIDL的基本步驟就是這些,現在我們需要著重分析自動生成的IMyService.java了,如果不知道文件位置,直接在IDE搜索一下就可以了。先把代碼貼出來:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Users/chenkai/Android/codeExcise/aidl/AidlDemo/app/src/main/aidl/com/chuck/aidldemo/IMyService.aidl */ package com.chuck.aidldemo; // Declare any non-default types here with import statements public interface IMyService extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.chuck.aidldemo.IMyService { private static final java.lang.String DESCRIPTOR = "com.chuck.aidldemo.IMyService"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.chuck.aidldemo.IMyService interface, * generating a proxy if needed. */ public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) { return ((com.chuck.aidldemo.IMyService) iin); } return new com.chuck.aidldemo.IMyService.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_getValue: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getValue(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.chuck.aidldemo.IMyService { 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; } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public java.lang.String getValue() 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); mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ public java.lang.String getValue() throws android.os.RemoteException; }
代碼結構很清晰,先來看一下類圖:
1.IInterface,我們定義的Binder接口都需要繼承自它。
2.我們定義的IMyService接口繼承自接口IInterface。
3.IBinder 遠程對象的接口。
4.Binder實現了IBinder,Android通過Binder進行IPC
5.BinderProxy是在定義在Binder.java中的,也是實現了IBinder接口,這裡只要知道它是Binder的代理就可以了。我們Client中拿到的就是這個類的對象,之後會有講。
6.IMyService.Stub繼承自Binder並且實現了IMyService。也就是說Stub其實是個Binder。還記得嗎?在前文MyService中我們生成了一個Stub的實例ims,Stub的本意是存根,這裡的用意就是Service的存根,通過它,我們在Service端就擁有了一個Binder。這樣我們就可以通過底層的Binder驅動進行跨進程通信了。
7.IMyService.Proxy,顧名思義它是一個代理,主要是實現了IMyService接口,客戶端通過它發起遠程請求。
簡單的介紹了涉及到的類,下面來分析一下請求的流程。
我們知道,在client bindService(這是一個比較復雜的過程,涉及到AMS,本身也是一個跨進程的通信)之後,如果遠程服務,這裡是MyService,處理完請求之後,通過一系列復雜的操作,client中的onServiceConnected方法將會回調,為了方便我把前邊相關的部分代碼放在這:
private ServiceConnection sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { ims = IMyService.Stub.asInterface(service); try { text = ims.getValue(); Log.e("text",text); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } };
第3行當回調onServiceConnected時,會傳過來一個IBinder對象service,client就是需要通過它來發起遠程請求的,那麼service的具體實現到底是Binder還是BinderProxy呢,如果對Binder的機制比較清楚的都會知道其實是BinderProxy的,為了驗證這個我們可以調試一下:
我們得到BinderProxy後會將其傳給IMyService.Stub.asInterface(service),asInterface接受一個IBinder對象作為參數,這裡就是剛才說的BinderProxy對象。具體看看asInterface做了什麼:
public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) { return ((com.chuck.aidldemo.IMyService) iin); } return new com.chuck.aidldemo.IMyService.Stub.Proxy(obj); }
看第5行,因為我們設置了MyService的process屬性,也就是說他和client是不在同一個進程的,所以obj.queryLocalInterface(DESCRIPTOR)這個方法返回為空,也就是iin為null,那麼將會執行第9行代碼,對沒錯new了一個IMyService.Proxy對象。回到前邊onServiceConnected方法中第4行ims就是剛剛生成的IMyService.Proxy對象。第6行ims.getValue()方法執行的就是IMyService.Proxy中的getValue方法:
/** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ @Override public java.lang.String getValue() 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); mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
第7.8行_data, _reply分別是進行遠程調用時的的請求和相應參數。第11行將DESCRIPTOR = “com.chuck.aidldemo.IMyService”作為Token寫入 _data.第12行mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);mRemote是在IMyService.Proxy構造方法中被賦值的,也就是我們在new IMyService.Proxy對象時傳進來的BinderProxy對象。所以mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0)的實現應該是在BinderProxy中,這裡需要注意一下第一個參數Stub.TRANSACTION_getValue,他是標識需要調用方法的code,在後邊會有用到。我們看看他的源碼:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { Binder.checkParcel(this, code, data, "Unreasonably large binder buffer"); return transactNative(code, data, reply, flags); }
最終會調用transactNative()方法,這是一個native方法,真正的實現是用c實現,他是實現在Android源碼對應/frameworks/base/core/jni/android_util_Binder.cpp中的static jboolean android_os_BinderProxy_transact函數,將java層的transact轉換成c層transact,有興趣的可以去研究一下。
通過底層Binder的一系列操作,最終會回調到遠程Stub的onTransact方法,至於怎麼調用的,需要去了解Binder機制。這裡只要知道會回調onTransact()方法就好了。不過還是要注意的是,這裡的Stub是在遠程服務端的,他和client不是在同一個進程中的。他是在遠程服務的Binder線程池中。跟進去看onTransact()方法做了什麼處理:
@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_getValue: { data.enforceInterface(DESCRIPTOR); java.lang.String _result = this.getValue(); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }
還記得在transact方法時提到的第一個參數Stub.TRANSACTION_getValue嗎,它就是這裡onTransact的第一個參數code。所以會執行8-13行代碼,第10行java.lang.String _result = this.getValue();很關鍵這個this是Stub,還記得我們在哪兒有生成它的實例嗎?沒錯就是在MyService中,我們把它作為onBind方法的返回參數。所以這裡的this.getValue就是MyService中我們寫的那個getValue方法,會返回一個”hello AIDL”的字符串。並把它賦值給_result。在第12行將其寫入_reply中。最後在通過Binder的一系列操作,我們將在client中得到這個字符串。
總的來說AIDL本身使用比較簡單,理解起來也比較簡單,但是其內部的Binder機制還是比較復雜的,我們先理解Java層的AIDL對學習Binder也是有幫助的。並且Messager通信是基於AIDL的,理解了AIDL也就理解了Messager。
什麼是emoji表情emoji表情是一種表情符號,在代碼中它現在其實是一組遵循Unicode的編碼,即每一個表情符號都對應了一個Unicode編碼。更進一步說,emoji
Android中的Toast是很常見的一個消息提示框,但是默認的消息提示框就是一行純文本,所以我們可以為它設置一些其他的諸如是帶上圖片的消息提示。 實現這個很簡單: 就是
官網: http://square.github.io/picasso/ 我們在上篇OkHttp的時候說過這個Picasso,學名畢加索,是Square公司開
Android 自定義輸入支付密碼的軟鍵盤 &nbs