編輯:關於Android編程
但用Messenger實現的IPC存在一點不足:Service內部維護著一個Messenger,Messenger內部又維護著一個Hanlder,當多個client向該Service發送Message時,這些Message需要依次進入Hanlder的消息隊列中,Hanlder只能處理完一個Message之後,再從消息隊列中取出下一個進行處理,這樣Service相當於只能對客戶端傳來的Message進行串行執行,而不能並行執行。如果想讓Service跨進程並發處理客戶端發來的請求,那麼就需要使用AIDL。
AIDL的全稱是Android Interface Definition Language,直譯過來就是Android接口定義語言。在Android中,正常情況下,不同進程是不能互相訪問對方的內存數據的。為了實現進程間內存訪問,可以將一個進程中的內存數據分解成Android認識的原子類型,然後在另一個進程中將原子類型的數據重新組合成對象。編寫將原子類型的數據重新組合成對象的代碼比較費時,Android通過AIDL可以實現該功能。
其實Messenger和AIDL並不是對立的,Messenger內部也是基於AIDL實現的。當使用Messenger的時候,Service會用一個Handler串行執行客戶端發來的多個請求,即單線程處理;當直接使用AIDL的時候,Service會維護一個線程池,用該線程池並發處理客戶端發來的多個請求。
再次強調一下,如果你想實現跨進程通信,但是並不關注多線程並發處理,那麼你只需要使用Messenger即可,無需編寫AIDL代碼,只有當你想讓Service並發處理客戶端的請求的時候,才需要編寫AIDL代碼。
使用AIDL的典型案例就是一個App的Activity啟動了一個Service,並且讓該Service運行在一個新的進程中,這種在新進程中的Service我們可以叫做遠程Service,該Service會從服務器拿到最新的數據,客戶端可以調用Service相應的方法獲得最新數據,更近一步的話,Service拿到最新數據之後可以主動向客戶端發送。
要想使用AIDL,要同時在Service和client都編寫相應的代碼。
在Service端,首先要創建.aidl
文件,然後需要實現.aidl
中定義的各種方法。具體如下:
首先要創建.aidl文件
在Android Studio中右鍵創建AIDL文件,該文件其實就是定義一個接口Interface,和定義一般的Java接口差不多,需要在該接口中定義一些方法,這些方法就是允許客戶端跨進程調用的方法,我創建了IRemoteService.aidl文件,如下所示:
// IRemoteInterface.aidl
package com.ispring.aidldemo;
import com.ispring.aidldemo.IRemoteInterfaceCallback;
interface IRemoteInterface {
//獲取Service運行的進程ID
int getPid();
//從Service中獲取最新的數據
int getData();
//通過向Service中注冊回調,可以實現Service主動向客戶端推送數據
void registerCallback(IRemoteInterfaceCallback cb);
//刪除注冊的回調
void unregisterCallback(IRemoteInterfaceCallback cb);
}
我們在上面的.aidl
文件中,定義了四個方法供客戶端調用,通過getPid()方法,可以獲取Service運行的進程ID;通過getData()方法,可以讓客戶端從Service獲取最新的數據;registerCallback和unregisterCallback是用來進行注冊、反注冊客戶端回調的,後面會詳細解釋。
AIDL中定義的方法可以接受如下的參數類型作為形參:
所有的基本類型,例如int, long, char, boolean等等
String、CharSequence、List、Map
在編寫完上述IRemoteInterface.aidl
文件後,我們編譯一下Project,Android Studio會自動幫我們生成一個IRemoteInterface.java
文件,如下所示(對於下面的代碼,我們無需關注具體實現,大家了解一下即可):
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\iWork\\AndroidStudioProjects\\AidlDemo\\app\\src\\main\\aidl\\com\\ispring\\aidldemo\\IRemoteInterface.aidl
*/
package com.ispring.aidldemo;
public interface IRemoteInterface extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ispring.aidldemo.IRemoteInterface {
private static final java.lang.String DESCRIPTOR = "com.ispring.aidldemo.IRemoteInterface";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ispring.aidldemo.IRemoteInterface interface,
* generating a proxy if needed.
*/
public static com.ispring.aidldemo.IRemoteInterface asInterface(android.os.IBinder obj) {
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ispring.aidldemo.IRemoteInterface))) {
return ((com.ispring.aidldemo.IRemoteInterface)iin);
}
return new com.ispring.aidldemo.IRemoteInterface.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_getPid:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getData:
{
data.enforceInterface(DESCRIPTOR);
int _result = this.getData();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_registerCallback:
{
data.enforceInterface(DESCRIPTOR);
com.ispring.aidldemo.IRemoteInterfaceCallback _arg0;
_arg0 = com.ispring.aidldemo.IRemoteInterfaceCallback.Stub.asInterface(data.readStrongBinder());
this.registerCallback(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterCallback:
{
data.enforceInterface(DESCRIPTOR);
com.ispring.aidldemo.IRemoteInterfaceCallback _arg0;
_arg0 = com.ispring.aidldemo.IRemoteInterfaceCallback.Stub.asInterface(data.readStrongBinder());
this.unregisterCallback(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.ispring.aidldemo.IRemoteInterface {
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 getPid() 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);
mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public int getData() 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);
mRemote.transact(Stub.TRANSACTION_getData, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void registerCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) 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.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_registerCallback, _data, _reply, 0);
_reply.readException();
}finally {
_reply.recycle();
_data.recycle();
}
}
@Override public void unregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) 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.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));
mRemote.transact(Stub.TRANSACTION_unregisterCallback, _data, _reply, 0);
_reply.readException();
}finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_registerCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregisterCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
public int getPid() throws android.os.RemoteException;
public int getData() throws android.os.RemoteException;
public void registerCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) throws android.os.RemoteException;
public void unregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) throws android.os.RemoteException;
}
對於上面的代碼,我們無需關注具體實現,我們只需要知道以下幾點即可:
IRemoteInterface.java中,定義了名為IRemoteInterface的interface,其對應著IRemoteInterface.aidl
定義的接口,囊括了IRemoteInterface.aidl
中定義的方法。IRemoteInterface繼承自android.os.IInterface
,如下所示:
public interface IRemoteInterface extends android.os.IInterface
IRemoteInterface
接口中有一個靜態內部類Stub
。Stub
是個抽象類,繼承自android.os.Binder
,且實現了IRemoteInterface
接口,如下所示:
public static abstract class Stub extends android.os.Binder implements com.ispring.aidldemo.IRemoteInterface
Sub
類中有一個asInterface
方法,該方法非常重要,其接收一個android.os.IBinder
類型的對象,返回IRemoteInterface
類型,其方法簽名如下定義:
public static com.ispring.aidldemo.IRemoteInterface asInterface(android.os.IBinder obj)
實現AIDL所定義的接口
我們定義了一個RemoteService
類,其繼承自Service
,該類表示客戶端要調用的遠程服務,我們在AndroidManifest.xml文件中對該Service進行如下配置:
我們通過android:process=":remote"
,讓Service運行在一個新的進程中,而不是原有App的進程,其新進程的名字是remote,當然也可以是其他的名稱。remote前面的冒號表示該進程被當前的App獨享,是該App的私有進程;如果去掉remote前面的冒號,那麼則表示該進程是被多個App所共享的。
RemoteService
源碼如下所示:
package com.ispring.aidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.*;
import android.util.Log;
import java.util.Random;
public class RemoteService extends Service {
private Random random = new Random();
//callbacks存儲了所有注冊過的客戶端回調
private final RemoteCallbackList callbacks = new RemoteCallbackList();
private static final int MSG_REPORT_DATA = 0;
//該handler用來每隔一秒主動向所有注冊過回調的客戶端發送信息
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_REPORT_DATA:
//開始廣播,獲取客戶端的數量
final int n = callbacks.beginBroadcast();
int data = random.nextInt(100);
for(int i = 0; i < n; i++){
try{
//遍歷客戶單回調
IRemoteInterfaceCallback callback = callbacks.getBroadcastItem(i);
//執行我們自定義的dataChanged方法,客戶端會受到信息
Log.i("DemoLog", "RemoteService: handleMessage -> callback.dataChanged(data), PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
callback.dataChanged(data);
}catch (RemoteException e){
e.printStackTrace();
}
}
//結束廣播
callbacks.finishBroadcast();
//構建新的Message,延遲1秒發送,這樣handler每隔一秒都會受到Message
Message pendingMsg = obtainMessage(MSG_REPORT_DATA);
sendMessageDelayed(pendingMsg, 1000);
break;
default:
super.handleMessage(msg);
}
}
};
//我們要實現
IRemoteInterface.Stub binder = new IRemoteInterface.Stub(){
@Override
public int getPid() throws RemoteException {
return android.os.Process.myPid();
}
@Override
public int getData() throws RemoteException {
Log.i("DemoLog", "RemoteService: binder -> getData, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
return random.nextInt(100);
}
@Override
public void registerCallback(IRemoteInterfaceCallback cb) throws RemoteException {
Log.i("DemoLog", "RemoteService: binder -> registerCallback, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
if(cb != null){
//注冊客戶端回調
callbacks.register(cb);
}
}
@Override
public void unregisterCallback(IRemoteInterfaceCallback cb) throws RemoteException {
Log.i("DemoLog", "RemoteService: binder -> unregisterCallback, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
if(cb != null){
//反注冊客戶端回調
callbacks.unregister(cb);
}
}
};
public RemoteService() {
}
@Override
public void onCreate() {
super.onCreate();
handler.sendEmptyMessage(MSG_REPORT_DATA);
Log.i("DemoLog", "RemoteService -> onCreate, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
@Override
public void onDestroy() {
Log.i("DemoLog", "RemoteService -> onDestroy, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
//反注冊所有的客戶端回調,並且不再接收新的客戶端回調
callbacks.kill();
//移除pedding message,停止message循環,防止內存洩露
handler.removeMessages(MSG_REPORT_DATA);
super.onDestroy();
}
}
我們對上述部分代碼進行說明:
首先我們通過IRemoteInterface.Stub binder = new IRemoteInterface.Stub()
實例化了一個IRemoteInterface.Stub
對象。之前我們提到IRemoteInterface.Stub
繼承自android.os.Binder
,所以該實例也就是一個Binder,並且其實現了接口IRemoteInterface
定義的抽象方法。
然後在Service的onBind
接口中將上述binder對象返回,這樣才能讓客戶端通過bindService
方法調用Service。
我們在後面會解釋handler、客戶端回調相關的代碼片段。
通常調用Service的客戶端是Activity,我在MainActivity中調用RemoteService,其界面如下所示:
客戶端MainActivity源碼如下所示:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
package com.ispring.aidldemo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.*;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity implements Button.OnClickListener {
private Button btnBindService;
private Button btnGetData;
private Button btnRegister;
private Button btnUnregister;
private Button btnUnbindService;
private Button btnKillProcess;
private TextView textView;
private boolean isRegistered = false;
private IRemoteInterface remoteInterface;
private static final int MSG_GET_DATA = 0;
private static final int MSG_DATA_CHANGED = 1;
//handler用於在主線程中更新UI
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_GET_DATA:
//通過遠程服務的getData方法獲取數據
Toast.makeText(MainActivity.this, "Data: " + msg.arg1, Toast.LENGTH_LONG).show();
break;
case MSG_DATA_CHANGED:
//遠程服務通過客戶端回調向客戶端推送數據
textView.setText("Receive data from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.i("DemoLog", "MainActivity: ServiceConnection -> onServiceConnected");
remoteInterface = IRemoteInterface.Stub.asInterface(binder);
//更新UI狀態
btnBindService.setEnabled(false);
btnGetData.setEnabled(true);
btnRegister.setEnabled(true);
btnUnregister.setEnabled(false);
btnUnbindService.setEnabled(true);
btnKillProcess.setEnabled(true);
textView.setText("已連接到RemoteService");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("DemoLog", "MainActivity: ServiceConnection -> onServiceDisconnected");
remoteInterface = null;
//更新UI狀態
btnBindService.setEnabled(true);
btnGetData.setEnabled(false);
btnRegister.setEnabled(false);
btnUnregister.setEnabled(false);
btnUnbindService.setEnabled(false);
btnKillProcess.setEnabled(false);
textView.setText("與RemoteService斷開連接");
}
};
//callback為客戶端向RemoteService注冊的回調接口
private IRemoteInterfaceCallback callback = new IRemoteInterfaceCallback.Stub(){
@Override
public void dataChanged(int data) throws RemoteException {
Log.i("DemoLog", "MainActivity: callback -> dataChanged, data: " + data + ", PID=" + + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
Message msg = Message.obtain();
msg.what = MSG_DATA_CHANGED;
msg.arg1 = data;
handler.sendMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnBindService = (Button)findViewById(R.id.btnBindService);
btnGetData = (Button)findViewById(R.id.btnGetData);
btnRegister = (Button)findViewById(R.id.btnRegister);
btnUnregister = (Button)findViewById(R.id.btnUnregister);
btnUnbindService = (Button)findViewById(R.id.btnUnbindService);
btnKillProcess = (Button)findViewById(R.id.btnKillProcess);
textView = (TextView)findViewById(R.id.textView);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btnBindService:
bindService();
break;
case R.id.btnGetData:
getData();
break;
case R.id.btnRegister:
registerCallback();
break;
case R.id.btnUnregister:
unregisterCallback();
break;
case R.id.btnUnbindService:
unbindService();
break;
case R.id.btnKillProcess:
killServiceProcess();
break;
}
}
private void bindService(){
if(remoteInterface != null){
return;
}
Intent intent = new Intent(MainActivity.this, RemoteService.class);
bindService(intent, sc, BIND_AUTO_CREATE);
}
private void getData(){
if(remoteInterface == null){
return;
}
try{
Log.i("DemoLog", "MainActivity -> getData");
int data = remoteInterface.getData();
Message msg = Message.obtain();
msg.what = MainActivity.MSG_GET_DATA;
msg.arg1 = data;
handler.sendMessage(msg);
}catch (RemoteException e){
e.printStackTrace();
}
}
private void registerCallback(){
if(remoteInterface == null || isRegistered){
return;
}
try{
Log.i("DemoLog", "MainActivity -> registerCallback");
//客戶端向遠程服務注冊客戶端回調
remoteInterface.registerCallback(callback);
isRegistered = true;
//更新UI
btnRegister.setEnabled(false);
btnUnregister.setEnabled(true);
Toast.makeText(this, "已向Service注冊Callback", Toast.LENGTH_LONG).show();
}catch (RemoteException e){
e.printStackTrace();
}
}
private void unregisterCallback(){
if(remoteInterface == null || !isRegistered){
return;
}
try{
Log.i("DemoLog", "MainActivity -> unregisterCallback");
//遠程服務反注冊客戶端回調
remoteInterface.unregisterCallback(callback);
isRegistered = false;
//更新UI
btnRegister.setEnabled(true);
btnUnregister.setEnabled(false);
}catch (RemoteException e){
e.printStackTrace();
}
}
private void unbindService(){
if(remoteInterface == null){
return;
}
unregisterCallback();
unbindService(sc);
remoteInterface = null;
//更新UI狀態
btnBindService.setEnabled(true);
btnGetData.setEnabled(false);
btnRegister.setEnabled(false);
btnUnregister.setEnabled(false);
btnUnbindService.setEnabled(false);
btnKillProcess.setEnabled(false);
textView.setText("與RemoteService斷開連接");
}
private void killServiceProcess(){
if(remoteInterface == null){
return;
}
try{
Log.i("DemoLog", "MainActivity -> killServiceProcess");
//獲取遠程服務的進程ID,並殺死遠程服務
int pid = remoteInterface.getPid();
android.os.Process.killProcess(pid);
remoteInterface = null;
//Service進程被殺死後,會觸發onServiceDisconnected的執行
}catch (RemoteException e){
e.printStackTrace();
}
}
}
bindService
與正常調用bindService一樣,首先我們實例化了一個ServiceConnection
對象sc
,然後將其傳遞給了bindService方法,即bindService(intent, sc, BIND_AUTO_CREATE)
。
點擊bindService按鈕後,輸出如下所示:
通過上圖可以看到,我們的MainActivity是運行在進程com.ispring.aidldemo
中,其進程ID為4986。在調用了bindService之後,RemoteService被實例化,RemoteService運行在名為com.ispring.aidldemo:remote
的進程中,其進程ID是6106。
RemoteService創建成功後,會通過其onBind方法返回IBinder對象,將其傳遞給MainActivity中定義的ServiceConnection對象的onServiceConnected
方法中,該方法是在客戶端的主線程中執行的。在該方法中通過IRemoteInterface.Stub
的asInterface(binder)
方法將IBinder
對象轉換成遠程接口IRemoteInterface.Stub
,這樣我們就相當於得到了遠程接口的實例,並將其通過字段remoteInterface進行存儲,可以通過該實例調用接口中定義的方法。該遠程接口相當於是遠程服務RemoteService的代言人,所以下面我們不區分遠程接口和遠程服務的概念,可以把二者看成一回事,這樣可以簡化描述。
getData
點擊getData
按鈕,可以通過int data = remoteInterface.getData()
執行遠程服務的getData
方法,可以從遠程服務中獲取最新的數據。我們在完成單擊之後過一段時間再次單擊getData
按鈕,這兩次單擊的輸出如下所示:
我們可以看到,兩次單擊導致RemoteService中binder對象的getData方法被調用了兩次,且都是在RemoteService的進程(進程ID為6106)中執行的,不是在客戶端進程執行。不過第一次是在Binder_1線程中執行,第二次是在Binder_2線程中執行。
當客戶端調用遠程服務AIDL的方法時,這些遠程服務的方法是在遠程服務的進程中執行的,但是在哪個線程中是不確定的。遠程服務內部會維護一個線程池,從線程池中隨機取出一個線程執行客戶端調用的方法。
當遠程服務的getData方法執行完畢後,客戶端會得到返回的結果,然後客戶端可以根據該值做相應處理,在本例中,我們將得到的data通過Message的形式發送到handler,然後在該handler中更新UI。
registerCallback
通過上面的getData方法可以讓客戶端從遠程服務獲取最新的數據,但是很多情況下我們希望遠程服務的最新數據發生變化後直接推送給我們客戶端,即客戶端和遠程服務通過觀察者模式進行數據更新的通信,此時我們就需要客戶端回調。
我們在IRemoteInterface中定義了與回調相關的兩個方法,registerCallback和unregisterCallback。registerCallback用於向遠程服務中注冊回調,可以實現Service主動向客戶端推送數據。unregisterCallback用於刪除注冊的回調。二者的方法簽名如下所示:
//通過向Service中注冊回調,可以實現Service主動向客戶端推送數據
void registerCallback(IRemoteInterfaceCallback cb);
//刪除注冊的回調
void unregisterCallback(IRemoteInterfaceCallback cb);
這兩個方法都接收一個IRemoteInterfaceCallback參數,IRemoteInterfaceCallback就是一個回調接口,該接口中定義的方法可以被遠程服務調用。IRemoteInterfaceCallback也是由AIDL定義的,IRemoteInterfaceCallback.aidl文件如下所示:
package com.ispring.aidldemo;
//關鍵字oneway表示該接口下面的所有方法不會造成客戶端阻塞等待服務端方法執行完成
oneway interface IRemoteInterfaceCallback {
void dataChanged(int data);
}
IRemoteInterfaceCallback.aidl被編譯後,會產生IRemoteInterfaceCallback.java文件,其定義了IRemoteInterfaceCallback接口和抽象類IRemoteInterfaceCallback.Stub,IRemoteInterfaceCallback.Stub繼承自IBinder,且實現了IRemoteInterfaceCallback接口。通過我們程序中的這兩個aidl文件的編譯,我們就會發現每個aidl文件編譯後,Android Studio都會默認幫我們添加一個其對應的Stub類,我們要和這個Stub類進行操作。
我們在客戶單MainActivity通過如下代碼實現了IRemoteInterfaceCallback.Stub
的一個實例:
//callback為客戶端向RemoteService注冊的回調接口
private IRemoteInterfaceCallback callback = new IRemoteInterfaceCallback.Stub(){
@Override
public void dataChanged(int data) throws RemoteException {
Log.i("DemoLog", "MainActivity: callback -> dataChanged, data: " + data + ", PID=" + + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
Message msg = Message.obtain();
msg.what = MSG_DATA_CHANGED;
msg.arg1 = data;
handler.sendMessage(msg);
}
};
當我們點擊registerCallback按鈕後,執行了代碼remoteInterface.registerCallback(callback)
,輸出如下所示:
我們對以上過程進行一下講解:
我們將callback作為參數傳遞給遠程服務的registerCallback方法。
RemoteService會執行binder的registerCallback方法,代碼如下所示:
@Override
public void registerCallback(IRemoteInterfaceCallback cb) throws RemoteException {
Log.i("DemoLog", "RemoteService: binder -> registerCallback, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
if(cb != null){
//注冊客戶端回調
callbacks.register(cb);
}
}
在RemoteService中,我們定義了一個RemoteCallbackList
類型的callbacks參數,用它存儲所有注冊過的客戶端回調。通過callbacks.register(cb)
,將客戶端傳遞過來的回調參數注冊到callbacks中。
我們在RemoteService中定義了一個handler,每隔一秒發送一個Message,模擬數據發生了變化,然後向所有的客戶端主動發送更新後的數據。handler中主要的相關代碼如下所示:
//開始廣播,獲取客戶端的數量
final int n = callbacks.beginBroadcast();
int data = random.nextInt(100);
for(int i = 0; i < n; i++){
try{
//遍歷客戶單回調
IRemoteInterfaceCallback callback = callbacks.getBroadcastItem(i);
//執行我們自定義的dataChanged方法,客戶端會受到信息
Log.i("DemoLog", "RemoteService: handleMessage -> callback.dataChanged(data), PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName());
callback.dataChanged(data);
}catch (RemoteException e){
e.printStackTrace();
}
}
//結束廣播
callbacks.finishBroadcast();
//構建新的Message,延遲1秒發送,這樣handler每隔一秒都會受到Message
Message pendingMsg = obtainMessage(MSG_REPORT_DATA);
sendMessageDelayed(pendingMsg, 1000);
通過callbacks的beginBroadcast()
方法獲取注冊的客戶端的數量,然後通過callbacks的getBroadcastItem()
方法遍歷所有的客戶端回調,通過調用客戶端回調dataChanged方法,向客戶端主動發送數據,客戶端MainActivity中的callback的dataChanged會回調被執行,且接收到遠程服務傳入的數據。向所有客戶端推送數據完成後,調用callbacks的finishBroadcast()
方法。
由於我們每隔一秒就發送一條Message數據,所以每隔一秒RemoteService就會向所有的客戶端主動發送一遍數據,所以每隔一秒MainActivity中callback的dataChanged方法就被執行一次。我們通過上面的控制台輸出可以發現,MainActivity中callback的dataChanged方法內部的代碼是在MainActivity所在的進程(進程ID為4986)中執行的,但是在哪個線程中執行是不確定的,有時候是線程Binder_3,有時候是線程Binder_2,有時候是線程Binder_1。
客戶端的AIDL回調方法內的代碼是在客戶端的進程中執行的,客戶端中也維護了一個線程池,從該線程池中隨機取出一個線程執行客戶端回調方法,所以不要在AIDL的客戶端回調方法中更新UI,需要通過hanlder更新UI。
unregisterCallback
通過單擊unregisterCallback按鈕,可以反注冊客戶端回調,這樣客戶端就不會再收到遠程服務發送的數據,單擊unregisterCallback按鈕後控制台輸出如下:
在遠程服務的進程中執行binder的unregisterCallback方法,通過callbacks的unregister()
方法將傳入的客戶端回調從callbacks中刪除。
unbindService
在客戶端與遠程服務處於綁定的情況下,點擊unbindService按鈕之後,遠程服務執行onDestroy方法,遠程服務銷毀,控制台輸出如下所示:
com.ispring.aidldemo:remote I/DemoLog: RemoteService -> onDestroy, PID=6106, Thread=main
需要注意的是,單純調用unbindService方法沒有觸發ServiceConnection的onServiceDisconnected()
方法的執行。
Kill Service Process
在客戶端與遠程服務處於綁定的情況下,點擊Kill Service Process
按鈕,可以殺死遠程服務的進程,控制台輸出如下所示:
首先通過我們在IRemoteService.aidl
中定義的getPid()
方法獲取遠程服務的進程ID,然後通過android.os.Process.killProcess(pid)
殺死遠程服務進程。需要注意的是,android.os.Process.killProcess()
方法並不是可以殺死任意的進程,你只能殺死你自己App的進程以及在你的App中所創建的新的進程(比如此例中的遠程服務的進程就是在App自身的進程中創建的)。
當客戶端調用AIDL中的方法時,默認情況下客戶端會阻塞式地等待遠程服務執行完畢,然後客戶端才能繼續執行代碼。比如在IRemoteService.aidl
中定義的getData方法,如果該方法在遠程服務中執行了較長時間才返回了數據,那麼客戶端也要等待該時間。很多時候,AIDL中的方法不需要返回具體的數據,這種情況下為了避免客戶端一直等待遠程方法執行完成,我們就可以將aidl接口聲明為oneway
,聲明為oneway
的AIDL接口中的所有方法在調用時都不會阻塞,具體來說,調用了遠程方法後,不用等著遠程方法執行完畢,會立即返回繼續執行後面的代碼,所以正因為此特性,oneway
接口下面的方法都必須是返回void類型,不能返回其他類型的數據。大部分情況下,我們一般將客戶端的回調接口AIDL定義為oneway
的,這樣遠程服務調用回調接口中的方法時不會阻塞遠程服務後面代碼的執行。
如果你想實現跨進程通信,但是並不關注多線程並發處理,那麼你只需要使用Messenger即可,無需編寫AIDL代碼,只有當你想讓Service並發處理客戶端的請求的時候,才需要編寫AIDL代碼。
當客戶端調用遠程服務AIDL的方法時,這些遠程服務的方法是在遠程服務的進程中執行的,但是在哪個線程中是不確定的。遠程服務內部會維護一個線程池,從線程池中隨機取出一個線程執行客戶端調用的方法。
通過定義客戶端回調AIDL接口,可以在客戶端和遠程服務之間創建觀察者模式,讓遠程服務主動向客戶端發送數據。並且應該將客戶端AIDL接口聲明為oneway
,這樣不會阻塞遠程服務代碼的執行。
客戶端的AIDL回調方法內的代碼是在客戶端的進程中執行的,客戶端中也維護了一個線程池,從該線程池中隨機取出一個線程執行客戶端回調方法,所以不要在AIDL的客戶端回調方法中更新UI,需要通過hanlder更新UI。
希望本文對大家理解和使用AIDL有所幫助!
前言之前說過了在Android中,動畫Animation的實現有兩種方式:Tween Animation(漸變動畫)和Frame Animation(幀動畫)。漸變動畫是
昨天發現自己的一個應用程序的名稱不見了,我很是納悶!!!如下圖所示: 我自己的錯誤的配置文件: 後來請教了一個大四學長,人家
我們知道apk生成後所有的java生成的class文件都被dx命令整合成了一個classes.dex文件,當apk運行時dalvik虛擬機加載classes.
公司項目中有這樣一個需求,當從網絡獲取json數據並解析後,動態的添加按鈕,點擊時切換對應按鈕下存儲的各種數據。如下圖:這裡只是單單為了動態添加RadioButton而已