Android常見設計模式三:單例模式

對于開發人員來說,設計模式有時候就是一道坎,但是設計模式又非常有用,過了這道坎,它可以讓你水平提高一個檔次。而在android開發中,必要的了解一些設計模式又是必須的,因為設計模式在Android源碼中,可以說是無處不在。對于想系統的學習設計模式的同學,這里推薦一本書,《大話設計模式》。


Android常用設計模式系列:

面向對象的基礎特征
面向對象的設計原則
單例模式
模板模式
適配器模式
工廠模式
代理模式
原型模式
策略模式
Build模式
觀察者模式
裝飾者模式
中介模式
門面模式


單例模式

單列模式是非常常見的設計模式之一,寫個筆記,記錄一下我的學習過程和心得。

首先了解一些單例模式的定義。

確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例。

這樣做有以下幾個優點

  • 對于那些比較耗內存的類,只實例化一次可以大大提高性能,尤其是在移動開發中。
  • 保持程序運行的時候該中始終只有一個實例存在內存中

由單例的定義,可以分析出,實現一個單例,有以下幾個要點

  • 構造函數必須私有化,防止外部調用構造函數進行實例。
  • 提供靜態函數獲得該單例。

單例主要有兩種種實現方式,懶漢模式和餓漢模式。

懶漢模式:

在類加載時,不創建實例,因此類加載速度快,但運行時獲取對象的速度慢,代碼如下

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判斷

餓漢模式:

在類加載時就完成了初始化,所以類加載較慢,但獲取對象的速度快,代碼如下

public class Singleton {
  private static Singleton instance = new Singleton();
  
  private Singleton(){
   }
 
  public static Singleton getInstance() {
   return instance;
  }
} 

兩種實現模式各有優缺點,綜合來說,個人比較偏向于懶漢模式。

并發測試

至于單例的并發測試,可以使用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;
    }

而Android 源碼中單例也是非常的多,如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;
    }

總結

總結一下單例模式的優缺點

優點

1.在單例模式中,活動的單例只有一個實例,對單例類的所有實例化得到的都是相同的一個實例。這樣就 防止其它對象對自己的實例化,確保所有的對象都訪問一個實例
2.單例模式具有一定的伸縮性,類自己來控制實例化進程,類就在改變實例化進程上有相應的伸縮性。
3.提供了對唯一實例的受控訪問。
4.由于在系統內存中只存在一個對象,因此可以 節約系統資源,當 需要頻繁創建和銷毀的對象時單例模式無疑可以提高系統的性能。
5.避免對共享資源的多重占用。

缺點

1.不適用于變化的對象,如果同一類型的對象總是要在不同的用例場景發生變化,單例就會引起數據的錯誤,不能保存彼此的狀態。
2.由于單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
3.單例類的職責過重,在一定程度上違背了“單一職責原則”。
4.濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。

適用場景:

單例模式只允許創建一個對象,因此節省內存,加快對象訪問速度,因此對象需要被公用的場合適合使用,如多個模塊使用同一個數據源連接對象等等。如:

  1. 需要頻繁實例化然后銷毀的對象。
  2. 創建對象時耗時過多或者耗資源過多,但又經常用到的對象。
  3. 有狀態的工具類對象。
  4. 頻繁訪問數據庫或文件的對象。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容