framework
PackageManageService
在开机的时候,会先去扫瞄系统目录下的apk,再去扫瞄普通apk,解析他们的清单信息吧他们加载到内存里,之前我一直以为,解析完后会吧对应的apk的清单信息,保存在数据库或者xml里
方便下次读取,现在看起来不是,比如我们push个apk到system/app,他下次开机就有了,扫瞄只有开机会扫,每次都是全部解析
解析apk的类为PackageParse2
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
''''''
ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
''''''
}
进入ParsingPackageUtils,这里可以看到获取assetmanager 以及resource通过assetmanager初始化的过程
private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
String codePath, SplitAssetLoader assetLoader, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final AssetManager assets = assetLoader.getBaseAssetManager();
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
return input.error(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Failed adding asset path: " + apkPath);
}
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
parser, flags);
if (result.isError()) {
return input.error(result.getErrorCode(),
apkPath + " (at " + parser.getPositionDescription() + "): "
+ result.getErrorMessage());
}
final ParsingPackage pkg = result.getResult();
if (assets.containsAllocatedTable()) {
final ParseResult<?> deferResult = input.deferError(
"Targeting R+ (version " + Build.VERSION_CODES.R + " and above) requires"
+ " the resources.arsc of installed APKs to be stored uncompressed"
+ " and aligned on a 4-byte boundary",
DeferredError.RESOURCES_ARSC_COMPRESSED);
if (deferResult.isError()) {
return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
deferResult.getErrorMessage());
}
}
ApkAssets apkAssets = assetLoader.getBaseApkAssets();
boolean definesOverlayable = false;
try {
definesOverlayable = apkAssets.definesOverlayable();
} catch (IOException ignored) {
// Will fail if there's no packages in the ApkAssets, which can be treated as false
}
if (definesOverlayable) {
SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
int size = packageNames.size();
for (int index = 0; index < size; index++) {
String packageName = packageNames.valueAt(index);
Map<String, String> overlayableToActor = assets.getOverlayableMap(packageName);
if (overlayableToActor != null && !overlayableToActor.isEmpty()) {
for (String overlayable : overlayableToActor.keySet()) {
pkg.addOverlayable(overlayable, overlayableToActor.get(overlayable));
}
}
}
}
pkg.setVolumeUuid(volumeUuid);
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
pkg.setSigningDetails(getSigningDetails(pkg, false));
} else {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
}
return input.success(pkg);
} catch (Exception e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to read manifest from " + apkPath, e);
}
}
获取am开始解析application的流程,,会在里面执行parseActivity,parseService,parseProvider等解析四大组件的流程,并把这些组建信息保存到 ParsingPackage这个类里
private ParseResult<ParsingPackage> parseBaseApplication(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws XmlPullParserException, IOException {
final String pkgName = pkg.getPackageName();
int targetSdk = pkg.getTargetSdkVersion();
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestApplication);
try {
// TODO(b/135203078): Remove this and force unit tests to mock an empty manifest
// This case can only happen in unit tests where we sometimes need to create fakes
// of various package parser data structures.
if (sa == null) {
return input.error("<application> does not contain any attributes");
}
String name = sa.getNonConfigurationString(R.styleable.AndroidManifestApplication_name,
0);
if (name != null) {
String packageName = pkg.getPackageName();
String outInfoName = ParsingUtils.buildClassName(packageName, name);
if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) {
return input.error("<application> invalid android:name");
} else if (outInfoName == null) {
return input.error("Empty class name in package " + packageName);
}
pkg.setClassName(outInfoName);
}
TypedValue labelValue = sa.peekValue(R.styleable.AndroidManifestApplication_label);
if (labelValue != null) {
pkg.setLabelRes(labelValue.resourceId);
if (labelValue.resourceId == 0) {
pkg.setNonLocalizedLabel(labelValue.coerceToString());
}
}
parseBaseAppBasicFlags(pkg, sa);
String manageSpaceActivity = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
R.styleable.AndroidManifestApplication_manageSpaceActivity, sa);
if (manageSpaceActivity != null) {
String manageSpaceActivityName = ParsingUtils.buildClassName(pkgName,
manageSpaceActivity);
if (manageSpaceActivityName == null) {
return input.error("Empty class name in package " + pkgName);
}
pkg.setManageSpaceActivityName(manageSpaceActivityName);
}
if (pkg.isAllowBackup()) {
// backupAgent, killAfterRestore, fullBackupContent, backupInForeground,
// and restoreAnyVersion are only relevant if backup is possible for the
// given application.
String backupAgent = nonConfigString(Configuration.NATIVE_CONFIG_VERSION,
R.styleable.AndroidManifestApplication_backupAgent, sa);
if (backupAgent != null) {
String backupAgentName = ParsingUtils.buildClassName(pkgName, backupAgent);
if (backupAgentName == null) {
return input.error("Empty class name in package " + pkgName);
}
if (DEBUG_BACKUP) {
Slog.v(TAG, "android:backupAgent = " + backupAgentName
+ " from " + pkgName + "+" + backupAgent);
}
pkg.setBackupAgentName(backupAgentName)
.setKillAfterRestore(bool(true,
R.styleable.AndroidManifestApplication_killAfterRestore, sa))
.setRestoreAnyVersion(bool(false,
R.styleable.AndroidManifestApplication_restoreAnyVersion, sa))
.setFullBackupOnly(bool(false,
R.styleable.AndroidManifestApplication_fullBackupOnly, sa))
.setBackupInForeground(bool(false,
R.styleable.AndroidManifestApplication_backupInForeground, sa));
}
TypedValue v = sa.peekValue(
R.styleable.AndroidManifestApplication_fullBackupContent);
int fullBackupContent = 0;
if (v != null) {
fullBackupContent = v.resourceId;
if (v.resourceId == 0) {
if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent specified as boolean=" +
(v.data == 0 ? "false" : "true"));
}
// "false" => -1, "true" => 0
fullBackupContent = v.data == 0 ? -1 : 0;
}
pkg.setFullBackupContent(fullBackupContent);
}
if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
}
}
if (sa.getBoolean(R.styleable.AndroidManifestApplication_persistent, false)) {
// Check if persistence is based on a feature being present
final String requiredFeature = sa.getNonResourceString(R.styleable
.AndroidManifestApplication_persistentWhenFeatureAvailable);
pkg.setPersistent(requiredFeature == null || mCallback.hasFeature(requiredFeature));
}
if (sa.hasValueOrEmpty(R.styleable.AndroidManifestApplication_resizeableActivity)) {
pkg.setResizeableActivity(sa.getBoolean(
R.styleable.AndroidManifestApplication_resizeableActivity, true));
} else {
pkg.setResizeableActivityViaSdkVersion(
targetSdk >= Build.VERSION_CODES.N);
}
String taskAffinity;
if (targetSdk >= Build.VERSION_CODES.FROYO) {
taskAffinity = sa.getNonConfigurationString(
R.styleable.AndroidManifestApplication_taskAffinity,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
taskAffinity = sa.getNonResourceString(
R.styleable.AndroidManifestApplication_taskAffinity);
}
ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
pkgName, pkgName, taskAffinity, input);
if (taskAffinityResult.isError()) {
return input.error(taskAffinityResult);
}
pkg.setTaskAffinity(taskAffinityResult.getResult());
String factory = sa.getNonResourceString(
R.styleable.AndroidManifestApplication_appComponentFactory);
if (factory != null) {
String appComponentFactory = ParsingUtils.buildClassName(pkgName, factory);
if (appComponentFactory == null) {
return input.error("Empty class name in package " + pkgName);
}
pkg.setAppComponentFactory(appComponentFactory);
}
CharSequence pname;
if (targetSdk >= Build.VERSION_CODES.FROYO) {
pname = sa.getNonConfigurationString(
R.styleable.AndroidManifestApplication_process,
Configuration.NATIVE_CONFIG_VERSION);
} else {
// Some older apps have been seen to use a resource reference
// here that on older builds was ignored (with a warning). We
// need to continue to do this for them so they don't break.
pname = sa.getNonResourceString(
R.styleable.AndroidManifestApplication_process);
}
ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
pkgName, null, pname, flags, mSeparateProcesses, input);
if (processNameResult.isError()) {
return input.error(processNameResult);
}
String processName = processNameResult.getResult();
pkg.setProcessName(processName);
if (pkg.isCantSaveState()) {
// A heavy-weight application can not be in a custom process.
// We can do direct compare because we intern all strings.
if (processName != null && !processName.equals(pkgName)) {
return input.error(
"cantSaveState applications can not use custom processes");
}
}
String classLoaderName = pkg.getClassLoaderName();
if (classLoaderName != null
&& !ClassLoaderFactory.isValidClassLoaderName(classLoaderName)) {
return input.error("Invalid class loader name: " + classLoaderName);
}
pkg.setGwpAsanMode(sa.getInt(R.styleable.AndroidManifestApplication_gwpAsanMode, -1));
pkg.setMemtagMode(sa.getInt(R.styleable.AndroidManifestApplication_memtagMode, -1));
if (sa.hasValue(R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized)) {
Boolean v = sa.getBoolean(
R.styleable.AndroidManifestApplication_nativeHeapZeroInitialized, false);
pkg.setNativeHeapZeroInitialized(
v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
}
if (sa.hasValue(
R.styleable.AndroidManifestApplication_requestRawExternalStorageAccess)) {
pkg.setRequestRawExternalStorageAccess(sa.getBoolean(R.styleable
.AndroidManifestApplication_requestRawExternalStorageAccess,
false));
}
if (sa.hasValue(
R.styleable.AndroidManifestApplication_requestForegroundServiceExemption)) {
pkg.setRequestForegroundServiceExemption(sa.getBoolean(R.styleable
.AndroidManifestApplication_requestForegroundServiceExemption,
false));
}
} finally {
sa.recycle();
}
boolean hasActivityOrder = false;
boolean hasReceiverOrder = false;
boolean hasServiceOrder = false;
final int depth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > depth)) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final ParseResult result;
String tagName = parser.getName();
boolean isActivity = false;
switch (tagName) {
case "activity":
isActivity = true;
// fall-through
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res, parser, flags, sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
} else {
hasReceiverOrder |= (activity.getOrder() != 0);
pkg.addReceiver(activity);
}
}
result = activityResult;
break;
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
hasServiceOrder |= (service.getOrder() != 0);
pkg.addService(service);
}
result = serviceResult;
break;
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
flags, sUseRoundIcon, input);
if (providerResult.isSuccess()) {
pkg.addProvider(providerResult.getResult());
}
result = providerResult;
break;
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
parser, sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
hasActivityOrder |= (activity.getOrder() != 0);
pkg.addActivity(activity);
}
result = activityResult;
break;
default:
result = parseBaseAppChildTag(input, tagName, pkg, res, parser, flags);
break;
}
if (result.isError()) {
return input.error(result);
}
}
if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
// Add a hidden app detail activity to normal apps which forwards user to App Details
// page.
ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
if (a.isError()) {
// Error should be impossible here, as the only failure case as of SDK R is a
// string validation error on a constant ":app_details" string passed in by the
// parsing code itself. For this reason, this is just a hard failure instead of
// deferred.
return input.error(a);
}
pkg.addActivity(a.getResult());
}
if (hasActivityOrder) {
pkg.sortActivities();
}
if (hasReceiverOrder) {
pkg.sortReceivers();
}
if (hasServiceOrder) {
pkg.sortServices();
}
// Must be run after the entire {@link ApplicationInfo} has been fully processed and after
// every activity info has had a chance to set it from its attributes.
setMaxAspectRatio(pkg);
setMinAspectRatio(pkg);
setSupportsSizeChanges(pkg);
pkg.setHasDomainUrls(hasDomainURLs(pkg));
return input.success(pkg);
}
桌面是如何展示出来的
桌面通过跨进程通信从packagemanagerService获取应用安装信息
packageManagerService 类图
ApplicationPackageManager 集成自packageManager
app通过ApplicationPackageManager 走IPackageManager.aidl走到PackageManagerService
既然桌面是跨进程获取数据,那是如何拿到图片的,总共三步 ,缓存 拿不到 -》applicationinfo ->resource
public Drawable getDrawable(String packageName, @DrawableRes int resId,
@Nullable ApplicationInfo appInfo) {
//1,先看有没有缓存
final ResourceName name = new ResourceName(packageName, resId);
final Drawable cachedIcon = getCachedIcon(name);
if (cachedIcon != null) {
return cachedIcon;
}
//2,拿出applicationInfo
if (appInfo == null) {
try {
appInfo = getApplicationInfo(packageName, sDefaultFlags);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
//3,通过application 拿出resource
if (resId != 0) {
try {
final Resources r = getResourcesForApplication(appInfo);
//通过resource拿出drawbale
final Drawable dr = r.getDrawable(resId, null);
if (dr != null) {
putCachedIcon(name, dr);
}
if (false) {
RuntimeException e = new RuntimeException("here");
e.fillInStackTrace();
Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resId)
+ " from package " + packageName
+ ": app scale=" + r.getCompatibilityInfo().applicationScale
+ ", caller scale=" + mContext.getResources()
.getCompatibilityInfo().applicationScale,
e);
}
if (DEBUG_ICONS) {
Log.v(TAG, "Getting drawable 0x"
+ Integer.toHexString(resId) + " from " + r
+ ": " + dr);
}
return dr;
} catch (PackageManager.NameNotFoundException e) {
Log.w("PackageManager", "Failure retrieving resources for "
+ appInfo.packageName);
} catch (Resources.NotFoundException e) {
Log.w("PackageManager", "Failure retrieving resources for "
+ appInfo.packageName + ": " + e.getMessage());
} catch (Exception e) {
// If an exception was thrown, fall through to return
// default icon.
Log.w("PackageManager", "Failure retrieving icon 0x"
+ Integer.toHexString(resId) + " in package "
+ packageName, e);
}
}
return null;
}
那么又是如何拿到resource,applicationInfo 实现了Parcelable 这个看起来也不是aidl可以办到的序列化的类呀
public Resources getResourcesForApplication(@NonNull ApplicationInfo app,
@Nullable Configuration configuration) throws PackageManager.NameNotFoundException {
//如果system 直接拿system ui 拿到
if (app.packageName.equals("system")) {
Context sysuiContext = mContext.mMainThread.getSystemUiContext();
if (configuration != null) {
sysuiContext = sysuiContext.createConfigurationContext(configuration);
}
return sysuiContext.getResources();
}
final boolean sameUid = (app.uid == Process.myUid());
//否则这个看不懂
final Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.overlayPaths, app.sharedLibraryFiles,
mContext.mPackageInfo, configuration);
if (r != null) {
return r;
}
throw new PackageManager.NameNotFoundException("Unable to open " + app.publicSourceDir);
}
```
# WorkManagerService
负责窗口添加删除,过度动画,事件传递,管理surface
# initPolicy
窗口管理策略,什么样的窗口才显示,窗口展示顺序,比如空白屏这些
# handler.runWithScissors
```
public final boolean runWithScissors(final Runnable r, long timeout) {
if (r == null) {
throw new IllegalArgumentException("runnable must not be null");
}
if (timeout < 0) {
throw new IllegalArgumentException("timeout must be non-negative");
}
if (Looper.myLooper() == mLooper) {
r.run();
return true;
}
BlockingRunnable br = new BlockingRunnable(r);
return br.postAndWait(this, timeout);
}
private static final class BlockingRunnable implements Runnable {
private final Runnable mTask;
private boolean mDone;
public BlockingRunnable(Runnable task) {
mTask = task;
}
@Override
public void run() {
try {
mTask.run();
} finally {
//执行完以后唤醒wait
synchronized (this) {
mDone = true;
notifyAll();
}
}
}
//post 以及等待当前结果执行
public boolean postAndWait(Handler handler, long timeout) {
if (!handler.post(this)) {
return false;
}
synchronized (this) {
if (timeout > 0) {
final long expirationTime = SystemClock.uptimeMillis() + timeout;
while (!mDone) {
long delay = expirationTime - SystemClock.uptimeMillis();
if (delay <= 0) {
return false; // timeout
}
try {
//释放锁
wait(delay);
} catch (InterruptedException ex) {
}
}
} else {
//用while防止别的释放
while (!mDone) {
try {
//释放锁
wait();
} catch (InterruptedException ex) {
}
}
}
}
return true;
}
}
```
# 看代码技巧
## 流程
1,第一遍 20分钟以内 ,看概括
2,看过程
3,看细节
5, 看支线流程
不影响流程的是直线,再不看完就结束了的是主线
没找到实现类就去前面一步
# 专业技能
必须写亮点,不然面试官乱问
研究过glide生命周期机制,三级缓存设计思想
以简历目标去学习