【Android】源码中的单例模式

本文是基于 Android 14 的源码解析

在 Android 系统中,我们经常会通过 Context 获取系统级别的服务,如 WindowsManagerService、ActivityManagerService 等,更常用的是一个 LayoutInflater 的类,这些服务会在合适的时候以单例的形式注册在系统中,在我们需要的时候就通过 Context 的 getSystemService(String name) 获取。

我们以 LayoutInflater 为例来说明,平时我们使用 LayoutInflater 较为常见的地方是在 RecyclerView 的 onCreateViewHolder 方法中:

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TestViewHolder {
    return TestViewHolder(
        ListItemTestBinding.inflate(
            LayoutInflater.from(parent.context), parent, false
        )
    )
}

通常我们使用 LayoutInflater.from(Context context) 来获取 LayoutInflater 服务,下面看看源码实现:

public static LayoutInflater from(@UiContext Context context) {
    LayoutInflater LayoutInflater =
            (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    if (LayoutInflater == null) {
        throw new AssertionError("LayoutInflater not found.");
    }
    return LayoutInflater;
}

可以看到 from(Context context) 函数内部调用的是 Context 类的 getSystemService(String name) 方法,我们跟踪到 Context 类看到,该类是抽象类:

public abstract class Context {
    ...
}

onCreateViewHolder 中使用的 Context 对象的具体实现类是什么呢?其实在 Application、Activity、Service 中都会存在一个 Context 对象,即 Context 的总个数为 Activity 个数 + Service 个数 + 1。而 RecyclerView 通常都是显示在 Activity 中,那么我们就以 Activity 中的 Context 来分析。

我们知道,一个 Activity 的入口是 ActivityThread 的 main 函数,在 main 函数中创建一个新的 ActivityThread 对象,并且启动消息循环(UI线程),创建新的 Activity、新的 Context 对象,然后将该 Context 对象传递给 Activity。下面看看 ActivityThread 源代码:

// 源码路径:/frameworks/base/core/java/android/app/ActivityThread.java

public static void main(String[] args) {
    ...

    // 主线程消息循环
    Looper.prepareMainLooper();

    ...

    // 创建ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    ...

    Looper.loop();

    ...
}
// 源码路径:/frameworks/base/core/java/android/app/ActivityThread.java

private void attach(boolean system, long startSeq) {
    ...

    // 不是系统应用的情况
    if (!system) {
        android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                UserHandle.myUserId());
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        final IActivityManager mgr = ActivityManager.getService();
        try {
            // 关联 mAppThread
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            ...
        }
        
        ...
    } else {
        ...
    }

    ...
}

在 main 方法中,创建一个 ActivityThread 对象后,调用了其 attach 函数,并且参数为 false。在 attach 函数中,参数为 false 的情况下(即非系统应用),会通过 Binder 机制与 ActivityManager Service 通信,并且最终调用 handleLaunchActivity 函数,我们看看该函数的实现:

// 源码路径:/frameworks/base/core/java/android/app/ActivityThread.java

public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, int deviceId, Intent customIntent) {
    ...

    final Activity a = performLaunchActivity(r, customIntent);

    ...
}
// 源码路径:/frameworks/base/core/java/android/app/ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 创建Activity
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);

        ...
    } catch (Exception e) {
        ...
    }

    try {
        // 创建 Application 对象
        Application app = r.packageInfo.makeApplicationInner(false, mInstrumentation);

        ...

        if (activity != null) {
            // 获取 Context 对象
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config =
                    new Configuration(mConfigurationController.getCompatConfiguration());
            
            ...

            // 将 appContext 等对象 attach 到 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.activityConfigCallback,
                    r.assistToken, r.shareableActivityToken);

            ...
            
            if (r.isPersistable()) {
                ...
            } else {
                // 调用 Activity 的 onCreate 方法
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }

            ...
        }
        r.setState(ON_CREATE);

    } catch (SuperNotCalledException e) {
        ...
    } catch (Exception e) {
        ...
    }

    return activity;
}
// 源码路径:/frameworks/base/core/java/android/app/ActivityThread.java

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
    final int displayId = ActivityClient.getInstance().getDisplayId(r.token);
    // 创建 Context 对象, 可以看到实现类是 ContextImpl
    ContextImpl appContext = ContextImpl.createActivityContext(
            this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

    final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
    String pkgName = SystemProperties.get("debug.second-display.pkg");
    if (pkgName != null && !pkgName.isEmpty()
            && r.packageInfo.mPackageName.contains(pkgName)) {
        for (int id : dm.getDisplayIds()) {
            if (id != DEFAULT_DISPLAY) {
                Display display =
                        dm.getCompatibleDisplay(id, appContext.getResources());
                appContext = (ContextImpl) appContext.createDisplayContext(display);
                break;
            }
        }
    }
    return appContext;
}

通过上面的代码分析可以知道,Context 的实现类为 ComtextImpl。我们继续跟踪 ContextImpl 类:

// 源码路径:/frameworks/base/core/java/android/app/ContextImpl.java

class ContextImpl extends Context {

    ...

    @UnsupportedAppUsage
    final Object[] mServiceCache = SystemServiceRegistry.createServiceCache();

    ...

    @Override
    public Object getSystemService(String name) {
        ...

        return SystemServiceRegistry.getSystemService(this, name);
    }

    ...
}
// 源码路径:/frameworks/base/core/java/android/app/SystemServiceRegistry.java

public final class SystemServiceRegistry {
    ...

    // Service 容器
    private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new ArrayMap<String, ServiceFetcher<?>>();
    private static int sServiceCacheSize;

    ...

    // 静态语句块,第—次加载该类时执行(只执行—次,保证实例的唯一性)
    static {
        registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
                new CachedServiceFetcher<LayoutInflater>() {
            @Override
            public LayoutInflater createService(ContextImpl ctx) {
                return new PhoneLayoutInflater(ctx.getOuterContext());
            }});
    }

    ...

    public static Object[] createServiceCache() {
        return new Object[sServiceCacheSize];
    }

    // 根据 key 获取对应的服务
    public static Object getSystemService(ContextImpl ctx, String name) {
        ...
        
        final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        
        ...

        final Object ret = fetcher.getService(ctx);
        
        ...

        return ret;
    }

    // 注册 Service
    private static <T> void registerService(@NonNull String serviceName,
            @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
        ...

        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
        
        ...
    }

    ...
}

从 ContextImpl 类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种 ServiceFetcher,其中就包含了 LayoutInflater Service。将这些服务以键值对的形式存储在一个 Map 中,用户使用时只需要根据 key 来获取到对应的 ServiceFetcher,然后通过 ServiceFetcher 对象的 getService 函数来获取具体的服务对象。当第一次获取时,会调用 ServiceFetcher 的 createService 函数创建服务对象,然后将该对象缓存到一个列表中,下次再取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴同学是个程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值