BroadcastReceiver中的那些设计模式

Posted 從前以後

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BroadcastReceiver中的那些设计模式相关的知识,希望对你有一定的参考价值。

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
原创文章,转载请注明 ( 来自:http://blog.csdn.net/tiefeng0606/article/details/51499199 IZZY的博客 )

前文写过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的设计。

以上是关于BroadcastReceiver中的那些设计模式的主要内容,如果未能解决你的问题,请参考以下文章

android 自定义视图中能不能定义BroadcastReceiver

如何将BroadcastReceiver中的信息传到Activity中

Android HOME键那些事

清单中的 ACTION_USER_PRESENT 与 BroadcastReceiver

从 BroadcastReceiver 中的 Service 获取 arraylist 给出 NullpointerException

从 Android 中的 BroadcastReceiver 调用 Activity 方法