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 。一套完整的流程和代碼都已經寫完了。跟市面上的加固流程原理都幾乎同樣。懂了原理再去使用第三方就輕車熟路了。