• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

用观察者模式 手写一个解耦的在Acticity之间传递信息的方案 借鉴EventBus Android kotlin

互联网 diligentman 3周前 (02-17) 16次浏览

看代码直接到最后,不过建议从头开始看

一. 起因(废话)

参加了一个android程序员招聘的面试,被问到广播相关的内容,我根据工作经验,做出了一些画蛇添足的回答(不一定对):

开始回答

广播可以作为进程之间的通信,也可以在Activity/Fragment之间传递内容。但是鉴于Activity/Fragment之间传递内容的各个方法都有比较明显的缺点。

1. 比如Bundle无法传递大容量数据,且传递复杂信息操作繁琐;

2. Handler回产生一个队列,且如果需要重绘UI会有线程之间切换带来的比较难以控制的效果;

3. 接口好使但比较难写(对于我来说,经验不足);

4. 使用Application传递消息在用户翻转,切换的时候,可能会导致内存泄露(这点不确定,没用过);

5. 单例的话,项目禁止使用,用的多了,会产生一些奇怪的单例,毕竟下次维护可能得半年后了;

6. 通过永固化存储虽然完全解决了对象复杂度的问题,但是其需要通过I/O速度太慢了。

所以原来组里通常使用EventBus来传递信息,因为它完成了Acticity之间的解耦。

回答结束

面试官突然来了兴趣,问道:“EventBus的源码有钻研过吗?

好家伙,我直接好家伙。我就去应聘个一年工作经历的android码农,居然问我源码。说实在的,我感觉如果我能扯出点什么说不定会给我发个Offer。可惜没有如果。

回头看了好几天的源码,横竖睡不着,满眼都是注解框架,回调。细细琢磨,字里行间写着再来过。

But,天无绝人之路,还是能够搜到EventBus用的是 观察者模式 。掏出大学时期买来却从没看过的《设计模式》一顿翻阅,决定自己写一个类似功能的,省的下次面试被问到说不出什么内容。

 

二.原理

(1. 2. 3.抄书,有书自己看,《设计模式 可复用面对对象软件的基础 机械工业出版社》 第五章 行为模式 OBSERVER(观察者)——对象行为型模式)

1.为什么要使用观察者模式

安卓系统在开发中被分割为一些列相互协作的,Activity/Fragment。需要维护相关对象的一致性。我们不希望萎了维护一致性而使各个Activity/Fragment紧密耦合,这增加了开发维护的难度。举个例子,一个区分普通用户和会员的app,其功能对非付费用户阉割,如果付费用户恰好会员过期了,就需要及时通知所有的Activity/Fragment页面更改显示,虽然可以通过强制关闭应用重启,但这样对用户体验极差。各个Activity/Fragment可以作为观察者,等待信息的更改。

2.观察者模式适用以下场景:

1. 当一个抽象模型有两个方面,其中一个方面依赖于另一方面。将这两者封装在独立的对象中以使它们可以各自独立地改变和复用。

2. 当一个对象的改变需要同时改变其他对象,而不知道具体又多少对象有待改变。

3. 当一个独享必须通知其他对象,而它又不能假定其他对象是谁。换言之,你不希望这些对象是紧密耦合的。

在应用层面,把一个个Activity/Fragment看作是一个个对象,显然其符合第三适用场景。

3.简易观察者模式:

用观察者模式 手写一个解耦的在Acticity之间传递信息的方案 借鉴EventBus Android kotlin

Subject(目标)

                   ——目标知道它的观察者。可以有任意多个观察者观察同一个目标

                   ——提供注册和删除观察者对象的接口

Observer(观察者)

                   ——为那些在目标发生改变时需要获得通知的对象定义一个更新接口

ConcreteSubject(具体目标)

                   ——将有关状态存入各个ConcreteObserver对象

                   ——当它的状态发生改变时,向它的各个观察者发出通知

ConcreteObserver(具体观察者)

                   ——维护一个指向ConcreteSubject对象的引用

                   ——存储有关状态,这些状态应与目标的状态保持一致

                   ——实现Observer的更新接口以使自身状态和目标的状态保持一致

4.具体到代码层面:(实在看不懂原理的可以直接粘贴创建一个项目进行测试)

//观察者抽象类
abstract class Observer {
    abstract fun Update(msg:Any?)
}


//被观察者抽象类
abstract class Subject {
    abstract fun Attach(observer: Observer)
    abstract fun Detach(observer:Observer)
    abstract fun Notify(msg:Any?)
}


//以下是随便定义的实体观察者和被观察者

//一号观察者,对类型为Message的对象有特殊处理,否则直接打印
class FirstObserver : Observer() {
    override fun Update(msg: Any?) {
        if(msg is Message){
            println("1号观察者收到Message消息,编号为"+msg.num+"内容为"+msg.msg )
        }else{
            println("1号观察者收到消息:内容======"+msg.toString())
        }
    }
}

//二号观察者,无论如何直接打印
class SecondObserver : Observer() {
    override fun Update(msg: Any?) {
        println("2号观察者收到消息:内容======"+msg.toString())
    }
}

//目标实体
class FirstSubject : Subject() {
    //存放观察者
    private  var observers =  ArrayList<Observer>()
    override fun Attach(observer: Observer) {
        observers.add(observer)
    }
    override fun Detach(observer: Observer) {
        observers.remove(observer)
    }
    override fun Notify(msg:Any?) {
        for (observe in observers){
            observe.Update(msg)
        }
    }
}

//Message类,在一号观察者中有特殊处理
class Message(var msg:String,var num:Int)

//实际测试代码
fun main(args: Array<String>){
    val firstSubject = FirstSubject()//被观察者实体
    val firstObserver = FirstObserver()//一号观察者
    val secondObserver = SecondObserver()//二号观察者

    firstSubject.Attach(firstObserver)//一号观察者订阅被观察者
    firstSubject.Attach(secondObserver)//二号观察者订阅被观察者
    firstSubject.Notify("来一份通知")//被观察者发送String信息

    firstSubject.Notify(123)//被观察者发送Int信息

    val msg = Message("说话",2)//被观察者发送Message对象信息
    firstSubject.Notify(msg)

    firstSubject.Detach(firstObserver)//撤销二号观察者对被观察者的查看
    firstSubject.Notify(null)//被观察者发送空
}

//测试结果
1号观察者收到消息:内容======来一份通知
2号观察者收到消息:内容======来一份通知
1号观察者收到消息:内容======123
2号观察者收到消息:内容======123
1号观察者收到Message消息,编号为2内容为说话
2号观察者收到消息:内容======com.example.eventbusapplication.example.Message@7e0ea639
2号观察者收到消息:内容======null

5.从简易观察者模式进化到具有更改管理器的观察者模式(谁才是观察目标

为什么不能让Activity/Fragment继承被观察者来发布消息

1. 在使用kotlin开发的时候第一个问题就是我们的Activity/Fragment是继承自安卓系统的Activity/Fragment的,只有继承了Activity/Fragment才能被认作是Activity/Fragment,由于kotlin和JAVA的单继承特性,是不能直接使用抽象类的方式进行继承。

2. 考虑实际开发过程中,各个Activity/Fragment之间是平级的,只有人为规定其为主Activity/Fragment或者起始Activity/Fragment才会有所区别。所以并不存在某一被观察Activity/Fragment知晓其观察者Activity/Fragment。破坏了"目标知道它的观察者。可以有任意多个观察者观察同一个目标"这一条件。

3. 在开发过程中,比如需要向后一Activity/Fragment传递一个信息,那么本Activity/Fragment作为观察对象没有很好的办法提前知道后一Activity/Fragment是什么,甚至后一Activity/Fragment不一定存在。破坏了“目标提供注册和删除观察者对象的接口”这一条件。

综上所述,目标必不能是一个Activity/Fragment;而观察者应该是一个Activity/Fragment。至此引入一个单例,更改管理器,作为目标。

设计具有更改管理器的Activity/Fragment间使用的观察者模式

改抽象类为接口实现

Subject(目标)

                   ——目标知道它的观察者。可以有任意多个观察者观察同一个目标

                   ——提供注册和删除观察者对象的接口

Observer(观察者)

                   ——为那些在目标发生改变时需要获得通知的对象定义一个更新接口

                   ——在向更改管理器注册观察者时,应主动查看更改管理器的延迟通知队列

ChangeManager(更改管理器) 唯一的Subject

                   ——进程中用于信息传递的唯一的目标,是单例

                   ——当接收到目标发来的通知,提醒各个观察者更新

                   ——维护延迟通知消息队列,并在观察者注册时,拿延迟消息对观察者试探

6. 代码实现

//观察者接口
interface Subject {
    fun Notify(msg:Any?)
    fun LateNotify(msg:Any?)
    fun Attach(observer: Observer)
    fun Detach(observer:Observer)
}


//观察者接口
interface Observer {
    fun Update(msg:Any?)
}

//唯一的观察对象
class ChangeManager:Subject {
    //观察者链表
    private val ObserverList = ArrayList<Observer>()
    //粘性事件链表
    private val LateMessageList = ArrayList<Any?>()

    //对某一个观察者遍历粘性事件
    fun checkLateMessageList(observer: Observer){
        for (latemessage in LateMessageList){
            observer.Update(latemessage)
        }
    }

    //添加粘性事件
    private fun addLateMessage (lateMessage: Any?){
        LateMessageList.add(lateMessage)
    }

    //添加粘性事件(对外抽象)
    override fun LateNotify(msg: Any?) {
        addLateMessage(msg)
    }

    //粘性事件需要手动解除,这点和EventBus是一样的
    fun removeLateMessage(lateMessage: Any?){
        LateMessageList.remove(lateMessage)
    }

    //通知所有观察者更新信息
    override fun Notify(msg: Any?) {
        for (observer in ObserverList){
            observer.Update(msg)
        }
    }

    //注册新的观察者
    override fun Attach(observer: Observer) {
        checkLateMessageList(observer)
        ObserverList.add(observer)
    }

    //注销某个观察者
    override fun Detach(observer: Observer) {
        ObserverList.remove(observer)
    }

}

//个性化一个Application,在里面单例化一个ChangeManager
class MyApp : Application() {
    companion object{
        var instance:MyApp  by Delegates.notNull()
        var changeManager: ChangeManager by Delegates.notNull()
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
        changeManager = ChangeManager()
    }
}

如何在Activity中使用(实例):

//信息类
class EventMessage (var num:Int, var str:String)

//第一个Activity
class FirstActivity : AppCompatActivity(), Observer {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        MyApp.changeManager.Attach(this)
        btn_first.setOnClickListener {
            //发布粘性事件
            MyApp.changeManager.LateNotify("来试试")
            val intent = Intent(this,SecondActivity().javaClass)
            startActivity(intent)
        }
    }
    override fun Update(msg: Any?) {
        if(msg is EventMessage) {
            Log.i("tofu", "FirstActivity收到了Message对象的消息,消息内容为===${msg.num}===${msg.str}")
        } else{
            Log.i("tofu", "FirstActivity收到了其他消息")
        }
    }
}

//第二个Activity
class SecondActivity : AppCompatActivity(),Observer {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        //注册监听(观察者)
        MyApp.changeManager.Attach(this)
        btn_second.setOnClickListener {
            val intent = Intent(this,ThirdActivity().javaClass)
            startActivity(intent)
        }
    }

    //观察者更新的事
    override fun Update(msg: Any?) {
        Log.i("tofu", "SecondActivity收到了信息")
    }
}

//第三个Activity
class ThirdActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_third)

        MyApp.changeManager.Notify(EventMessage(11,"来,更新"))
    }
}


///
//第一第二个Activity各自有一个按钮跳转到下一个Activity

//结果
I/tofu: SecondActivity收到了信息
I/tofu: FirstActivity收到了Message对象的消息,消息内容为===11===来,更新
I/tofu: SecondActivity收到了信息

三. 技术总结

                 重点使用了观察者模式 和 单例模式 两种重要的设计模式。与一般观察者模式用法不一样的是,对于在Activity/Fragment间传递信息的观察者模式,只有一个被观察者,且不是某一个Activity/Fragment。

                 显然在实例中可以看出,远不如原版的EventBus,比方说,需要手动在Update中判定msg对象是什么类型。

 

 

手写不易,如果有帮助转载标明:https://blog.csdn.net/zxy7311074/article/details/113803571。欢迎大佬指正

感谢大佬指正,欢迎进开发交流群讨论问题:200409033

用观察者模式 手写一个解耦的在Acticity之间传递信息的方案 借鉴EventBus Android kotlin


喜欢 (0)