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