Android開發中無處不在的設計模式——單例模式
對於開發人員來說,設計模式有時候就是一道坎,但是設計模式又非常有用,過了這道坎,它可以讓你水平提高一個檔次。而在android開發中,必要的瞭解一些設計模式又是非常有必要的。對於想系統的學習設計模式的同學,這裡推薦2本書。一本是Head First系列的Head Hirst Design Pattern,英文好的可以看英文,可以多讀幾遍。另外一本是大話設計模式。
這篇文章介紹一個模式,就是單例模式,因為個人覺得這個模式理解起來最容易,而且不是太複雜。
首先了解一些什麼是單例,從名字中就可以聽出來就是在記憶體中維護唯一物件。這樣做有以下幾個優點
- 對於那些比較耗記憶體的類,只例項化一次可以大大提高效能,尤其是在移動開發中。
- 保持程式執行的時候該中始終只有一個例項存在記憶體中
其實單例有很多種實現方式,但是個人比較傾向於其中1種。可以見單例模式
程式碼如下
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
要保證單例,需要做一下幾步
- 必須防止外部可以呼叫建構函式進行例項化,因此建構函式必須私有化。
- 必須定義一個靜態函式獲得該單例
- 單例使用volatile修飾
- 使用synchronized 進行同步處理,並且雙重判斷是否為null,我們看到synchronized (Singleton.class)裡面又進行了是否為null的判斷,這是因為一個執行緒進入了該程式碼,如果另一個執行緒在等待,這時候前一個執行緒建立了一個例項出來完畢後,另一個執行緒獲得鎖進入該同步程式碼,例項已經存在,沒必要再次建立,因此這個判斷是否是null還是必須的。
至於單例的併發測試,可以使用CountDownLatch,使用await()等待鎖釋放,使用countDown()釋放鎖從而達到併發的效果。可以見下面的程式碼
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(1);
int threadCount = 1000;
for (int i = 0; i < threadCount; i++) {
new Thread() {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Singleton.getInstance().hashCode());
}
}.start();
}
latch.countDown();
}
看看打印出來的hashCode會不會出現不一樣即可,理論上是全部都一樣的。
而在Android中,很多地方用到了單例。
比如Android-Universal-Image-Loader中的單例
private volatile static ImageLoader instance;
/** Returns singleton class instance */
public static ImageLoader getInstance() {
if (instance == null) {
synchronized (ImageLoader.class) {
if (instance == null) {
instance = new ImageLoader();
}
}
}
return instance;
}
比如EventBus中的單例
private static volatile EventBus defaultInstance;
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
上面的單例都是比較規規矩矩的,當然實際上有很多單例都是變了一個樣子,單本質還是單例。
如InputMethodManager 中的單例
static InputMethodManager sInstance;
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
return sInstance;
}
}
AccessibilityManager 中的單例,看程式碼這麼長,其實就是進行了一些判斷,還是一個單例
private static AccessibilityManager sInstance;
public static AccessibilityManager getInstance(Context context) {
synchronized (sInstanceSync) {
if (sInstance == null) {
final int userId;
if (Binder.getCallingUid() == Process.SYSTEM_UID
|| context.checkCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS)
== PackageManager.PERMISSION_GRANTED
|| context.checkCallingOrSelfPermission(
Manifest.permission.INTERACT_ACROSS_USERS_FULL)
== PackageManager.PERMISSION_GRANTED) {
userId = UserHandle.USER_CURRENT;
} else {
userId = UserHandle.myUserId();
}
IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
sInstance = new AccessibilityManager(context, service, userId);
}
}
return sInstance;
}
當然單例還有很多種寫法,比如惡漢式,有興趣的自己去了解就好了。
最後,我們應用一下單例模式。典型的一個應用就是管理我們的Activity,下面這個可以作為一個工具類,程式碼也很簡單,也不做什麼解釋了。
public class ActivityManager {
private static volatile ActivityManager instance;
private Stack<Activity> mActivityStack = new Stack<Activity>();
private ActivityManager(){
}
public static ActivityManager getInstance(){
if (instance == null) {
synchronized (ActivityManager.class) {
if (instance == null) {
instance = new ActivityManager();
}
}
return instance;
}
public void addActicity(Activity act){
mActivityStack.push(act);
}
public void removeActivity(Activity act){
mActivityStack.remove(act);
}
public void killMyProcess(){
int nCount = mActivityStack.size();
for (int i = nCount - 1; i >= 0; i--) {
Activity activity = mActivityStack.get(i);
activity.finish();
}
mActivityStack.clear();
android.os.Process.killProcess(android.os.Process.myPid());
}
}
這個類可以在開源中國的幾個客戶端中找到類似的原始碼
以上兩個類是一樣的,沒區別。