AIDL使用學習(二):跨程式回撥以及RemoteCallbackList
前言#
五一假期終於結束了,回來一直也是在面試,今天先把之前的AIDL的內容講完,再整理一下面試總結。
正文#
上一篇我們已經瞭解了AIDL的基本使用方法,一般服務內都是要做耗時操作的,等處理結束之後在回撥給呼叫方,首先我們需要定義一個callback:
// IOnCallbackListener.aidl
package com.lzp.aidlstudy.callback;
interface IOnCallbackListener{
void callback(int result);
}
有些朋友可能有疑問了:這不是跟之前定義的Service一樣嗎?
恭喜你答對了,他倆就是一樣的,這個時候在好好的體會一下AIDL的定義:Android 介面定義語言。也就是說他本身就是定義介面的,只不過他自動生成了內部的Binder機制,我們就以這個IOnCallbackListener為例,看看到底生成了什麼東西:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/li504799868/Desktop/AIDLStudy/app/src/main/aidl/com/lzp/aidlstudy/callback/IOnCallbackListener.aidl
*/
package com.lzp.aidlstudy.callback;
public interface IOnCallbackListener extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.lzp.aidlstudy.callback.IOnCallbackListener {
private static final java.lang.String DESCRIPTOR = "com.lzp.aidlstudy.callback.IOnCallbackListener";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.lzp.aidlstudy.callback.IOnCallbackListener interface,
* generating a proxy if needed.
*/
public static com.lzp.aidlstudy.callback.IOnCallbackListener asInterface(android.os.IBinder 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 {
...
}
private static class Proxy implements com.lzp.aidlstudy.callback.IOnCallbackListener {
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 void callback(int result) throws android.os.RemoteException {
...
}
}
static final int TRANSACTION_callback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public void callback(int result) throws android.os.RemoteException;
}
由於這個檔案有點太長了,所以一些方法的程式碼就省略了,更為具體的大家可以自己執行去看一下原始檔,簡單的分析一下:
1、IOnCallbackListener裡面除了定義了Stub內部類以外,只有callback()方法。
2、在Stub裡面有很多熟悉的方法,例如獲取代理,返回binder之類的,內部還有代理類Proxy。
3、Proxy中跨程式通訊的Binder機制實現,這個是最核心的程式碼。
ok,總結也就是說,系統為我們自定義的介面實現了Binder機制,這樣就可以實現跨進行,只不過這個Binder沒有我們之前繫結服務使用的那麼明顯。
然後在之前的ITestInterface.aidl檔案中新定義一個方法:
// aidl 定義實現的Service方法
package com.lzp.aidlstudy;
import com.lzp.aidlstudy.bean.TestBean;
import com.lzp.aidlstudy.callback.IOnCallbackListener;
interface ITestInterface {
// 定義一個計算方法
int getCalculateResult(in TestBean bean);
// 通過執行緒回撥的方式返回計算結果
void getCalculateResultByThread(in TestBean bean, IOnCallbackListener callback);
}
接下來去實現getCalculateResultByThread方法,開啟TestService檔案:
@Override
public void getCalculateResultByThread(final TestBean bean, final IOnCallbackListener callback) throws RemoteException {
new Thread() {
@Override
public void run() {
callback.callback(bean.getX() + bean.getY());
}
}.start();
}
最後在MainActivity中新增一個按鈕:
findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
TestBean testBean = new TestBean();
testBean.setX(100);
testBean.setY(250);
// 這裡要使用IOnCallbackListener.Stub
binder.getCalculateResultByThread(testBean, new IOnCallbackListener.Stub() {
@Override
public void callback(final int result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, result + "", Toast.LENGTH_SHORT).show();
}
});
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
首先強調一下,跨程式callback回撥是在子執行緒中,千萬不要忘記。
帶著無比激動的心情,趕緊執行一下,完美!
擴充套件#
很多朋友在網上檢視資料的時候發現,還有一個RemoteCallbackList,他的主要作用是可以把多個callback儲存到列表裡,在合適的時機同時回撥,也可以防止重複的呼叫相同的任務,只保證你需要的一個結果回撥,這個就厲害了,他的原始碼也非常的簡單:
package android.os;
import android.util.ArrayMap;
/**
* 擅長 簡單的持續性的一系列的遠端介面的使用,尤其是Service對他的客戶端的回撥。
* 需要注意的是:
* 使用的時候,請確保每一個註冊的callback唯一性,這樣可以在程式停止的時候,清空這些callback。
* 多執行緒請注意鎖的問題。
*
* 使用這個類只要在Service使用單例模式就可以了,使用register和unregister方法來新增客戶端的回撥,使用時,先beginBroadcast,在getBroadcastItem,最後finishBroadcast。
*
* 如果一個註冊的會滴啊程式結束了,這個類將自動從列中中移除,如果你想做一些額外的工作,就通過繼承來實現onCallbackDied方法。
*
/
public class RemoteCallbackList<E extends IInterface> {
/*package*/ ArrayMap<IBinder, Callback> mCallbacks
= new ArrayMap<IBinder, Callback>();
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
private final class Callback implements IBinder.DeathRecipient {
final E mCallback;
final Object mCookie;
Callback(E callback, Object cookie) {
mCallback = callback;
mCookie = cookie;
}
public void binderDied() {
synchronized (mCallbacks) {
mCallbacks.remove(mCallback.asBinder());
}
onCallbackDied(mCallback, mCookie);
}
}
/**
* 註冊方法
*/
public boolean register(E callback) {
return register(callback, null);
}
/**
*註冊的具體實現方法,註冊的callback,只有在呼叫unregister或者程式結束才會被解綁,返回新增到集合中的結果(true、false)
*/
public boolean register(E callback, Object cookie) {
synchronized (mCallbacks) {
if (mKilled) {
return false;
}
IBinder binder = callback.asBinder();
try {
Callback cb = new Callback(callback, cookie);
binder.linkToDeath(cb, 0);
mCallbacks.put(binder, cb);
return true;
} catch (RemoteException e) {
return false;
}
}
}
/**
* 解綁callback
*/
public boolean unregister(E callback) {
synchronized (mCallbacks) {
Callback cb = mCallbacks.remove(callback.asBinder());
if (cb != null) {
cb.mCallback.asBinder().unlinkToDeath(cb, 0);
return true;
}
return false;
}
}
/**
* 清空之前所有註冊的callback
*/
public void kill() {
synchronized (mCallbacks) {
for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
Callback cb = mCallbacks.valueAt(cbi);
cb.mCallback.asBinder().unlinkToDeath(cb, 0);
}
mCallbacks.clear();
mKilled = true;
}
}
/**
* 老版本的onCallbackDied
*/
public void onCallbackDied(E callback) {
}
/**
* 預設呼叫的是onCallbackDied(E callback)r
*/
public void onCallbackDied(E callback, Object cookie) {
onCallbackDied(callback);
}
/**
* 開始對儲存的集合進行回撥,返回目前回撥集合的大小
*/
public int beginBroadcast() {
synchronized (mCallbacks) {
if (mBroadcastCount > 0) {
throw new IllegalStateException(
"beginBroadcast() called while already in a broadcast");
}
final int N = mBroadcastCount = mCallbacks.size();
if (N <= 0) {
return 0;
}
Object[] active = mActiveBroadcast;
if (active == null || active.length < N) {
mActiveBroadcast = active = new Object[N];
}
for (int i=0; i<N; i++) {
active[i] = mCallbacks.valueAt(i);
}
return N;
}
}
/**
* 獲取指定索引的callback
*/
public E getBroadcastItem(int index) {
return ((Callback)mActiveBroadcast[index]).mCallback;
}
/**
* 回去指定索引的callback的Cookie
*/
public Object getBroadcastCookie(int index) {
return ((Callback)mActiveBroadcast[index]).mCookie;
}
/**
* 清楚之前的beginBroadcast的初始狀態,當處理結束請呼叫這個方法。
*
* @see #beginBroadcast
*/
public void finishBroadcast() {
synchronized (mCallbacks) {
if (mBroadcastCount < 0) {
throw new IllegalStateException(
"finishBroadcast() called outside of a broadcast");
}
Object[] active = mActiveBroadcast;
if (active != null) {
final int N = mBroadcastCount;
for (int i=0; i<N; i++) {
active[i] = null;
}
}
mBroadcastCount = -1;
}
}
/**
* 返回當前的的要處理的callback數量
* beginBroadcast 返回的是當前註冊的數量
* getRegisteredCallbackCount返回的是處理的數量
* 兩者的返回結果可能不同
*/
public int getRegisteredCallbackCount() {
synchronized (mCallbacks) {
if (mKilled) {
return 0;
}
return mCallbacks.size();
}
}
}
他的程式碼不多,但是英文註釋是真心的多,我就直接在註釋裡面給大家簡單的翻譯一下了,具體大家可以自己去看原始碼註釋。
下面就簡單的修改一下我們的程式碼:
// 新增RemoteCallbackList
private final RemoteCallbackList<IOnCallbackListener> mCallbacks
= new RemoteCallbackList<>();
// 修改後的getCalculateResultByThread
@Override
public void getCalculateResultByThread(final TestBean bean, final IOnCallbackListener callback) throws RemoteException {
new Thread() {
@Override
public void run() {
//註冊callback
mCallbacks.register(callback);
callback(bean.getX() + bean.getY(), callback);
}
}.start();
}
/**
* 處理callback
*/
void callback(int result, IOnCallbackListener callback) {
final int N = mCallbacks.beginBroadcast();
try {
for (int i = 0; i < N; i++) {
IOnCallbackListener ibc = mCallbacks.getBroadcastItem(i);
ibc.callback(result);
// 處理結束,解綁callback
mCallbacks.unregister(ibc);
}
} catch (RemoteException e) {
e.printStackTrace();
}
mCallbacks.finishBroadcast();
}
執行結果也是一樣的,就不貼出來了。
總結#
這樣AIDL的跨程式回撥我們也瞭解了,那麼他具體的呼叫過程是怎麼樣的,系統為我們生成的程式碼都起到了什麼作用呢?這些我們在下一篇在深入的討論。
相關文章
- 使用AIDL實現跨程式介面回掉AI
- Android跨程式通訊之非AIDL(二)AndroidAI
- AIDL使用學習(一):基礎使用學習AI
- AIDL 跨程式呼叫 -- 介面層解析AI
- jQuery 原始碼學習 (三) 回撥函式jQuery原始碼函式
- 4-AIII–Service跨程式通訊:aidlAI
- 從AIDL看Android跨程式通訊AIAndroid
- 第二章 回撥函式函式
- 跟我學習javascript的call(),apply(),bind()與回撥JavaScriptAPP
- 回撥函式快速使用 (轉)函式
- jeesite學習(二)----回顧SpringMVCSpringMVC
- [JS]回撥函式和回撥地獄JS函式
- Python深度學習(使用 Keras 回撥函式和 TensorBoard 來檢查並監控深度學習模型)--學習筆記(十六)Python深度學習Keras函式ORB模型筆記
- DeathRecipient & RemoteCallbackListREM
- iOS如何優雅的處理“回撥地獄Callback hell”(二)——使用SwiftiOSSwift
- Activity生命週期回撥是如何被回撥的?
- 使用Await減少回撥巢狀AI巢狀
- C++回撥函式(callback)的使用C++函式
- 回撥函式函式
- 微博回撥介面
- java介面回撥Java
- 非同步/回撥非同步
- JS 回撥模式JS模式
- C++回撥C++
- js 回撥 callbackJS
- java回撥函式-非同步回撥-簡明講解Java函式非同步
- java 介面回撥經典案例--網路請求回撥Java
- 非同步與回撥的設計哲學非同步
- nodeJS的回撥程式設計 體驗NodeJS程式設計
- 【詳細、開箱即用】.NET企業微信回撥配置(資料回撥URL和指令回撥URL驗證)
- Flutter如何優雅的使用typedef回撥方法Flutter
- JavaScript 回撥函式JavaScript函式
- JavaScript回撥函式JavaScript函式
- JS—回撥函式JS函式
- 簡單理解回撥
- 動畫回撥函式動畫函式
- 介面返回前回撥
- Java——回撥機制Java