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

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---

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容