這是百度面試官問的一個問題,當時沒答上來。我們知道AIDL底層是基於Binder機制通信的,而Binder本身是C/S架構的。Activity寫個AIDL接口可以實現跟Service的通信,那么Service如何主動回調或者主動推送消息到Activity呢?
定義通信接口
這個接口是Activity發數據給Service用的,addPerson會在Service中的List中新增一個數據,getPersonList返回Person列表。registerCallback注冊回調對象,等一會兒會說。
package com.billshen.offerlearn.service; import com.billshen.offerlearn.service.IPersonCallBack; interface IPerson { void addPerson(String name); List<String> getPersonList(); void registerCallback(IPersonCallBack cb); void unregisterCallback(IPersonCallBack cb); }
IPersonCallBack.aidl這個接口是Service主動回調Activity用的,用於獲取Activity中的時間。
package com.billshen.offerlearn.service; interface IPersonCallBack { String getTime(); }
在AndroidManifest中定義service為另一個進程,並指明action
<service android:name="com.billshen.offerlearn.service.AIDLService" android:enabled="true" android:exported="true" android:process=":aidl_service"> <intent-filter> <action android:name="com.aidl.person" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
定義完成后,在AS中build一下,編譯器會自動為我們生成binder抽象對象,當然你也可以自己寫。
IPerson是個interface繼承於IInterface
內部包含我剛才在AIDL里面聲明的四個方法。
還有三個內部靜態類Default,Stub和Proxy。其中Stub交給服務方實現,Proxy交給客戶端持有。Proxy主要是怎么往內核空間寫數據和拿數據,Default是默認實現,基本不用。
實現具體方法
編譯器為我們生成的IPerson.java文件中有個Stub()內部靜態類,這是個抽象類,具體實現需要我們自己去寫。RemoteCallbackList是一個回調對象列表,可以把Service想象成一個服務器,多個Activity為客戶端。Service在建立連接的時候保存了客戶端的回調對象。
public class AIDLService extends Service { private String TAG = AIDLService.class.getSimpleName(); private RemoteCallbackList<IPersonCallBack> iPersonCallBacks = new RemoteCallbackList<>();//回調對象列表 List<String> mPersons = new ArrayList<>(); public AIDLService() { mPersons.add("初始人"); } @Nullable @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind: " + mPersons); BroadcastThread broadcastThread = new BroadcastThread(); broadcastThread.start(); return new IPerson.Stub() { @Override public void addPerson(String name) throws RemoteException { mPersons.add(name); } @Override public List<String> getPersonList() throws RemoteException { return mPersons; } @Override public void registerCallback(IPersonCallBack cb) throws RemoteException { iPersonCallBacks.register(cb);//注冊回調對象 } @Override public void unregisterCallback(IPersonCallBack cb) throws RemoteException { iPersonCallBacks.unregister(cb);//反注冊回調對象 } }; } class BroadcastThread extends Thread { @Override public void run() { while (true) { try { Thread.sleep(1000); int len = iPersonCallBacks.beginBroadcast();//開始發送廣播 for (int i = 0; i < len; i++) {//遍歷回調對象,將拿回來的數據打印再控制台上 Log.i(TAG, "run: " + iPersonCallBacks.getBroadcastItem(0).getTime()); } iPersonCallBacks.finishBroadcast(); } catch (Exception e) { e.printStackTrace(); } } } } }
Activity中實現客戶端的回調函數和開啟新線程不斷讓Service增加Person並拿回數據顯示在TextView上。
public class AIDLActivity extends BaseMvpActivity { IPerson iPerson = null; TextView resultTv; Handler mHandler = new Handler() { @Override public void handleMessage(@NonNull Message msg) { try { resultTv.setText(iPerson.getPersonList().toString()); } catch (RemoteException e) { e.printStackTrace(); } super.handleMessage(msg); } }; @Override public void initView() { Log.i(TAG, "initView: "); resultTv = findViewById(R.id.result_tv); } @Override public void initData() { Log.i(TAG, "initData: "); Intent intent = new Intent(); intent.setPackage("com.billshen.offerlearn"); intent.setAction("com.aidl.person"); ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iPerson = IPerson.Stub.asInterface(service); IPersonCallBack iPersonCallBack = new IPersonCallBack.Stub() { @Override public String getTime() throws RemoteException { //回調函數,返回時間給服務端 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return df.format(System.currentTimeMillis()); } }; try { iPerson.registerCallback(iPersonCallBack);//在Service中注冊回調 } catch (RemoteException e) { e.printStackTrace(); } new PersonThread().start(); } @Override public void onServiceDisconnected(ComponentName name) { } }; bindService(intent, serviceConnection, BIND_AUTO_CREATE); } class PersonThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { try { iPerson.addPerson("person" + i); Thread.sleep(1000); mHandler.sendEmptyMessage(0); } catch (RemoteException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } } @Override public int getLayoutId() { return R.layout.activity_aidl; } @Override public void showLoading() { } @Override public void hideLoading() { } @Override public void refreshView() { } @Override public void onError(String errMessage) { } }
運行結果
Activity客戶端結果
Service服務端結果