在下载模块中经常要用到的一个模式就是观察者模式了,这是为了提高用户体验所决定的。
但用户在下载页面点击了下载之后,肯定希望除了这个下载页面能看到下载进度之外还能在其他地方看到,如通知栏等地方。
这时我们需要把下载任务进度实时更新到各个组件或者页面,如下载详情页,应用页,通知栏等地方。
实时更新下载进度有多种方法,
1、使用广播,在每次更新进度的时候发广播,当时比较耗电
2、使用handler,在要更新进度的时候发送message
3、使用观察者模式,注册观察者,在更新进度的时候提醒每个观察者。
当然,其中handler是可以结合起来使用的,因为handler可是实现线程间通信。
使用观察者模式能够较为便捷的通知组件更新进度,那么改怎么使用它呢?
-
首先一个存储信息的DownloadInfo类,代码如下:
public class DownloadInfo { private int progress; public int getProgress() { return progress; } public void setProgress(int progress) { this.progress = progress; } }
这个类并不复杂,主要是存储和取出下载信息
-
其次要创建观察者接口,也就是观察者想要得到哪些信息,在这里,观察者要得到的信息是下载状态,观察者接口代码如下:
public interface DownloadObserver { // 下载进度发生变化 public void onDownloadProgressChanged(int progress); }
-
第三步是要注册和注销观察者,注册即哪个组件需要得到进度发生改变的信息,注销即这一个观察者不在得到改变的信息。当然得到观察者对象我们要将其保存到一个集合之中,这时我们才能用for循环统一的通知观察者,代码如下:
public class DownLoadTask { private DownloadInfo info; public DownLoadTask(DownloadInfo info) { this.info = info; } private ArrayList<DownloadObserver> observers = new ArrayList<>(); public void registerObserver(DownloadObserver Observer) { if (Observer != null) { observers.add(Observer); } } public void unregisterObserver(DownloadObserver Observer) { if (Observer != null) { observers.remove(Observer); } } public void notifyAllObserver() { if (observers != null) { for (Iterator<DownloadObserver> iterator = observers.iterator(); iterator.hasNext();) { DownloadObserver observer = iterator.next(); observer.onDownloadProgressChanged(info.getProgress()); } } } }
在上面,我们将观察者对象存到ArrayList集合中,然后在notifyAllObserver()通过遍历的方式通知所有的观察者对象,这时就能让所有的观察者获得进度改变的信息
这样一来,我们的观察者的工具类已经准备好了,只需要我们调用registerObserver()注册好观察者,观察者就会添加进观察者的集合mObservers中,当有了观察者的数据之后,想获取通知就调用notifyDownloadStateChanged()或者notifyDownloadProgressChanged(),这两个方法就将信息传给了DownloadObserver观察者对象中。
然后调用的时候我们仅仅是获取了更新的数据,但是我们并不知道要更新哪一个控件,这就需要一个具体的观察者来实现了。
我们创建了三个简单的实现类,当获得进度更新的时候就输出进度到控制台上,代码如下:
实现类1:
public class DeskShow implements DownloadObserver{
@Override
public void onDownloadProgressChanged(int progress) {
System.out.println("桌面显示进度: " + progress);
}
}
实现类2:
public class NotionShow implements DownloadObserver{
@Override
public void onDownloadProgressChanged(int progress) {
System.out.println("通知栏显示进度 : " + progress);
}
}
一切准备好之后,就可以在主方法中使用它们了,主方法代码如下:
public class Download {
public static void main(String[] args) {
// 创建三个观察者对象
DeskShow deskShow = new DeskShow();
NotionShow notionShow = new NotionShow();
OtherShow otherShow = new OtherShow();
DownloadInfo info = new DownloadInfo();
DownLoadTask downLoadTask = new DownLoadTask(info);
// 注册观察者对象到集合中
downLoadTask.registerObserver(deskShow);
downLoadTask.registerObserver(notionShow);
downLoadTask.registerObserver(otherShow);
new Thread() {
public void run() {
int progress = 0;
while (true) {
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 每隔0.5秒更改一次DownloadInfo的信息
progress = progress + 1;
info.setProgress(progress);
// 更改信息之后通知观察者
downLoadTask.notifyAllObserver();
}
};
}.start();
}
}
这样整个观察者的模型就已经完成了,在外部传入DownloadInfo的进度值之后,直接调用downLoadTask的notifyAllObserver(),就可以通知所有的观察者当前的进度。
探究观察者模式的本质,就是java中回调的使用方式,获得具体的信息,把不确定的信息设置为一个方法,归由子类实现。
总结
或许大家看完这个模型,对于观察者模式还有一点疑惑,那么我们来总结一下使用观察者模式的步骤吧!
1、创建一个类,这个类是用于保存动态的需要改变的数据(被观察者),在本文表示DownloadInfo类;
2、创建一个接口类,这个类用于观察对象状态的改变(观察者),在本文表示DownloadObserver类;
3、创建一个注册和注销观察者的任务类(DownLoadTask),这个类拥有注册和注销观察者,并且在被观察这信息发送改变时通知所有的观察者。
4、实现DownloadObserver类,这些实现类实现接收到DownloadInfo类信息发生改变的时候要实现的功能,在本文即是DeskShow类,输出进度语句。
这样一个观察者的模型就组件好了!
最后就是在主方法中注册观察者,然后传入改变的数据,通知所有的观察者。