多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > php開源 > 綜合技術(shù) > Android 之 IPC 進(jìn)程通信全解析

Android 之 IPC 進(jìn)程通信全解析

來源:程序員人生   發(fā)布時間:2016-07-06 13:39:51 閱讀次數(shù):2876次

Android 之 IPC 進(jìn)程通訊全解析

本篇博客的框架

這里寫圖片描述

甚么是IPC

IPC(Inter-Process Communication) 進(jìn)程間通訊,是指兩個不同進(jìn)程之間數(shù)據(jù)交換的進(jìn)程。

在明確其之前,需要先弄懂幾個概念:

  • 線程:CPU可調(diào)度的最小單位,是程序履行流的最小單元;線程是進(jìn)程中的1個實體,是被系統(tǒng)獨立調(diào)度和分派的基本單位,線程自己不具有系統(tǒng)資源,只具有1點兒在運行中必不可少的資源,但它可與同屬1個進(jìn)程的其它線程同享進(jìn)程所具有的全部資源。
  • 進(jìn)程: 1個履行單元,在PC 和移動裝備上1般指1個程序或利用,1個進(jìn)程可以包括多個線程。每個進(jìn)程都有它自己的地址空間,1般情況下,包括文本區(qū)域(text region)、數(shù)據(jù)區(qū)域(data region)和堆棧(stack region)。

在Android程序中,1般情況下1個程序就是1個進(jìn)程(在無特別的代碼實現(xiàn)下),UI線程即主線程。如果有耗時操作,則會致使主線程堵死。而在Android中主線程負(fù)責(zé)UI,和用戶交互,如果堵死肯定影響用戶的使用度。所以Android要求不能再主線程中有耗時操作,這時候就要將耗時操作放在子線程中。

IPC 的使用處景

  • 程序由于本身緣由,需要采取多進(jìn)程模式來實現(xiàn)。
    • 有些模塊由于特殊緣由需要運行運行在單獨的進(jìn)程中。
    • 為了加大1個利用可以使用的內(nèi)存所以需要通過量進(jìn)程來獲得內(nèi)存空間。
  • 當(dāng)前利用需要向其他利用獲得數(shù)據(jù)。由因而兩個利用,即兩個進(jìn)程。

在Android 中,每個利用可以使用的內(nèi)存大小有限制,早起的1些版本在16M左右,不同的裝備有不同的大小。可以通過量進(jìn)程獲得多分內(nèi)存空間。

Android中的多進(jìn)程

如何開啟多進(jìn)程

Android中開啟多進(jìn)程只有1種方法,便是給4大組件(ActivityReceiverContentProviderService)指定android
:process
屬性,初次以外沒有其他方法。請注意,不能指定某1個線程或?qū)嶓w類指定其所運行的進(jìn)程

通過jni調(diào)用底層去開啟多進(jìn)程也是1種方法,但屬于特殊情況,不進(jìn)行斟酌;

首先編寫3個Activity,并在清單文件中做以下注冊:

<activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.ipc.SecondActivity" android:process=":remote" /> <activity android:name="com.example.ipc.ThirdActivity" android:process=".remote" />

MainActivity不進(jìn)行指定,則默許為當(dāng)前進(jìn)程。

SecondActivity指定屬性android:process=":remote"

ThirdActivity指定屬性android:process=".remote"

注意SencodActivityThirdActivity的進(jìn)程參數(shù)不同。

把3個頁面都打開,通過DDMS可以看到3個進(jìn)程的開啟

這里寫圖片描述

啟動了3個進(jìn)程:分別是

  • com.example.ipc:默許的程序進(jìn)程。和包名相同。
  • com.example.ipc:remoteSecondActivity所在的進(jìn)程。
  • .remoteThirdActivity所在的進(jìn)程。

那末2和3 ,進(jìn)程名不同有甚么區(qū)分嗎;

  • 如果進(jìn)程名以:開始,表示是要在當(dāng)前的進(jìn)程名前附加上當(dāng)前的包名,表示該進(jìn)程是本利用的私有進(jìn)程,其他利用不可以和其跑在同1個進(jìn)程。
  • 如果進(jìn)程名不以:開始,表示不需附加包名信息,是1個完全的命名。同時該進(jìn)程是全局進(jìn)程,其他利用可以通過ShareUID和其跑在同1個進(jìn)程中。

開啟多進(jìn)程存在的問題

通過如上方式,很簡單的變開啟了多進(jìn)程,但是,如果僅僅這樣的話,會有大問題。

看下面1個例子:

添加1個公有的類,添加靜態(tài)字段:

/** * 類似平常開發(fā)中的工具類等 * @author MH * */ public class PublicContant { public static int m = 1; }

MainActivity中Log1下并修改字段

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("info", PublicContant.m+""); PublicContant.m++; }

SecondActivity中Log1下:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); Log.i("info", PublicContant.m+""); }

根據(jù)上面的邏輯,Log信息應(yīng)當(dāng)是1,和2 。但是呢,不是這樣的。

這里寫圖片描述
兩個都是1,我靠。。先不問緣由,看結(jié)果就知道是錯的。多進(jìn)程這么不靠譜,腫么回事??

Android 為每個進(jìn)程都分配1個獨立的虛擬機(jī),不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間,這就致使在不同虛擬機(jī)中訪問同1個類對象會產(chǎn)生多個副本。

對當(dāng)前來講,進(jìn)程com.example.ipccom.example.ipc:remote都存在1個PublicContant類,并且這兩個類是相互不干擾的,1個進(jìn)程中修改了該值的對象,對其他進(jìn)程中的該值不會造成任何影響。

運行在同1個進(jìn)程中的組件是屬于同1個虛擬機(jī)和同1個Application的。同理,運行在不同進(jìn)程中的組件是屬于兩個不同的虛擬機(jī)和Application的。

根據(jù)如上所述,多進(jìn)程所釀成的問題分為以下幾個方面:

  • 靜態(tài)成員和單例模式完全失效
    • 如上分析,創(chuàng)建了不同的內(nèi)存,多個對象,固然單例甚么的都無效了。
  • 線程同步機(jī)制完全失效
    • 不是1塊內(nèi)存區(qū)域,線程鎖固然無效了。
  • SharedPreference的可靠性降落
    • sharedPreference的底層實現(xiàn)是通過讀寫XML文件,兩個進(jìn)程去讀寫,并發(fā)明顯是可能出現(xiàn)問題的。
  • Application會屢次創(chuàng)建

序列化和反序列化

在了解多進(jìn)程通訊之前,我們需要了解兩個基礎(chǔ)的概念,序列化和反序列化。

  • 序列化:將對象轉(zhuǎn)化為可保存的字節(jié)序列。(注意,是對象)。
  • 反序列:將字節(jié)序列恢復(fù)為對象的進(jìn)程。

序列化和反序列的用處:

  • 以某種存儲情勢使自定義對象序列化。
  • 將對象從1個地方傳遞到另外一個地方。
  • 通過序列化在進(jìn)程間傳遞對象。

Android中實現(xiàn)序列化的方式有兩種,SerializableParcelable

Serializable

Serializable是Java提供的1個序列化接口,他是1個空接口,是類實現(xiàn)該接口便可實現(xiàn)序列化。

/** * Serializable 序列化對象 * @author MH * */ public class Book implements Serializable { /** * 序列化和反序列的關(guān)鍵 */ private static final long serialVersionUID = 1L; public int bookId; public String bookName; }

在實現(xiàn)Serializable時候,編譯器會提示,讓我們添加serialVersionUID字段,該字段是1個關(guān)鍵的字段,后面會說。

相應(yīng)的實現(xiàn)好了,那末如何寫入和讀取呢?

  • 寫入
public void writeSerializable() { try { // 構(gòu)造對象 Book book = new Book(); // 構(gòu)造序列化輸出字節(jié)流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xxx.txt")); // 序列化對象 oos.writeObject(book); // 關(guān)閉流 oos.close(); } catch (Exception e) { e.printStackTrace(); } }
  • 讀取
public void readSerializable() { try { // 創(chuàng)建序列化讀取字節(jié)流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( "xxx.txt")); // 反序列化(讀取)對象 Book book = (Book) ois.readObject(); // 關(guān)閉流 ois.close(); } catch (Exception e) { e.printStackTrace(); } }

在序列化時,如果我們序列化對象以后,改變了我們的類結(jié)構(gòu)(添加或改變字段),乃至是修改了字段的類型,修改了類名,那末我們能反序列化成功嗎。

那末關(guān)鍵就在于serialVersionUID字段。

如果我們不指定的話。在序列化時,會計算當(dāng)前類結(jié)構(gòu)的hash值并將該值賦給serialVersionUID,當(dāng)反序列時,會比對該值是不是相同,如果不相同,則沒法序列化成功。

我們也能夠手動指定,手動指定的好處是在類結(jié)構(gòu)產(chǎn)生變化時,能夠最大程度的反序列,固然條件是只是刪除或添加了字段,如果是變量類型產(chǎn)生了變化,則仍然沒法反序列成功。

serialVersionUID 的工作機(jī)制:序列化時系統(tǒng)會把當(dāng)前類的serialVersionUID寫入序列化文件中,當(dāng)反序列化時候系統(tǒng)會去檢測文件中的serialVersionUID,看它是不是和當(dāng)前類的serialVersionUID1致,如果1致說明序列化類的版本和當(dāng)前類的版本是相同的,這個時候可以成功反序列化,否則就說明當(dāng)前類和序列化的類相比產(chǎn)生了某些變化。所以,我們最好指定serialVersionUID,避免他自定生成。

Parcelable

Parcelable是Android中獨有的1種序列化方式,在intent傳值時,通常使用該方式。

該方式實現(xiàn)序列化,仍然實現(xiàn)Parcelable,然后實現(xiàn)1些該接口的方法。

/** * Parcelable 對象的使用方式 * @author MH * */ public class Book implements Parcelable { public int bookId; public String bookName; @Override public int describeContents() { // 返回當(dāng)前對象的內(nèi)容描寫。幾近所有情況下都是返回0 return 0; } @Override public void writeToParcel(Parcel dest, int flags) { // 將當(dāng)前對象寫入到序列化結(jié)構(gòu)中 dest.writeInt(bookId); dest.writeString(bookName); } public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() { @Override public Book createFromParcel(Parcel source) { //從序列化后的對象中創(chuàng)建原始的值 Book book = new Book(); book.bookId = source.readInt(); book.bookName = source.readString(); return book; } @Override public Book[] newArray(int size) { //創(chuàng)建指定長度的原始對象數(shù)組 return new Book[size]; } }; }

Parcelable實現(xiàn)兩個方法,創(chuàng)建1個字段:

  • 實現(xiàn)describeContents():返回當(dāng)前對象的內(nèi)容描寫。幾近所有情況下都是返回0。
  • 實現(xiàn)public void writeToParcel(Parcel dest, int flags):// 將當(dāng)前對象寫入到序列化結(jié)構(gòu)中
  • 構(gòu)造Parcelable.Creator字段,該對象需要實現(xiàn)兩個方法:
    • public Book createFromParcel(Parcel source):從序列化后的對象中創(chuàng)建原始的值。
    • public Book[] newArray(int size):創(chuàng)建指定長度的原始對象數(shù)組

Serializable和Parcelable的比較

  • Serializable是Java中的序列化接口,其使用起來簡單但是開消較大,序列化和反序列化需要大量的I/O操作。
  • Parcelable是Android中的序列化方式,更適用于Android的平臺上,他的缺點是使用起來略微麻煩,但是效力很高。
  • Parcelable合適進(jìn)程間的通訊,運行期。Serializable合適文件存儲即網(wǎng)絡(luò)傳輸。

Android 進(jìn)程間通訊的方式

使用Bundle 傳輸數(shù)據(jù)

Android中的4大組件中,其中有3大組件(Activity,Service,Receiver)都支持Intent中傳遞Bundle數(shù)據(jù),如果看其源碼,會發(fā)現(xiàn)其也是實現(xiàn)了Parcelable接口,所以其能夠在不同進(jìn)程中傳輸。

固然在傳輸?shù)倪M(jìn)程中,其所傳輸?shù)臄?shù)據(jù)必須支持序列化。比如基本數(shù)據(jù)類型,字符串,Parcelable的實現(xiàn)類,Serializable的實現(xiàn)類。由于該方法非常經(jīng)常使用,不在多說。

文件同享

文件同享: 將對象序列化以后保存到文件中,在通過反序列,將對象從文件中讀取。

MainActvity中寫寫入對象

/** * 寫入序列化對象 */ public void wirte() { Book book = new Book(); book.bookId = 1; book.bookName = "si"; try { // 構(gòu)造序列化輸出字節(jié)流 ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(PATH)); // 序列化對象 oos.writeObject(book); // 關(guān)閉流 oos.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println(book); }

SecondActivity中,讀取文件(反序列化)

public void read() { Book book = null; try { // 創(chuàng)建序列化讀取字節(jié)流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream( MainActivity.PATH)); // 反序列化(讀取)對象 book = (Book) ois.readObject(); // 關(guān)閉流 ois.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println(book); }

LOG 結(jié)果

06-28 09:20:47.916: com.example.ipc(進(jìn)程名) I/System.out(12399): Book [bookId=1, bookName=si] 06-28 09:20:53.376: com.example.ipc:remote(進(jìn)程名) I/System.out(12866): Book [bookId=1, bookName=si]

分屬不同的進(jìn)程成功的獲得到了同享的數(shù)據(jù)。

通過同享文件這類方式來同享數(shù)據(jù)對文件的格式是沒有具體的要求的。比如可以是文件,也能夠是Xml,JSON 等。只要讀寫雙方約定1定的格式便可。

同文件同享方式也存在著很大的局限性。即并發(fā)讀/ 寫的問題。讀/寫會造成數(shù)據(jù)不是最新。同寫很明顯會出現(xiàn)毛病。

文件同享合適在對數(shù)據(jù)同步要求不高的進(jìn)程之間進(jìn)行通訊。并且要妥善處理并發(fā)讀寫的問題。

SharedPreference 底層文件的方式。不合適在多進(jìn)程中同享數(shù)據(jù)。

Messenger

Messenger 可以翻譯為信使,通過該對象,可以在不同的進(jìn)程中傳遞Message對象。注意,兩個單詞不同。

下面就通過服務(wù)端(Service)和客戶端(Activity)的方式進(jìn)行演示。

客戶端向服務(wù)端發(fā)送消息,可分為以下幾步。

服務(wù)端

  • 創(chuàng)建Service
  • 構(gòu)造Handler對象,實現(xiàn)handlerMessage方法。
  • 通過Handler對象構(gòu)造Messenger信使對象。
  • 通過ServiceonBind()返回信使中的Binder對象。

客戶端

  • 創(chuàng)建Actvity
  • 綁定服務(wù)
  • 創(chuàng)建ServiceConnection,監(jiān)聽綁定服務(wù)的回調(diào)。
  • 通過onServiceConnected()方法的參數(shù),構(gòu)造客戶端Messenger對象
  • 通過Messenger向服務(wù)端發(fā)送消息。

實現(xiàn)服務(wù)端

/** * Messenger 的使用 服務(wù)端 * @author MH * */ public class MessengerService extends Service { /** * 構(gòu)建handler 對象 */ public static Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { // 接受客戶端發(fā)送的消息 String msgClient = msg.getData().getString("msg"); Log.i("messenger","接收到客戶真?zhèn)€消息--"+msgClient); }; }; // 通過handler 構(gòu)建Mesenger 對象 private final Messenger messenger = new Messenger(handler); @Override public IBinder onBind(Intent intent) { // 返回binder 對象 return messenger.getBinder(); } }

注冊服務(wù)別忘了 ,同時對服務(wù)修改其進(jìn)程。

實現(xiàn)客戶端

/** * Messenger 的使用 客戶端 * * @author MH * */ public class MessengerActivity extends AppCompatActivity { /** * Messenger 對象 */ private Messenger mService; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // IBinder 對象 // 通過服務(wù)端返回的Binder 對象 構(gòu)造Messenger mService = new Messenger(service); Log.i("messenger", "客戶端以獲得服務(wù)端Messenger對象"); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messenger); // 啟動服務(wù) Intent intent = new Intent(this, MessengerService.class); bindService(intent, conn, BIND_AUTO_CREATE); } /** * 布局文件中添加了1個按鈕,點擊該按鈕的處理方法 * @param view */ public void send(View view) { try { // 向服務(wù)端發(fā)送消息 Message message = Message.obtain(); Bundle data = new Bundle(); data.putString("msg", "lalala"); message.setData(data); // 發(fā)送消息 mService.send(message); Log.i("messenger","向服務(wù)端發(fā)送了消息"); } catch (Exception e) { e.printStackTrace(); } } }

看1下結(jié)果:

這里寫圖片描述

注釋很清楚,不在多說,依照流程實現(xiàn)便可。 其中有1點需要注意:

我們是通過Message作為媒介去攜帶數(shù)據(jù)的。但是,Message的obj 并沒有實現(xiàn)序列化(實現(xiàn)SerializableParcelable),也就是其不能保存數(shù)據(jù)。必須使用message.setData()方法去傳入1個Bundle對象,Bundle中保存需要傳入的數(shù)據(jù)。

傳遞時使用的是Messenger.send(Message)方法。

服務(wù)端向客戶端發(fā)送了消息,那末服務(wù)端向客戶端發(fā)送消息也類似:

關(guān)鍵點: 客戶端向服務(wù)端發(fā)送消息是,通過msg.replyTo將客戶端Messenger對象傳給服務(wù)端。

客戶端代碼進(jìn)行修改:

  • 創(chuàng)建客戶端HandlerMessenger對象。
  • 修改send()方法。
/** * 構(gòu)建handler 對象 */ public static Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { // 接受服務(wù)端發(fā)送的消息 String msgService = msg.getData().getString("msg"); Log.i("messenger","接收到服務(wù)真?zhèn)€消息--"+msgService); }; }; // 通過handler 構(gòu)建Mesenger 對象 private final Messenger messengerClient = new Messenger(handler); /** * 布局文件中添加了1個按鈕,點擊該按鈕的處理方法 * @param view */ public void send(View view) { try { // 向服務(wù)端發(fā)送消息 Message message = Message.obtain(); Bundle data = new Bundle(); data.putString("msg", "lalala"); message.setData(data); // ----- 傳入Messenger 對象 message.replyTo = messengerClient; // 發(fā)送消息 mService.send(message); Log.i("messenger","向服務(wù)端發(fā)送了消息"); } catch (Exception e) { e.printStackTrace(); } }

服務(wù)端代碼修改

/** * 構(gòu)建handler 對象 */ public static Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { // 接受客戶端發(fā)送的消息 String msgClient = msg.getData().getString("msg"); Log.i("messenger", "接收到客戶真?zhèn)€消息--" + msgClient); // 獲得客戶端Messenger 對象 Messenger messengetClient = msg.replyTo; // 向客戶端發(fā)送消息 Message message = Message.obtain(); Bundle data = new Bundle(); data.putString("msg", "ccccc"); message.setData(data); try { // 發(fā)送消息 messengetClient.send(message); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } }; };

結(jié)果不在演示了。

AIDL

AIDL是1種接口定義語言,用于束縛兩個進(jìn)程間的通訊規(guī)則,供編譯器生成代碼,實現(xiàn)Android裝備上的兩個進(jìn)程間通訊(IPC)。

進(jìn)程之間的通訊信息,首先會被轉(zhuǎn)換成AIDL協(xié)議消息,然后發(fā)送給對方,對方收到AIDL協(xié)議消息后再轉(zhuǎn)換成相應(yīng)的對象。

AIDL的關(guān)鍵便是Binder,關(guān)于Binder,后面的博客會分析。在這里之將如何使用它。

由于需要服務(wù)端和客戶端共用aidl文件,所以最好單獨建1個包,合適拷貝到客戶端。

服務(wù)端:

  • 添加以下包名:com.example.ipc.aidl
  • 創(chuàng)建BookAidl.java,該對象需要作為傳輸。所以需要實現(xiàn)Parcelable
public class BookAidl implements Parcelable { public int bookId; public String bookName; public BookAidl() { super(); } public BookAidl(int bookId, String bookName) { super(); this.bookId = bookId; this.bookName = bookName; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } public static final Parcelable.Creator<BookAidl> CREATOR = new Creator<BookAidl>() { @Override public BookAidl[] newArray(int size) { return new BookAidl[size]; } @Override public BookAidl createFromParcel(Parcel source) { BookAidl book = new BookAidl(); book.bookId = source.readInt(); book.bookName = source.readString(); return book; } }; @Override public String toString() { return "BookAidl [bookId=" + bookId + ", bookName=" + bookName + "]"; } }

Parcelable在前面已說過,不在多說。

  • 創(chuàng)建.aidl文件。由于需要用到BookAidl對象,所以需要先聲明。

創(chuàng)建BookAidl.aidl文件,并手動添加

package com.example.ipc.aidl; parcelable BookAidl;

創(chuàng)建IBookManager.aidl文件,接口文件,面向客戶端調(diào)用

package com.example.ipc.aidl; import com.example.ipc.aidl.BookAidl; interface IBookManager{ List<BookAidl> getBookList(); void addBook(in BookAidl book); }

寫完以后clean1下工程,以后會在gen目錄下生成對應(yīng)的java文件。此java中的具體含義后面會解釋,在此不做多述。

  • 繼續(xù)編寫服務(wù)端,創(chuàng)建Service類。
public class BookService extends Service { /** * 支持線程同步,由于其存在多個客戶端同時連接的情況 */ private CopyOnWriteArrayList<BookAidl> list = new CopyOnWriteArrayList<>(); /** * 構(gòu)造 aidl中聲明的接口的Stub對象,并實現(xiàn)所聲明的方法 */ private Binder mBinder = new IBookManager.Stub() { @Override public List<BookAidl> getBookList() throws RemoteException { return list; } @Override public void addBook(BookAidl book) throws RemoteException { list.add(book); Log.i("aidl", "服務(wù)端添加了1本書"+book.toString()); } }; @Override public void onCreate() { super.onCreate(); //加點書 list.add(new BookAidl(1, "java")); list.add(new BookAidl(2, "android")); } @Override public IBinder onBind(Intent intent) { // 返回給客戶真?zhèn)€Binder對象 return mBinder; } }

Service中,主要干了兩件事情:

  • 實現(xiàn)aidl文件中的接口的Stub對象。并實現(xiàn)方法。
  • Binder對象通過onBinder返回給客戶端。

為了省事,在這里不在另起1個工程了,直接將Service在另外一個進(jìn)程中運行。

<service android:name="com.example.ipc.BookService" android:process=":remote" />

開始編寫客戶端

由于在同1個工程中,不需要拷貝aidl包中的文件。如果不在同1個工程,需要拷貝。

public class BookActivity extends AppCompatActivity{ /** * 接口對象 */ private IBookManager mService; /** * 綁定服務(wù)的回調(diào) */ private ServiceConnection conn = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { // 獲得到書籍管理的對象 mService = IBookManager.Stub.asInterface(service); Log.i("aidl", "連接到服務(wù)端,獲得IBookManager的對象"); } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book); // 啟動服務(wù) Intent intent = new Intent(this,BookService.class); bindService(intent, conn, BIND_AUTO_CREATE); } /** * 獲得服務(wù)端書籍列表 * @param view */ public void getBookList(View view){ try { Log.i("aidl","客戶端查詢書籍"+mService.getBookList().toString()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 添加書籍 */ public void add(View view){ try { // 調(diào)用服務(wù)端添加書籍 mService.addBook(new BookAidl(3,"ios")); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }

客戶真?zhèn)€代碼和之前的Messenger很類似:

  • 綁定服務(wù),監(jiān)聽回調(diào)。
  • 將回調(diào)中的IBinder service通過IBookManager.Stub.asInterface()轉(zhuǎn)化為借口對象。
  • 調(diào)用借口對象的方法。

效果

這里寫圖片描述

總結(jié)來講可分為以下幾步

服務(wù)端:

  • 服務(wù)端創(chuàng)建.aidl文件和聲明接口
  • 創(chuàng)建類,繼承Service,并實現(xiàn)onBind方法
  • 在Service類中定義aidl中聲明接口的Stub對象,并實現(xiàn)aidl接口中聲明的方法
  • 在onBind方法中返回Stub對象
  • 在AndroidManifest.xml中注冊Service并聲明其Action

客戶端

  • 使用服務(wù)端提供的aidl文件
  • 在Activity定義aidl接口對象
  • 定義ServiceConnection對象,監(jiān)聽綁定服務(wù)的回調(diào)
  • 回調(diào)中通過方法獲得借口對象

ContentProvider

作為android 4大組件之1,雖然用的地方不是太多。但是其確切是多進(jìn)程通訊的1種方式。例如,獲得通訊錄信息,這明顯跨利用了,肯定是多進(jìn)程通訊啊。

其底層實現(xiàn)和Messenger1樣,都是通過Binder,后面會專門分析Binder對象。

ContentProvider很多介紹,在這不在多提。

Socket實現(xiàn)

Socket也稱為“套接字”,是網(wǎng)絡(luò)通訊中的概念,它分為流式套接字和用戶數(shù)據(jù)報套接字,分別對應(yīng)于網(wǎng)絡(luò)傳輸中的傳輸控制層的TCP和UDP。

該方面使用的是JAVA 方面的知識。該舉例只是說明1個思路。不做細(xì)致的實現(xiàn)。

服務(wù)端

public class SocketService extends Service { /** * 連接的狀態(tài) */ private boolean isConnState = true; @Override public void onCreate() { super.onCreate(); // 啟動TCP 服務(wù) new Thread(new TCPServer()).start(); } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { // 結(jié)束TCP 服務(wù) isConnState = false; super.onDestroy(); } /** * 服務(wù)端TCP 服務(wù),相當(dāng)于服務(wù)器,接受Socket 連接 * @author MH * */ class TCPServer implements Runnable{ @Override public void run() { try { // 監(jiān)聽本地的12345 端口 ServerSocket ss = new ServerSocket(12345); while(isConnState){ // 獲得客戶真?zhèn)€Socket 對象 Socket socket = ss.accept(); // 獲得輸入流 --- BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 通過輸入流讀取客戶真?zhèn)€消息 //String line = br.readLine(); // 輸出流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); // 通過輸出流向客戶端發(fā)送消息 //bw.write("...."); // 關(guān)閉連接 socket.close(); } } catch (Exception e) { e.printStackTrace(); } } } }

服務(wù)啟動時,在onCreate方法中啟動了TCPServer,該線程時刻接受客戶真?zhèn)€要求。

客戶端

public void conn(){ try { // 指定ip和端口 Socket s = new Socket("localhost", 12345); // ----- 和服務(wù)端類似 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); //String line = br.readLine(); // 輸出流 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //bw.write("...."); // 關(guān)閉連接 s.close(); } catch (Exception e) { e.printStackTrace(); } }

關(guān)于Socket,在此只是1個簡單的示范,具體的使用博大精深。知道能實現(xiàn)便可。

Android 進(jìn)程間通訊不同方式的比較

  • Bundle:4大組件間的進(jìn)程間通訊方式,簡單易用,但傳輸?shù)臄?shù)據(jù)類型受限。
  • 文件同享: 不合適高并發(fā)場景,并且沒法做到進(jìn)程間的及時通訊。
  • Messenger: 數(shù)據(jù)通過Message傳輸,只能傳輸Bundle支持的類型
  • ContentProvider:android 系統(tǒng)提供的。簡單易用。但使用受限,只能根據(jù)特定規(guī)則訪問數(shù)據(jù)。
  • AIDL:功能強(qiáng)大,支持實時通訊,但使用略微復(fù)雜。
  • Socket:網(wǎng)絡(luò)數(shù)據(jù)交換的經(jīng)常使用方式。不推薦使用。

TO DO

Binder 的細(xì)致分析

在實現(xiàn)多進(jìn)程通訊時,其中Messenger,ContentProvider,AIDL的底層實現(xiàn)都是Binder,很有必要對其進(jìn)行繼續(xù)分析。

該博客中的代碼以同享到github。 https://github.com/AlexSmille/Android-IPC-Example

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 亚洲玖玖 | 亚洲在线网 | 性欧美激情videos | 久久精品国内偷自一区 | 美女私人影院 | 色综合天天综合网国产成人 | 欧美午夜三级 | 中文在线播放 | 国产在线播放成人免费 | 欧美精品1区2区 | 啪啪午夜视频 | 男女激情视频网站 | 国产欧美久久一区二区 | 午夜网站免费 | 欧美另类videos粗暴黑人 | 亚洲精品日韩一区二区日本 | 国产成人免费永久播放视频平台 | 伊人网网站 | 亚洲性猛交xx乱 | av中文字幕网免费观看 | 手机看片福利在线 | 欧美视频福利 | 免费播放观看在线视频 | 精品在线观看免费 | 岛国福利视频 | tube欧美巨大 | 久久国产视频在线观看 | 欧美另类videosbestsex高清 | 在线亚州 | 久久综合欧美 | 视色4setv.com | 性生活免费视频网站 | 小说 都市 欧美 亚洲 | 免费一区二区三区四区五区 | 久久久亚洲欧美综合 | 啪啪小视频网站 | 欧美日韩加勒比一区二区三区 | 伊人免费 | 欧美一级毛片免费观看 | 亚洲一区二区三区高清 | 在线亚洲观看 |