MainThreadInitializedObject 是 Android 開發中用于確保對象在主線程上初始化的一種設計模式
一、用途
MainThreadInitializedObject 通常用于確保那些需要在主線程上創建的對象(比如UI組件或依賴于主線程環境的對象)能夠安全地進行初始化
二、優點
- 可以避免多線程環境下的同步問題,因為它限定了對象的初始化必須在主線程上完成。
- 實現了單例和懶加載,適用于那些需要訪問UI組件或者依賴于應用程序上下文的單例對象,如全局配置管理器或資源管理器
三、使用
MainThreadInitializedObject 提供了一個 get(Context context) 方法,用于獲取單例對象的實例。如果實例尚未創建,它將在調用線程上初始化,確保對象的創建發生在主線程上
以Launcher中的DisplayController為例:
初始化:
public static final MainThreadInitializedObject<DisplayController> INSTANCE =new MainThreadInitializedObject<>(DisplayController::new);
獲取對象:
/*** Returns the current navigation mode*/public static NavigationMode getNavigationMode(Context context) {return INSTANCE.get(context).getInfo().navigationMode;}
通過get方法獲取對象
四、實現
初始化
public MainThreadInitializedObject(ObjectProvider<T> provider) {mProvider = provider;}
初始化傳入一個ObjectProvider,ObjectProvider是一個內部接口,里面只有一個get方法
public interface ObjectProvider<T> {T get(Context context);}
獲取對象
public T get(Context context) {if (context instanceof SandboxContext) {return ((SandboxContext) context).getObject(this, mProvider);}if (mValue == null) {if (Looper.myLooper() == Looper.getMainLooper()) {mValue = TraceHelper.allowIpcs("main.thread.object",() -> mProvider.get(context.getApplicationContext()));} else {try {return MAIN_EXECUTOR.submit(() -> get(context)).get();} catch (InterruptedException|ExecutionException e) {throw new RuntimeException(e);}}}return mValue;}
get會先檢查context對象是否屬于SandboxContext,SandboxContext是一個用于管理和控制在沙盒環境中創建和銷毀的對象的抽象類。它提供了對象的緩存、創建和銷毀機制,確保對象的生命周期和線程安全性。
public static abstract class SandboxContext extends ContextWrapper {private static final String TAG = "SandboxContext";protected final Set<MainThreadInitializedObject> mAllowedObjects;protected final Map<MainThreadInitializedObject, Object> mObjectMap = new HashMap<>();protected final ArrayList<Object> mOrderedObjects = new ArrayList<>();private final Object mDestroyLock = new Object();private boolean mDestroyed = false;public SandboxContext(Context base, MainThreadInitializedObject... allowedObjects) {super(base);mAllowedObjects = new HashSet<>(Arrays.asList(allowedObjects));}.../*** Find a cached object from mObjectMap if we have already created one. If not, generate* an object using the provider.*/private <T> T getObject(MainThreadInitializedObject<T> object, ObjectProvider<T> provider) {synchronized (mDestroyLock) {if (mDestroyed) {Log.e(TAG, "Static object access with a destroyed context");}T t = (T) mObjectMap.get(object);if (t != null) {return t;}if (Looper.myLooper() == Looper.getMainLooper()) {t = createObject(provider);// Check if we've explicitly allowed the object or if it's a SafeCloseable,// it will get destroyed in onDestroy()if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {throw new IllegalStateException("Leaking unknown objects " + object + " " + provider + " " + t);}mObjectMap.put(object, t);mOrderedObjects.add(t);return t;}}try {return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get();} catch (InterruptedException | ExecutionException e) {throw new RuntimeException(e);}}@UiThreadprotected <T> T createObject(ObjectProvider<T> provider) {return provider.get(this);}}
在SandboxContext和MainThreadInitializedObject中獲取對象的方式相似,如果已經有了就直接獲取,沒有就通過MAIN_EXECUTOR在主線程上創建對象。MAIN_EXECUTOR是一個主線程的LooperExecutor,LooperExecutor繼承自AbstractExecutorService,最終會在主線程執行ObjectProvider的get方法,也就是創建所需的對象,然后通過LooperExecutor的get方法獲取任務執行的結果
public class LooperExecutor extends AbstractExecutorService {private final Handler mHandler;...@Overridepublic void execute(Runnable runnable) {if (getHandler().getLooper() == Looper.myLooper()) {runnable.run();} else {getHandler().post(runnable);}}
}
五、總結
MainThreadInitializedObject 在Android開發中扮演著重要的角色,特別是在需要確保對象在主線程上安全初始化的場景中。它提供了一種可靠的方式來管理單例對象的生命周期,同時避免了多線程環境下的潛在問題。