2017年開始上班的第一天。老不想工做了,假期感受還沒開始就已經結束了,唉,時間就是這樣,新的一年開始了,儘管很是不想幹正事,沒辦法,必須幹起來。因爲後邊的路還很是長,距離六十歲還很是遠。java
剛上班也沒什麼事,複習一下以前的東西,看了一下Aidl相關的知識。細緻瞅了瞅Aidl的調用流程,這裏寫篇文章整理一下。幫助你們的同一時候。本身也加深一下印象。對Aidl不太瞭解的童鞋可以先看一下我以前的一篇文章,android
連接例如如下:http://blog.csdn.net/liuyonglei1314/article/details/54317902 架構
案例下載連接:http://download.csdn.net/detail/liuyonglei1314/9734165 。ide
在上篇文章中咱們已經說過了Android中Aidl的簡單應用及對象的傳遞方式,還包括了在AS中進行Aidl開發會遇到的一些問題及決解方法,本篇文章針對用法咱們不在多說。咱們將以傳遞對象爲例深刻的剖析Aidl的具體調用流程,繼續以上文中傳遞Person對象爲例展開,經過本片文章的學習你會學到下面相關知識:1.Aidl工做調用流程;2.Aidl傳遞對象時修飾符in、out、inout的深刻理解。3.對實現Parcelable接口對象中需要實現方法的深刻理解。函數
首先看一下咱們要傳遞對象的代碼:post
public class Person implements Parcelable { private String name; private int age; public Person() { } protected Person(Parcel in) { name = in.readString(); age = in.readInt(); } public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } public void readFromParcel(Parcel dest) { name = dest.readString(); age = dest.readInt(); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
Person類實現了parcelable接口。有兩個構造方法,有name、age兩個成員變量。編譯器可以幫咱們生成一個static、final的CREATOR跟writeToParcel()方法,咱們本身寫readFromParcel()方法,這裏切記順序要與writeToParcel()方法一直。學習
下邊再看一下咱們的.aidl文件代碼:gradle
import com.jason.aidl.aidldemo.Person; interface IMyAidlInterface { String inPerson(in Person p); String outPerson(out Person p); String inOutPerson(inout Person p); }
這裏需要注意咱們的Person也需要寫相應的Person.aidl文件,並在build.gradle中配置。具體信息就不介紹了。上篇文章中進行了具體解說。在這裏看到了修飾符in、out、inout。後文會作具體解說。ui
咱們繼續看Aidl文件生成相應的.java文件的一個整體架構。縮略代碼例如如下:this
public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.jason.aidl.aidldemo.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.jason.aidl.aidldemo.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.jason.aidl.aidldemo.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder 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 { //... return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.jason.aidl.aidldemo.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; } @Override public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException { //... return _result; } @Override public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException { //... return _result; } @Override public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException { //... return _result; } } static final int TRANSACTION_inPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_outPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_inOutPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); } public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException; public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException; public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException; }
大體分爲三部分。一個interface(最外層的類)兩個內部類(Stub、Proxy)。先看interface。它跟咱們定義的.aidl文件名稱字一樣,繼承了IInterface,它的內容分爲兩部分:一個內部類Stub及咱們定義的Person抽象操做方法。每一個都拋出RemoteException。接下來看他的內部類Stub,它是一個抽象類。繼承了Binder類,實現了外層的Interface,他比較重要的是一個asInterface()方法和onTransact()方法。它另外一個static的內部類Proxy。它也實現了最外層的Interface,另外有一個需要傳遞Ibinder的構造函數,還有就是與咱們在Aidl類中定義的方法名稱一樣的方法。這就是咱們生成的.java文件的一個大體的架構。
由上邊可知最外層Interface是IInterface的子類,而Stub與Proxy是最外層Interface的實現類。Stub繼承了Binder,他們之間存在這種關係。如下咱們就對代碼的調用流程進行具體解說。
首先咱們在應用Aidl時經過client綁定的方式來獲取咱們的IMyAidlInterface,並經過它來調用服務端的方法進行通訊,例如如下代碼:
private IMyAidlInterface mService; Intent intent = new Intent(); intent.setAction("com.lyl.aidl"); Intent intent1 = new Intent(createExplicitFromImplicitIntent(this, intent));//兼容5.0之後版本號 bindService(intent1, mServiceC, Context.BIND_AUTO_CREATE); ServiceConnection mServiceC = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IMyAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { } }; public void do(View v) { try { Person p1 = new Person(); p1.setName("劉大"); p1.setAge(3); Person p2 = new Person(); p2.setName("趙二"); p2.setAge(3); Person p3 = new Person(); p3.setName("張三"); p3.setAge(3); tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3)); } catch (RemoteException e) { e.printStackTrace(); } }
上邊代碼經過IMyAidlInterface.Stub.asInterface(service)獲取了咱們的IMyAidlInterface,do()方法中經過它才調用了咱們服務端的代碼,那咱們先看一下這個asInterface方法中的參數是什麼,咱們知道當經過綁定的方式獲取的binder是咱們在服務中的onBind()方法中返回的,看一下咱們服務端的代碼:
public class MyAidlService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.i("Log_LYL", "Onstart"); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return stub; } IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() { @Override public String inPerson(Person p) throws RemoteException { ... } @Override public String outPerson(Person p) throws RemoteException { ... } @Override public String inOutPerson(Person p) throws RemoteException { ... } }; }
經過以上服務端代碼可以知道咱們返回的是一個stub 事例。而它是咱們的.aidl文件本身主動生成的.java文件裏Stub的一個實例對象。現在咱們知道IMyAidlInterface.Stub.asInterface(service)中的service是編譯器爲咱們生成的.java文件的一個實例。咱們接着看IMyAidlInterface中Stub.asInterface(service)方法,代碼例如如下:
public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.jason.aidl.aidldemo.IMyAidlInterface))) { return ((com.jason.aidl.aidldemo.IMyAidlInterface) iin); } return new com.jason.aidl.aidldemo.IMyAidlInterface.Stub.Proxy(obj); }
因爲咱們的Stub類繼承了Binder。Binder實現了Ibinder接口,因此傳遞Stub的實例在這裏沒問題。上述代碼首先推斷了obj是否爲空,不爲空往下走,第二個if推斷是在推斷經過obj獲取的iin是否屬於當前程序執行的進程。假設是,直接返回,這裏也就是說咱們要調用的服務與咱們當前程序在同一進程。不需要遠程通訊,直接調用便可。假設不是可以看見系統新生成了一個Proxy對象實例,並把咱們的stub實例對象傳遞進去。看一下Proxy的構造函數,代碼例如如下:
private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; }
可以看到經過構造函數將Stub實例從新傳到了Proxy中並賦值給mRemote,mRemote也變成了Stub實例。
到這裏綁定服務獲取ImyAidlInterface對象就介紹完了,作一下總結:經過綁定的方式獲取到了在服務端的系統生成的.aidl文件相應的.java文件裏子類Stub的一個實例,調用Stub類中的asInterface()方法推斷Stub實例所在進程與當前進程是否爲同一個。是直接可以操做。不是生成一個Proxy實例並將Stub傳入。
如下咱們看一下調用代碼實現:
public void do(View v) {
try {
Person p1 = new Person();
p1.setName("劉大");
p1.setAge(3);
Person p2 = new Person();
p2.setName("趙二");
p2.setAge(3);
Person p3 = new Person();
p3.setName("張三");
p3.setAge(3);
tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3));
} catch (RemoteException e) {
e.printStackTrace();
}
}
咱們首先分析第一個inPerson(p1);這種方法咱們在定義時的修飾符爲in,及String inPerson(in Person p);下邊咱們看一下它的實現過程,經過上邊的介紹可以知道mService假設不是在同一個進程中是返回的Proxy對象實例,那麼咱們就進入Proxy中查找相應的方法,代碼例如如下:
@Override public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) 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); if ((p != null)) { _data.writeInt(1); p.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
可以看到系統生成了_data、_reply兩個Parcel類型的對象,而後操做_data。寫入Token,推斷假設傳入的Person對象不爲空寫入了一個1,接下來調用了person類的writeToParcel()方法,傳入了_data,咦,注意這裏。咱們就知道了實現Parcelable接口時的writeToParcel()方法在這裏會用到。好的,咱們跟進看一下,代碼例如如下:
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); }
這一步看來就是給_data賦值,將咱們從開始調用inPerson(com.jason.aidl.aidldemo.Person p) p實例中的值以writeString、writeInt的方式保存到_data中,假設傳入的p爲null則寫入一個0。繼續往下走。我標紅的那句是重點,也是這句進行了binder通訊,關於binder通信機制有很是多很是多東西。由於本人能力有限在這裏就很少講了,感興趣的可以去了解一下。經過前邊可以知道mRemote爲asInterface方法傳入的service,及Stub實例,而Stub中根本沒有transact()方法,而它是Binder的子類,好,咱們去Binder中找,還真有:
public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { if (false) Log.v("Binder", "Transact: " + code + " to " + this); if (data != null) { data.setDataPosition(0); } boolean r = onTransact(code, data, reply, flags); if (reply != null) { reply.setDataPosition(0); } return r; }
可以看到。在這裏調用了onTransact(code, data, reply, flags);咱們知道在Stub類中咱們實現了這種方法,咱們看一下Stub類中的這種方法:
@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_inPerson: { data.enforceInterface(DESCRIPTOR); com.jason.aidl.aidldemo.Person _arg0; if ((0 != data.readInt())) { _arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data); } else { _arg0 = null; } java.lang.String _result = this.inPerson(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); }
在這裏咱們僅僅摘取了inPerson()方法相關內容。先了解一下傳入的四個參數:1.code 每一個方法相應一個code,咱們定義了多少方法就會有多少code,經過它來作不一樣處理;2.data、reply爲在proxy相應方法中生成的兩個Parcel對象。3.flags 通常爲1。這個不用管先。 首先經過code找到相應方法的操做,推斷data中寫入的int值是否爲0,不爲0說明以前的p不爲null。並且已經存入到data中。調用Person中的CREATOR的createFromParcel()方法,咦。這裏又用到了Parcelable的東西。看一下這種方法:
public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } }; protected Person(Parcel in) { name = in.readString(); age = in.readInt(); }
可以看到,系統在這裏建立了一個Person實例。傳入了咱們以前的data(保存有person信息),並將data中的信息賦值給新的person對象。
回到Stub類的onTransace()方法中繼續往下看:
java.lang.String _result = this.inPerson(_arg0); reply.writeNoException(); reply.writeString(_result); return true;
接着系統調用了this.inPerson(_arg0)方法。_arg0爲上邊經過createFromParcel()方法新生成的person(包括傳遞過來的person值)對象,咱們知道Stub爲抽象類。他實現了咱們外層的Interface類。但是沒有實現inPerson()方法。那麼咱們就需要它的子類實現,而咱們的mRemote.transace()中的mRemote就是服務端返回的Stub的一個實現子類,也就是說這裏調用的this.inPerson(_arg0)方法實際是調用了服務端的那個Stub實現類裏邊的inPerson()方法,看一下咱們以前服務端的inPerson()方法的詳細實現:
@Override public String inPerson(Person p) throws RemoteException { String old = "name:" + p.getName() + " age:" + p.getAge(); Log.d("Log_LYL:iPerson_", old); p.setName("李四"); p.setAge(13); return "name:" + p.getName() + " age:" + p.getAge(); }
經過以上分析,你們就知道了這裏的 Log.d("Log_LYL:iPerson_", old); 是爲何有值了,因爲咱們一開始就將傳入的person值附到了Parcel類型的data中了,而後又經過createFromParcel()方法將data中的值付給了新的person對象。
而後回到Stub類的onTransace()方法,會看到調用了reply.writeNoException();reply.writeString(_result);兩個方法,將服務端處理完後返回的信息寫入了reply中。最後return。
再回到Proxy類中的inPerson()方法中:
mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result;
將服務端返回的信息讀出來並付給_result。最後返回,這樣咱們在activity中就可以獲得服務端處理後的結果了。到現在爲止。咱們就應該明確了,經過in修飾符傳遞person後服務端生成了新的person對象。並對新person對象進行處理,這種話無論服務端怎樣改動person對象咱們client的person對象是不會受不論什麼影響的,爲何,因爲不是一個對象。這就是in操做符。
如下咱們分析一下out操做符,直接看代碼:
String outPerson(out Person p);//.aidl文件裏定義; mService.outPerson(p2)//client調用;
咱們在client直接經過綁定時返回的Stub實例調用outPerson()方法。經過上邊對inPerson()方法的介紹咱們知道不是同一個進程中的會終於調用到Proxy中對應的方法中,那咱們直接看Proxy中代碼:
@Override public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) 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_outPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); if ((0 != _reply.readInt())) { p.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } return _result; }
有了上邊對inPerson()方法的瞭解這個相對就簡單多了,相同生成兩個Parcel對象。做用相同,不一樣的是用out修飾後沒有對傳入的p作null推斷,也沒有取值保存到data中。而是直接調用了mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0)方法,那好,經過前邊咱們直接去Stub中找onTransact()方法吧:
case TRANSACTION_outPerson: { data.enforceInterface(DESCRIPTOR); com.jason.aidl.aidldemo.Person _arg0; _arg0 = new com.jason.aidl.aidldemo.Person(); java.lang.String _result = this.outPerson(_arg0); reply.writeNoException(); reply.writeString(_result); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; }
咱們僅僅要out方法相關信息,這裏系統經過new Person()的方式建立了一個person對象實例賦值個_arg0,這個對象與咱們調用時傳進來的對象沒有一點關係,而後直接調用了this.outPerson()方法,經過inPerson()的介紹咱們就知道這裏直接調用了服務端outPerson()實現:
@Override public String outPerson(Person p) throws RemoteException { //這裏的p是空的,因爲在IMyAidlInterface的Stub類中onTransact()方法中沒有寫入; // _arg0 = new com.jason.aidl.aidldemo.Person(); //java.lang.String _result = this.outPerson(_arg0); String old = "name:" + p.getName() + " age:" + p.getAge(); Log.d("Log_LYL:outPerson_", old); p.setName("週六"); p.setAge(20); return "name:" + p.getName() + " age:" + p.getAge(); }
這裏可以知道old是沒有賦值的,如下賦值return,回到Stub中:
java.lang.String _result = this.outPerson(_arg0); reply.writeNoException(); reply.writeString(_result); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true;
將處理結果寫入reply中。推斷_arg0是否爲null,不是,寫入1,這裏從新用到了writeToParcel()方法,經過上邊inPerson()方法介紹可知這裏是將_arg0的值付給reply。假設爲null,reply中賦值0。回到Proxy中:
try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); if ((0 != _reply.readInt())) { p.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } return _result;
運行完transact()方法後從_reply中讀出服務端操做返回結果,推斷寫入的int值,假設不爲0,說明_reply中寫入了服務端產生的person信息,則將經過readFromParcel()方法將新的_reply中保存的服務端的person信息寫入到client傳入的p中,這樣client就收到了服務端的person信息。
總結一下out修飾符。即便咱們在服務端將p的值寫好。服務端也不會接收咱們的client信息。可以說服務端根本不關係client傳入的信息,服務端經過new Person()方式產生新對象,並返回給client,client的p經過readFromParcel()方法得到服務端值,並無作對象的交換。
下邊看一下inout修飾符代碼:
String inOutPerson(inout Person p);//.aidl文件裏; mService.inOutPerson(p3)。//client調用;
相同看一下Proxy中對應方法:
@Override public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) 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); if ((p != null)) { _data.writeInt(1); p.writeToParcel(_data, 0); } else { _data.writeInt(0); } mRemote.transact(Stub.TRANSACTION_inOutPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); if ((0 != _reply.readInt())) { p.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } return _result; }
case TRANSACTION_inOutPerson: { data.enforceInterface(DESCRIPTOR); com.jason.aidl.aidldemo.Person _arg0; if ((0 != data.readInt())) { _arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data); } else { _arg0 = null; } java.lang.String _result = this.inOutPerson(_arg0); reply.writeNoException(); reply.writeString(_result); if ((_arg0 != null)) { reply.writeInt(1); _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; }
可以看到在inout修飾符時推斷了傳入的p對象,也就是說服務端接收了保存了client的p對象,操做相似與in修飾符,也是經過Person.CREATOR.createFromParcel(data)建立新對象,在Proxy中相同推斷了_reply中寫入的int值,也就是說,client也能收到服務端對對象的改動,也就是說。inout操做符是int、out操做符的合體。到這裏相信你們已經明確in、out、inout操做符的差異了。並且連實現都明確了。
到現在今天要跟你們分享的東西就寫完了,相信您看完這篇文章會對Android中的Aidl有一個新的瞭解。對in、out、inout修飾也明確了,對實現Parcelable接口有了新的認識,假設是這樣,那我這篇文章就沒白寫。謝謝你們。