Android Binder機制完全解析
概述
之前我寫過一篇文章Android Service全面解析,簡單實現瞭如何通過AIDL實現Service的跨程序通訊(IPC),其實是通過Binder機制來實現的,本文我們就重點來看看Binder機制的原理。
Binder可以提供系統中任何程式都可以訪問的全域性服務。這個功能當然是任何系統都應該提供的,下面我們簡單看一下Android的Binder的框架:
Android Binder框架分為伺服器介面、Binder驅動、以及客戶端介面;簡單想一下,需要提供一個全域性服務,那麼全域性服務那端即是伺服器介面,任何程式即客戶端介面,它們之間通過一個Binder驅動訪問。
- 伺服器介面:實際上是Binder類的物件,該物件一旦建立,內部則會啟動一個隱藏執行緒,接收Binder驅動傳送的訊息,收到訊息後,會執行Binder物件中的onTransact()函式,並按照該函式的引數執行不同的伺服器端程式碼。
- Binder驅動:該物件也為Binder類的例項,客戶端通過該物件訪問遠端服務。
- 客戶端介面:獲得Binder驅動,呼叫其transact()傳送訊息至伺服器。
例項實現
如果你覺得上面的描述太抽象了,沒關係,下面我們通過一個具體的例子來看看Binder機制的原理。例子我仍然使用上一篇文章的例子,不過之前我是使用Eclipse建立工程,今天我們使用Studio來建立專案,效果都是一樣的。
(1)Studio建立兩個Module,app代表客戶端程式,binder_server代表伺服器端程式。
(2)建立aidl檔案
在app目錄上右鍵,NEW->AIDL->AIDL File,建立一個aidl檔案(IMyAidlInterface.aidl),同時必須要指明包名,包名必須和java目錄下的包名一致。此aidl檔案會預設生成到aidl目錄下,aidl目錄和java目錄同級別。
IMyAidlInterface.aidl檔案內容:
interface IMyAidlInterface {
int plus(int a, int b);
String toUpperCase(String str);
}
build一下,會自動生成IMyAidlInterface.java檔案,不同於Eclipse的gen目錄,studio下的java檔案目錄為:
*(專案名)\app\build\generated\source\aidl\debug\com\hx\binder\IMyAidlInterface.java
關於IMyAidlInterface.java檔案內容,我們後面會具體分析,這裡先省略。
(3)將aidl檔案連同目錄一起拷貝到伺服器端
(4)伺服器端新建服務MyRemoteService
public class MyRemoteService extends Service {
@Override
public void onCreate() {
super.onCreate();
MainActivity.showlog("onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MainActivity.showlog("onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
MainActivity.showlog("onDestroy()");
}
@Override
public IBinder onBind(Intent intent) {
MainActivity.showlog("onBind()");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
MainActivity.showlog("onUnbind()");
return super.onUnbind(intent);
}
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public String toUpperCase(String str) throws RemoteException {
if (str != null) {
return str.toUpperCase();
}
return null;
}
@Override
public int plus(int a, int b) throws RemoteException {
return a + b;
}
};
}
在Manifest中進行註冊:
<service android:name=".MyRemoteService"
android:exported="true">
<intent-filter>
<action android:name="com.hx.action.remoteService" />
</intent-filter>
</service>
(4)編寫客戶端程式碼
public class MainActivity extends Activity implements View.OnClickListener {
private Button bindService;
private Button unbindService;
private Button plus;
private Button toUpperCase;
private IMyAidlInterface myAIDLInterface;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
myAIDLInterface = null;
Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);
plus = (Button) findViewById(R.id.plus);
toUpperCase = (Button) findViewById(R.id.toUpperCase);
//button點選事件
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
plus.setOnClickListener(this);
toUpperCase.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
Intent intent = new Intent("com.hx.action.remoteService");
//5.0以上安卓裝置,service intent必須為顯式指出
Intent eintent = new Intent(getExplicitIntent(this,intent));
bindService(eintent, connection, Context.BIND_AUTO_CREATE);
// bindService(intent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
if(myAIDLInterface != null){
unbindService(connection);
}
break;
case R.id.plus:
if (myAIDLInterface != null) {
try {
int result = myAIDLInterface.plus(13, 19);
Toast.makeText(this, result + "", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
}
break;
case R.id.toUpperCase:
if (myAIDLInterface != null) {
try {
String upperStr = myAIDLInterface.toUpperCase("hello aidl service");
Toast.makeText(this, upperStr + "", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Toast.makeText(this, "伺服器被異常殺死,請重新繫結服務端", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical">
<Button
android:id="@+id/bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="bind service" />
<Button
android:id="@+id/unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="unbind service" />
<Button
android:id="@+id/plus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="13 + 19" />
<Button
android:id="@+id/toUpperCase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="hello aidl service" />
</LinearLayout>
執行程式,看效果:
我們首先點選BIND SERVICE按鈕,繫結服務,會彈出“onServiceConnected”的Toast,說明服務繫結成功,獲取到了伺服器端的Binder驅動。
服務端Log:
然後分別點選13+19和hello aidl service按鈕,可以通過Binder驅動呼叫服務端的程式碼並返回正確的計算結果。
最後點選UNBIND SERVICE按鈕,我們的期望是彈出“onServiceDisconnected”的Toast,解除繫結,實際上呢?很遺憾沒有彈出。
服務端Log:
由於我們當前只有一個客戶端綁定了此Service,所以Service呼叫了onUnbind和onDestory。當我們繼續點選13+19按鈕,發現依然可以正確執行得到結果,也就是說即使onUnbind被呼叫,連線也是不會斷開的,那麼什麼時候會斷開連線呢?
即當服務端被異常終止的時候,比如我們現在在手機的正在執行的程式中找到該服務,並強行停止它:
可以看到這時彈出了“onServiceDisconnected”的Toast,說明連線被斷開。之後再次點選13+19按鈕,則會彈出Toast提示“伺服器被異常殺死,請重新繫結服務端”。
原理分析
還記得我們上面根據aidl檔案生成的Java檔案嗎?我們來看看它的結構吧:
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.hx.binder.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.hx.binder.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.hx.binder.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.hx.binder.IMyAidlInterface))) {
return ((com.hx.binder.IMyAidlInterface) iin);
}
return new com.hx.binder.IMyAidlInterface.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_plus: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.plus(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_toUpperCase: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.toUpperCase(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.hx.binder.IMyAidlInterface {
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 plus(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_plus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(str);
mRemote.transact(Stub.TRANSACTION_toUpperCase, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_plus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_toUpperCase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int plus(int a, int b) throws android.os.RemoteException;
public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException;
}
程式碼比較長,但思路還是比較清晰的,IMyAidlInterface.java檔案包含兩個靜態內部類—Stub和Proxy(其中Proxy是Stub的內部類)。
public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface
其中Stub是個抽象類,它繼承了Binder,並實現了IMyAidlInterface介面。Stub提供了幾個方法:asInterface、asBinder、onTransact,但並沒有實現IMyAidlInterface介面的方法,所以需要交給Stub的實現類去實現。
private static class Proxy implements com.hx.binder.IMyAidlInterface
Proxy是Stub的內部類,也實現了IMyAidlInterface介面。並提供了幾個方法:asBinder、getInterfaceDescriptor,並實現了IMyAidlInterface介面的方法plus和toUpperCase。
接下來看看服務端和客戶端是如何和這個檔案建立關聯的吧。
服務端:
IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {
@Override
public String toUpperCase(String str) throws RemoteException {
if (str != null) {
return str.toUpperCase();
}
return null;
}
@Override
public int plus(int a, int b) throws RemoteException {
return a + b;
}
};
可以看到我們服務端提供的服務是由IMyAidlInterface.Stub來執行的,上面分析過,Stub這個類是Binder的子類,是不是符合我們文章開頭所說的服務端其實是一個Binder類的例項。而且mBinder實現了IMyAidlInterface介面的方法。
接下來看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_plus: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.plus(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_toUpperCase: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = this.toUpperCase(_arg0);
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
文章開頭也說到服務端的Binder例項會根據客戶端依靠Binder驅動發來的訊息,執行onTransact方法,然後由其引數決定執行服務端的程式碼。
可以看到onTransact有四個引數:code , data ,replay , flags
- code:是一個整形的唯一標識,用於區分執行哪個方法,客戶端會傳遞此引數,告訴服務端執行哪個方法
- data:客戶端傳遞過來的引數
- reply:伺服器返回回去的值
- flags:標明是否有返回值,0為有(雙向),1為沒有(單向)
我們仔細看case TRANSACTION_plus中的程式碼:
data.enforceInterface(DESCRIPTOR);
與客戶端的writeInterfaceToken對應,標識遠端服務的名稱
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
接下來分別讀取了客戶端傳入的兩個引數
int _result = this.plus(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
然後執行this.plus,即我們服務端實現的plus方法;返回result由reply寫回。
toUpperCase同理,可以看到服務端通過AIDL生成的Stub類,封裝了服務端本來需要寫的程式碼。
客戶端
客戶端主要通過ServiceConnected與服務端連線
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
isConnected = false;
Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
isConnected = true;
myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);
Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();
}
};
其實這個onServiceConnected中的IBinder例項,其實就是我們文章開頭所說的Binder驅動,也是一個Binder例項。
在IMyAidlInterface.Stub.asInterface中最終呼叫了:
return new com.hx.binder.IMyAidlInterface.Stub.Proxy(obj);
這個Proxy例項傳入了我們的Binder驅動,並且封裝了我們呼叫服務端的程式碼,文章開頭說,客戶端會通過Binder驅動的transact()方法呼叫服務端程式碼。
直接看Proxy中的plus方法
public int plus(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_plus, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
首先宣告兩個Parcel物件,一個用於傳遞資料,一個使用者接收返回的資料
_data.writeInterfaceToken(DESCRIPTOR);
與伺服器端的enforceInterfac對應
_data.writeInt(a);
_data.writeInt(b);
寫入需要傳遞的引數
mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);
終於看到了我們的transact方法,第一個對應服務端的code,_data,_reply分別對應服務端的data,reply,0表示是雙向的
_reply.readException();
_result = _reply.readInt();
最後讀出我們服務端返回的資料,然後return。可以看到和服務端的onTransact基本是一行一行對應的。
注意:
- 當客戶端呼叫transact方法發起RPC(遠端過程呼叫)請求後,當前執行緒會掛起,等待伺服器端的返回結果。所以如果一個遠端方法很耗時,那麼不能再UI執行緒中呼叫此遠端方法。
- 服務端的Binder方法(onTransact)執行在Binder執行緒池中,所以Binder方法不管是否耗時,都應該採取同步機制,因為它已經執行在一個執行緒中了。
到此,我們已經通過AIDL生成的程式碼解釋了Android Binder框架的工作原理。Service的作用其實就是為我們建立Binder驅動,即服務端與客戶端連線的橋樑。
AIDL其實通過我們寫的aidl檔案,幫助我們生成了一個介面,一個Stub類用於服務端,一個Proxy類用於客戶端呼叫。
不依賴AIDL實現IPC通訊
那麼我們是否可以不通過寫aidl檔案來實現遠端的通訊呢?下面向大家展示如何完全不依賴AIDL來實現客戶端與服務端的通訊。
服務端程式碼:
public class MyRemoteService extends Service {
private static final String DESCRIPTOR = "MyRemoteService";
private static final int TRANSACTION_plus = 0x110;
private static final int TRANSACTION_toUpperCase = 0x111;
@Override
public void onCreate() {
super.onCreate();
MainActivity.showlog("onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MainActivity.showlog("onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
MainActivity.showlog("onDestroy()");
}
@Override
public IBinder onBind(Intent intent) {
MainActivity.showlog("onBind()");
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
MainActivity.showlog("onUnbind()");
return super.onUnbind(intent);
}
private MyBinder mBinder = new MyBinder();
private class MyBinder extends Binder {
@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_plus: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = _arg0 + _arg1;
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_toUpperCase: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
java.lang.String _result = _arg0.toUpperCase();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
};
}
<service android:name=".MyRemoteService"
android:exported="true">
<intent-filter>
<action android:name="com.hx.action.remoteService" />
</intent-filter>
</service>
客戶端程式碼:
public class MainActivity extends Activity implements View.OnClickListener {
private Button bindService;
private Button unbindService;
private Button plus;
private Button toUpperCase;
private IBinder myBinder;
private static final String DESCRIPTOR = "MyRemoteService";
private static final int TRANSACTION_plus = 0x110;
private static final int TRANSACTION_toUpperCase = 0x111;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
myBinder = null;
Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = service;
Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindService = (Button) findViewById(R.id.bind_service);
unbindService = (Button) findViewById(R.id.unbind_service);
plus = (Button) findViewById(R.id.plus);
toUpperCase = (Button) findViewById(R.id.toUpperCase);
//button點選事件
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
plus.setOnClickListener(this);
toUpperCase.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bind_service:
Intent intent = new Intent("com.hx.action.remoteService");
//5.0以上安卓裝置,service intent必須為顯式指出
Intent eintent = new Intent(getExplicitIntent(this,intent));
bindService(eintent, connection, Context.BIND_AUTO_CREATE);
// bindService(intent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
if(myBinder != null){
unbindService(connection);
}
break;
case R.id.plus:
if (myBinder != null) {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(78);
_data.writeInt(95);
myBinder.transact(TRANSACTION_plus, _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();
}
break;
case R.id.toUpperCase:
if (myBinder != null) {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString("my new programe");
myBinder.transact(TRANSACTION_toUpperCase, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
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();
}
break;
default:
break;
}
}
public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical">
<Button
android:id="@+id/bind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="bind service" />
<Button
android:id="@+id/unbind_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="unbind service" />
<Button
android:id="@+id/plus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="78 + 95" />
<Button
android:id="@+id/toUpperCase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="my new programe" />
</LinearLayout>
這裡我們並沒有寫aidl檔案,而是將aidl生成的java檔案的處理邏輯移動到我們的服務端和客戶端來實現。同樣會達到和上面一樣的效果。
IPC傳遞自定義型別Bean
不過還有一點需要說明的是,由於這是在不同的程序之間傳遞資料(引數和返回值),Android對這類