观察者模式,由观察者和被观察对象组成,java已经提供了相关类供我们开发者调用!
Observable - 被观察者
Observer- 观察者
在app开发中,很多情况下都会监听网络变化,如Socket长链接,视频在线播放,唤醒某些服务,友好的用户体验考虑...
那么在这种场景下,Observable就是网络状态,Observer就是Activity
Observable里面维护着一个观察者集合
List<Observer> observers = new ArrayList<Observer>();
引用官方解释
/** * Observable is used to notify a group of Observer objects when a
change * occurs. On creation, the set of observers is empty. After a
change occurred, * the application can call the {@link
#notifyObservers()} method. This will * cause the invocation of the
{@code update()} method of all registered * Observers. The order of
invocation is not specified. This implementation will * call the Observers
in the order they registered. Subclasses are completely * free in what
order they call the update methods. * * @see Observer */
当数据变化时,Observable会通知集合里的所有观察者对象!具体在数据变化后,app调用Observable的notifyObservers方法,那么 集合里的所有Observer的update()会被执行!
具体写法:
第一步:Observable<被观察者对象>
class NetObservable extends Observable {
private Context context;
public NetObservable(Context context) {
super();
this.context = context;
}
@Override
public void addObserver(Observer observer) {
try {
super.addObserver(observer);
NetworkInfo networkInfo = Network.getCurrentActiveNetwork(this.context);//获取当前连接可用的网络
if (networkInfo != null) {
if (!networkInfo.isAvailable()) {//网络不可用
observer.update(this, new NetObserver.NetAction(false,
false, Network.getSubType(context)));
return;
}
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {//WIFI
observer.update(this, new NetObserver.NetAction(true,
true, Network.getSubType(context)));
return;
}
//非WIFI
observer.update(this, new NetObserver.NetAction(true,
false, Network.getSubType(context)));
return;
}
//无网络
observer.update(this, new NetObserver.NetAction(false,
false, Network.getSubType(context)));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void notifyObservers(Object data) {
try {
this.setChanged();//用来设置一个内部标志位注明数据发生了变化
super.notifyObservers(data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
分析:
1:被观察者提供了这样一个方法addObserver(Observer observer),用来添加观察者,可以绑定多个观察者,父类做了啥呢?
public void addObserver(Observer observer) {
if (observer == null) {
throw new NullPointerException("observer == null");
}
synchronized (this) {
if (!observers.contains(observer))
observers.add(observer);
}
}
很简单,父方法,就是无重复加入到内部集合里面!
2:上面NetObservable重写了addObserver方法,仅仅是为了第一添加的时候,去立即反馈观察者当前网络状态!还重写了notifyObservers方法,做法是先调用this.setChanged(); 然后调用父通知方法super.notifyObservers(data);
这里setChanged()是关键,她将内部标志置为真changed = true,下面是通知父方法
public void notifyObservers(Object data) {
int size = 0;
Observer[] arrays = null;
synchronized (this) {
if (hasChanged()) {//如果changed==true,就填充arrays数组
clearChanged();
size = observers.size();
arrays = new Observer[size];
observers.toArray(arrays);
}
}
if (arrays != null) {//如果被填充过,就通知数组类所有对象
for (Observer observer : arrays) {
observer.update(this, data);
}
}
}
第二步:Observer<观察者对象>
写法:
public abstract class NetObserver implements Observer {
public static class NetAction {
private boolean isAvailable;
private boolean isWifi;
private Network.Type type;
public NetAction(boolean isAvailable, boolean isWifi, Network.Type type) {
super();
this.isAvailable = isAvailable;
this.isWifi = isWifi;
this.type = type;
}
public boolean isAvailable() {
return this.isAvailable;
}
public Network.Type getType() {
return type;
}
public void setType(Network.Type type) {
this.type = type;
}
public boolean isWifi() {
return this.isWifi;
}
}
public abstract void notify(NetAction action);
@Override
public void update(Observable observable, Object data) {
this.notify(((NetAction) data));
}
}
分析:
这里衍生了一个抽象方法notify(NetAction action),这里观察者只关心NetAction(具体消息)这类结果,而不关心Observable情况,所以子类重写该抽象方法就够了,如果你的情况是需要 得到 被观察者对象Observable,那么还需重写update方法!
**第3步:什么时候通知观察者 **
当然是收到网络状态变化的广播时候啦,看看接收器
public class NetMonitor extends BroadcastReceiver {
private static final String TAG = "NetMonitor";
private static NetMonitor instance;
private NetObservable observable;
public void addObserver(NetObserver observer) {//添加观察者
this.observable.addObserver(observer);
}
public void delObserver(NetObserver observer) {//删除某个观察者
this.observable.deleteObserver(observer);
}
public void destory() {//销毁方法,删除所有观察者
this.observable.deleteObservers();
this.observable = null;
}
public static NetMonitor getInstance() {//单例模式
if (instance == null) {
synchronized (NetMonitor.class) {
if (instance == null) {
instance = new NetMonitor();
}
}
}
return instance;
}
public void init(Context context) {//初始化,最好在Application,仅仅一次
this.observable = new NetObservable(context);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
context.registerReceiver(this, intentFilter);//Context注册广播
}
//数据源有变化,告诉被观察者NetObservable(唯一的)
private void notifyNetState(Context context) {
try {
NetworkInfo networkInfo = Network.getCurrentActiveNetwork(context);
if (networkInfo != null) {
if (!networkInfo.isAvailable()) {
this.observable.notifyObservers(new NetObserver.NetAction(false, false, Network.getSubType(context)));
return;
}
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
this.observable.notifyObservers(new NetObserver.NetAction(true, true, Network.getSubType(context)));
return;
}
this.observable.notifyObservers(new NetObserver.NetAction(true, false, Network.getSubType(context)));
return;
}
this.observable.notifyObservers(new NetObserver.NetAction(false, false, Network.getSubType(context)));
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onReceive(Context context, Intent intent) {
this.notifyNetState(context);
}
}
分析:在应用启动的时候,先拿单例初始化一下,哈哈,套路!没错,源头都在这NetMonitor,想想主动MVC模式,这里NetMonitor就扮演着Controller控制层,NetObservable相当Model,而NetObserver就是View了,中间传递的事件是NetAction!NetMonitor告诉模型层NetObservable你的NetAction事件来了, 既然来了,那就通知出去吧,于是立即通知View层,完成事件传递!
最后看看上层:
private NetObserver mNetObserver = new NetObserver() {//观察者
@Override
public void notify(NetAction action) {
if (action.isAvailable()) {
Log.e(MainActivity.class.getSimpleName(), "网络可用 > " + "网络类型:" + action.getType().toString());
} else {
Log.e(MainActivity.class.getSimpleName(), "网络不可用 > " + "网络类型:" + action.getType().toString());
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NetMonitor.getInstance().addObserver(this.mNetObserver);//注册
}
@Override
protected void onDestroy() {
super.onDestroy();
NetMonitor.getInstance().delObserver(this.mNetObserver);//取消注册
}
最后还要提一下NetAction中有一个枚举类成员Network.Type,详细描述了是哪一种网络:
public enum Type {
UNKNOWN, WIFI, MOBILE, MOBILE2G, MOBILE3G, MOBILE4G
}
PS:在做App的时候,为了给用户省流量,为了不激起用户的愤怒,为了更好的用户体验,是需要根据用户当前网络情况来做一些调整的,也可以在 App 的设置模块里,让用户自己选择,在 2G / 3G / 4G 网络条件下,是否允许请求一些流量比较大的数据。
那么怎么区分呢?这里主要根据TelephonyManager里的常量值 (NETWORK_TYPE_GPRS\NETWORK_TYPE_LTE....)来分类,具体常量值有19中(可能版本不同,值会有变化),其实如何判断用户2G/3G/4G移动数据网络,源码TelephonyManager里有一个方法getNetworkClass可以满足,但悲剧的是被系统隐藏了,打上了@hide标签,不过可以考虑反射调用哦! API 23下代码如下:
public static int getNetworkClass(int networkType) {
switch (networkType) {
case NETWORK_TYPE_GPRS:
case NETWORK_TYPE_GSM:
case NETWORK_TYPE_EDGE:
case NETWORK_TYPE_CDMA:
case NETWORK_TYPE_1xRTT:
case NETWORK_TYPE_IDEN:
return NETWORK_CLASS_2_G;
case NETWORK_TYPE_UMTS:
case NETWORK_TYPE_EVDO_0:
case NETWORK_TYPE_EVDO_A:
case NETWORK_TYPE_HSDPA:
case NETWORK_TYPE_HSUPA:
case NETWORK_TYPE_HSPA:
case NETWORK_TYPE_EVDO_B:
case NETWORK_TYPE_EHRPD:
case NETWORK_TYPE_HSPAP:
case NETWORK_TYPE_TD_SCDMA:
return NETWORK_CLASS_3_G;
case NETWORK_TYPE_LTE:
case NETWORK_TYPE_IWLAN:
return NETWORK_CLASS_4_G;
default:
return NETWORK_CLASS_UNKNOWN;
}
}
到此,整个注册,通知流程就分析完了,最好自己亲自打印一下网络状态变化,尤其是由WIFI切换到移动网络,或者由移动网络切换到WIFI的情况,看看广播发出情况,谢谢小伙伴的浏览~~~
项目传送:
https://github.com/shonegg/NetMonitor