Android Activity 生命周期调用流程

注:源码分析基于 Android SDK API 28 

我们知道,Activity A 启动 Activity B ,其生命周期方法调用如下:

  1. Activity A onPause()
  2. Activity B onCreate()
  3. Activity B onStart()
  4. Activity B onResume()
  5. Activity A onStop()

那首先我们来看看 Activity A 的 onPause() 是什么地方调用的?

onPause()

startActivity 的流程中有一步是 resumeTopActivityInnerLocked 。

阅读更多

Android startActivity 启动流程

注:源码分析基于 Android SDK API 28

对于 Activity 大家都已经很熟悉很亲切了吧,在这就不过多介绍了。

直接进入正题,走起!

一般我们启动 Activity 的入口都是 startActivity ,所以这也成为了我们分析整个流程的切入口。

Activity

startActivity(Intent intent)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
startActivityForResult(intent, requestCode, null);
}
阅读更多

Android Tinker源码分析(七):dex合成流程

tryRecoverDexFiles

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected static boolean tryRecoverDexFiles(Tinker manager, ShareSecurityCheck checker, Context context,
String patchVersionDirectory, File patchFile) {
// 检查是否开启支持dex补丁开关
if (!manager.isEnabledForDex()) {
TinkerLog.w(TAG, "patch recover, dex is not enabled");
return true;
}
// 检查补丁包中的 dex_meta.txt 是否存在
String dexMeta = checker.getMetaContentMap().get(DEX_META_FILE);

if (dexMeta == null) {
TinkerLog.w(TAG, "patch recover, dex is not contained");
return true;
}

long begin = SystemClock.elapsedRealtime();
// 到这个方法中执行具体的操作
boolean result = patchDexExtractViaDexDiff(context, patchVersionDirectory, dexMeta, patchFile);
long cost = SystemClock.elapsedRealtime() - begin;
TinkerLog.i(TAG, "recover dex result:%b, cost:%d", result, cost);
return result;
}
阅读更多

Android Tinker源码分析(六):补丁合成流程

本系列 Tinker 源码解析基于 Tinker v1.9.12

补丁合成流程

下发的补丁包其实并不能直接加载,因为补丁包只是差异包,需要和本地的 dex 、资源等进行合成后,得到全量的 dex 才能被完整地使用。这样也就避免了热修复中 dex 的 pre-verify 问题,也减少了补丁包的体积,方便用户下载。

补丁合成的入口在 TinkerInstaller.onReceiveUpgradePatch 方法

TinkerInstaller.onReceiveUpgradePatch

1
2
3
public static void onReceiveUpgradePatch(Context context, String patchLocation) {
Tinker.with(context).getPatchListener().onPatchReceived(patchLocation);
}
阅读更多

Android Apk 安全之校验签名

校验签名

一般绝大多数的 app 在上线前都会做一层安全防护,比如代码混淆、加固等。

今天就来讲讲其中的一项:校验签名。

校验签名可以有效的防止二次打包,避免你的 app 被植入广告甚至破解等。而今天就从两个角度来讲签名的具体校验:

  • Java 层
  • C/C++ 层

那么就先开始讲 java 层好了。

Java 层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private static boolean validateAppSignature(Context context, String apkSignature) {
try {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : packageInfo.signatures) {
String lowerCaseSignature = signature.toCharsString().toLowerCase();
if (lowerCaseSignature.equals(apkSignature)) {
return true;
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return false;
}
阅读更多

Android Tinker 源码分析(五):加载so补丁流程

本系列 Tinker 源码解析基于 Tinker v1.9.12

校验so补丁流程

与加载资源补丁类似,加载so补丁也要先从校验开始看起。

其实总体来说,Tinker 中加载 so 补丁文件的关键代码就一句:

System.load(String filePath)

tryLoadPatchFilesInternal

1
2
3
4
5
6
7
8
9
10
11
final boolean isEnabledForNativeLib = ShareTinkerInternals.isTinkerEnabledForNativeLib(tinkerFlag);

if (isEnabledForNativeLib) {
//tinker/patch.info/patch-641e634c/lib
boolean libCheck = TinkerSoLoader.checkComplete(patchVersionDirectory, securityCheck, resultIntent);
if (!libCheck) {
//file not found, do not load patch
Log.w(TAG, "tryLoadPatchFiles:native lib check fail");
return;
}
}
阅读更多

Android Tinker源码分析(四):加载资源补丁流程

本系列 Tinker 源码解析基于 Tinker v1.9.12

加载资源补丁流程

将到资源补丁的加载,首先还要回过头来先看资源补丁的校验和检查。

我们回到 TinkerLoader.tryLoadPatchFilesInternal 方法中来看。

tryLoadPatchFilesInternal

1
2
3
4
5
6
7
8
9
10
11
//check resource
final boolean isEnabledForResource = ShareTinkerInternals.isTinkerEnabledForResource(tinkerFlag);
Log.w(TAG, "tryLoadPatchFiles:isEnabledForResource:" + isEnabledForResource);
if (isEnabledForResource) {
boolean resourceCheck = TinkerResourceLoader.checkComplete(app, patchVersionDirectory, securityCheck, resultIntent);
if (!resourceCheck) {
//file not found, do not load patch
Log.w(TAG, "tryLoadPatchFiles:resource check fail");
return;
}
}
阅读更多

Android Tinker源码分析(三):加载dex补丁流程

本系列 Tinker 源码解析基于 Tinker v1.9.12

加载dex补丁流程

TinkerDexLoader.loadTinkerJars

判断一下 dexList 和 classLoader

1
2
3
4
5
6
7
8
9
10
11
12
13
if (loadDexList.isEmpty() && classNDexInfo.isEmpty()) {
Log.w(TAG, "there is no dex to load");
return true;
}

PathClassLoader classLoader = (PathClassLoader) TinkerDexLoader.class.getClassLoader();
if (classLoader != null) {
Log.i(TAG, "classloader: " + classLoader.toString());
} else {
Log.e(TAG, "classloader is null");
ShareIntentUtil.setIntentReturnCode(intentResult, ShareConstants.ERROR_LOAD_PATCH_VERSION_DEX_CLASSLOADER_NULL);
return false;
}
阅读更多

Android Tinker源码分析(二):加载补丁流程

本系列 Tinker 源码解析基于 Tinker v1.9.12

利用反射执行的是 TinkerLoader.tryLoad 方法

tryLoad

1
2
3
4
5
6
7
8
9
10
@Override
public Intent tryLoad(TinkerApplication app) {
Intent resultIntent = new Intent();

long begin = SystemClock.elapsedRealtime();
tryLoadPatchFilesInternal(app, resultIntent);
long cost = SystemClock.elapsedRealtime() - begin;
ShareIntentUtil.setIntentPatchCostTime(resultIntent, cost);
return resultIntent;
}
阅读更多

Android Tinker 源码分析(一):TinkerApplication

本系列 Tinker 源码解析基于 Tinker v1.9.12

自动生成TinkerApplication

接入 Tinker 第一步就是改造 Application 。官方推荐是利用 @DefaultLifeCycle 动态生成 Application

1
2
3
4
5
@DefaultLifeCycle(application = "tinker.sample.android.app.SampleApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class SampleApplicationLike extends DefaultApplicationLike {
}
阅读更多