1. 概述
今天學姐復習了下Android進程間通信方式怖现,打算從基本概念
琉预、使用原因
、基本使用
姐直、原理分析
幾個方面來講講倦淀。
想要對進程間通信方式有個相對全面的了解,就先從幾個比較重要的概念IPC
声畏、AIDL
撞叽、Binder
說起吧。
(1)IPC
:Inter-Process Communication插龄,即進程間通信
(2)AIDL
:Android Interface Definition Language愿棋,即Android接口定義語言。Client和Service要實現
跨進程通信均牢,必須遵循的接口規(guī)范糠雨。需要創(chuàng)建.aidl文件,外在表現上和Java中的interface有點類似膨处。
(3)Binder
:Android進程間通信是通過Binder
來實現的见秤。遠程Service在Client綁定服務時,會在onBind()的回調中返回一個Binder真椿,當Client調用bindService()與遠程Service建立連接成功時,會拿到遠程Binder實例乎澄,從而使用遠程Service提供的服務突硝。
2. 為什么使用Binder?
下面內容學姐參考別人的博文,進行了總結置济。
Linux系統(tǒng)進程間通信方式:
(1)傳統(tǒng)機制解恰。如管道(Pipe)锋八、信號(Signal)和跟蹤(Trace),適用于父子進程或兄弟進程护盈,其中命名管道(Named Pipe)挟纱,支持多種類型進程
(2)System V IPC機制。如報文隊列(Message)腐宋、共享內存(Share Memory)和信號量(Semaphore)
(3)Socket通信
然而紊服,以上方式在性能和安全性方面存在不足:
(1)性能方面。管道和隊列采用存儲轉發(fā)方式胸竞,數據從發(fā)送方緩存區(qū)->內核緩存區(qū)->接收方緩存區(qū)欺嗤,會經歷兩次拷貝過程;共享內存無拷貝但控制復雜卫枝;Socket傳輸效率低下煎饼,且連接的建立與中斷有一定開銷。
(2)安全性方面校赤。傳統(tǒng)IPC方式無法獲得對方進程可靠的UID和PID吆玖,無法鑒別對方身份,若采用在數據包里填入UID/PID的方式马篮,容易被惡意程序利用衰伯;傳統(tǒng)IPC方式的接入點是開放性的,無法建立私有通道积蔚,容易被惡意程序猜測出接收方地址意鲸,獲得連接。
而Binder
是基于C/S通信模式尽爆,傳輸過程只需要一次拷貝怎顾,且為Client添加UID/PID身份,性能和安全性更好漱贱,因此Android進程間通信使用了Binder
槐雾。
3. 基本使用
假設本地Client需要使用遠程Service的計算器功能。示例Demo: binderDemo
(1)新建Client和遠程Service兩個Android工程幅狮。
(2)在遠程Service工程中募强,創(chuàng)建ICalculator.aidl文件,并創(chuàng)建CalculatorService類崇摄。
// ICalculator.aidl
package me.wangxinghe.ipc;
// Declare any non-default types here with import statements
interface ICalculator {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int add(int a, int b);
int minus(int a, int b);
}
//給Client提供服務的Service文件
public class CalculatorService extends Service {
public CalculatorService() {
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
//當Client調用bindService()時擎值,遠程Service通過onBind()回調返給Client
return mBinder;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
遠程Service向Client提供的服務,接口方法add()和minus()實現由Service決定
private ICalculator.Stub mBinder = new ICalculator.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
@Override
public int minus(int a, int b) throws RemoteException {
return a - b;
}
};
}
//AndroidManifest中Service組件聲明逐抑,由于是Client工程不能顯示調用遠程Service鸠儿,只能采用隱式調用的方
式,因此需要填寫category和action
<service
android:name=".CalculatorService"
android:enabled="true"
android:exported="true"
android:process=":remote">
<intent-filter>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="com.wangxinghe.ipc_server.CalculatorService"/>
</intent-filter>
</service>
(3)在Client工程中,拷貝以上.aidl文件及目錄进每,使用Service提供的服務汹粤。
//遠程Service連接回調
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//連接成功,獲取遠程Service的Binder
mCalculator = ICalculator.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mCalculator = null;
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
//綁定服務
Intent intent = new Intent(ACTION_CALCULATOR_SERVICE);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
//斷開服務
unbindService(mServiceConnection);
break;
case R.id.add:
//調用遠程服務的add()
int result = 0;
try {
if (mCalculator != null) {
result = mCalculator.add(2, 1);
}
} catch (RemoteException e) {
e.printStackTrace();
}
mResultTv.setText(result + "");
break;
case R.id.minus:
//調用遠程服務的minus()
int _result = 0;
try {
if (mCalculator != null) {
_result = mCalculator.minus(2, 1);
}
} catch (RemoteException e) {
e.printStackTrace();
}
mResultTv.setText(_result + "");
break;
}
}
(4)啟動Client和Service工程田晚,即可實現進程間通信嘱兼。
4. 原理分析
由于Binder
機制涉及的東西很多。學姐本文并不打算深入到內核源碼贤徒,關于Client是怎樣獲取到遠程Binder的會在后續(xù)文章再講述芹壕。下面主要是從應用層角度分析。
我們先看看ICalculator.aidl編譯生成的ICalculator.java文件泞莉。
package me.wangxinghe.ipc;
// Declare any non-default types here with import statements
public interface ICalculator extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
me.wangxinghe.ipc.ICalculator
{
//接口描述符
private static final java.lang.String DESCRIPTOR = "me.wangxinghe.ipc.ICalculator";
/** Construct the stub at attach it to the interface. */
public Stub()
{
//添加Binder和接口描述符到該Binder中份汗,便于后續(xù)本地查找Binder
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an me.wangxinghe.ipc.ICalculator interface,
* generating a proxy if needed.
*/
public static me.wangxinghe.ipc.ICalculator asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//根據接口描述符邪财,從Binder中查找對應的接口,若有則直接返回
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof me.wangxinghe.ipc.ICalculator))) {
return ((me.wangxinghe.ipc.ICalculator)iin);
}
//否則返回接口的代理對象,并將遠端Binder傳入代理
return new me.wangxinghe.ipc.ICalculator.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder()
{
//返回Binder
return this;
}
@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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_add:
{
//解析data Parcel參數锣光,執(zhí)行具體的加法操作光坝,并將計算結果寫進reply Parcel
//add()由Service實現
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_minus:
{
//解析data Parcel參數罗捎,執(zhí)行具體的減法操作钥平,并將計算結果寫進reply Parcel
//minus()由Service實現
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.minus(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
//代理類
private static class Proxy implements me.wangxinghe.ipc.ICalculator
{
//遠程Binder
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override
public android.os.IBinder asBinder()
{
//返回遠程Binder
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
//返回接口描述符
return DESCRIPTOR;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int add(int a, int b) throws android.os.RemoteException
{
//執(zhí)行加法操作,將接口描述符和參數封裝進data Parcel疫剃,調用遠程Binder執(zhí)行加法操作钉疫,
//并從reply Parcel中解析出結果,返回結果巢价。該過程是同步操作
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int minus(int a, int b) throws android.os.RemoteException
{
//執(zhí)行減法操作牲阁,將接口描述符和參數封裝進data Parcel,調用遠程Binder執(zhí)行減法操作壤躲,
//并從reply Parcel中解析出結果城菊,返回結果。該過程是同步操作
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION +
0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_minus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double
aDouble, java.lang.String aString) throws android.os.RemoteException;
//接口方法
public int add(int a, int b) throws android.os.RemoteException;
//接口方法
public int minus(int a, int b) throws android.os.RemoteException;
}
在基本使用
部分碉克,我們可以看到凌唬,Client在與Service建立連接成功后,會拿到遠程Binder實例(此處不能直接使用遠程Binder原因還不是很清楚)漏麦,并調用Stub的asInterface方法將其轉換成ICalculator接口客税。
mCalculator = ICalculator.Stub.asInterface(service);
這一步執(zhí)行的操作是:根據接口描述符,從Binder中本地查找對應的接口撕贞,若有則直接返回更耻;否則,將Binder對象傳給本地代理Stub.Proxy對象麻掸,并返回本地代理酥夭,由本地代理來接管相應的服務,Proxy也實現了ICalculator接口脊奋。
這一步之后熬北,Client就可以使用遠程Service提供的服務了。
再看看onClick()事件中的加法操作诚隙。
result = mCalculator.add(2, 1);
mCalculator即為上面得到的本地代理對象讶隐,其add()的實現是
@Override
public int add(int a, int b) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
可以看到實際上是調用連接建立成功后的遠程Binder的transact(add)方法。再看看Binder.transact實現
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;
}
可以看到最后調用了遠程Binder的onTransact()方法久又,也就是走到了遠程Binder的Stub.onTransact()方法巫延。這個方法實現是
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
throws android.os.RemoteException
{
switch (code)
{
case TRANSACTION_add:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
...
}
}
這里最終調用到了遠程Service工程創(chuàng)建的ICalculator.Stub實例mBinder的add()方法即a+b,因此得到了最后的結果地消。
好了炉峰,我們再來梳理下,Client獲得并使用Service服務的過程:
(1)Client與Service建立連接脉执,得到遠程Binder(這個Binder就是Service的onBind返回的Binder疼阔,但是客戶端不能直接使用,具體原因還不是很明確)半夷。
(2)將遠程Binder傳給本地代理婆廊,得到本地代理Stub.Proxy實例。
(3)通過本地代理Stub.Proxy實例間接調用遠程Binder對象的add()方法巫橄。
具體實現是:由于遠程Binder已經傳給了本地代理Stub.Proxy淘邻。那么通過本地代理Stub.Proxy實例間接調用遠程Binder的transact(TRANSACTION_add)操作;調用遠程Binder的onTransact()方法湘换;調用遠程Binder的實現類ICalculator.Stub的add()方法
(4)得到結果
下面再總結下ICalculator
宾舅、Stub
、Proxy
這3個類的關系:
ICalculator
:就是一個接口類彩倚。內部包括之前的接口方法和靜態(tài)Stub類筹我。
Stub
:遠程Binder實現類。繼承類Binder
和ICalculator
接口署恍。
Proxy
:遠程Binder代理類崎溃。實現ICalculator
接口。
5. 總結
(1)Binder相當于不同進程間數據通信的通道
(2)核心是代理模式盯质,使用本地代理Proxy操作遠端Binder袁串,使用相應服務
(3)如理解有誤,歡迎指正
6. 參考鏈接
(1)http://blog.csdn.net/singwhatiwanna/article/details/19756201
(2)http://blog.csdn.net/luoshengyang/article/details/6618363