BroadcastReceiver中的那些設計模式
前文寫過BroadCastReceiver的原始碼的分析BroadcastReceiver原始碼解析(二),文章很長終於把廣播的註冊和傳送流程詳細的分析了一遍。最近用RationalRose畫了畫它的類圖,於是將BroadCastReceiver中涉及的設計模式分析分析,準備再扒一層。
由於技術水平有限,研究了BroadCastReceiver的過程中,目前只發現涉及到了三種設計模式,分別為觀察者,代理和介面卡模式。
一,觀察者模式
觀察者模式類圖:
類圖完了,下面是具體的程式碼實現:
//定義觀察者介面
public interface Observer {
public void update(String s);
}
//定義主題介面
public interface Subject {
public void regist(Observer observer);//定義註冊觀察者方法
public void unRegist(Observer observer);//定義反註冊觀察者方法
public void notifyObserver(String s);//通知觀察者訊息
}
//主題的實現類
public class ConcreteSubject implements Subject{
//定義一個集合,存放註冊進來的觀察者物件
private ArrayList< Observer> mList=new ArrayList<Observer>();
public void regist(Observer observer) {
mList.add(observer);
}
public void unRegist(Observer observer) {
mList.remove(observer);
}
//遍歷集合,逐個呼叫其update方法更新資料
public void notifyObserver(String s) {
for (int i=0;i<mList.size();i++)
{
mList.get(i).update(s);
}
}
}
//1號觀察者,實現了觀察者類
public class ConcreteObserver1 implements Observer {
public ConcreteObserver1(Subject subject) {
subject.regist(this);
}
public void update(String s) {
System.out.println("1號接收者接收到了訊息=="+s);
}
}
//2號觀察者,實現了觀察者類
public class ConcreteObserver2 implements Observer {
public ConcreteObserver2(Subject subject) {
subject.regist(this);
}
public void update(String s) {
System.out.println("2號接收者接收到了訊息==" + s);
}
}
//測試類
public class Test {
public static void main(String[] s) {
Subject subject=new ConcreteSubject();
Observer ob1=new ConcreteObserver1(subject);
Observer ob2=new ConcreteObserver2(subject);
subject.notifyObserver("這是廣播發送的訊息");
}
}
執行結果:
上面是觀察者模式的具體實現。其中模擬了廣播接收者的呼叫過程。首先定義觀察者介面和主題介面,然後在觀察者的構造方法中拿到主題Subject的物件,目的就是為了使用Subject的regist(),把Observer註冊到ConcreteSubject的ArrayList中,ArrayList儲存了所有註冊到ConcreteSubject的物件。接著ConcreteSubject呼叫notifyObserver(),遍歷ArrayList中的元素,呼叫元素的update(),Observer就接收到訊息了。
BroadCastReceiver中觀察者模式的應用:
首先註冊廣播是在ContextImpl中的registerReceiver()中進行的。
return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
這個方法中首先拿到的是AMS遠端的代理物件,有了代理物件後就可以執行AMS中的registerReceiver()。相當於上面的先new一個subject物件,然後在ConcreteObserver的構造方法中會呼叫Subject的regist()。註冊完之後就可以傳送廣播了,相當於上面Subject的notifyObserver();
在廣播原始碼分析裡有說明,AMS中的每個ReceiverList都關聯著上面的一個廣播接收者物件(PS:由於是跨程序的,所以真正儲存的是遠端Binder代理物件。)廣播的註冊其實就是把BroadcastReceiver物件加入到AMS的過程。當傳送廣播的時候,AMS會根據BroadCastFilter遍歷出對應的ReceiverList,然後呼叫對應ReceiverList所關聯的遠端Binder代理物件去執行客戶端的onReceiver()。
二,代理模式:
代理模式中有三種角色:一個是真正的你要訪問的物件(RealSubject),一個是代理物件(Proxy),還有一個是介面。真正物件與代理物件實現同一個介面(Subject)。客戶端首先訪問代理物件的方法,進而訪問到真正物件中的方法。
Android中Binder的通訊就是代理模式。如果把上圖中的request()換成transact()就更好理解了,客戶端通過代理物件mRemote.transact(),因為mRemote是IBinder型別,最終呼叫到真正物件的onTransact()方法。為什麼不是transact()方法呢?因為真正物件Binder實現了IBinder,在Binder中的transact()又呼叫了onTransact()因而最終執行到onTransact()。
BroadCastReceiver中代理模式的應用:
(ps:嚴格意思上不僅應用在BroadCastReceiver中,跨IPC功能都一樣):
以下程式碼位於ActivityManagerProxy中:
public Intent registerReceiver(IApplicationThread caller, String packageName,
IIntentReceiver receiver,
IntentFilter filter, String perm, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
data.writeString(packageName);
data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
filter.writeToParcel(data, 0);
data.writeString(perm);
data.writeInt(userId);
mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
reply.readException();
Intent intent = null;
int haveIntent = reply.readInt();
if (haveIntent != 0) {
intent = Intent.CREATOR.createFromParcel(reply);
}
reply.recycle();
data.recycle();
return intent;
}
在註冊廣播的時候最終會走到ActivityManagerProxy 中的registerReceiver(),接著registerReceiver()中會呼叫mRemote.transact()。
mRemote是IBinder型別的,它其實就是ActivityManagerNative的代理物件。既然這樣,那麼就會呼叫到ActivityManagerNative中的onTransact()。
以下程式碼位於ActivityManagerNative中:
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
。。。。。。
case REGISTER_RECEIVER_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app =
b != null ? ApplicationThreadNative.asInterface(b) : null;
String packageName = data.readString();
b = data.readStrongBinder();
IIntentReceiver rec
= b != null ? IIntentReceiver.Stub.asInterface(b) : null;
IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
String perm = data.readString();
int userId = data.readInt();
Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);
reply.writeNoException();
if (intent != null) {
reply.writeInt(1);
intent.writeToParcel(reply, 0);
} else {
reply.writeInt(0);
}
return true;
}
。。。。。。
}
上面說道會呼叫到遠端的Binder的onTransact()。所以ActivityManagerNative必然是繼承Binder的。
public abstract class ActivityManagerNative extends Binder implements IActivityManager
到此代理模式就完成了,上面ActivityManagerNative不僅繼承了Binder而且還實現了IActivityManager,這個IActivityManager又是幹啥的呢? 這就要說道第三種設計模式,設配器模式。
三,介面卡模式
在計算機程式設計中,介面卡模式(有時候也稱包裝樣式或者包裝)將一個類的介面適配成使用者所期待的。一個適配允許通常因為介面不相容而不能在一起工作的類工作在一起,做法是將類自己的介面包裹在一個已存在的類中。
下面是Android中介面卡類圖。
上圖中ActivityManagerProxy 擔任的是Adapter的角色,至於為什麼擔任Adapter還要取名Proxy,猜想是為了更好的封裝Binder,讓呼叫者完全感知不到Binder的存在,對使用者透明。
因為Android中跨IPC採用的是Binder,而transact()中接收的引數是Parcel型別,不是很方便使用,於是Android FrameWork就用ActivityManagerProxy 把遠端Binder物件給包起來,包裝起來之後就可以直接呼叫IActivityManager中的方法,而完全感知不到Binder的存在。關於對Binder物件的包裝在
IPC(五)——淺談AIDL的架構原理已經手動包裝過。
下面利用上面的UML圖完成另外一個例子,把上面的registerReceiver()換成sayHello()。利用sayHello呼叫Binder中的onTransact().
//定義介面IActivityManager
public interface IActivityManager {
public void sayHello();
}
//定義介面IBinder
public interface IBinder {
public void transact();
}
//定義IActivityManager 的實現類ActivityManagerProxy
public class ActivityManagerProxy implements IActivityManager {
private IBinder mRemote;
//拿到Binder的物件
public ActivityManagerProxy(IBinder mRemote) {
this.mRemote = mRemote;
}
//呼叫Binder的物件的transact();
public void sayHello() {
mRemote.transact();
}
}
//定義IBinder的實現類Binder
public class Binder implements IBinder{
public void transact() {
onTransact();//呼叫onTransact()
}
private void onTransact() {
System.out.println(" 這是遠端Binder發來的問候");
}
}
//測試類
public class Test {
public static void main(String []s)
{
IBinder mRemote=new Binder();
ActivityManagerProxy p=new ActivityManagerProxy(mRemote);
p.sayHello();
}
}
執行結果
上述模擬了ActivityManagerProxy包裝Binder物件的過程,利用本地的Binder物件模擬,最終呼叫到了Binder中的onTransact()。但是測試類中全程隱藏了transact().
總結:BroadCastReceiver是四大元件中典型性的跨IPC實現。觀察者模式是個例,而代理模式和介面卡模式其實是基於IPC的設計。