您的位置 首页 java

Java多线程下载框架02:观察者模式通知下载内容状态更新

场景描述

Java 多线程下载框架中,我们需要知道下载状态比如暂停下载,恢复下载,取消下载等状态的通知,而且不仅仅是更新当前页面,在任意页面都能接收到状态变化的更新,所以这里要用到观察者模式。

关于设计模式的详细介绍,我这里有几本电子书籍推荐 ,点作者头像,然后私信:设计模式,即可获取下载方式。

那么什么是观察者模式(Observer)?

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己。

举一个例子来说明,牛奶送奶站就是主题,订奶客户为监听者,客户从送奶站订阅牛奶后,会每天收到牛奶。如果客户不想订阅了,可以取消,以后就不会收到牛奶。

为什么要使用观察者模式?为什么不用广播, EventBus ,RxBus呢?

广播的劣势

广播是相对消耗时间、空间最多的一种方式,但是大家都知道,广播是四大组件之一,许多系统级的事件都是通过广播来通知的,比如说网络的变化、电量的变化,短信发送和接收的状态,所以,如果与android系统进行相关的通知,还是要选择本地广播;在BroadcastReceiver的 onReceive方法中,可以获得Context 、 intent 参数,这两个参数可以调用许多的sdk中的方法。

应用发送某个广播时,系统会将广播中的intent与系统中所有注册的BroadcastReceiver进行匹配,如果能匹配成功则调用相关的onReceive函数进行处理。这里存在2个问题: a、性能问题 。每个广播都会与所有BroadcastReceiver进行匹配。 b、安全问题 。广播发出去的数据可能被其他应用监听。

因此广播相对于其他的方式而言,广播是重量级的,消耗资源较多的方式。他的优势体现在与sdk连接紧密,如果需要同 android 交互的时候,广播的便捷性会抵消掉它过多的资源消耗,但是如果不同android交互,或者说,只做很少的交互,使用广播是一种浪费。

为什么不使用EventBus,RxBus?

这里不对二者的优缺点进行分析,各有各的好处,看实际需要。因为我们是封装自己的多线程下载框架,所以不能依赖第三方的一些库,因为你不知道用户会使用 RxJava 还是EventBus。比如你这里用到了RxJava的库,而别人使用你的SDK的之前就集成了EventBus,那不是又要集成RxJava?或者说你这里使用的是Rx1.0,而用户使用的是Rx2.0,所以为了避免不必要的麻烦,我们尽量不被依赖外部资源。

为什么使用观察者模式?

  • 松耦合,观察者增加或删除无需修改主题的代码,只需调用主题对应的增加或者删除的方法即可。

  • 主题只负责通知观察者,但无需了解观察者如何处理通知。举个例子,送奶站只负责送递牛奶,不关心客户是喝掉还是洗脸。

  • 观察者只需等待主题通知,无需观察主题相关的细节。还是那个例子,客户只需关心送奶站送到牛奶,不关心牛奶由哪个快递人员,使用何种交通工具送达。

具体实践

一、创建一个Observable

public class DataChanger extends Observable{
/** * 对外提供一个单列引用,用于注册和取消注册监听 */private static DataChanger mDataChanger;
public static synchronized DataChanger getInstance(){
if (null == mDataChanger){
mDataChanger = new DataChanger();
}
return mDataChanger;
}
public void notifyDataChange(DownloadEnty mDownloadEnty){
//Marks this <tt>Observable</tt> object as having been changed
setChanged();
//通知观察者 改变的内容 也可不传递具体内容 notifyObservers()
notify observer s(mDownloadEnty);
}} 

主要用于提供注册和删除观察者对象以及通知更新的方法 ,此处直接继承的是Java提供的Observable,其内部已经实现了

* addObserver
* deleteObserver
* notifyObservers()
* notifyObservers(Object arg)
* deleteObservers()
* setChanged()
* clearChanged()
* hasChanged()
* countObservers() 

当内容变化的时候,使用setChanged()和notifyObservers(mDownloadEnty)通知观察者。

二、setChanged和notifyObservers为何物

上述代码中存在这样一处代码setChanged();, 如果在通知之前没有调用这个方法,观察者是收不到通知的 ,这是为什么呢?

这里我们看一下setChanged的源码

protected synchronized void setChanged() {
changed = true;
} 

此处把boolen变量changed改为了true

再看notifyObservers源码

public void notifyObservers(Object arg) {
/* * a temporary array buffer, used as a snapshot of the state of * current Observers. */Observer[] arrLocal;
synchronized (this) {
if (!hasChanged())
return;
arrLocal = observers.toArray(new Observer[observers.size()]);
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
arrLocal[i].update(this, arg);
} 

可以看到

if (!hasChanged())
return; 

所以这就是为什么通知更新前一定要调用setChanged的原因

但是为什么要加入这样一个开关呢?可能原因大致有三点

1.筛选有效通知 ,只有有效通知可以调用setChanged。比如,我的WX朋友圈一条状态,好友A点赞,后续该状态的点赞和评论并不是每条都通知A,只有A的好友触发的操作才会通知A。

2.便于撤销通知操作 ,在主题中,我们可以设置很多次setChanged,但是在最后由于某种原因需要取消通知,我们可以使用clearChanged轻松解决问题。

3.主动权控制 ,由于setChanged为protected,而notifyObservers方法为public,这就导致存在外部随意调用notifyObservers的可能,但是外部无法调用setChanged,因此真正的控制权应该在主题这里。

三、创建Observer

public abstract class DataWhatcher implements Observer {
@ Override 
public void update(Observable observable, Object data) {
if (data instanceof DownloadEnty){
notifyDataChange(data);
}
}
public abstract void notifyDataChange(Object data);} 

为那些在目标发生改变时需获得通知的类定义个更新的接口,这里对接口再进行了判断,对外提供了notifyDataChange抽象方法,外部可在此抽象方法在获取到更新的回调以及更新的对象。

四、添加和取消观察者

private DataWhatcher dataWhatcher = new DataWhatcher() {
@Override
public void notifyDataChange(Object data) {
downloadEnty = (DownloadEnty) data;
if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloading){
LogUtil.e("download","===notifyDataChange===downloading"+downloadEnty.currentLenth);
}else if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloadcomplete){
LogUtil.e("download","===notifyDataChange===downloadcomplete");
}else if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloadcansel){
downloadEnty = null;
LogUtil.e("download","===notifyDataChange===downloadcansel");
}else if (downloadEnty.downloadStatus == DownloadEnty.DownloadStatus.downloadpause){
LogUtil.e("download","===notifyDataChange===downloadpause");
}else{
LogUtil.e("download","===notifyDataChange===下载进度"+downloadEnty.currentLenth);
}
}
}; 
@Override
protected void onResume() {
super.onResume();
DownloadManager. getInstance ().addObserve(dataWhatcher);
}
@Override
protected void onStop() {
super.onStop();
DownloadManager.getInstance().removeObserve(dataWhatcher);
} 

五、运行效果

六、观察者模式使用总结

从上面可以看出,实际上观察者和被观察者是通过接口回调来通知更新的,首先创建一个观察者(数据监听)实例并实现数据变化接口,通过注册监听将实例传入被观察者(数据变化),当被观察者数据变化的时候使用该实例的接口回传状态。了解原理之后,我们可以利用观察者模式自定义实现。

拿WXGZH来举例,假设WX用户就是观察者,WXGZH是被观察者,有多个的WX用户关注了 陈守印同学 这个GZH,当这个GZH更新时就会通知这些订阅的WX用户。我们来看看用代码如何实现:

1.抽象观察者(Observer)

public interface Observer {
public void update(String  message );} 

2.具体观察者(ConcrereObserver)

WX用户是观察者,里面实现了更新的方法:

里面定义了一个更新的方法:

public class WeixinUser implements Observer {
//WX用户名字
private String name;
public WeixinUser(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + ":" + message);
}} 

3.抽象被观察者(Subject)

抽象主题,提供了attach、detach、notify三个方法:

public interface Subject {
/** * 增加订阅者 * @param observer */public void attach(Observer observer);
/** * 删除订阅者 * @param observer */public void detach(Observer observer);
/** * 通知订阅者更新消息 */public void notify(String message);} 

4.具体被观察者(ConcreteSubject)

WXGZH是具体主题(具体被观察者),里面存储了订阅该GZH的WX用户,并实现了抽象主题中的方法:

public class SubscriptionSubject implements Subject {
//储存订阅GZH的WX用户
private List<Observer> weixinUserlist = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
weixinUserlist.add(observer);
}
@Override
public void detach(Observer observer) {
weixinUserlist.remove(observer);
}
@Override
public void notify(String message) {
for (Observer observer : weixinUserlist) {
observer.update(message);
}
}} 

5.客户端调用

public class Client {
public static void main(String[] args) {
SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
//创建WX用户
WeixinUser user1=new WeixinUser("陈守印同学GZH粉丝A");
WeixinUser user2=new WeixinUser("陈守印同学GZH粉丝B");
WeixinUser user3=new WeixinUser("陈守印同学GZH粉丝C");
WeixinUser user4=new WeixinUser("陈守印同学GZH粉丝D");
//订阅GZH
mSubscriptionSubject.attach(user1);
mSubscriptionSubject.attach(user2);
mSubscriptionSubject.attach(user3);
mSubscriptionSubject.attach(user4);
//GZH更新发出消息给订阅的WX用户
mSubscriptionSubject.notify("陈守印同学GZH的文章更新啦");
}} 

6.运行结果

陈守印同学GZH粉丝A:陈守印同学GZH的文章更新啦
陈守印同学GZH粉丝B:陈守印同学GZH的文章更新啦
陈守印同学GZH粉丝C:陈守印同学GZH的文章更新啦
陈守印同学GZH粉丝D:陈守印同学GZH的文章更新啦 

7.观察者模式优缺点

  • 解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换。

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。

公号后台回复”设计模式”,获取设计模式书籍

上一篇: Java多线程下载01:多线程的好处以及断点续传原理

文章来源:智云一二三科技

文章标题:Java多线程下载框架02:观察者模式通知下载内容状态更新

文章地址:https://www.zhihuclub.com/188871.shtml

关于作者: 智云科技

热门文章

网站地图