從Android源碼的角度分析Binder機制




IPC

為了弄懂IPC的來龍去脈,我將從以下三個方面為大家來講解,希望對大家理解IPC會有幫助

什么是IPC

IPC是Inter Process Communication的縮寫,其意思就是進程間的通信,也就是兩個進程之間的通信過程。我們都知道在Android系統中,每個應用都運行在一個進程上,具有自己的DVM實例,而且進程之間是相互隔離的,也就是說各個進程之間的數據是互相獨立,互不影響的,而如果一個進程崩潰了,也不會影響到另一個進程。
采取這樣的設計是有一定道理的,例如這樣的前提下將互相不影響的系統功能分拆到不同的進程里面去,有助于提升系統的穩定性,畢竟我們都不想自己的應用進程崩潰會導致整個手機系統的崩潰。
進程之間隔離是不錯的選擇,可是如果進程之間想要互相通信,進行數據交互的時候那該怎么辦呢?例如我們在自己的應用中想要訪問手機通訊錄中的聯系人,很顯然這是兩個不同的進程,如果Android沒有提供一種進程之間交流的機制,那么這種功能將無法實現。
不過由于Android系統使用的是Linux內核,而在Linux系統中進程之間的交互是有一套機制的,所以Android也借鑒了其中的一些機制,從而形成了Android的IPC機制。
上面只是粗略的講解了IPC是啥,關于它的使用和原理我將一一為大家呈上。

為什么要用IPC

上一點中我舉了訪問手機通訊錄的例子。但你可能覺得我不需要用到這種功能,那么我就不用管IPC啦!其實不然,IPC在我們的應用開發過程中隨處可見,下面我將舉一個例子來說明他的重要性。
我們在MainActivity中修改一個靜態變量,接著在另一個進程的SecondActivity中去訪問該變量,看看能否讀取已經修改過的變量。

1、新建一個Student類,并聲明一個靜態變量

public?class?Student?{????public?static?String?name="BOB";
}

2、在MainActivity的onCreate方法中修改name的值,并打印log

@Overrideprotected?void?onCreate(Bundle?savedInstanceState)?{????super.onCreate(savedInstanceState);????setContentView(R.layout.activity_main);Student.name?=?"JACK";Log.d("MainActivity:Sname=",?Student.name);
}

3、將SecondActivity設置為新進程,并在其onCreate方法中訪問name

<!--?在清單文件中通過android:process屬性為SecondActivity指定特定的進程:com.bob.aidltest:second?--><activity?????android:name=".SecondActivity"????android:process=":second"></activity>
public?class?SecondActivity?extends?AppCompatActivity?{????@Overrideprotected?void?onCreate(@Nullable?Bundle?savedInstanceState)?{????????super.onCreate(savedInstanceState);????????setContentView(R.layout.second_activity);Log.d("SecondActivity:Sname="?,?Student.name);}
}

4、通過log,可以看到在MainActivity中修改了name的值,但是在SecondActivity中卻無法讀取修改后的值

通過以上的實驗,大家應該明白了一點:在不同的進程之間訪問同一個靜態變量是行不通的。其原因是:每一個進程都分配有一個獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間,這就導致在不同的虛擬機上訪問同一個對象會產生多個副本。例如我們在MainActivity中訪問的name的值只會影響當前進程,而對其他進程不會造成影響,所以在SecondActivity中訪問name時依舊只能訪問自己進程中的副本。

Android中解決IPC的方法

上面也講到,為了解決這些跨進程的問題,Android沿用了一些Linux的進程管理機制,使得進程之間能夠進行交互,下面我將列出一些常見的IPC方式,需要指出的是本文主要講解Binder機制,所以會注重講解AIDL,其他方式請讀者自行查閱相關資料。

名稱特點使用場景
Bundle只能傳輸實現了Serializable或者Parcelable接口或者一些Android支持的特殊對象適合用于四大組件之間的進程交互
文件不能做到進程間的即時通信,并且不適合用于高并發的場景適合用于SharedPreference以及IO操作
ContentProvider可以訪問較多的數據,支持一對多的高并發訪問,因為ContentProvider已經自動做好了關于并發的機制適合用于一對多的數據共享并且需要對數據進行頻繁的CRUD操作
Socket通過網絡傳輸字節流,支持一對多的實時通信,但是實現起來比較復雜適合用于網絡數據共享
Messenger底層原理是AIDL,只是對其做了封裝,但是不能很好的處理高并發的場景,并且傳輸的數據只能支持Bundle類型低并發的一對多的即時通信
AIDL功能強大,使用Binder機制(接下來會講解),支持一對多的高并發實時通信,但是需要處理好線程同步一對多并且有遠程進程通信的場景

Binder

終于來到這篇文章的重頭戲了,上面講到Android解決IPC的方法中有一種是AIDL,它使用的原理就是Binder,只有理解了Binder,我們才算是理解了Android跨進程通信的原理。在這里我會帶大家看看Android中有哪一些重要的地方使用到了Binder,接著我們會通過一個實例來了解如何使用Binder,最后我們會分析Binder的源碼來理解他的工作流程。

Binder在Android中的運用

說起Binder在Android的使用場景,可以說是無處不在,我列出一些最常見的場景:

  • 四大組件的生命周期都是使用Binder機制進行管理的

  • View的工作原理也使用了Binder

  • WindowManager的工作機制同樣使用了Binder

以上三個方面只是最常見的場景,但是卻幾乎包括了我們開發的整個流程。我們開發的應用都離不開四大組件,而四大組件也正是依靠Binder機制運行的;對于我們最常見的View,他是如何顯示的,View又是如何響應我們的動作的,這其中也用到了Binder(關于這些內容我會在后續的文章中為大家分析)。可以說了解Binder對于我們的開發是很有幫助的,那接下來我們就來看看我們該如何使用Binder進行進程間的通信吧!

如何使用Binder

現在我們需要實現這樣的功能:客戶端與服務端位于不同的進程,客戶端需要向服務端添加學生,同時客戶端還可以向服務端發起查詢學生列表的請求。

1、創建Student.java,Student.aidl,IStudentManager.aidl

  • Student.java

package?com.bob.aidltest.aidl;import?android.os.Parcel;import?android.os.Parcelable;/**?*?Created?by?bob?on?17-7-3.?*?所有需要在Binder傳遞的數據類型都需要實現Parcelable接口?*/public?class?Student?implements?Parcelable{????public?static?String?name="BOB";????public?int?s_id;????public?String?s_name;????public?String?s_gender;????public?Student(Parcel?in)?{s_id?=?in.readInt();s_name?=?in.readString();s_gender?=?in.readString();}????public?Student(int?s_id,?String?s_name,?String?s_gender)?{????????this.s_id?=?s_id;????????this.s_name?=?s_name;????????this.s_gender?=?s_gender;}????@Overridepublic?int?describeContents()?{????????return?0;}????@Overridepublic?void?writeToParcel(Parcel?dest,?int?flags)?{dest.writeInt(s_id);dest.writeString(s_name);dest.writeString(s_gender);}????public?static?final?Creator<Student>?CREATOR?=?new?Creator<Student>()?{????????@Overridepublic?Student?createFromParcel(Parcel?in)?{????????????return?new?Student(in);}????????@Overridepublic?Student[]?newArray(int?size)?{????????????return?new?Student[size];}};????@Overridepublic?String?toString()?{????????return?String.format("[StudentID:?%s?,?StudentName:?%s?,?StudentGender:?%s]",?s_id,?s_name,?s_gender);}
}
  • Student.aidl

//?Student1.aidlpackage?com.bob.aidltest.aidl;parcelable?Student;
  • IStudentManager.aidl

//?IStudentManager.aidlpackage?com.bob.aidltest.aidl;import?com.bob.aidltest.aidl.Student;interface?IStudentManager?{????List<Student>?getStudentList();????void?addStudent(in?Student?student);
}

創建完畢之后手動編譯項目(Build-->ReBuild Project),接著就會在app/build/generated/source/aidl/debug/com/bob/aidltest/aidl/IStudentManager.java中看到自動生成的IStudentManager接口,如下圖:

2、分析IStudentManager.java

先來看看自動生成的代碼:

public?interface?IStudentManager?extends?android.os.IInterface{????/**?內部類Stub,繼承自Binder并且實現了IStudentManager接口,因此他也是一個Binder對象,這個內部類是需要在服務端手動實現的,并且會通過onBind方法返回給客戶端?*/public?static?abstract?class?Stub?extends?android.os.Binder?implements?com.bob.aidltest.aidl.IStudentManager{????????private?static?final?java.lang.String?DESCRIPTOR?=?"com.bob.aidltest.aidl.IStudentManager";????????/**?構造方法?*/public?Stub(){????????????this.attachInterface(this,?DESCRIPTOR);}????????/**?????????*?將服務端的Binder對象轉換為客戶端的所需的AIDL接口類型的對象,客戶端拿到這個對象就可以通過這個對象遠程訪問服務端的方法?????????*/public?static?com.bob.aidltest.aidl.IStudentManager?asInterface(android.os.IBinder?obj){????????????if?((obj==null))?{????????????????return?null;}android.os.IInterface?iin?=?obj.queryLocalInterface(DESCRIPTOR);????????????if?(((iin!=null)&&(iin?instanceof?com.bob.aidltest.aidl.IStudentManager)))?{????????????????return?((com.bob.aidltest.aidl.IStudentManager)iin);}????????????return?new?com.bob.aidltest.aidl.IStudentManager.Stub.Proxy(obj);}????????@Override?public?android.os.IBinder?asBinder(){????????????return?this;}????????/**?????????*?運行在服務端進程的Binder線程池中;當客戶端進程發起遠程請求時,遠程請求會要求系統底層執行回調該方法?????????*?@param?code?客戶端進程請求方法標識符。服務端進程會根據該標識確定所請求的目標方法?????????*?@param?data?目標方法的參數,他是客戶端進程傳進來的,當我們調用addStudent(Student?student)方法時,參數就是Student對象?????????*?@param?reply?目標方法執行后的結果,將會返回給客戶端,例如當我們調用getStudentList,返回的就是一個Student的列表?????????*/@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_getStudentList:{data.enforceInterface(DESCRIPTOR);java.util.List<com.bob.aidltest.aidl.Student>?_result?=?this.getStudentList();reply.writeNoException();reply.writeTypedList(_result);????????????????????return?true;}????????????????case?TRANSACTION_addStudent:{data.enforceInterface(DESCRIPTOR);com.bob.aidltest.aidl.Student?_arg0;????????????????????if?((0!=data.readInt()))?{_arg0?=?com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);}????????????????????else?{_arg0?=?null;}????????????????????this.addStudent(_arg0);reply.writeNoException();????????????????????return?true;}}????????????return?super.onTransact(code,?data,?reply,?flags);}????????/**?????????*?代理的內部類,他實現了IStudentManager接口,這個代理類就是服務端返回給客戶端的AIDL接口對象,客戶端可以通過這個代理類訪問服務端的方法?????????*/private?static?class?Proxy?implements?com.bob.aidltest.aidl.IStudentManager{????????????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.util.List<com.bob.aidltest.aidl.Student>?getStudentList()?throws?android.os.RemoteException{android.os.Parcel?_data?=?android.os.Parcel.obtain();android.os.Parcel?_reply?=?android.os.Parcel.obtain();java.util.List<com.bob.aidltest.aidl.Student>?_result;????????????????try?{_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getStudentList,?_data,?_reply,?0);_reply.readException();_result?=?_reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);}????????????????finally?{_reply.recycle();_data.recycle();}????????????????return?_result;}????????????@Override?public?void?addStudent(com.bob.aidltest.aidl.Student?student)?throws?android.os.RemoteException{android.os.Parcel?_data?=?android.os.Parcel.obtain();android.os.Parcel?_reply?=?android.os.Parcel.obtain();????????????????try?{_data.writeInterfaceToken(DESCRIPTOR);????????????????????if?((student!=null))?{_data.writeInt(1);student.writeToParcel(_data,?0);}????????????????????else?{_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_addStudent,?_data,?_reply,?0);_reply.readException();}????????????????finally?{_reply.recycle();_data.recycle();}}}????????static?final?int?TRANSACTION_getStudentList?=?(android.os.IBinder.FIRST_CALL_TRANSACTION?+?0);????????static?final?int?TRANSACTION_addStudent?=?(android.os.IBinder.FIRST_CALL_TRANSACTION?+?1);}????public?java.util.List<com.bob.aidltest.aidl.Student>?getStudentList()?throws?android.os.RemoteException;????public?void?addStudent(com.bob.aidltest.aidl.Student?student)?throws?android.os.RemoteException;
}

可能看了上面的注釋大家還是一頭霧水,那就先看看這個類的結構圖吧:

有關這個類的細節我們待會講,現在只需要知道我們需要在服務端手動實現Proxy類并實現其中的方法。

創建StudentManagerService.java,并為其指定進程

/**?*?Created?by?bob?on?17-7-3.?*?服務端代碼?*/public?class?StudentManagerService?extends?Service?{????private?static?final?String?TAG?=?"StudentManagerService";????//判斷Service是否銷毀private?AtomicBoolean?mIsServiceDestroyed?=?new?AtomicBoolean(false);????//適合用于進程間傳輸的列表類private?CopyOnWriteArrayList<Student>?mStudentList?=?new?CopyOnWriteArrayList<Student>();????@Overridepublic?void?onCreate()?{????????super.onCreate();????????//在服務端手動添加兩位默認的學生mStudentList.add(new?Student(1,?"BOB",?"man"));mStudentList.add(new?Student(2,?"MAY",?"woman"));}????@Overridepublic?IBinder?onBind(Intent?intent)?{????????return?mBinder;}????@Overridepublic?void?onDestroy()?{mIsServiceDestroyed.set(false);????????super.onDestroy();}????private?Binder?mBinder?=?new?IStudentManager.Stub()?{????????@Overridepublic?List<Student>?getStudentList()?throws?RemoteException?{SystemClock.sleep(5000);//休眠5s模擬耗時操作return?mStudentList;}????????@Overridepublic?void?addStudent(Student?student)?throws?RemoteException?{mStudentList.add(student);}};}

在清單文件中指定服務的進程

<service?????android:name=".StudentManagerService"????android:process=":remote"></service>

可以看到這個服務類跟普通的服務類相差并不大,唯一的區別在于它創建了一個IStudentManager.Stub的匿名內部類并且實現了其中的方法,在onBind方法中將這個IBinder對象返回給客戶端。這里需要說明一下:Binder是實現了IBinder接口的,所以他同時也是一個IBinder對象。

在客戶端愉快的綁定Service吧!

public?class?MainActivity?extends?AppCompatActivity?{????private?static?final?String?TAG?=?"MainActivity_Client";????private?static?final?int?MESSAGE_QUERY_STUDENTLIST=1;????private?int?student_size?=?3;????private?IStudentManager?mRemoteStudentManager;????private?ServiceConnection?mConnection=new?ServiceConnection()?{????????//onServiceConnected與onServiceDisconnected都是在主線程中的,所以如果里面如果涉及到服務端的耗時操作那么需要在子線程中進行@Overridepublic?void?onServiceConnected(ComponentName?name,?IBinder?service)?{????????????//獲取到IStudentManager對象final?IStudentManager?studentManager?=IStudentManager.Stub.asInterface(service);mRemoteStudentManager?=?studentManager;}????????@Overridepublic?void?onServiceDisconnected(ComponentName?name)?{mRemoteStudentManager?=?null;Log.d(TAG,?"onServiceDisconnected.threadname:"?+?Thread.currentThread().getName());}};????@Overrideprotected?void?onCreate(Bundle?savedInstanceState)?{????????super.onCreate(savedInstanceState);????????setContentView(R.layout.activity_main);Intent?intent?=?new?Intent(this,?StudentManagerService.class);????????bindService(intent,?mConnection,?BIND_AUTO_CREATE);}????@Overrideprotected?void?onDestroy()?{????????unbindService(mConnection);????????super.onDestroy();}????//將服務端返回的數據顯示在界面上private?Handler?mHandler=new?Handler(){????????@Overridepublic?void?handleMessage(Message?msg)?{????????????switch?(msg.what)?{????????????????case?MESSAGE_QUERY_STUDENTLIST:Toast.makeText(MainActivity.this,?msg.obj.toString(),Toast.LENGTH_SHORT).show();????????????????default:????????????????????super.handleMessage(msg);}}};????/**?????*?在客戶端向服務端添加一名學生?????*?@param?view?????*/public?void?addStudent(View?view)?{????????if?(mRemoteStudentManager?!=?null)?{????????????try{????????????????int?student_id?=?student_size+?1;Student?newStudent;????????????????if?(student_id?%?2?==?0)?{newStudent=?new?Student(student_id,?"新學生"?+?student_id,?"man");}?else?{newStudent=?new?Student(student_id,?"新學生"?+?student_id,?"woman");}mRemoteStudentManager.addStudent(newStudent);Log.d(TAG,?"添加一位學生:"?+?newStudent.toString());}catch(Exception?e){e.printStackTrace();}}}????/**?????*?在客戶端向服務端發起查詢學生的請求?????*?@param?view?????*/public?void?get_student_list(View?view)?{Toast.makeText(this,?"正在獲取學生列表",?Toast.LENGTH_SHORT).show();????????//由于服務端的查詢操作是耗時操作,所以客戶端需要開啟子線程進行工作new?Thread(new?Runnable()?{????????????@Overridepublic?void?run()?{????????????????if?(mRemoteStudentManager?!=?null)?{????????????????????try{????????????????????????final?List<Student>?students?=?mRemoteStudentManager.getStudentList();student_size?=?students.size();Log.d(TAG,?"從服務器成功獲取到學生列表:"?+?students.toString());mHandler.obtainMessage(MESSAGE_QUERY_STUDENTLIST,?students).sendToTarget();}catch(Exception?e){e.printStackTrace();}}}}).start();}
}

可以看到我們在客戶端只需要綁定遠程的服務端,服務端就會返回一個IBinder對象,接著我們需要調用IStudentManager.Stub.asInterface()方法,將這個IBinder對象轉換為我們客戶端可用的AIDL接口對象,拿到這個對象之后我們就可以遠程調用服務端的方法了。是不是很容易?
但是需要注意的一點是為了模擬耗時操作,我們在服務端的getStudentList的方法中使用休眠以模擬耗時操作,所以客戶端在調用該方法時不能直接在主線程中調用,而是應該開啟一個子線程,在子線程中調用這個耗時的操作。

看看效果

首先我們獲取學生列表,接著連續添加4個學生,再次查看學生列表,最終的結果如下圖,可以看到我們已經實現了兩個進程之間的交互,接下來我們將分析Binder的原理。

Binder的原理

進程的機制

首先我們需要了解進程之間為什么不能直接進行通信,以下是兩個進程的示意圖:

從上面的圖我們可以得到以下幾點:

  • 一個進程空間分為:用戶態和內核態,即把進程內用戶和內核隔離開來

  • 進程之間,由于Android系統為每個進程分配了一個獨立的虛擬機,用戶空間和內核空間的數據不可交互

  • Binder作為進程間的介質,充當了中介,使得進程間的內核態可以通過Binder進行數據交互

IPC交互示意圖


圖中總共有四個元素,分別是充當客戶端的Activity,服務端的StudentManagerService,充當服務管理者的IStudentManager以及充當訪問介質的Binder驅動。他們的職責如下:

  • StudentManagerService:?服務提供者,這里面會有許多我們常用的服務,在本例中提供的服務就是添加學生以及獲取學生列表。而在系統中則包括有ActivityService 、 WindowMananger等服務,這些系統服務提供的功能,對四大組件以及Window的工作提供的保障。

  • Activity:?服務調用者,一般就是我們的應用,在這里我們通過調用StudentManagerService的服務來完成工作。

  • IStudentManager:?他是負責管理服務的,在其內部通過map集合來存儲Service與Binder的映射關系,這樣客戶端在向其請求服務的時候就能夠返回特定的Binder。

  • Binder驅動:?他是IStudentManager連接各種Service的橋梁,同時也是客戶端與服務端交流的橋梁。

總結起來說,應用程序(Activity)首先向IStudentManager發送請求StudentManagerService的服務,IStudentManager查看已經注冊在里面的服務的列表,找到相應的服務后,通過Binder驅動將其中的Binder對象返回給客戶端,從而完成對服務的請求。

源碼分析

我們主要分析的就是IStudentManager這個類,從上面得到講解我們已經知道它包含了兩個類:Stub和Proxy。先來看看Proxy類

//Proxy.javapublic?java.util.List<com.bob.aidltest.aidl.Student>?getStudentList()?throws?android.os.RemoteException{android.os.Parcel?_data?=?android.os.Parcel.obtain();android.os.Parcel?_reply?=?android.os.Parcel.obtain();java.util.List<com.bob.aidltest.aidl.Student>?_result;????try?{_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getStudentList,?_data,?_reply,?0);_reply.readException();_result?=?_reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR);}????finally?{_reply.recycle();_data.recycle();}????return?_result;
}public?void?addStudent(com.bob.aidltest.aidl.Student?student)?throws?android.os.RemoteException{android.os.Parcel?_data?=?android.os.Parcel.obtain();android.os.Parcel?_reply?=?android.os.Parcel.obtain();????try?{_data.writeInterfaceToken(DESCRIPTOR);????????if?((student!=null))?{_data.writeInt(1);student.writeToParcel(_data,?0);}????????else?{_data.writeInt(0);}mRemote.transact(Stub.TRANSACTION_addStudent,?_data,?_reply,?0);_reply.readException();}????finally?{_reply.recycle();_data.recycle();}
}

上面截取了Proxy的兩個方法,其中Proxy是運行在客戶端的,他是用服務端返回來的Binder對象調用了public static IStudentManager asInterface(IBinder obj)方法返回來的。
既然Proxy運行在客戶端,那么客戶端也是通過Proxy來調用遠程服務端的方法的,也就是說我們將調用方法需要用到的參數傳遞給Proxy,接著由Proxy來訪問服務端,所以我們能夠看到,Proxy將我們的參數寫進了_data,而_reply則代表從服務端返回來的結果。
從代碼中我們還看到客戶端在將數據傳遞給服務端之后就處于阻塞狀態,直到服務端返回結果,所以如果調用的服務端方法是一個耗時方法,那么我們就需要在子線程中進行工作了。
數據準備好之后當然是需要傳遞了,可以看到Proxy通過transact方法講數據傳遞出去了,接下來就來看transact方法:

//Binder#transactpublic?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);}????????//調用了Binder的onTransactboolean?r?=?onTransact(code,?data,?reply,?flags);????????if?(reply?!=?null)?{reply.setDataPosition(0);}????????return?r;}

可以看到transact方法實際上調用了Binder的onTransact,而這里的Binder就是指Stub了,我們看一下Stub的定義:

public?static?abstract?class?Stub?extends?android.os.Binder?implements?com.bob.aidltest.aidl.IStudentManager

可以看到Stub確實繼承了Binder并且也實現了IStudentManager接口,接下來我們繼續看Stub中的onTransact方法:

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_getStudentList:{data.enforceInterface(DESCRIPTOR);java.util.List<com.bob.aidltest.aidl.Student>?_result?=?this.getStudentList();reply.writeNoException();reply.writeTypedList(_result);????????????return?true;}????????case?TRANSACTION_addStudent:{data.enforceInterface(DESCRIPTOR);com.bob.aidltest.aidl.Student?_arg0;????????????if?((0!=data.readInt()))?{_arg0?=?com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data);}????????????else?{_arg0?=?null;}????????????this.addStudent(_arg0);reply.writeNoException();????????????return?true;}}????return?super.onTransact(code,?data,?reply,?flags);
}

可以看到,服務端通過客戶端傳遞過來的code常量來判斷客戶端需要調用的是哪個方法,接著就執行該方法,執行完之后如果有數據返回則將結果寫入reply,接著Proxy就可以收到結果了。而整個通信過程也就結束了。
最后我借用Carson_Ho的一張流程圖來描述這個完整的流程:


本文轉自lzwxx 51CTO博客,原文鏈接:http://blog.51cto.com/13064681/1944339


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/454372.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/454372.shtml
英文地址,請注明出處:http://en.pswp.cn/news/454372.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

excel vba 調用webbrowser_VBA 公式與函數

一, 在單元格中輸入公式的3種方法:1) 用VBA在單元格中輸入普通公式Sub formula_1() Range("d2") ("B2 * C2") End Sub運行程序后,在D2的單元格內顯示的是公式 B2 * C2 ,并非程序返回值.下文(二)中會介紹另外一種直接返回值的方式想要通過程序一…

內部類可以引用它的包含類的成員嗎?有沒有什么限制?

最近看到一道面試題&#xff1a;內部類可以引用它的包含類的成員嗎&#xff1f;有沒有什么限制&#xff1f; 答案大部分都是這樣子的&#xff1a; 完全可以。如果不是靜態內部類&#xff0c;那沒有什么限制&#xff01; 一個內部類對象可以訪問創建它的外部類對象的成員包括私有…

松下NPM服務器怎么備份系統,松下(Panasonic)-NPM校正amp;CPK完整版教程,一步步帶你成為SMT設備大神!...

馬上注冊&#xff0c;結交更多技術專家&#xff0c;享用更多功能&#xff0c;讓你輕松解決各種三星貼片機問題您需要 登錄 才可以下載或查看&#xff0c;沒有帳號&#xff1f;立即注冊 xa8f80375060fa05b8aebe69ffa21080c.gif (5.26 KB, 下載次數: 3)2019-8-12 00:02 上傳f5aae…

Python 模塊之科學計算 Pandas

目錄 一、Pandas簡介 數據結構 二、Series series 的創建 Series值的獲取 Series的運算 Series缺失值檢測 Series自動對齊 Series及其索引的name屬性 三、DataFrame 創建 Index對象 通過索引值或索引標簽獲取數據 自動化對齊 四、文件操作 文件讀取 數據庫數據…

根據 設備名(br0/eth0/em0)稱獲取 當前機器的IP地址與子網掩碼信息

#!/usr/bin/env python 根據 設備名(br0/eth0/em0)稱獲取 當前機器的IP地址與子網掩碼信息import socket, struct, fcntldef get_ipaddress(ifname eth0):s socket.socket(socket.AF_INET, socket.SOCK_DGRAM)return socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915, # SI…

我的程序生涯

本文僅為愛好程序及向往真正之程序員者所作&#xff0c;其余人等可忽略下文。 如今&#xff0c;接觸CS幾近八年&#xff0c;不學無術&#xff0c;所精之物鮮也&#xff0c;以至一事無成。 現回憶吾程序之生涯&#xff0c;以整理繁雜之心緒。 1. 接觸計算機和編程語言 02年始大…

機器學習中qa測試_如何對機器學習做單元測試

作者&#xff1a;Chase Roberts編譯&#xff1a;ronghuaiyang導讀養成良好的單元測試的習慣&#xff0c;真的是受益終身的&#xff0c;特別是機器學習代碼&#xff0c;有些bug真不是看看就能看出來的。在過去的一年里&#xff0c;我把大部分的工作時間都花在了深度學習研究和實…

項目寶提供的服務器,開源WebSocket服務器項目寶貝魚CshBBrain V4.0.1 和 V2.0.2發布

開源WebSocket服務器項目寶貝魚CshBBrain V4.0.1 和 V2.0.2發布更新的功能列表如下&#xff1a;1.解決開啟廣播消息開關時&#xff0c;不能同時接入2個客戶端的重大缺陷。2.對廣播消息做了重大優化&#xff0c;從以前一個線程發送廣播消息進化到使用工作線程池中的線程并行的發…

c# 無損高質量壓縮圖片代碼

/// 無損壓縮圖片 /// <param name"sFile">原圖片</param> /// <param name"dFile">壓縮后保存位置</param> /// <param name"dHeight">高度</param> /// <param name"dWidth"…

一個從文本文件里“查找并替換”的功能

12345678910111213141516171819202122232425# -*- coding: UTF-8 -*-file input("請輸入文件路徑:") word1 input("請輸入要替換的詞:") word2 input("請輸入新的詞&#xff1a;") fopen(file,"r") AAAf.read() count 0 def BBB()…

機器學習算法之 KNN

K近鄰法(k-nearst neighbors,KNN)是一種很基本的機器學習方法了&#xff0c;在我們平常的生活中也會不自主的應用。比如&#xff0c;我們判斷一個人的人品&#xff0c;只需要觀察他來往最密切的幾個人的人品好壞就可以得出了。這里就運用了KNN的思想。KNN方法既可以做分類&…

安裝云端服務器操作系統,安裝云端服務器操作系統

安裝云端服務器操作系統 內容精選換一換SAP云服務器規格在申請SAP ECS之前&#xff0c;請參考SAP標準Sizing方法進行SAPS值評估&#xff0c;并根據Sizing結果申請云端ECS服務器資源&#xff0c;詳細信息請參考SAP Quick Sizer。SAP 各組件最低硬盤空間、RAM&#xff0c;以及軟件…

python 進度條_六種酷炫Python運行進度條

轉自&#xff1a;一行數據閱讀文本大概需要 3 分鐘你的代碼進度還剩多少&#xff1f;今天給大家介紹下目前6種比較常用的進度條&#xff0c;讓大家都能直觀地看到腳本運行最新的進展情況。1.普通進度條2.帶時間進度條3.tpdm進度條4.progress進度條5.alive_progress進度條6.可視…

js 獲取多少天前

getBeforeDate: function(day, str) { var now new Date().getTime(); //獲取毫秒數 var before new Date(now - ((day > 0 && day ? day : 0) * 86400 * 1000)); var year before.getFullYear(); var month before.getMonth()1; var date before.getDate(); …

程序員的基本素質

給所有立志成為程序員的朋友 以及 自勉之&#xff01; 程序員基本素質&#xff1a; 作一個真正合格的程序員&#xff0c;或者說就是可以真正合格完成一些代碼工作的程序員&#xff0c;應該具有的素質。 1&#xff1a;團隊精神和協作能力 把它作為基本素質&#xff0c;并…

權限之淺理解

白馬過隙&#xff0c;在感嘆時光流逝的同時不得不承認在學習中隨著知識面的不斷擴展所接受的東西也越來越多&#xff0c;尤其是那些外形比較容易混淆的命令&#xff0c;著實讓作為新手的吃了很多苦頭&#xff0c;趁著學習緊張之時偷個懶整理這周易混淆的命令&#xff1a; chgrp…

機器學習算法之生成樹

一、什么是決策樹&#xff1f; 決策樹&#xff08;Decision Tree&#xff09;是一種基本的分類和回歸的方法。 分類決策樹模型是一種描述對實例進行分類的樹形結構。決策樹由結點&#xff08;node&#xff09;和有向邊&#xff08;directed edge&#xff09;組成。結點有兩種…

強烈推薦給從事IT業的同行們 (轉載)

作者&#xff1a;李學凌 文章來源&#xff1a;bbs.ustc.edu.cn 中國有很多小朋友&#xff0c;他們18,9歲或21,2歲&#xff0c;通過自學也寫了不少代碼&#xff0c;他們有的代碼寫的很漂亮&#xff0c;一些技術細節相當出眾&#xff0c;也很有鉆研精神&#xff0c;但是他…

微機原理控制轉移類指令

1.無條件跳轉指令 指令格式;JMP 目標地址 功能&#xff1a;JMP可以使程序無條件地跳轉到程序存儲器中某目標地址 注意點&#xff1a; 1&#xff09;指令目標地址若在JMP指令所在的代碼段內&#xff0c;屬段內跳轉&#xff0c;指令只修改IP內容。指令目標地址若在JMP指令所在的代…

OPENNMS的后臺并行管理任務

Concurrent management tasks: 1. . Action daemon - automated action (work flow)2. .數據采集Collection daemon - collects data3. .能力檢查Capability daemon - capability check on nodes4. .動態主機配置協議DHCP daemon - DHCP clien…