Android之IPC機制
IPC全稱是Intent Process Communication即進程通信。
爲什麼需要進程通信呢?
android是基於linux內核的,linux中兩個進程鎖分配的虛擬機的地址是不一致的,是兩個完全不同
的虛擬機,所以也就意味着兩個進程運行不相互影響(進程隔離)。linux中對於進程之間的交流有着
限制,那麼在Android中也一樣。linux中的進程通信有管道,socket,那麼Android中相對應的就有
Binder機制。
由於進程之間交流存在限制,所以需要進程通信,方便進程之間傳遞數據等信息。
Binder是什麼?
binder的中文意思是粘合劑,也就意味者把兩個進程粘合在一起,即所謂的進程通信。
1.直接來說,Binder是Android中的一個類,實現了IBinder接口。
2.從ICP角度說,Binder是Android中一種跨進程通信的機制。
3.從Android Framework角度說,它是ServiceManager連接各種Manager(比如ActivityManager
WindowManager等)和相應的ManagerService的橋樑。
4.從應用層來說,Binder是客戶端和服務端進行通信的媒介。
在Android中,Binder主要用在Service中,包括AIDL和Messenger。(普通service不涉及進程通信)
通過AIDL來實現進程通信,理解了AIDL的模式對Binder通信的機制會更加容易認識。
在項目中新建一個AIDL文件。
// IProcess.aidl
package com.example.lu.okhttpdemo;
// Declare any non-default types here with import statements
interface IProcess {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
int add(int x, int y);
int min(int x, int y);
}
接着Make Project。然後會在build/generated/source/aidl生成一個相對應的文件。
我們先新建一個服務端Service
package com.example.lu.okhttpdemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "service";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder;
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.d(TAG, "onDRebind");
super.onRebind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
private final IProcess.Stub mBinder = new IProcess.Stub() {
@Override
public int add(int x, int y) throws RemoteException {
return x + y;
}
@Override
public int min(int x, int y) throws RemoteException {
return x - y;
}
};
}
在AndroidManifest文件中註冊service
<service android:name=".MyService"
android:process=":remote">
<intent-filter>
<action android:name="com.example.lu.calc" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
這裏我加了process屬性,就是爲service單獨開啓一個進程。用來模擬多進程通信。
客戶端的activity代碼
package com.example.lu.okhttpdemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
private IProcess iProcess;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("client", "onServiceConnected");
iProcess = IProcess.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("client", "onServiceDisConnected");
}
};
private Button button1, button2, button3, button4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
public void bindService() {
Intent intent = new Intent("com.example.lu.calc");
intent.setPackage("com.example.lu.okhttpdemo");
bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
}
public void unbindService() {
if (serviceConn != null)
unbindService(serviceConn);
}
public void addInvoked() throws RemoteException {
if (iProcess != null) {
int addRes = iProcess.add(12, 12);
Toast.makeText(this, addRes + " ", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this, "異常,重新綁定", Toast.LENGTH_SHORT).show();
}
}
public void minInvoked() throws RemoteException {
if (iProcess != null) {
int addRes = iProcess.min(58, 12);
Toast.makeText(this, addRes + " ", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(this, "異常,重新綁定", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
bindService();
break;
case R.id.button2:
unbindService();
break;
case R.id.button3:
try {
addInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.button4:
try {
minInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
相應的佈局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="BindService" />
<Button
android:id="@+id/button2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="UnbindService" />
<Button
android:id="@+id/button3"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="12+12" />
<Button
android:id="@+id/button4"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="50-12" />
</LinearLayout>
運行,查看結果
綁定服務,查看log日誌:
成功,客戶端連接到了另一個進程的service
08-09 05:02:07.856 3988-3988/? D/client: onServiceConnected
08-09 05:02:07.837 4024-4024/com.example.lu.okhttpdemo:remote D/service: onCreate
08-09 05:02:07.838 4024-4024/com.example.lu.okhttpdemo:remote D/service: onBind
其它的輸出可以自己嘗試。
分析AIDL自動生成的文件
我們在build/generated/source/aidl裏面打開生成的文件。我這裏是排了下版,方便瀏覽
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: C:\\Users\\74650\\AndroidStudioProjects\\OkHttpDemo\\app\\src\\main\\aidl\\com\\example\\lu\\okhttpdemo\\IProcess.aidl
*/
package com.example.lu.okhttpdemo;
// Declare any non-default types here with import statements
public interface IProcess extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.lu.okhttpdemo.IProcess
{
private static final java.lang.String DESCRIPTOR = "com.example.lu.okhttpdemo.IProcess";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.lu.okhttpdemo.IProcess interface,
* generating a proxy if needed.
*/
public static com.example.lu.okhttpdemo.IProcess asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.lu.okhttpdemo.IProcess))) {
return ((com.example.lu.okhttpdemo.IProcess)iin);
}
return new com.example.lu.okhttpdemo.IProcess.Stub.Proxy(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
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
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;
}
case TRANSACTION_min:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.min(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.lu.okhttpdemo.IProcess
{
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 int add(int x, int y) 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(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int min(int x, int y) 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(x);
_data.writeInt(y);
mRemote.transact(Stub.TRANSACTION_min, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int add(int x, int y) throws android.os.RemoteException;
public int min(int x, int y) throws android.os.RemoteException;
}
DESCRIPTOR Binder的唯一標識,一般爲當前的Binder類名標識。
asInterface(android.os.IBinder obj)
該方法用於將服務度的Binder對象轉換爲客戶端所需的AIDL接口類型的對象。如果客戶端和服務端在同一進程中
那麼返回的就是服務端的Stub對象本身,如果不同進程,那麼返回的是系統封裝後的Stub.proxy(代理)對象
由上面第一章圖可以得知,proxy對象是由ServiceManager返回的。
asBinder()
返回當前Binder對象
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) *重點*
該方法原型爲Binder類中的
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法
code參數代表客戶端請求的目標方法,data中爲目標方法所需的參數,就是客戶端傳遞過來的參數,
reply爲服務端返回給客戶端的值,
flag爲是否有返回值,0爲有(雙向),1爲沒有(單向)
Proxy這個類就是代理類
理解完這個AIDL生成的文件之後,我們接下來看看這兩個進程是如何通信的。
Binder進程通信的機制
首先,我們的Activity(客戶端)通過創建一個ServiceConnection來進行對Service(服務端)的連接
,如果連接成功,那麼Service就會在onBind回調方法中返回一個IBinder對象,接着我們使用剛纔
AIDL文件生成的類Stub的asInterface(android.os.IBinder obj) 方法,傳進IBinder對象,得
到一個代理的對象,裏面包含者Service(服務端)相關的調用方法,接着通過這個代理對象調用相關
方法來進行對應的操作。
接下來我們不適用AIDL文件來實現Binder。
手動實現Binder,不依賴AIDL
Service(服務端)代碼
package com.example.lu.okhttpdemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
public class MyService extends Service {
private static final String TAG = "service";
private static final String DESCRIPTOR = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind");
return mBinder;
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
super.onDestroy();
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
Log.d(TAG, "onDRebind");
super.onRebind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
private MyBinder mBinder = new MyBinder();
private class MyBinder extends Binder{
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case 0x110:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();//讀取客戶端發過來的第一個參數
int _arg1;
_arg1 = data.readInt();//讀取客戶端發過來的第二個參數
int _result = _arg0 + _arg1;
reply.writeNoException();
reply.writeInt(_result);//結果寫進reply中,通過Binder傳遞迴給客戶端
return true;
}
case 0x111:
{
data.enforceInterface(DESCRIPTOR);
int _arg0 = data.readInt();
int _arg1 = data.readInt();
int _result = _arg0 - _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
}
Activity(客戶端)代碼
package com.example.lu.okhttpdemo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
private IBinder iBinder;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d("client", "onServiceConnected");
iBinder = service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("client", "onServiceDisConnected");
}
};
private Button button1, button2, button3, button4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
button1 = findViewById(R.id.button1);
button2 = findViewById(R.id.button2);
button3 = findViewById(R.id.button3);
button4 = findViewById(R.id.button4);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
button3.setOnClickListener(this);
button4.setOnClickListener(this);
}
public void bindService() {
Intent intent = new Intent("com.example.lu.calc");
intent.setPackage("com.example.lu.okhttpdemo");
bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
}
public void unbindService() {
if (serviceConn != null)
unbindService(serviceConn);
}
public void addInvoked() throws RemoteException {
if (iBinder != null) {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
//寫入傳遞的參數數值
_data.writeInterfaceToken("MyService");//通過該名字查找到相應的服務端
_data.writeInt(12);
_data.writeInt(12);
//通過transact方法傳遞參數,通過Binder調用相關代理方法
iBinder.transact(0x110, _data, _reply, 0);
_reply.readException();
//獲取服務端返回的數值
_result = _reply.readInt();
Toast.makeText(this, _result + " ", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
}
else {
Toast.makeText(this, "異常,重新綁定", Toast.LENGTH_SHORT).show();
}
}
public void minInvoked() throws RemoteException {
if (iBinder != null) {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken("MyService");
_data.writeInt(50);
_data.writeInt(12);
iBinder.transact(0x111, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
Toast.makeText(this, _result + " ", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
} finally {
_reply.recycle();
_data.recycle();
}
}
else {
Toast.makeText(this, "異常,重新綁定", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
bindService();
break;
case R.id.button2:
unbindService();
break;
case R.id.button3:
try {
addInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case R.id.button4:
try {
minInvoked();
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
運行結果一致,Binder進程通信可以不依靠AIDL。其實AIDL文件只是爲了方便系統幫我們生成代碼
節省一定的工作量。
代碼參考自鴻洋前輩
圖文參考自《Android開發藝術探索》