ActivityThread 中一些常用的知识点记录

5个月前 (12-10) 0 点赞 0 收藏 0 评论 9 已阅读

ActivityThread:
它管理 应用程序进程 中主线程的执行,调度和执行Activity,广播,
以及ActivityManager 请求的其他操作。
简单的说,可以通过这个类,获取到当前应用的一些信息

TIPS:
(1).可以使用以下的开源代码网站查看源码
http://aosp.opersys.com/xref/android-12.0.0_r2/xref/frameworks/base/core/java/android/app/ActivityThread.java
(2).也可以通过AndroidStudio 下载SDK Source code 查看

1 代码目录分析

它在源码的目录是在framework下: /frameworks/base/core/java/android/app/
包名为 android.app;
但它有 @hide 标记,则在app无法直接访问,但是可以通过反射获取(如Hook 技术中常用)

package android.app;

/**
 * This manages the execution of the main thread in an
 * application process, scheduling and executing activities,
 * broadcasts, and other operations on it as the activity
 * manager requests.
 *
 * {@hide}
 */
public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {

2. 获取当前应用 currentApplication()

Application application = ActivityThread.currentApplication();

即可以获取到当前代码运行到的地方,它所属的 应用.
application.mLoadedApk 直接获取到当前 加载的应用对象

3. 获取当前 已加载的应用(LoadedApk) 对象 (getPackageInfo())

3. 1 资源目录成员变量 mResourcePackages

是一个map, 表示1个或者多个路径.
同时,相当于一个缓存, 后续即使App 更改了 LoadedApk里的内容,但是在这里保存的对象可能是不变的.

    @GuardedBy("mResourcesManager")
    @UnsupportedAppUsage
    final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();

PS: 还有另外一个变量mPackages是保存 代码所在的目录
@GuardedBy("mResourcesManager")
@UnsupportedAppUsage
final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();

3. 2 public 方法

在通过API getPackageInfo 获取 LoadedApk ,由几个public 的方法,参数不同处理流程略有差异.
都是不公开的,@UnsupportedAppUsage

    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo, int flags) {}
    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, int flags) {}
    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, int flags, int userId) {    
    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai, CompatibilityInfo compatInfo) {}   

3. 3 private 实现方法

内部的私有方法, 会根据packageName 查询 mResourcePackages 尝试获取 LoadedApk 对象
如果不存在,可能会创建并保存(当前进程 和 传入的 appinfo 的userId不同时除外!!!)
判断userId的关键代码:

 final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));

重点在于是从哪个进程调用这个方法的, 即UserHandle.myUserId() 的值取决于 !!调用者!! 的进程

私有API(1) 仅仅是获取:

    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage, boolean isSdkSandbox, boolean isCallFromReceiver) {
        /* APK_OPTIMIZATION } */
        final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser || isSdkSandbox) {
                // Caching not supported across users and for sdk sandboxes
                ref = null;
            } else if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }   

私有方法API(2): 会进行创建 LoadedApk 并缓存起来 ()
如果缓存中没有,则会根据传入的 appInfo 创建一个新的 loadedApk 对象
当前进程 和 appInfo是同个用户的话, 保存到mResourcePackages

2485    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
2486            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
2487            boolean registerPackage) {
2488        final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
2489        synchronized (mResourcesManager) {
2490            WeakReference<LoadedApk> ref;
2491            if (differentUser) {  ////>>>>>>>>>>>>>>>>>> 检查用户userId
2492                // Caching not supported across users
2493                ref = null;
2494            } else if (includeCode) {
2495                ref = mPackages.get(aInfo.packageName);
2496            } else {
2497                ref = mResourcePackages.get(aInfo.packageName);
2498            }
2499
2500            LoadedApk packageInfo = ref != null ? ref.get() : null;
2501
2502            if (packageInfo != null) {
2503                if (!isLoadedApkResourceDirsUpToDate(packageInfo, aInfo)) {
2504                    List<String> oldPaths = new ArrayList<>();
2505                    LoadedApk.makePaths(this, aInfo, oldPaths);
2506                    packageInfo.updateApplicationInfo(aInfo, oldPaths);
2507                }
2508
2509                return packageInfo; //////>>>>>>>>>>>>>>>>>> 非空则返回
2510            }
2511
2512            if (localLOGV) {
2513                Slog.v(TAG, (includeCode ? "Loading code package "
2514                        : "Loading resource-only package ") + aInfo.packageName
2515                        + " (in " + (mBoundApplication != null
2516                        ? mBoundApplication.processName : null)
2517                        + ")");
2518            }
2519
2520            packageInfo =
2521                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
2522                            securityViolation, includeCode
2523                            && (aInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage); //////>>创建新的
2524
2525            if (mSystemThread && "android".equals(aInfo.packageName)) {
2526                packageInfo.installSystemApplicationInfo(aInfo,
2527                        getSystemContext().mPackageInfo.getClassLoader());
2528            }
2529
2530            if (differentUser) {
2531                // Caching not supported across users
2532            } else if (includeCode) {
2533                mPackages.put(aInfo.packageName,
2534                        new WeakReference<LoadedApk>(packageInfo));
2535            } else {
2536                mResourcePackages.put(aInfo.packageName,
2537                        new WeakReference<LoadedApk>(packageInfo));//////>>做缓存
2538            }
2539
2540            return packageInfo;
2541        }
2542    }

3.4 调用 ActivityThread.getPackageInfo() 的地方:

(1) 通过 ApplicationInfo 创建 Context的时候
Context.createApplicationContext(mApplication, Context.CONTEXT_RESTRICTED);
---> ContextWrapper -> ContextImpl
其中 mApplication 为ApplicationInfo 对象.

//ContextImpl.java
    @Override
    public Context createApplicationContext(ApplicationInfo application, int flags)
            throws NameNotFoundException {
        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE);
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY,
                    mAttributionSource.getAttributionTag(),
                    mAttributionSource.getNext(),
                    null, mToken, new UserHandle(UserHandle.getUserId(application.uid)),
                    flags, null, null);

            final int displayId = getDisplayId();
            final Integer overrideDisplayId = mForceDisplayOverrideInResources
                    ? displayId : null;

            c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
                    getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
            if (c.mResources != null) {
                return c;
            }
        }

        throw new PackageManager.NameNotFoundException(
                "Application package " + application.packageName + " not found");
    }

从上面可以看出,会先通过 ActivityThread.getPackageInfo 获取到 LoadedApk 实例对象.

3.5 拓展

ContextImpl 有几个通过不同参数,获取Context 的方法:
这里关注的是createApplicationContext 和 createPackageContextAsUser,
主要差异在于前者传入 applicationInfo ,而后者是 packageName。
所以,差异会提现在 ActivityThread.getPackageInfo 获取 LoadedApk 对象上,
在根据 packageName查找缓存数组mResourcePackage, 后
如果不存在,前者会根据 applicationInfo 创建对象loadedApk对象, 后者则是通过packageName获取applicationInfo再创建.

//ContextImpl.java 获取Context 的几个方法
  @Override
    public Context createApplicationContext(ApplicationInfo application, int flags)
            throws NameNotFoundException {
        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE);
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, ContextParams.EMPTY,
                    mAttributionSource.getAttributionTag(),
                    mAttributionSource.getNext(),
                    null, mToken, new UserHandle(UserHandle.getUserId(application.uid)),
                    flags, null, null);

            final int displayId = getDisplayId();
            final Integer overrideDisplayId = mForceDisplayOverrideInResources
                    ? displayId : null;

            c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
                    getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
            if (c.mResources != null) {
                return c;
            }
        }

        throw new PackageManager.NameNotFoundException(
                "Application package " + application.packageName + " not found");
    }


    @Override
    public Context createPackageContext(String packageName, int flags)
            throws NameNotFoundException {
        return createPackageContextAsUser(packageName, flags, mUser);
    }

    @Override
    public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
            throws NameNotFoundException {
        if (packageName.equals("system") || packageName.equals("android")) {
            // The system resources are loaded in every application, so we can safely copy
            // the context without reloading Resources.
            return new ContextImpl(this, mMainThread, mPackageInfo, mParams,
                    mAttributionSource.getAttributionTag(),
                    mAttributionSource.getNext(),
                    null, mToken, user, flags, null, null);
        }

        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
        if (pi != null) {
            ContextImpl c = new ContextImpl(this, mMainThread, pi, mParams,
                    mAttributionSource.getAttributionTag(),
                    mAttributionSource.getNext(),
                    null, mToken, user, flags, null, null);

            final int displayId = getDisplayId();
            final Integer overrideDisplayId = mForceDisplayOverrideInResources
                    ? displayId : null;

            c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
                    getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
            if (c.mResources != null) {
                return c;
            }
        }

        // Should be a better exception.
        throw new PackageManager.NameNotFoundException(
                "Application package " + packageName + " not found");
    }

    @Override
    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
        try {
            return createPackageContextAsUser(getPackageName(), flags, user);
        } catch (NameNotFoundException e) {
            throw new IllegalStateException("Own package not found: package=" + getPackageName());
        }
    }

createPackageContextAsUser 涉及在ActivityThread获取applicationInfo 的逻辑如下
(最终还是回归到 包含applicationInfo参数 的 getPackageInfo方法)

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
            int flags) {
        return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
    }

    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
            int flags, int userId) {
        final boolean differentUser = (UserHandle.myUserId() != userId);
        ApplicationInfo ai = PackageManager.getApplicationInfoAsUserCached(
                packageName,
                PackageManager.GET_SHARED_LIBRARY_FILES
                | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                (userId < 0) ? UserHandle.myUserId() : userId);
        synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (differentUser) {
                // Caching not supported across users
                ref = null;
            } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) {
                ref = mPackages.get(packageName);
            } else {
                ref = mResourcePackages.get(packageName);
            }

            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (ai != null && packageInfo != null) {
                if (!isLoadedApkResourceDirsUpToDate(packageInfo, ai)) {
                    List<String> oldPaths = new ArrayList<>();
                    LoadedApk.makePaths(this, ai, oldPaths);
                    packageInfo.updateApplicationInfo(ai, oldPaths);
                }

                if (packageInfo.isSecurityViolation()
                        && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                    throw new SecurityException(
                            "Requesting code from " + packageName
                            + " to be run in process "
                            + mBoundApplication.processName
                            + "/" + mBoundApplication.appInfo.uid);
                }
                return packageInfo;
            }
        }

        if (ai != null) {
            return getPackageInfo(ai, compatInfo, flags);
        }

        return null;
    }

    @UnsupportedAppUsage(trackingBug = 171933273)
    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
            int flags) {
        boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
        boolean securityViolation = includeCode && ai.uid != 0
                && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
                        ? !UserHandle.isSameApp(ai.uid, mBoundApplication.appInfo.uid)
                        : true);
        boolean registerPackage = includeCode && (flags&Context.CONTEXT_REGISTER_PACKAGE) != 0;
        if ((flags&(Context.CONTEXT_INCLUDE_CODE
                |Context.CONTEXT_IGNORE_SECURITY))
                == Context.CONTEXT_INCLUDE_CODE) {
            if (securityViolation) {
                String msg = "Requesting code from " + ai.packageName
                        + " (with uid " + ai.uid + ")";
                if (mBoundApplication != null) {
                    msg = msg + " to be run in process "
                        + mBoundApplication.processName + " (with uid "
                        + mBoundApplication.appInfo.uid + ")";
                }
                throw new SecurityException(msg);
            }
        }
        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
                registerPackage);
    }

4. App 创建 Application对象

(20230322更新)
单进程,创建1个application对象,执行一次onCreate()方法
多进程(N),创建N个application对象,执行N次onCreate()方法

虽然Application的虚拟内存地址相同(打印出来的),但它们的真实物理地址却不同
反射去调用Java的 "sun.misc.Unsafe" 类,获取物理内存地址.

参考>https://juejin.cn/post/7208345469658415159
--- End Now---


ActivityThread 中一些常用的知识点记录

本文收录在
0评论

登录

忘记密码 ?

切换登录

注册