APP 启动优化java
UI 绘制优化android
内存优化git
图片压缩github
长图优化面试
电量优化性能优化
Dex 加解密app
APP 稳定性之热修复原理探索post
上一篇讲了 dex 加密解密 尚未看过的能够先去了解下 dex 怎么加解密,这篇就来带你们完成剩下的工做,dex 解密完成以后须要把代理 ProxyApplication 给删除掉,而后把咱们本身的 Application 给添加到咱们程序中。想要替换 ProxyApplication 可不是一件简单的事儿,首先必须的对 Application 启动源码很熟悉才能对它进行操做,下面由我来带着你们一块儿进入源码的世界吧。
APP 启动流程能够看我另一篇文章性能优化(一)启动优化,今天主要从 ActivityThread => main() 开始,下面以一个流程图来讲明一下:
ActivityThread.java
mian() -> thread.attach() -> attachApplication() -> 接收 AMS 发过来的参数以后 sendMessage(H.BIND_APPLICATION)-> 处理 BIND_APPLICATION -> handleBindApplication() 在这里准备好 application - > Application app = data.info.makeApplication() - > mInitialApplication = app;
LoadedApk.java
这个类就是 APK 在内存中的表示,能够获得如代码,资料,功能清单等信息
boolean isBindReal;
Application delegate;
private void bindRealApplicatin() throws Exception {
if (isBindReal) {
return;
}
if (TextUtils.isEmpty(app_name)) {
return;
}
//获得attachBaseContext(context) 传入的上下文 ContextImpl
Context baseContext = getBaseContext();
//建立用户真实的application (MyApplication)
Class<?> delegateClass = Class.forName(app_name);
delegate = (Application) delegateClass.newInstance();
//获得attach()方法
Method attach = Application.class.getDeclaredMethod("attach", Context.class);
attach.setAccessible(true);
attach.invoke(delegate, baseContext);
// ContextImpl---->mOuterContext(app) 经过Application的attachBaseContext回调参数获取
Class<?> contextImplClass = Class.forName("android.app.ContextImpl");
//获取mOuterContext属性
Field mOuterContextField = contextImplClass.getDeclaredField("mOuterContext");
mOuterContextField.setAccessible(true);
mOuterContextField.set(baseContext, delegate);
// ActivityThread--->mAllApplications(ArrayList) ContextImpl的mMainThread属性
Field mMainThreadField = contextImplClass.getDeclaredField("mMainThread");
mMainThreadField.setAccessible(true);
Object mMainThread = mMainThreadField.get(baseContext);
// ActivityThread--->>mInitialApplication
Class<?> activityThreadClass=Class.forName("android.app.ActivityThread");
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
mInitialApplicationField.setAccessible(true);
mInitialApplicationField.set(mMainThread,delegate);
// ActivityThread--->mAllApplications(ArrayList) ContextImpl的mMainThread属性
Field mAllApplicationsField = activityThreadClass.getDeclaredField("mAllApplications");
mAllApplicationsField.setAccessible(true);
ArrayList<Application> mAllApplications =(ArrayList<Application>) mAllApplicationsField.get(mMainThread);
mAllApplications.remove(this);
mAllApplications.add(delegate);
// LoadedApk------->mApplication ContextImpl的mPackageInfo属性
Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");
mPackageInfoField.setAccessible(true);
Object mPackageInfo=mPackageInfoField.get(baseContext);
Class<?> loadedApkClass=Class.forName("android.app.LoadedApk");
Field mApplicationField = loadedApkClass.getDeclaredField("mApplication");
mApplicationField.setAccessible(true);
mApplicationField.set(mPackageInfo,delegate);
//修改ApplicationInfo className LooadedApk
Field mApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo");
mApplicationInfoField.setAccessible(true);
ApplicationInfo mApplicationInfo = (ApplicationInfo)mApplicationInfoField.get(mPackageInfo);
mApplicationInfo.className=app_name;
delegate.onCreate();
isBindReal = true;
}
复制代码
如今从新签名打包完成,启动咱们的 APK 看下 Log
2019-06-04 23:17:30.892 6064-6064/com.yk.dexdeapplication I/DevYK: provider onCreate:com.example.proxy_core.ProxyApplication@1ec3c70
2019-06-04 23:17:30.892 6064-6064/com.yk.dexdeapplication I/DevYK: provider onCreate:com.example.proxy_core.ProxyApplication@1ec3c70
2019-06-04 23:17:30.892 6064-6064/com.yk.dexdeapplication I/DevYK: provider onCreate:com.example.proxy_core.ProxyApplication
2019-06-04 23:17:30.895 6064-6064/com.yk.dexdeapplication I/DevYK: MyApplication onCreate() 2019-06-04 23:17:30.995 6064-6064/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@300b5f6 2019-06-04 23:17:30.995 6064-6064/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@300b5f6 2019-06-04 23:17:30.995 6064-6064/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App 2019-06-04 23:17:31.001 6064-6064/com.yk.dexdeapplication I/DevYK: provider delete:com.example.proxy_core.ProxyApplication@1ec3c70 2019-06-04 23:17:31.021 6064-6064/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@300b5f6 2019-06-04 23:17:31.021 6064-6064/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@300b5f6 2019-06-04 23:17:31.021 6064-6064/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App 2019-06-04 23:17:31.022 6064-6064/com.yk.dexdeapplication I/DevYK: reciver:android.app.ReceiverRestrictedContext@9b92293 2019-06-04 23:17:31.022 6064-6064/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App@300b5f6 2019-06-04 23:17:31.022 6064-6064/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App 复制代码
注意看 LOG
MyApplication onCreate() 复制代码
这里已经替换成咱们本身的 MyApplication , 并且 Activity 和 Service 获取上下文也已是咱们替换成功的 Applicaton。可是...也许有的眼神比较好的已经看出问题了,为何内容提供者 Context 仍是代理的 Application 并且比咱们本身的应用还要先执行,那么咱们带着这个问题去看 Application onCreate 以前作了什么事儿。
咱们点击 installlContentProviders(app,providers);
注意这里传进去的仍是 代理 Context
重点在最后
注意看我 勾画 的圈里面的逻辑判断,判断当前应用的包名是否跟 XML 中的包名一致,若是一致咱们就赋值,再次提醒下 这里的 context 是咱们代理的 context ,那么咱们怎么作勒,咱们在代理中重写 PackageName 只要都不等 那么就会走 else 会根据包名建立一个 Context
/** * 让代码走入if中的第三段中 * @return */
@Override
public String getPackageName() {
if(!TextUtils.isEmpty(app_name)){
return "";
}
return super.getPackageName();
}
@Override
public Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {
if(TextUtils.isEmpty(app_name)){
return super.createPackageContext(packageName, flags);
}
try {
bindRealApplicatin();
} catch (Exception e) {
e.printStackTrace();
}
return delegate;
}
复制代码
最后咱们来验证一下:
2019-06-05 00:12:30.271 7570-7570/com.yk.dexdeapplication I/DevYK: MyApplication onCreate() 2019-06-05 00:12:30.273 7570-7570/com.yk.dexdeapplication I/DevYK: provider onCreate:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.273 7570-7570/com.yk.dexdeapplication I/DevYK: provider onCreate:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.273 7570-7570/com.yk.dexdeapplication I/DevYK: provider onCreate:com.yk.dexdeapplication.App 2019-06-05 00:12:30.381 7570-7570/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.381 7570-7570/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.381 7570-7570/com.yk.dexdeapplication I/DevYK: activity:com.yk.dexdeapplication.App 2019-06-05 00:12:30.387 7570-7570/com.yk.dexdeapplication I/DevYK: provider delete:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.406 7570-7570/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.406 7570-7570/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.406 7570-7570/com.yk.dexdeapplication I/DevYK: service:com.yk.dexdeapplication.App 2019-06-05 00:12:30.408 7570-7570/com.yk.dexdeapplication I/DevYK: reciver:android.app.ReceiverRestrictedContext@b7a3b82 2019-06-05 00:12:30.408 7570-7570/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App@1ec3c70 2019-06-05 00:12:30.408 7570-7570/com.yk.dexdeapplication I/DevYK: reciver:com.yk.dexdeapplication.App 复制代码
日志中除了 BroadCase Context 是系统的之外,全部的 Context 都是咱们替换的 Application Context。完美解决。不过这里有一个隐藏 BUG ,听说面试题会问那么是什么勒?
能够在广播中使用 context 在开启一个广播或者绑定一个服务吗?
咱们其实能够带着这个问题看下源码
H -> RECEIVER 消息
果真注册广播和绑定服务会抛一个异常。
到这里咱们的加固已经讲完了,从 dex 分包 -> 加密 -> 对齐 > 签名 - > 打包压缩成 APK 。一套完整的流程和代码都已经写完了。跟市面上的加固流程原理都几乎同样。懂了原理再去使用第三方就轻车熟路了。