對于開發人員來說,設計模式有時候就是一道坎,但是設計模式又非常有用,過了這道坎,它可以讓你水平提高一個檔次。而在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.濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認為是垃圾而被回收,這將導致對象狀態的丟失。
適用場景:
單例模式只允許創建一個對象,因此節省內存,加快對象訪問速度,因此對象需要被公用的場合適合使用,如多個模塊使用同一個數據源連接對象等等。如:
- 需要頻繁實例化然后銷毀的對象。
- 創建對象時耗時過多或者耗資源過多,但又經常用到的對象。
- 有狀態的工具類對象。
- 頻繁訪問數據庫或文件的對象。