Android進程間通信

2021-02-15 郭霖

昨日,世預賽亞洲區12強賽,國足坐鎮長沙主場迎戰韓國隊。第34分鐘,于大寶為國足打破僵局,下半場,門將曾誠連續做出神撲,幫助國足將1-0的比分守到終場。這是12強賽以來,國足取得的首場勝利,將晉級的希望繼續保留了下去。

明天就是周末了,這裡提前祝大家周末愉快!

本篇來自 夏雨 的投稿,詳細地介紹了跨進程通信的兩種方案,希望能夠幫助到大家。

夏雨 的博客地址:

http://blog.csdn.net/yulyu

進程間通信(Inter-Process Communication),簡稱 IPC,就是指進程與進程之間進行通信.一般來說,一個app只有一個進程,但是可能會有多個線程,所以我們用得比較多的是多線程通信,比如 Handler,AsyncTask.

但是在一些特殊的情況下,我們app會需要多個進程,或者是我們在遠程服務調用時,就需要跨進程通信了

Android設置多進程的步驟很簡單,只用在清單文件中為四大組件加上 process 屬性:

( :messager 最終的進程名會變成 包名+:messager)

雖然多進程設置起來很簡單,但是使用的時候卻會有一系列的問題:

兩個進程對應的是不同的內存區域

1. Application對象會創建多次

2. 靜態成員不共用

3. 同步鎖失效

4. 單例模式失效

5. 數據傳遞的對象必須可序列化

進程間通信傳遞的對象是有嚴格要求的,除了基本數據類型,其他對象要想可以傳遞,必須可序列化,Android實現可序列化一般是通過實現 Serializable 或者是 Parcelable

如果你在進程通信中不需要傳非基本數據類型的對象,那麼你可以不了解序列化,但是可序列化是進程間通信的基礎,所以還是建議不了解的朋友先熟悉一下

筆者之前介紹過序列化的相關知識,這裡就不重複介紹了:

序列化–Serializable與Parcelable

http://blog.csdn.net/yulyu/article/details/56481665

跨進程通信的方法有很多,比如通過 Intent傳遞,通過 AIDL 以及 Messager通信,通過 socket通信,這裡主要介紹的是基於 Binder 的 AIDL 和 Messager。

Intent

Intent 進行數據的傳遞是我們平時最常用的,他的原理其實是對於 Binder 的封裝,但是他只能做到單向的數據傳遞,所以並不能很好的實現跨進程通信,我們這裡就不展開來介紹了

Messenger

Messenger 的底層也是基於 Binder 的,其實應該說他是在 AIDL 的基礎上封裝了一層.

一般來說安卓中使用 Binder 主要是通過綁定服務(bindService),服務端(這裡指的不是後臺,是指其中一個進程)主要是運行 Service,客戶端通過 bindService 獲取到相關的 Binder,Binder 就作為橋梁進行跨進程的通信.

這裡我們先演示同一個應用內的多進程通信

首先我們先創建一個Service:

並在清單文件中配置他的進程:

在 Service 裡面創建一個 Hander 用來接受消息:

在 Service 裡面創建一個 Messenger,並把 Handler 放入其中:

private final static Messenger mMessenger = new Messenger(mHandler);

重寫 onBind 方法,返回 Messenger 裡面的 Binder:

public IBinder onBind(Intent intent) {
   return mMessenger.getBinder();}

創建一個對象實現 ServiceConnection:

綁定服務:

綁定服務後,會調用 ServiceConnection 的 onServiceConnected 方法,通過 Messenger 發送消息,伺服器端的 Handler 就能夠收到消息了:

這樣的話我們就能夠通過 bindService 獲取到一個包含 Binder 的 Messenger 進行通信了,但是我們目前只實現了客戶端對伺服器端傳遞消息,那麼伺服器端如何對客戶端傳遞消息呢?

我們先對伺服器端的代碼進行修改,首先修改 Service 的 Handler:

接著我們在客戶端也增加一個 Handler 和 Messenger 來處理消息:

還有一個比較關鍵的地方,就是要在客戶端發送消息的時候把客戶端的 Messenger 通過消息傳送到伺服器端

這樣一來,伺服器端和客戶端就能很好的實現跨進程通信了.

如果需要傳送數據的話,可以通過 Bundle 設置數據,除了基本數據類型,還可以通過消息傳送可序列化的對象

發送方:

接收方:

上面我們已經實現了跨進程通信,但是這裡面其實是有弊端的,服務端處理客戶端的消息是串行的,必須一個一個來處理,所以如果是並發量比較大的時候,通過 Messenger 來通信就不太適合了

上面演示的是應用內跨進程通信,綁定服務可以通過顯示意圖來綁定,但是如果是跨應用的進程間通信,那麼就需要用到隱式意圖了.這裡有一點需要注意的就是,在 5.0 以後隱式意圖開啟或者綁定 service 要 setPackage(Service的包名),不然會報錯

AIDL

上面提到過通過 Messenger 跨進程不適合併發量大的情況,那麼如果並發量大的話,我們用什麼來處理呢?那就可以通過 AIDL 來進行,這裡是Google的描述:

Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

主要意思就是你可以用 Messenger 處理簡單的跨進程通信,但是高並發量的要用AIDL

我們還是先演示一下同一個應用內的跨進程通信.

首先我們創建一個 Service:

然後在清單文件裡面設置 Service 的進程:

然後右鍵選擇新建 AIDL 文件,Android Studio就會幫你在你的aidl目錄的同名文件夾下面創建一個AIDL文件

在AIDL文件裡面會有一個接口,並聲明了一個方法,那個方法主要是告訴你AIDL支持哪些數據類型傳輸,所以我們把這個方法刪掉,我們再自己聲明一個方法,用於之後的調用

(注意:每次修改了AIDI文件後,需要同步一下才會生效,因為每次同步後,Android Studio會在 項目/build/generated/source/aidl/debug 目錄下生成相應的Java文件)

我們在 Service 中創建一個 Binder,並在 onBind 的時候返回:

創建自定義一個類實現 ServiceConnection:

綁定服務,當綁定成功時會走 Connection的onServiceConnected 方法,並把 Binder 傳過來

在 onServiceConnected 方法裡面通過 asInterface 獲取伺服器傳過來的對象,並調用服務端的方法

現在客戶端就可以調用 sell方法 來進行跨進程通信了,但目前只能傳輸基本數據類型的數據,那麼如果想要傳其他數據呢?那麼我們接著往下講:

首先我們要知道AIDL支持那麼數據類型

1. 基本數據類型

2. 實現了Parcelable接口的對象

3. List:只支持ArrayList,並且裡面的元素需要時AIDL支持的

4. Map:只支持HashMap,並且裡面的key和value都需要是被AIDL支持的

那麼我們定義一個對象 Product 實現 Parcelable 接口,Product 我們設置了兩個欄位:

接著我們需要在 aidl文件夾 的相同目錄創建一個相同文件名的 aidl文件

注意:這裡我們是要通過 new File 的方式創建,並且要自己輸入文件後綴aidl,如果你用new AIDL的方式創建的話,他會提示你 Interface Name must be unique

接著我們需要在這個 aidl文件 裡面輸入包名,並且聲明一下變量為 Parcelable類型(注意:這裡聲明的時候是用小寫的 parcelable)

// Product.aidl
package com.xiayu.aidldemo;parcelable Product;

我們回到之前的 IShop.aidl,刪掉之前的 sell方法,並再創建兩個新方法:

這裡有三個需要注意的地方

(1). IShop.aidl雖然跟Product.aidl在同一個包下,但是這裡還是需要手動import進來

(2). 這裡聲明方法時,需要在參數前面增加一個tag,這個tag有三種,in,out,inout,這裡表示的是這個參數可以支持的流向:

in: 這個對象能夠從客戶端到伺服器,但是作為返回值從伺服器到客戶端的話數據不會傳送過去(不會為null,但是欄位都沒有賦值)

out: 這個對象能夠作為返回值從伺服器到客戶端,但是從客戶端到伺服器數據會為空(不會為null,但是欄位都沒有賦值)

inout: 能從客戶端到伺服器,也可以作為返回值從伺服器到客戶端

用一張圖來總結: 

(不要都設為inout,要看需求來設置,因為會增加開銷)

(3). 默認實現 Parcelable 的模版只支持 in ,如果需要需要支持 out 或 inout 需要手動實現 readFromParcel 方法:

現在就可以在客戶端中通過 IShop 調用方法來進行通信了

不同應用間的多進程通信(AIDL)

上面我們介紹了同一個應用內的進程間通信,接下來我們就來介紹不同應用之間的進程間通信

首先我們需要把 Product.java 放到aidl目錄相同名字的文件夾下(如果要提供服務給其他app,最好把需要的對象都放在aidl目錄下,這樣比較容易拷貝) 

但是這個時候你運行程序的話,編譯會提示說找不到 Product,那是因為Android Studio默認會去java目錄下找,這時候需要在build.gradle文件 android{ } 中間增加一段代碼,讓aidl目錄裡面的java文件也能被識別

接著我們為 Service 增加 intent-filter,這樣其他應用才能通過隱式意圖綁定服務,伺服器端的修改就結束了

我們需要創建一個新的應用來作為客戶端,並且把伺服器端的 aidl 目錄下的所有文件都拷貝過來,這裡要注意的就是裡面的目錄不能改變,需要與以前一致:

點擊同步,Android Studio會自動生成相應的java文件供我們使用

這個時候我們需要通過隱式意圖來綁定服務了(注意:5.0以後隱式意圖開啟或者綁定service要setPackage,不然會報錯)

mIntent.setAction("action.xiayu");mIntent.setPackage("com.xiayu.aidldemo");

接下來的操作就和之前一樣了,創建一個類實現 ServiceConnection:

綁定服務

bindService(mIntent, mXiayuConnection, BIND_AUTO_CREATE);

通過 ServiceConnection 的 onServiceConnected 裡面的 IBinder 進行通信

解除綁定的時候釋放資源

這樣我們就可以通過獲得的IShop進行不同應用之間的進程間通信了

最後再提幾點用到服務時需要注意的地方(很簡單,但是有些人經常會忽略這幾點)

1: startService和stopService需要用同一個Intent對象

2: bindService和unbindService需要用同一個ServiceConnection對象

3: 5.0以後隱式意圖開啟或者綁定service要setPackage(包名)

每天學習累了,看些搞笑的段子放鬆一下吧。關注最具娛樂精神的公眾號,每天都有好心情。

如果你有好的技術文章想和大家分享,歡迎向我的公眾號投稿,投稿具體細節請在公眾號主頁點擊「投稿」菜單查看。

歡迎長按下圖 -> 識別圖中二維碼或者掃一掃關注我的公眾號:

相關焦點

  • Linux進程間通信的socketpair()函數
    文件描述符,屬於進程的資源之一,與進程的變量、代碼類似,都會在fork()時被父子進程共享。所以,socketpair()這個函數就被master+worker型的多進程伺服器廣泛用於master和各個worker的通信。
  • 如何使用am命令啟動Android應用
    命令格式為:adb shell am start -n package/launch_activity示例:打開設置App命令:adb shell am start -n com.android.settings/com.android.settings.Settings命令的關鍵在於獲取package和launch_activity。
  • 技術內幕:Android的IPC機制-Binder
    所以,作業系統內核必須提供進程間的通信機制(IPC)。  在Linux中,進程間的通信機制有很多種,例如可以採用命名管道(named pipe)、消息隊列(message queue)、信號(signal)、共享內存(share memory)、socket等方式,它們都可以實現進程間的通信。
  • android關閉開機啟動 - CSDN
    = 0) { enableScreenAfterBoot(); } break; }在finishBooting裡面,一開始會判斷mBootAnimationComplete,然後啟動一些一直被hold的進程等,最後會通過UserController的sendBootCompletedLocked
  • android 排列 - CSDN
    每一個LinearLayout裡面又可分為垂直布局(android:orientation="vertical")和水平布局(android:orientation="horizontal" )。當垂直布局時,每一行就只有一個元素,多個元素依次垂直往下;水平布局時,只有一行,每一個元素依次向右排列。
  • android常用的五大布局 - CSDN
    ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent
  • 進程和線程常見的19個問題
    由於各個進程不共享相同的地址空間,任何一個進程的全局變量在另一個進程中都不可見,所以如果想要在進程之間傳遞數據就需要通過內核,在內核中開闢出一塊區域,該區域對多個進程都可見,即可用於進程間通信。有讀者可能有疑問了,文件方式也是進程間通信啊,也要在內核開闢區域嗎?這裡說的內核區域其實是一段緩衝區,文件方式傳輸數據也有內核緩衝區的參與(零拷貝除外)。
  • 面試進階之binder通信
    原文作者:TryLoveCatch原文連結:http://tinyurl.com/y6zqf7k9Binder什麼是Binder從機制來說,Binder是Android系統中進程間通訊(IPC)的一種方式,也是Android系統中最重要的特性之一。
  • 定義Android 清單
    package=com.demo.android.bmi「package」 是「manifest」(清)嘶`的一特e儺裕例中的熱菘捎嗣鰨@用程式的M入c存在於「com.demo.android.bmi」@名Q空g/路街小android:versionCode=1android:versionName=1.0「android:versionCode
  • Android 自定義Switch開關按鈕的樣式
    ><shape xmlns:android="http://schemas.android.com/apk/res/android"    android:shape="oval">    <solid android:color="#94C5FF" />    <size        android:width="20dp
  • Android內幕:Binder驅動的原理和實現
    一、Binder驅動的原理  為了完成進程間通信,Binder採用了AIDL(Android Interface Definition Language)來描述進程間的接口。比如,A進程如果要使用B進程的服務,B進程首先要註冊此服務,A進程通過Binder獲取該服務的hanlde,通過這個handle,A進程就可以使用該服務了。此外,你可以把handle理解成地址。A進程使用B進程的服務還意味著二者遵循相同的協議,這個協議反映在代碼上就是二者要實現IBinder接口。  1.
  • android布局和界面 - CSDN
    android:layout_gravity用於設置組件自身在父組件中的對齊方式。需要注意的是,此屬性與布局屬性android: gravity的區別。android:gravity用於設置View組件的對齊方式。
  • Android中的Drawable基礎與自定義Drawable
    ><selector    xmlns:android=http://schemas.android.com/apk/res/android    android:constantSize=false //StateListDrawable的固有大小是否根據狀態而改變,默認false=根據狀態而改變    android:dither
  • 外媒:美國聯邦通信委員會啟動暫停中國電信在美運營進程
    「美國聯邦通信委員會(FCC)開始暫停中國電信在美運營進程,」路透社11日以此為題報導稱,美國聯邦通信委員會(FCC)當地時間10日表示,該委員會開始撤銷中國電信在美國運營的授權。路透社說,這是美方進一步採取措施,打壓中國電信在美電信領域扮演角色的最新行動。
  • android平臺通知體提醒機制研究
    本文從androidQQ用戶為樣本,通過定性和定量研究,得出android系統不同類型apps通知提醒機制,我們在進行手機系統和app設計的時候可以套用這個機制,評估當前設計的app屬於哪個類型,找到對應的通知提醒機制。一、 研究方法和流程1.
  • Android一種信息提示機制:Toast
    答案當然是肯定的啦,android已經為我們提供了一個非常簡便的方法,那就是Toast,直譯為吐司~ 很可愛的名字吧~ 用起來像它的名字一樣,非常方便。例如我們要想用戶顯示一些文本信息,可以使用下面的方法~  Toast.makeText(self, "Hello World!"
  • android 水平布局寬度 - CSDN
    ><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:gravity="center
  • android可文本框專題及常見問題 - CSDN
    <EditTextandroid:id="@+id/textNormal"android:layout_width="fill_parent"android:layout_height="wrap_content"android:hint="Normaltext"android:inputType="text
  • Android實戰項目真槍實彈第二炮
    ><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"
  • android開發中分享功能 - CSDN
    :enable要設置為true如果要動態設置是否在列表中出現時,可以在代碼中設置enable的值 <activity android:theme="@style/No_title_transparent" android:label="@string/other_apps" android:name="