热点新闻
ActivityThread 中一些常用的知识点记录
2023-07-08 22:58  浏览:4094  搜索引擎搜索“混灰机械网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在混灰机械网看到的信息,谢谢。
展会发布 展会网站大全 报名观展合作 软文发布

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; 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) { 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---

发布人:d678****    IP:117.173.23.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发