Android深刻源代碼分析理解Aidl整體調用流程(雷驚風)

        2017年開始上班的第一天。老不想工做了,假期感受還沒開始就已經結束了,唉,時間就是這樣,新的一年開始了,儘管很是不想幹正事,沒辦法,必須幹起來。因爲後邊的路還很是長,距離六十歲還很是遠。java

剛上班也沒什麼事,複習一下以前的東西,看了一下Aidl相關的知識。細緻瞅了瞅Aidl的調用流程,這裏寫篇文章整理一下。幫助你們的同一時候。本身也加深一下印象。對Aidl不太瞭解的童鞋可以先看一下我以前的一篇文章,android

連接例如如下:http://blog.csdn.net/liuyonglei1314/article/details/54317902 架構

案例下載連接:http://download.csdn.net/detail/liuyonglei1314/9734165 ide

   在上篇文章中咱們已經說過了AndroidAidl的簡單應用及對象的傳遞方式,還包括了在AS中進行Aidl開發會遇到的一些問題及決解方法,本篇文章針對用法咱們不在多說。咱們將以傳遞對象爲例深刻的剖析Aidl的具體調用流程,繼續以上文中傳遞Person對象爲例展開,經過本片文章的學習你會學到下面相關知識:1.Aidl工做調用流程;2.Aidl傳遞對象時修飾符inoutinout的深刻理解。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接口。有兩個構造方法,有nameage兩個成員變量。編譯器可以幫咱們生成一個staticfinalCREATORwriteToParcel()方法,咱們本身寫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中配置。具體信息就不介紹了。上篇文章中進行了具體解說。在這裏看到了修飾符inoutinout。後文會作具體解說。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(最外層的類)兩個內部類(StubProxy)。先看interface。它跟咱們定義的.aidl文件名稱字一樣,繼承了IInterface,它的內容分爲兩部分:一個內部類Stub及咱們定義的Person抽象操做方法。每一個都拋出RemoteException。接下來看他的內部類Stub,它是一個抽象類。繼承了Binder類,實現了外層的Interface,他比較重要的是一個asInterface()方法和onTransact()方法。它另外一個static的內部類Proxy。它也實現了最外層的Interface,另外有一個需要傳遞Ibinder的構造函數,還有就是與咱們在Aidl類中定義的方法名稱一樣的方法。這就是咱們生成的.java文件的一個大體的架構。

由上邊可知最外層InterfaceIInterface的子類,而StubProxy是最外層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文件的一個實例。咱們接着看IMyAidlInterfaceStub.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類繼承了BinderBinder實現了Ibinder接口,因此傳遞Stub的實例在這裏沒問題。上述代碼首先推斷了obj是否爲空,不爲空往下走,第二個if推斷是在推斷經過obj獲取的iin是否屬於當前程序執行的進程。假設是,直接返回,這裏也就是說咱們要調用的服務與咱們當前程序在同一進程。不需要遠程通訊,直接調用便可。假設不是可以看見系統新生成了一個Proxy對象實例,並把咱們的stub實例對象傳遞進去。看一下Proxy的構造函數,代碼例如如下:

private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote) {
    mRemote = remote;
}

可以看到經過構造函數將Stub實例從新傳到了Proxy中並賦值給mRemotemRemote也變成了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();
    }
}

咱們首先分析第一個inPersonp1);這種方法咱們在定義時的修飾符爲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修飾後沒有對傳入的pnull推斷,也沒有取值保存到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有一個新的瞭解。對inoutinout修飾也明確了,對實現Parcelable接口有了新的認識,假設是這樣,那我這篇文章就沒白寫。謝謝你們。

相關文章
相關標籤/搜索