---
title: 'Singleton 單例模式'
disqus: kyleAlien
---
Singleton 單例模式
===
如有引用參考請詳註出處,感謝 :smile:
## Overview of Content
:::success
* 如果喜歡讀更好看一點的網頁版本,可以到我新做的網站 [**DevTech Ascendancy Hub**](https://devtechascendancy.com/)
本篇文章對應的是 [**Singleton 單例模式、設計 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)
:::
[TOC]
## Singleton UML
> ![](https://i.imgur.com/5jzgdit.png)
### Singleton 實做特性
1. construct 必須設定為 **private**,不讓使用者隨意創建目標物件
2. 通過靜態方法、成員、列舉返回單例物件
3. 注意使用場景,**別注意 ==多執行序的安全性== 問題**
4. 確保單例物件在 **==反序列化== 時,不會重新建構物件**
### 單例設計 - 優缺點
* 單例設計優點
* 最大的特點就是 **減少對內存的使用**,避免重複創建
* 可以設定該 APP 進程的全局訪問點
* 單例設計缺點
* **不容易符合開閉原則**:單例要拓展通常需要手動修改,一般來講也不會有 interface
* 建議不要對內部傳入 Context,否則會造成內存無法回收的問題(由於是單例,可達性分析總會分析到 static 仍在使用,導致 GC 無法回收沒有要用的 Activity)
* 建議傳入 Application#context 來使用,因為 Application#context 的生命週期同 App 進程,所以不被回收也沒關係
## 單例實現方法
其特色都有相似的點,目的是為了不要有多個物件,要使用該物件都必須使用同一個物件,在這個條件下在考慮其性能,**而每種實現方式都有不同特色**
### Eager Initialization
* **私有化建構函數,創立私有靜態變數**,取得實例的 Func 靜態化,沒有多執行序問題
| 優點 | 缺點 |
| -------- | -------- |
| 沒有加鎖,效率較高 | 類加載時初始化,稍微浪費內存 |
```java=
class Hungry {
private static Hungry h = new Hungry();
private Hungry() {
}
public static Hungry getInstance() {
return h;
}
public static void say() {
System.out.println("Hello");
}
}
```
這種單例方式比較常用,類加載時就初始化,它是 **基於 `ClassLoader` 機制** 避免了多執行序同步問題(類加載時 static 就會被實例化)
:::warning
* 但是初次呼叫時不一定是呼叫 getInstance 方法,由此可知 **可能會造成在不必要創建時實例時被創建**,可能會浪費資源
```java=
public class Test {
public static void main(String[] args) {
// 呼叫一個靜態方法,仍會創建 Hungry Field
Hungry.say();
}
}
```
:::
**--實作--**
> 創建 20 個任務,確認沒有多執行序問題
>
> ![](https://i.imgur.com/YqTR0Wf.png)
### Lazy Initialization
* `Lazy Initialization` 不初始化靜態變量,避免了不必要的資源浪費
> 這樣會有兩種狀況,執行序安全,執行序不安全
1. **執行序不安全**
```java=
class LazyNoSyn {
private static LazyNoSyn l;
private LazyNoSyn() {
}
public static LazyNoSyn getInstance() {
if(l == null) {
l = new LazyNoSyn();
}
return l;
}
}
```
**--實作--**
> 創建 20 個任務,執行序不安全 問題(看下圖左邊的藍標,明明是單例模式,但是它卻創建了多個物件)
>
> 這個範例並非每次執行都會產生執行序多次創建物件的狀況(多執行序預設是沒有鎖的不安全操作,但是被非每次都會發生),你可以多按執行幾次嘗試看看
>
> ![](https://i.imgur.com/nfyzLEg.png)
2. **執行序安全**
重點多了 `synchronized` 關鍵字來保證多執行序訪問同段代碼的「同步」行為
```java=
class LazySyn {
private static LazySyn l;
private LazySyn() {
}
public static synchronized LazySyn getInstance() {
if(l == null) {
l = new LazySyn();
}
return l;
}
}
```
**--實作--**
> 創建 20 個任務,`執行序安全`
>
> ![](https://i.imgur.com/aefkhKY.png)
| 優點 | 缺點 |
| -------- | -------- |
| 延遲加載,在需要時才載入 | 類使用同步加鎖機制又會增加負載,每次呼叫 getInstance 都會進行一次同步動作,會造成不必要的開銷 |
### 雙鎖 DCL (Double Check Lock)
* 雙鎖機制是優化 `Lazy Initialization` ,保證執行序安全
```java=
class DCL {
private volatile static DCL d; // 4
private DCL() { }
public static DCL getInstance() {
if(d == null) { // 1
synchronized(DCL.class) { // 3
if(d == null) { // 2
d = new DCL();
}
}
}
return d;
}
}
```
1. 第一個 Check:**避免不必要的 `synchronized` 同步**,優化開銷
2. 第二次 Check:第一次加載才創建,第二次判斷 null **是為了避免並發問題** (可能有多個 thread 進入上一個判斷)
3. **區塊同步**:使用 class 類同步 (Java 加載時 class 時會在 class 後創建一個不變的模板),或是使用一個靜態物件同步也可以
4. **`volatile` 關鍵字**:我們來看看 JVM 是如何創建一個物件實例的(假設類已經被 JVM 加載完畢)
> `new DCL()` 看起來是一句程式,但實際上**它並不是一個原子操作**,它做了以下三件事
> > 1. 初始化記憶體:給 DCL 的實例分配記憶體
> > 2. 初始化物件:呼叫 DCL 的建構函數,初始化成員元素 (Field, Static Field)
> > 3. 賦予 Field:將 DCL 物件指向分配的記憶體空間 (給予它內存 Addr 此時 DCL's d Field 不再是 null)
>
> **Java 編譯器允許 CPU 失序執行**,也就是上面執行的順序可能是 `1-2-3` or `1-3-2`,如果是 `1-3-2` 這種狀況很難追蹤,已經分配記憶體空見卻未創建 !
> 這時可以使用 **==volatile 關鍵字==**
> ```java=
> private volatile static DCL d;
> ```
> `volatile` 也加減會影響效能(`volatile 修飾語每次更改時都會即時存入記憶體`,在 Android 執行序會詳說)
**--實作--**
> 創建 20 個任務,`執行序安全`
>
> ![](https://i.imgur.com/ZswvBYr.png)
| 優點 | 缺點 |
| -------- | -------- |
| 確保多執行序的單例化 | 第一次加載較慢,在高併發的環境下可能失敗(由於 JMM 模型) |
:::warning
* JDK 1.5 以後才具體實現 volatile 關鍵字
:::
### 靜態內部類
* 在某些情況下 DCL 會失效(高並發時),`Java 並發編程實踐` 也有提及這種 DCL 最佳化是醜陋的,建議使用靜態內部類
```java=
class StaticInnerClass {
private StaticInnerClass() {
}
public static StaticInnerClass getInstance() {
return innerClass.sic;
}
private static class innerClass {
private static final StaticInnerClass sic = new StaticInnerClass();
}
}
```
這同餓漢式是使用 **classLoader 加載機制**,如果未使用內部類,那該 class 就不會被加載,它的 Field 也不會被創建,可以達到 **延遲加載機制**
**--實作--**
> 創建 20 個任務,`執行序安全`
>
> ![](https://i.imgur.com/HyUDyER.png)
### enum 列舉單例
* enum 類舉在 Java 中與普通類別是一樣的,可以有自己的方法,並在`任何情況下都是單例` (透過反編譯 Class 可以看出 enum 成員各個都是一個列別)
```java=
public enum SingletonEnum {
INSTANCE;
public void toDo() {
System.out.println("to Do Something");
}
}
```
**--實作--**
> ![](https://i.imgur.com/I9frziR.png)
* 可以透過 `javac` 自行編譯,在透過 `javap` 查看 enum 成員的實現方式,可以發現每個 Enum 成員都是 SingletonEnum 的實現物件 (static final)!
```java=
public enum SingletonEnum {
INSTANCE,
HELLO,
WORLD;
public void toDo() {
System.out.println("to Do Something");
}
}
```
> ![](https://i.imgur.com/aneybTL.png)
### 單例 - 反序列化
* 注意,在一種情況下會出現重新建構物件的情況,就是**反序列化**,每次反序列化都會導致物件重新被創建
* 反序列化有一個特別的`鉤子函數 readResolve` ,只要寫這個方法並返回被實例化的物件就不會創建出新物件
```java=
private Object readResolve() throws ObjectStreamException {
return ...;
}
```
* 以下為序列化 TestClass 與反序列化 TestClass
```java=
public class Main {
public static void main(String[] args) {
makeFile();
readFile();
}
private static void readFile() {
File in = new File("/home/alienpan/IdeaProjects/algorithm_ds/src/main/java/oop/singleton/serializable/SerialFile");
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(in))) {
System.out.println("readFile: " + objectInputStream.readObject().hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void makeFile() {
File out = new File("/home/alienpan/IdeaProjects/algorithm_ds/src/main/java/oop/singleton/serializable/SerialFile");
if(out.exists() && out.delete()) {
System.out.println("Delete file");
}
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(out))) {
TestClass test = TestClass.getInstance();
objectOutputStream.writeObject(test);
objectOutputStream.flush();
System.out.println("makeFile: " + test.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
```
1. 首先測試尚未使用 `readResolve` 方法
```java=
public class TestClass implements Serializable {
private static final TestClass instance = new TestClass();
private TestClass() {
}
public static TestClass getInstance() {
return instance;
}
}
```
透過 `hashCode` 可以看到序列化、反序列化時兩者是 **不同物件**
> ![](https://i.imgur.com/z8b6giy.png)
:::success
* 目前使用靜態創建加載(Lazy Initialize),相同的你使用在 DCL 也一樣,序列化、反序列化時兩者是 **不同物件**
:::
2. 使用 `readResolve` 方法,並返回相同物件
```java=
public class TestClass implements Serializable {
private static final TestClass instance = new TestClass();
private TestClass() {
}
public static TestClass getInstance() {
return instance;
}
@Serial
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
```
透過 `hashCode` 可以看到序列化、反序列化時兩者是 **相同物件**
> ![](https://i.imgur.com/fPJBZqb.png)
:::info
* enum 宣告則很特別,就算不用添加 `readResolve` 方法一樣是可以達到 序列化、反序列化時兩者是 **同物件**
:::
```java=
public enum TestClass {
INSTANCE;
public static TestClass getInstance() {
return INSTANCE;
}
}
```
## Android Source
以下是一段很常見到的 ListView 加載方式,並使用了簡單的 set/getTag 去緩存資源來達到快速加載,這個優化並不是當前文章的主要內容,所以不去細說
### 取得 LayoutInflater
* 最常使用在 ListView 的 BaseAdapter 做出 Layout 界面加載,**這次主要關注在加載圖片時的 ++LayoutInflater.from(Context)++** 方法
```java=
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = findViewById(R.id.listView);
MyBaseAdapter adapter = new MyBaseAdapter(new String[] {"Apple", "Box", "Car", "Drop"}, this);
listView.setAdapter(adapter);
}
}
class MyBaseAdapter extends BaseAdapter {
private String[] strings;
private Context context;
MyBaseAdapter(String[] strings, Context context) {
this.strings = strings;
this.context = context;
}
@Override
public int getCount() {
return strings.length;
}
@Override
public Object getItem(int position) {
return strings[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if(convertView == null) {
// @ 分析 LayoutInflater.from 方法
convertView = LayoutInflater.from(context).inflate(R.layout.my_item, null);
holder = new ViewHolder();
holder.textView = convertView.findViewById(R.id.MyTitle);
holder.imageView = convertView.findViewById(R.id.MyImage);
// 設定緩存儲存,設定一個 Tag 方便下次取出 ViewHolder
convertView.setTag(holder);
} else {
// 使用之前設定的 Tag 取出已有的 ViewHolder
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(strings[position]);
return convertView;
}
private static class ViewHolder {
ImageView imageView;
TextView textView;
}
}
```
* LayoutInflater 的靜態 from 方法:它內部使用 context 抽象方法 getSystemService (Context 本身也是抽象類)
```java=
// LayoutInflater.java
public static LayoutInflater from(Context context) {
// 內部透過 Context#getSystemService 方法取得 LayoutInflater
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
// --------------------------------------------------------------------
// Context.java
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
```
### Context 包裝 Activity
* 這裡要從 App 程式的 main 入口開始分析,**分析的過程可以得到 Context 是如何去註冊服務的**
| 類 | 關注功能 |
| -------- | -------- |
| [**ActivityThread**](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java?q=ActivityThread.java).java | 內部有一個 main 方法,是 Java 程式的起點方法 |
| ApplicationThread.java | 屬於 ActivityThread 的內部類,是該進程與外部進程通訊的橋樑 |
```java=
// ActivityThread.class
public class ActivityThread {
final ApplicationThread mAppThread = new ApplicationThread();
...
// 目前 system 是 false
private void attach(boolean system, long startSeq) {
// sCurrentActivityThread 使用 volatile 修飾
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
// 取得與 Binder 通訊的代理
// 這裡由 AIDL 實現
final IActivityManager mgr = ActivityManager.getService();
try {
// @ 追蹤 attachApplication
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
} else {
... SystemSerivce 才會走到這
}
...
}
public static void main(String[] args) {
...
// "1. "
// 準備主執行序的 Looper (建構)
Looper.prepareMainLooper();
// 創建 ActivityThread 物件
ActivityThread thread = new ActivityThread();
// 非系統載入
thread.attach(false);
if (sMainThreadHandler == null) {
// 取得該 class 的 Handler
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop(); // 啟動 Looper
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
```
1. 這段程式可以看出 Android 的消息驅動是透過 Looper 推動 (要先了解 [**Handler 機制**](https://hackmd.io/7fBX6uEtQt6AzCpuBWTHMQ?view))
2. [**IActivityManager**](https://cs.android.com/android/platform/superproject/+/master:out/soong/.intermediates/frameworks/base/framework-minus-apex-intdefs/android_common/xref33/srcjars.xref/android/app/IActivityManager.java)#`attachApplication` 會觸及 Binder 機制,這裡我們當作一個黑盒子 (略過與 AMS 進程通訊),而 `attachApplication` 函數最終會回到該進程中的 ActivityThread#`handleLaunchActivity` 方法
```java=
// ActivityThread.class
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
... 省略部份
// @ 追蹤 performLaunchActivity
final Activity a = performLaunchActivity(r, customIntent);
...
}
```
* ActivityThreadperformLaunchActivity:準備啟動 Activity
1. 反射創建 **Activity**、對應的 Context 類 (Context 實做類是 ContextImpl 物件)
```java=
// ActivityThread.class
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
... 省略部份
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 透過 Instrumentation 創建 Actiivty
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
} /* 省略 catch */
...
}
private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
...
ContextImpl appContext = ContextImpl.createActivityContext(
this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
... 省略部份
return appContext;
}
// ----------------------------------------------------------------
// ContextImpl.java
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
...
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, ContextParams.EMPTY,
attributionTag, null, activityInfo.splitName, activityToken, null, 0, classLoader,
null);
...
return context;
}
```
2. 創建 **Application** 類,並讓 Context 將 Activity 包裝起來,之後在呼叫 Activity#attach 方法
```java=
// ActivityThread.class
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
... 省略部份
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
...
// Context 內包裝 Activity
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken, r.shareableActivityToken);
...
// 透過 Instrumentation 呼叫 onCreate 方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
}
} /* 省略 catch */
...
}
```
> ![](https://i.imgur.com/NaaZUyp.png)
:::info
* 從這裡我們可以知道
1. Context 的實做類是 `ContextImpl` (接下來我們會繼續分析)
2. Activity 的生命週期大部分可以透過 Instrumentation Hook 改寫,因為 Activity 的控制大分份也都通過 **Instrumentation**
:::
### [ContextImpl](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ContextImpl.java) 分析 - [SystemServiceRegistry](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/SystemServiceRegistry.java) 單例創建 PhoneLayoutInflater
* 我們了解到 Activity 是由 Context 來包裝,而 Context 的實做類是 ContextImpl 類,現在我們來看看 ContextImpl 類
1. 複習回憶:現在的目的是 **了解 LayoutInflater 這個服務 ++註冊時機++**
```java=
// LayoutInflater.java
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
// --------------------------------------------------------------------
// ContextImpl.java
@Override
public Object getSystemService(String name) {
// @ 分析 SystemServiceRegistry 物件
return SystemServiceRegistry.getSystemService(this, name);
}
```
> ![](https://i.imgur.com/eKi7OXD.png)
2. 分析 SystemServiceRegistry:SystemServiceRegistry 類是一個 **單例物件**(符合當前主題)
* 在 SystemServiceRegistry 類加載時會先加載 static Field,包含 static 區塊
* 每個 registerService 實做 ServiceFetcher#createService 方法,也就是說 **==LayoutInflater 的實現物件是 PhoneLayoutInflater 類==**
```java=
// SystemServiceRegistry.java
// Activity 可以透過 Context#getSystemService 獲取服務
final class SystemServiceRegistry {
private SystemServiceRegistry() { } // 私有化 - 建構函數
static {
... 省略其他服務
// @ 追蹤 registerService
registerService(
// 服務名
Context.LAYOUT_INFLATER_SERVICE,
// Class 類
LayoutInflater.class,
// @ 追蹤 CachedServiceFetcher
new CachedServiceFetcher<LayoutInflater>() {
// 實做 createService 方法
@Override
public LayoutInflater createService(ContextImpl ctx) {
// 服務真正實現類
return new PhoneLayoutInflater(ctx.getOuterContext());
}}
);
}
// 儲存在兩個 Map 中,這個函數只能使用在初始化
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
// 各種 Map 緩存
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
// 服務 Map
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
}
// ContextImpl 呼叫
// 透過緩存 SYSTEM_SERVICE_FETCHERS 找到對應的服務
public static Object getSystemService(ContextImpl ctx, String name) {
if (name == null) {
return null;
}
final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
if (fetcher == null) {
... 無法取得對應緩存
return null;
}
// 呼叫實做 ServiceFetcher 的類 getService 方法
final Object ret = fetcher.getService(ctx);
...
return ret;
}
}
```
3. CachedServiceFetcher:功能有兩個
* 懶加載 Service 物件,在需要時才會呼叫 createService 方法創建
* 多 Thread 要求 Service 時會讓第一個 thread 創建,其他 thread 休眠(wait)
```java=
// SystemServiceRegistry.java
// Activity 可以透過 Context#getSystemService 獲取服務
final class SystemServiceRegistry {
private static int sServiceCacheSize;
// 匿名靜態內部類
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
// 緩存的 index
private final int mCacheIndex;
public CachedServiceFetcher() {
// 屬性 sServiceCacheSize 是靜態變數
mCacheIndex = sServiceCacheSize++;
}
@Override
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
final int[] gates = ctx.mServiceInitializationStateArray;
boolean interrupted = false;
T ret = null;
for (;;) {
boolean doInitialize = false;
// 同步鎖
synchronized (cache) {
// ContextImpl 內有緩存就直接返回
T service = (T) cache[mCacheIndex];
if (service != null) {
ret = service;
break; // exit the for (;;)
}
// 如果是 `STATE_READY` or `STATE_NOT_FOUND`
// 則定義 ContextImpl 中 Service 尚未初始化
if (gates[mCacheIndex] == ContextImpl.STATE_READY
|| gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED;
}
// 如果有多的 thread 到這一步,必須確保第一個 Thread 呼叫 createService()
// 所以必須定義 STATE_UNINITIALIZED 為 STATE_INITIALIZING 狀態
if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) {
doInitialize = true;
gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING;
}
}
// 只有第一個 thread 才會執行以下 if 條件
if (doInitialize) {
T service = null;
@ServiceInitializationState
int newState = ContextImpl.STATE_NOT_FOUND;
try {
// 呼叫 createService 方法(每個 Service 實做類)
service = createService(ctx);
newState = ContextImpl.STATE_READY;
} catch (ServiceNotFoundException e) {
onServiceNotFound(e);
} finally {
// 最終會呼叫所有等待的 thread 醒來
synchronized (cache) {
cache[mCacheIndex] = service;
gates[mCacheIndex] = newState;
cache.notifyAll();
}
}
ret = service;
break; // exit the for (;;)
}
synchronized (cache) {
// 所有不是第一個 thread 都會進入這
while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
try {
// Clear the interrupt state.
interrupted |= Thread.interrupted();
cache.wait();
} catch (InterruptedException e) {
// Thread 被中斷
interrupted = true;
}
}
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
return ret;
}
public abstract T createService(ContextImpl ctx);
}
}
```
* 在這其中也可以發現 依賴倒置的關係,高層模(SystemServiceRegistry)組依賴抽象(ServiceFetcher),而不是細節(CachedServiceFetcher、StaticServiceFetcher)
> ![](https://i.imgur.com/xYSvmkW.png)
### ContextImpl 與 SystemServiceRegistry 關係
* ContextImpl 本身並不是單例(可以有多個),但是**其==服務就是單例==**,呼應上面所 `LayoutInflater.from()` 所呼叫的函數,Context.getSystemService 其 context 透過上面的追蹤可以知道實做類就是 ContextImpl 類
* 從這邊我們可以看到 SystemSerivceRegistry 的單例是**使用 static 靜態區塊加載 (也稱為餓漢式)**,代表所有的服務在 SystemSerivceRegistry 這個類加載的時候就已經註冊完畢
:::warning
雖說是靜態加載,但是也只加載了服務的數量,並未真正創建,**所以應該是說 ++懶加載 + 延遲加載++**
> 靜態加載 -> 定義出總數量,以及其 index 位置
>
> 延遲加載 -> 真正的服務物件
:::
* 特別說說 ContextImpl 中的 `getSystemService` 函數,它把 this 作為參數傳入,並且可以看到
:::success
1. 一個 Activity 有一個 ContextImpl 物件
2. 在 SystemServiceRegistry.getSystemService 中透過傳入的 Context 物件取得服務的物件,若沒有該物件則**使用當前 context 創建物件**
:::
結論: 可以透過當前的 Context 創建服務,若尚未使用到該服務則不加載
:::danger
* 這裡的服務並非進程間的服務(並非 AMS、WMS、PKMS... 等等)
:::
> ![](https://i.imgur.com/FNPWDX0.png)
## 更多的物件導向設計
物件導向的設計基礎如下,如果是初學者或是不熟悉的各位,建議可以從這些基礎開始認識,打好基底才能走個更穩(在學習的時候也需要不斷回頭看)!
:::info
* [**設計建模 2 大概念- UML 分類、使用**](https://devtechascendancy.com/introduction-to-uml-and-diagrams/)
* [**物件導向設計原則 – 6 大原則(一)**](https://devtechascendancy.com/object-oriented-design-principles_1/)
* [**物件導向設計原則 – 6 大原則(二)**](https://devtechascendancy.com/object-oriented-design-principles_2/)
:::
### 創建模式 - Creation Patterns
* [**創建模式 PK**](https://devtechascendancy.com/pk-design-patterns-factory-builder-best/)
* **創建模式 - `Creation Patterns`**:
創建模式用於「**物件的創建**」,它關注於如何更靈活、更有效地創建物件。這些模式可以隱藏創建物件的細節,並提供創建物件的機制,例如單例模式、工廠模式… 等等,詳細解說請點擊以下連結
:::success
* [**Singleton 單例模式 | 解說實現 | Android Framework Context Service**](https://devtechascendancy.com/object-oriented_design_singleton/)
* [**Abstract Factory 設計模式 | 實現解說 | Android MediaPlayer**](https://devtechascendancy.com/object-oriented_design_abstract-factory/)
* [**Factory 工廠方法模式 | 解說實現 | Java 集合設計**](https://devtechascendancy.com/object-oriented_design_factory_framework/)
* [**Builder 建構者模式 | 實現與解說 | Android Framwrok Dialog 視窗**](https://devtechascendancy.com/object-oriented_design_builder_dialog/)
* [**Clone 原型模式 | 解說實現 | Android Framework Intent**](https://devtechascendancy.com/object-oriented_design_clone_framework/)
* [**Object Pool 設計模式 | 實現與解說 | 利用 JVM**](https://devtechascendancy.com/object-oriented_design_object-pool/)
* [**Flyweight 享元模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_flyweight/)
:::
### 行為模式 - Behavioral Patterns
* [**行為模式 PK**](https://devtechascendancy.com/pk-design-patterns-cmd-strat-state-obs-chain/)
* **行為模式 - `Behavioral Patterns`**:
行為模式關注物件之間的「**通信**」和「**職責分配**」。它們描述了一系列物件如何協作,以完成特定任務。這些模式專注於改進物件之間的通信,從而提高系統的靈活性。例如,策略模式、觀察者模式… 等等,詳細解說請點擊以下連結
:::warning
* [**Stragety 策略模式 | 解說實現 | Android Framework 動畫**](https://devtechascendancy.com/object-oriented_design_stragety_framework/)
* [**Interpreter 解譯器模式 | 解說實現 | Android Framework PackageManagerService**](https://devtechascendancy.com/object-oriented_design_interpreter_framework/)
* [**Chain 責任鏈模式 | 解說實現 | Android Framework View 事件傳遞**](https://devtechascendancy.com/object-oriented_design_chain_framework/)
* [**State 狀態模式 | 實現解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_state/)
* [**Specification 規格模式 | 解說實現 | Query 語句實做**](https://devtechascendancy.com/object-oriented_design_specification-query/)
* [**Command 命令、Servant 雇工模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_command_servant/)
* [**Memo 備忘錄模式 | 實現與解說 | Android Framwrok Activity 保存**](https://devtechascendancy.com/object-oriented_design_memo_framework/)
* [**Visitor 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_visitor_dispatch/)
* [**Template 設計模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_template/)
* [**Mediator 模式設計 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_mediator/)
* [**Composite 組合模式 | 實現與解說 | 物件導向設計**](https://devtechascendancy.com/object-oriented_programming_composite/)
:::
### 結構模式 - Structural Patterns
* [**結構模式 PK**](https://devtechascendancy.com/pk-design-patterns-proxy-decorate-adapter/)
* **結構模式 - `Structural Patterns`**:
結構模式專注於「物件之間的組成」,以形成更大的結構。這些模式可以幫助你確保當系統進行擴展或修改時,不會破壞其整體結構。例如,外觀模式、代理模式… 等等,詳細解說請點擊以下連結
:::danger
* [**Bridge 橋接模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_bridge/)
* [**Decorate 裝飾模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_decorate/)
* [**Proxy 代理模式 | 解說實現 | 分析動態代理**](https://devtechascendancy.com/object-oriented_design_proxy_dynamic-proxy/)
* [**Iterator 迭代設計 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_iterator/)
* [**Facade 外觀、門面模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_facade/)
* [**Adapter 設計模式 | 解說實現 | 物件導向設計**](https://devtechascendancy.com/object-oriented_design_adapter/)
:::
## Appendix & FAQ
:::info
:::
###### tags: `Java 設計模式` `基礎進階` `Android Framework`