在Pass Data(4)中對aidl有簡單介紹,但是那個範例並不能展現為何需要使用aidl的方式來傳遞資料。因為如果service和activity都在同一個apk,其實只要利用bindservice就可以傳遞資料了,並不用非得使用aidl。那到底aidl用在何時,通常應該是service未來會被其他apk(客戶端)使用 ,即兩者不在同一個process 運行時,這時會把xxx.aidl檔產生出的xxx.java檔,給其他apk使用。這樣2者即可進行IPC。因為xxx.java當初產生的套件名稱,和客戶端的套件名稱可能不同,所以有可能要另外開資料夾在客戶端的src裡面,這樣xxx.java才會符合套件名稱,而客戶端才能import進來。
舉例來說,比如你開發了一支一般可在Launcher執行的程式,另外又開發了一支Widget,
兩支是不同的apk,那在Launcher就可以有activity和service,而widget就去呼叫service取得資料。
本範例中是用API Demos裡面App/Service/Remote Service Binding修改而成,
總共三支apk:
(1) TestAidlServeice.apk
(2) TestAidlActivity1.apk
(3) TestAidlActivity2.apk
TestAidlActivity1利用bindService的方式和TestAidlService綁定後,
一直用Call Back的方式,取得來自TestAidlService的資料
TestAidlActivity2的程式和TestAidlActivity1相同。而且CallBack物件是跑在相同的process,
所以可以發現TestAidlService傳給TestAidlActivity1和TestAidlActivity2的值是連續的,
而不會有重覆的情形發生。
TestAidlService主要程式如下:
public class RemoteService extends Service {
// 因為這支RemoteService可能會和很多支客戶端程式連接,每支客戶端如果都要用到ICallBack功能
// 所以RemoteCallbackList<ICallBack>可以儲存很多個客戶端的ICallBack
final RemoteCallbackList<ICallBack> mCallbacks= new RemoteCallbackList<ICallBack>();
int mValue = 0;
private static final int REPORT_MSG = 1;
private final IService.Stub mBinder = new IService.Stub() {
//增加一個CallBack
public void registerCallback(ICallBack cb) {
Log.d("gill","registerCallback() - service");
if (cb != null) mCallbacks.register(cb);
}
//移除一個CallBack
public void unregisterCallback(ICallBack cb) {
Log.d("gill","unregisterCallback() - service");
if (cb != null) mCallbacks.unregister(cb);
}
};
@Override
public void onCreate() {
Log.d("gill","onCreate() - service");
mHandler.sendEmptyMessage(REPORT_MSG);
}
@Override
public void onDestroy() {
mCallbacks.kill();
mHandler.removeMessages(REPORT_MSG);
}
@Override
public IBinder onBind(Intent intent) {
Log.d("gill","onBind() - IService | intent.getAction() = " + intent.getAction());
if(intent.getAction().equals("com.test.aidl.service.RemoteService")){
return mBinder;
}
return null;
}
private final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
Log.d("gill","handleMessage() - service");
switch (msg.what) {
case REPORT_MSG: {
int value = ++mValue;
Log.d("gill","handleMessage()/what - service | value = " + value);
// Broadcast 所有的客戶端程式,因為本例有2個Activity,所以N會等於2
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
Log.d("gill","N = " +N);
try {
//執行valueChanged()的動作,而具體要做的事情,已經被寫在Activity裡面了
mCallbacks.getBroadcastItem(i).valueChanged(value);
} catch (RemoteException e) {
}
}
mCallbacks.finishBroadcast();
// 每一秒就做一次改變 mValue的動作,然後再把值送到客戶端(就是2支Activity)
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
} break;
default:
super.handleMessage(msg);
}
}
};
}
TestAidlActivity1主要程式如下:
import com.test.aidl.service.ICallBack;
import com.test.aidl.service.IService;
public class Main1 extends Activity {
// 這個 mService是由RemoteService所傳過來的
IService mService = null;
TextView mCallbackText;
private boolean mIsBound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("gill","onCreate() - Binding activity");
setContentView(R.layout.main);
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
Log.d("gill","onServiceConnected() - mConnection");
//當和RemoteService綁定時,獲得實作IService所有方法(registerCallback/registerCallback)的IBinder實體
mService = IService.Stub.asInterface(service);
mCallbackText.setText("Attached.");
try {
//mCallback由客戶端實作ICallBack裡的方法
//把mCallback丟進RemoteService裡面的RemoteCallbackList<ICallBack> mCallbacks
//如此一來,mCallbacks可以用"Call Back function"的機制,利用valueChanged(value)把值秀回到客戶端上
mService.registerCallback(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName className) {
Log.d("gill","onServiceDisconnected() - mConnection");
mService = null;
mCallbackText.setText("Disconnected.");
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
//按下bind按鈕後,就去bindService (和RemoteService綁定)
Log.d("gill","onBindListener() - Binding activity | IService.class.getName() = "+IService.class.getName());
bindService(new Intent("com.test.aidl.service.RemoteService"),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
Log.d("gill","onUnbindListener() - Binding activity");
if (mIsBound) {
//如果之前有綁定的話,此時要取消註冊
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
}
}
//按下unbind按鈕後,不再和RemoteService綁定
unbindService(mConnection);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};
private ICallBack mCallback = new ICallBack.Stub() {
//因為這個mCallback會被丟到RemoteService去,此時他的運作是在RemoteService的process裡,
//因為他不是在Main1這個Activity的main thread,所以無法更改畫面,只能透過hander機制來改變畫面
public void valueChanged(int value) {
Log.d("gill","valueChanged() - Binding activity | the value = "+value);
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
Log.d("gill","handleMessage() - Binding activity");
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
}
TestAidlActivity2的程式和TestAidlActivity1程式相同,不再描述。
執行畫面如下:
先 前提過用bindService的方式,如果Activity結束,Service也會結束。反之Service則會在背景繼續運作。測試方式可以先執行 TestAidlActivity1,然後跑到Received from service:10,按下home再執行TestAidlActivity2去bindServeice,會發現Received from service:10之後的數值。
但是如果先執行TestAidlActivity1,然後跑到Received from service:10,按下BackKey再執行TestAidlActivity2去bindServeice,會發現Received from service:會從新開始。
舉例來說,比如你開發了一支一般可在Launcher執行的程式,另外又開發了一支Widget,
兩支是不同的apk,那在Launcher就可以有activity和service,而widget就去呼叫service取得資料。
本範例中是用API Demos裡面App/Service/Remote Service Binding修改而成,
總共三支apk:
(1) TestAidlServeice.apk
(2) TestAidlActivity1.apk
(3) TestAidlActivity2.apk
TestAidlActivity1利用bindService的方式和TestAidlService綁定後,
一直用Call Back的方式,取得來自TestAidlService的資料
TestAidlActivity2的程式和TestAidlActivity1相同。而且CallBack物件是跑在相同的process,
所以可以發現TestAidlService傳給TestAidlActivity1和TestAidlActivity2的值是連續的,
而不會有重覆的情形發生。
TestAidlService主要程式如下:
public class RemoteService extends Service {
// 因為這支RemoteService可能會和很多支客戶端程式連接,每支客戶端如果都要用到ICallBack功能
// 所以RemoteCallbackList<ICallBack>可以儲存很多個客戶端的ICallBack
final RemoteCallbackList<ICallBack> mCallbacks= new RemoteCallbackList<ICallBack>();
int mValue = 0;
private static final int REPORT_MSG = 1;
private final IService.Stub mBinder = new IService.Stub() {
//增加一個CallBack
public void registerCallback(ICallBack cb) {
Log.d("gill","registerCallback() - service");
if (cb != null) mCallbacks.register(cb);
}
//移除一個CallBack
public void unregisterCallback(ICallBack cb) {
Log.d("gill","unregisterCallback() - service");
if (cb != null) mCallbacks.unregister(cb);
}
};
@Override
public void onCreate() {
Log.d("gill","onCreate() - service");
mHandler.sendEmptyMessage(REPORT_MSG);
}
@Override
public void onDestroy() {
mCallbacks.kill();
mHandler.removeMessages(REPORT_MSG);
}
@Override
public IBinder onBind(Intent intent) {
Log.d("gill","onBind() - IService | intent.getAction() = " + intent.getAction());
if(intent.getAction().equals("com.test.aidl.service.RemoteService")){
return mBinder;
}
return null;
}
private final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
Log.d("gill","handleMessage() - service");
switch (msg.what) {
case REPORT_MSG: {
int value = ++mValue;
Log.d("gill","handleMessage()/what - service | value = " + value);
// Broadcast 所有的客戶端程式,因為本例有2個Activity,所以N會等於2
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
Log.d("gill","N = " +N);
try {
//執行valueChanged()的動作,而具體要做的事情,已經被寫在Activity裡面了
mCallbacks.getBroadcastItem(i).valueChanged(value);
} catch (RemoteException e) {
}
}
mCallbacks.finishBroadcast();
// 每一秒就做一次改變 mValue的動作,然後再把值送到客戶端(就是2支Activity)
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
} break;
default:
super.handleMessage(msg);
}
}
};
}
TestAidlActivity1主要程式如下:
import com.test.aidl.service.ICallBack;
import com.test.aidl.service.IService;
public class Main1 extends Activity {
// 這個 mService是由RemoteService所傳過來的
IService mService = null;
TextView mCallbackText;
private boolean mIsBound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("gill","onCreate() - Binding activity");
setContentView(R.layout.main);
Button button = (Button)findViewById(R.id.bind);
button.setOnClickListener(mBindListener);
button = (Button)findViewById(R.id.unbind);
button.setOnClickListener(mUnbindListener);
mCallbackText = (TextView)findViewById(R.id.callback);
mCallbackText.setText("Not attached.");
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
Log.d("gill","onServiceConnected() - mConnection");
//當和RemoteService綁定時,獲得實作IService所有方法(registerCallback/registerCallback)的IBinder實體
mService = IService.Stub.asInterface(service);
mCallbackText.setText("Attached.");
try {
//mCallback由客戶端實作ICallBack裡的方法
//把mCallback丟進RemoteService裡面的RemoteCallbackList<ICallBack> mCallbacks
//如此一來,mCallbacks可以用"Call Back function"的機制,利用valueChanged(value)把值秀回到客戶端上
mService.registerCallback(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName className) {
Log.d("gill","onServiceDisconnected() - mConnection");
mService = null;
mCallbackText.setText("Disconnected.");
}
};
private OnClickListener mBindListener = new OnClickListener() {
public void onClick(View v) {
//按下bind按鈕後,就去bindService (和RemoteService綁定)
Log.d("gill","onBindListener() - Binding activity | IService.class.getName() = "+IService.class.getName());
bindService(new Intent("com.test.aidl.service.RemoteService"),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("Binding.");
}
};
private OnClickListener mUnbindListener = new OnClickListener() {
public void onClick(View v) {
Log.d("gill","onUnbindListener() - Binding activity");
if (mIsBound) {
//如果之前有綁定的話,此時要取消註冊
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
}
}
//按下unbind按鈕後,不再和RemoteService綁定
unbindService(mConnection);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
};
private ICallBack mCallback = new ICallBack.Stub() {
//因為這個mCallback會被丟到RemoteService去,此時他的運作是在RemoteService的process裡,
//因為他不是在Main1這個Activity的main thread,所以無法更改畫面,只能透過hander機制來改變畫面
public void valueChanged(int value) {
Log.d("gill","valueChanged() - Binding activity | the value = "+value);
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
Log.d("gill","handleMessage() - Binding activity");
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
}
TestAidlActivity2的程式和TestAidlActivity1程式相同,不再描述。
執行畫面如下:
先 前提過用bindService的方式,如果Activity結束,Service也會結束。反之Service則會在背景繼續運作。測試方式可以先執行 TestAidlActivity1,然後跑到Received from service:10,按下home再執行TestAidlActivity2去bindServeice,會發現Received from service:10之後的數值。
但是如果先執行TestAidlActivity1,然後跑到Received from service:10,按下BackKey再執行TestAidlActivity2去bindServeice,會發現Received from service:會從新開始。
請問有此篇的androidmanifast配置檔嗎?謝謝
回覆刪除ICallBacl請教是interface還是class??
回覆刪除