找寻android中的设计模式(一)
-
概述
维护android系统中的联系人模块有一段时间了,最近开始学习java设计模式,对书(HEAD_FIRST设计模式)中精彩设计非常崇拜和对OO原则有了更深刻的理解。作为对设计模式的初学者,我将记录对它的追求之路。该系列文章将结合联系人应用,寻找google原生代码中设计模式。
-
寻找单例模式
定义:确保一个类只有一个实例,并提供一个全局访问点。
对于初学单例模式,我往往会这样写:
public class Singleton { privatestatic SingletonmSingleton; private Singleton() { } public Singleton getInstance() { if (mSingleton == null) { mSingleton =new Singleton(); } returnmSingleton; } }
后来才发现在多线程的应用中就会有问题。对于多线程问题的解决,书中给了三中方案:1、对getInstance方法同步;2、急切实例化、3、双重检测加锁。在android应用中都有用到。如下:
方案1:
public static synchronized ContactEditorUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new ContactEditorUtils(context.getApplicationContext());
}
return sInstance;
}
方案2:
private static TouchPointManager sInstance = new TouchPointManager();
public static TouchPointManager getInstance() {
return sInstance;
}
方案3:
public static AccountTypeManager getInstance(Context context) {
synchronized (mInitializationLock) {
if (mAccountTypeManager == null) {
context = context.getApplicationContext();
mAccountTypeManager = new AccountTypeManagerImpl(context);
}
}
return mAccountTypeManager;
}
下面看下下面代码中的单例,分析下如何改善。
public static final ContactsSimUtils getInstance(int whichCard) {
ContactsSimUtils ret = null;
try {
ret = mListCard.get(whichCard);
if (null == ret) {
throw new NullPointerException();
}
} catch (NullPointerException e) {
ret = mListCard.get(CARD_1);
} finally {
}
return ret;
}
不考虑多线程的话,上面代码或许没啥大问题,但我搜索了下该方法还是有其他线程访问的。这样的话还是有必要优化一下。方案一不适用,因为同步方法会影响程序性能且这里调用的比较频繁。方案二也不推荐使用,因为创建该实例会比较繁重,会影响启动效率。看来方案三比较适用,它只有第一次的时候会同步并初始化。优化代码如下:
public static final ContactsSimUtils getInstance(int whichCard) {
if(mListCard == null){
synchronized (ContactsSimUtils.class) {
if(mListCard == null){
init();
}
}
}
return mListCard.get(whichCard);
}
-
寻找观察者模式
定义:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖都会收到通知并自动更新。
学习了之后,自己也试着寻找生活当中的例子,就以旅游为例。
-
定义主题和观察者的接口
public interface Subject {
public void registerObserver(Observer observer);
public void removeObserver(Observer observer);
public void notifyObservers();
}
public interface Observer {
public void update(String msg);
}
-
实现主题和几个客户
public class Teacher implements Subject { private ArrayList<Observer> observers = new ArrayList<>(); private String msg; @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { int index = observers.indexOf(observer); if (index >= 0) { observers.remove(index); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(msg); } } public void sendMsg(String msg) { this.msg = msg; notifyObservers(); } }
public class Student1 implements Observer { public Student1(Subject subject) { subject.registerObserver(this); } @Override public void update(String msg) { System.out.println("Student1 接收到通知:" + msg); } }
public class Student2 implements Observer { public Student2(Subject subject) { subject.registerObserver(this); } @Override public void update(String msg) { System.out.println("Student2 接收到通知:" + msg); } }
这是一个老师带领学生旅游的例子,同学们要在老师那里报名要去旅游,老师会通知报名的同学旅游的时间。 -
测试下同学们是否收到旅游的通知
public static void main(String[] args) { Teacher subject = new Teacher(); Observer observer1 = new Student1(subject); Observer observer2 = new Student2(subject); subject.sendMsg("出发~~~~~"); }
打印结果:
Student1 接收到通知:出发~~~~~ Student2 接收到通知:出发~~~~~
看来同学们都收到了旅游通知。
既然学会了如何使用观察者模式,接下来我们来查找下自己维护的应用模块当中是如何使用该模式的。
-
很快找到一处:
查看一个抽象的ContactsActivity,它是很多界面的父类,子类都会调用该父类的onCreate方法,在该方法中会往主题里面注册自己。如下:
protected void onCreate(Bundle savedInstanceState) { ContactSaveService.registerListener(this); super.onCreate(savedInstanceState); }
明显这里的主题是ContactSaveService这个服务类,观察者是继承ContactsActivity的子类。然而主题ContactSaveService并没有实现一个类似上面Subject的接口,而观察者实现的接口在主题里面。确实在android开发里面都习惯这样用。主题里面的注册和解注册代码如下:
public static void registerListener(Listener listener) { if (!(listener instanceof Activity)) { throw new ClassCastException("Only activities can be registered to" + " receive callback from " + ContactSaveService.class.getName()); } sListeners.add(0, listener); } public static void unregisterListener(Listener listener) { sListeners.remove(listener); }
这样做的好处是,确保在保存联系人的时候方便将保存过程的状态通知到activity(观察者)。
-
接着找下一处
这是监听数据库变化的一个例子,从下面的方法可以明显的看出,是在往主题里面注册观察者。这里的观察者都继承于抽象类ContentObserver并非接口。因为抽象类里面需要一些公共的模块(handler对象和获取binder方法),这样是合理的。就类似主题可以继承java.util.Observable而不用自己定义接口一样。
getActivity().getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver); getActivity().getContentResolver().registerContentObserver( ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver); getActivity().getContentResolver().registerContentObserver( Status.CONTENT_URI, true, mVoicemailStatusObserver);
跟踪代码发现,这里注册的是ContentObserver里的binder对象,而且是通过binder机制远程注册到服务里面。看来主题是这个服务了。查找代码可以发现,注册过去的binder对象会保存到服务端的一个列表里面。ObserverEntry是实现Ibinder的一个类。
那么如何通知观察者呢?像联系人的数据库,任何应用都可以注册的,就是说服务端会保存很多个binder对象。下面去寻找下操作数据库之后做了什么。如下:
protected void notifyChange(boolean syncToNetwork) { getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null, syncToNetwork); }
增删更新数据库都会调用上面方法来通知各个观察者。如何通知呢?继续查找。最终找到ContentService里的notifyChange方法,截取了主要代码如下:
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>(); synchronized (mRootNode) { mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, userHandle, calls); } final int numCalls = calls.size(); for (int i=0; i<numCalls; i++) { ObserverCall oc = calls.get(i); try { oc.mObserver.onChange(oc.mSelfChange, uri, userHandle); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri); } } catch (RemoteException ex) { synchronized (mRootNode) { Log.w(TAG, "Found dead observer, removing"); IBinder binder = oc.mObserver.asBinder(); final ArrayList<ObserverNode.ObserverEntry> list = oc.mNode.mObservers; int numList = list.size(); for (int j=0; j<numList; j++) { ObserverNode.ObserverEntry oe = list.get(j); if (oe.observer.asBinder() == binder) { list.remove(j); j--; numList--; } } } }
先是收集所有注册该uri的观察者(binder对象),最后for循环远程通知(onChange)到所有的观察者。
-
最后再找一处
代码里面看到这个方法
getAdapter().notifyDataSetChanged();
非常类似主题当中的notifyObservers方法。难道也是一种观察者模式?跟踪源码。。。在BaseAdapter看到如下:public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); }
mDataSetObservable是DataSetObservable类型。
public class DataSetObservable extends Observable<DataSetObserver> { /** * Invokes {@link DataSetObserver#onChanged} on each observer. * Called when the contents of the data set have changed. The recipient * will obtain the new contents the next time it queries the data set. */ public void notifyChanged() { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } }
看来DataSetObservable是继承主题抽象模版类的一个具体目标,T为抽象观察者类。在主题抽象模版类里面实现了注册和解注册。下面看下观察者是在哪里注册的。我们知道列表绑定数据到适配器一般都要用setAdapter方法,举例listview的setAdapter方法看到如下:
mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver);
AdapterDataSetObserver的实现如下:
class AdapterDataSetObserver extends AdapterView<ListAdapter>.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } }
它继承抽象观察者类,看来在列表绑定数据的时候将该观察者注册到了适配器里的一个主题对象里面。如果多个列表绑定同一个适配器就类似多个观察者注册到了主题里面。适配器一旦调用notifyDataSetChanged方法,会通知所有观察者中的数据跟着变化。
-
总结
以上记录了寻找单例模式和观察者模式的路程,后面还会继续其他设计模式的寻找。
- 1楼yymmbb前天 14:14
- 下面的图挂了