編輯:關於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 ArrayListobservers = 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方法,截取了主要代碼如下:
ArrayListcalls = new ArrayList (); synchronized (mRootNode) { mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications, userHandle, calls); } final int numCalls = calls.size(); for (int i=0; i list = oc.mNode.mObservers; int numList = list.size(); for (int j=0; j
先是收集所有注冊該uri的觀察者(binder對象),最後for循環遠程通知(onChange)到所有的觀察者。
最後再找一處
代碼裡面看到這個方法
getAdapter().notifyDataSetChanged();
非常類似主題當中的notifyObservers方法。難道也是一種觀察者模式?跟蹤源碼。。。在BaseAdapter看到如下:
public void notifyDataSetChanged() { mDataSetObservable.notifyChanged(); }
mDataSetObservable是DataSetObservable類型。
public class DataSetObservable extends Observable{ /** * 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.AdapterDataSetObserver { @Override public void onChanged() { super.onChanged(); if (mFastScroll != null) { mFastScroll.onSectionsChanged(); } }
它繼承抽象觀察者類,看來在列表綁定數據的時候將該觀察者注冊到了適配器裡的一個主題對象裡面。如果多個列表綁定同一個適配器就類似多個觀察者注冊到了主題裡面。適配器一旦調用notifyDataSetChanged方法,會通知所有觀察者中的數據跟著變化。
總結
以上記錄了尋找單例模式和觀察者模式的路程,後面還會繼續其他設計模式的尋找。
原文來自官方文檔:https://developer.android.com/guide/components/tasks-and-back-stack.html 應用
今天接觸到了Fragment碎片,也是現在編寫APP的主流道具。那麼今天我用ViewPager、Fragment和ListView做了一個新聞列表,分享給大家。效果圖裡上
新的權限獲取方式除了要求像之前版本一樣在AndroidManifest文件中靜態申請之外,應用還需根據需要請求權限,方式采用向用戶顯示一個請求權限的對話框。這些被動態申
關鍵點 canvas.drawBitmap(bitmap, srcRect, dstRect, null); 將bitmap的srcRect區域繪制到canva