• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            專職C++

            不能停止的腳步

              C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              163 Posts :: 7 Stories :: 135 Comments :: 0 Trackbacks

            常用鏈接

            留言簿(28)

            我參與的團(tuán)隊(duì)

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            原文:http://www.cnblogs.com/zjoch/p/6018432.html

            Dexposed是基于久負(fù)盛名的開源Xposed框架實(shí)現(xiàn)的一個(gè)Android平臺(tái)上功能強(qiáng)大的無侵入式運(yùn)行時(shí)AOP框架。

            Dexposed的AOP實(shí)現(xiàn)是完全非侵入式的,沒有使用任何注解處理器,編織器或者字節(jié)碼重寫器。集成Dexposed框架很簡(jiǎn)單,只需要在應(yīng)用初始化階段加載一個(gè)很小的JNI庫(kù)就可以,這個(gè)加載操作已經(jīng)封裝在DexposedBridge函數(shù)庫(kù)里面的canDexposed函數(shù)中,源碼如下所示:

            /**  * Check device if can run dexposed, and load libs auto.  */ public synchronized static boolean canDexposed(Context context) {     if (!DeviceCheck.isDeviceSupport(context)) {         return false;     }     //load xposed lib for hook.     return loadDexposedLib(context); }  private static boolean loadDexposedLib(Context context) {     // load xposed lib for hook.     try {         if (android.os.Build.VERSION.SDK_INT > 19){             System.loadLibrary("dexposed_l");         } else if (android.os.Build.VERSION.SDK_INT == 10                 || android.os.Build.VERSION.SDK_INT == 9 ||                  android.os.Build.VERSION.SDK_INT > 14){             System.loadLibrary("dexposed");         }         return true;     } catch (Throwable e) {         return false;     } }

            Dexposed實(shí)現(xiàn)的hooking,不僅可以hook應(yīng)用中的自定義函數(shù),也可以hook應(yīng)用中調(diào)用的Android框架的函數(shù)。Android開發(fā)者將從這一點(diǎn)得到很多好處,因?yàn)槲覀儑?yán)重依賴于Android SDK的版本碎片化。

            基于動(dòng)態(tài)類加載技術(shù),運(yùn)行中的app可以加載一小段經(jīng)過編譯的Java AOP代碼,在不需要重啟app的前提下實(shí)現(xiàn)修改目標(biāo)app的行為。

            典型的使用場(chǎng)景

            • AOP編程
            • 插樁(例如測(cè)試,性能監(jiān)控等)
            • 在線熱更新,修復(fù)嚴(yán)重的,緊急的或者安全性的bug
            • SDK hooking以提供更好的開發(fā)體驗(yàn)

            如何集成

            集成方式很簡(jiǎn)單,只需要將一個(gè)jar包加入項(xiàng)目的libs文件夾中,同時(shí)將兩個(gè)so文件添加到j(luò)niLibs中對(duì)應(yīng)的ABI目錄中即可。Gradle依賴如下所示:

            buildscript {   repositories {     mavenCentral()   }   dependencies {     classpath 'com.android.tools.build:gradle:0.10.+'     classpath 'com.nabilhachicha:android-native-dependencies:0.1'   } }  ...  native_dependencies {     artifact 'com.taobao.dexposed:dexposed_l:0.2+:armeabi'     artifact 'com.taobao.dexposed:dexposed:0.2+:armeabi' } dependencies {     compile files('libs/dexposedbridge.jar') }

            其中,native_dependencies是一個(gè)第三方插件,使用方法可參考《如何在Android Gradle中添加原生so文件依賴》。當(dāng)然,我們也可以直接把需要用到的so文件直接拷貝到j(luò)niLibs目錄中,這樣的話,可以把上面的native_dependencies代碼段注釋掉。

            同時(shí)應(yīng)該在應(yīng)用初始化的地方(盡可能早的添加)添加初始化Dexposed的代碼,例如在MyApplication中添加:

            public class MyApplication extends Application {      private boolean mIsSupported = false; // 設(shè)備是否支持dexposed      private boolean mIsLDevice = false;  // 設(shè)備Android系統(tǒng)是否是Android 5.0及以上      @Override     public void onCreate() {         super.onCreate();          // check device if support and auto load libs         mIsSupported = DexposedBridge.canDexposed(this);         mIsLDevice = Build.VERSION.SDK_INT >= 21;     }      public boolean isSupported() {         return mIsSupported;     }      public boolean isLDevice() {         return mIsLDevice;     } }

            基本用法

            對(duì)于某個(gè)函數(shù)而言,有三個(gè)注入點(diǎn)可供選擇:函數(shù)執(zhí)行前注入(before),函數(shù)執(zhí)行后注入(after),替換函數(shù)執(zhí)行的代碼段(replace),分別對(duì)應(yīng)于抽象類XC_MethodHook及其子類XC_MethodReplacement中的函數(shù):

            public abstract class XC_MethodHook extends XCallback {      /**      * Called before the invocation of the method.      * <p>Can use {@link MethodHookParam#setResult(Object)} and {@link MethodHookParam#setThrowable(Throwable)}      * to prevent the original method from being called.      */     protected void beforeHookedMethod(MethodHookParam param) throws Throwable {}      /**      * Called after the invocation of the method.      * <p>Can use {@link MethodHookParam#setResult(Object)} and {@link MethodHookParam#setThrowable(Throwable)}      * to modify the return value of the original method.      */     protected void afterHookedMethod(MethodHookParam param) throws Throwable  {} }
            public abstract class XC_MethodReplacement extends XC_MethodHook {      @Override     protected final void beforeHookedMethod(MethodHookParam param) throws Throwable {         try {             Object result = replaceHookedMethod(param);             param.setResult(result);         } catch (Throwable t) {             param.setThrowable(t);         }     }      protected final void afterHookedMethod(MethodHookParam param) throws Throwable {     }      /**      * Shortcut for replacing a method completely. Whatever is returned/thrown here is taken      * instead of the result of the original method (which will not be called).      */     protected abstract Object replaceHookedMethod(MethodHookParam param) throws Throwable; }

            可以看到這三個(gè)注入回調(diào)函數(shù)都有一個(gè)類型為MethodHookParam的參數(shù),這個(gè)參數(shù)包含了一些很有用的信息:

            • MethodHookParam.thisObject:這個(gè)類的一個(gè)實(shí)例
            • MethodHookParam.args:用于傳遞被注入函數(shù)的所有參數(shù)
            • MethodHookParam.setResult:用于修改原函數(shù)調(diào)用的結(jié)果,如果在beforeHookedMethod回調(diào)函數(shù)中調(diào)用setResult,可以阻止對(duì)原函數(shù)的調(diào)用。但是如果有返回值的話仍然需要通過hook處理器進(jìn)行return操作。

            MethodHookParam代碼如下所示:

            public static class MethodHookParam extends XCallback.Param {     /** Description of the hooked method */     public Member method;     /** The <code>this</code> reference for an instance method, or null for static methods */     public Object thisObject;     /** Arguments to the method call */     public Object[] args;      private Object result = null;     private Throwable throwable = null;     /* package */ boolean returnEarly = false;      /** Returns the result of the method call */     public Object getResult() {         return result;     }      /**      * Modify the result of the method call. In a "before-method-call"      * hook, prevents the call to the original method.      * You still need to "return" from the hook handler if required.      */     public void setResult(Object result) {         this.result = result;         this.throwable = null;         this.returnEarly = true;     }      /** Returns the <code>Throwable</code> thrown by the method, or null */     public Throwable getThrowable() {         return throwable;     }      /** Returns true if an exception was thrown by the method */     public boolean hasThrowable() {         return throwable != null;     }      /**      * Modify the exception thrown of the method call. In a "before-method-call"      * hook, prevents the call to the original method.      * You still need to "return" from the hook handler if required.      */     public void setThrowable(Throwable throwable) {         this.throwable = throwable;         this.result = null;         this.returnEarly = true;     }      /** Returns the result of the method call, or throws the Throwable caused by it */     public Object getResultOrThrowable() throws Throwable {         if (throwable != null)             throw throwable;         return result;     } }

            例子一:AOP編程

            AOP(Aspect Oriented Programming),也就是面向方面編程,是通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開發(fā)的效率。

            AOP一般應(yīng)用在日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等方面,它的主要意圖是將日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等代碼從業(yè)務(wù)邏輯代碼中劃分出來,通過對(duì)這些行為的分離,我們希望可以將它們獨(dú)立到非業(yè)務(wù)邏輯的方法中,進(jìn)而改變這些行為的時(shí)候不影響業(yè)務(wù)邏輯的代碼。

            例如我們可以在應(yīng)用中所有的Activity.onCreate(Bundle)函數(shù)調(diào)用之前和之后增加一些相同的處理:

            // Target class, method with parameter types, followed by the hook callback (XC_MethodHook). DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {      // To be invoked before Activity.onCreate().     @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable {         // "thisObject" keeps the reference to the instance of target class.         Activity instance = (Activity) param.thisObject;          // The array args include all the parameters.         Bundle bundle = (Bundle) param.args[0];         Intent intent = new Intent();         // XposedHelpers provide useful utility methods.         XposedHelpers.setObjectField(param.thisObject, "mIntent", intent);          // Calling setResult() will bypass the original method body use the result as method return value directly.         if (bundle.containsKey("return"))             param.setResult(null);     }      // To be invoked after Activity.onCreate()     @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable {         XposedHelpers.callMethod(param.thisObject, "sampleMethod", 2);     } });

            當(dāng)然也可以替換目標(biāo)函數(shù)原來執(zhí)行的代碼段:

            DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodReplacement() {          @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {             // Re-writing the method logic outside the original method context is a bit tricky but still viable.             ...         }      });

            例子二:在線熱更新

            在線熱更新一般用于修復(fù)線上嚴(yán)重的,緊急的或者安全性的bug,這里會(huì)涉及到兩個(gè)apk文件,一個(gè)我們稱為宿主apk,也就是發(fā)布到應(yīng)用市場(chǎng)的apk,一個(gè)稱為補(bǔ)丁apk。宿主apk出現(xiàn)bug時(shí),通過在線下載的方式從服務(wù)器下載到補(bǔ)丁apk,使用補(bǔ)丁apk中的函數(shù)替換原來的函數(shù),從而實(shí)現(xiàn)在線修復(fù)bug的功能。

            為了實(shí)現(xiàn)這個(gè)功能,需要再引入一個(gè)名為patchloader的jar包,這個(gè)函數(shù)庫(kù)實(shí)現(xiàn)了一個(gè)熱更新框架,宿主apk在發(fā)布時(shí)會(huì)將這個(gè)jar包一起打包進(jìn)apk中,而補(bǔ)丁apk只是在編譯時(shí)需要這個(gè)jar包,但打包成apk時(shí)不包含這個(gè)jar包,以免補(bǔ)丁apk集成到宿主apk中時(shí)發(fā)生沖突。因此,補(bǔ)丁apk將會(huì)以provided的形式依賴dexposedbridge.jar和patchloader.jar,補(bǔ)丁apk的build.gradle文件中依賴部分腳本如下所示:

            dependencies {     provided files('libs/dexposedbridge.jar')     provided files('libs/patchloader.jar') }

            這里我們假設(shè)宿主apk的MainActivity.showDialog函數(shù)出現(xiàn)問題,需要打補(bǔ)丁,宿主代碼如下所示:(類完整路徑是com.taobao.dexposed.MainActivity)

            package com.taobao.dexposed;  public class MainActivity extends Activity {      private void showDialog() {         AlertDialog.Builder builder = new AlertDialog.Builder(this);         builder.setTitle("Dexposed sample")                 .setMessage(                         "Please clone patchsample project to generate apk, and copy it to \"/Android/data/com.taobao.dexposed/cache/patch.apk\"")                 .setPositiveButton("ok", new DialogInterface.OnClickListener() {                     public void onClick(DialogInterface dialog, int whichButton) {                     }                 }).create().show();     } }

            補(bǔ)丁apk只有一個(gè)名為DialogPatch的類,實(shí)現(xiàn)了patchloader函數(shù)庫(kù)中的IPatch接口,IPatch接口代碼如下所示:

            /**  * The interface implemented by hotpatch classes.  */ public interface IPatch {      void handlePatch(PatchParam lpparam) throws Throwable;  }

            DialogPatch類實(shí)現(xiàn)IPatch的handlePatch函數(shù),在該函數(shù)中通過反射得到宿主APK中com.taobao.dexposed.MainActivity類實(shí)例,然后調(diào)用dexposedbridge函數(shù)庫(kù)中的DexposedBridge.findAndHookMethod函數(shù),對(duì)MainActivity中的showDialog函數(shù)進(jìn)行Hook操作,替換宿主apk中的相應(yīng)代碼,DialogPatch代碼如下所示:

            public class DialogPatch implements IPatch {      @Override     public void handlePatch(final PatchParam arg0) throws Throwable {                 Class<?> cls = null;         try {             cls= arg0.context.getClassLoader()                 .loadClass("com.taobao.dexposed.MainActivity");         } catch (ClassNotFoundException e) {             e.printStackTrace();             return;         }                   DexposedBridge.findAndHookMethod(cls, "showDialog",             new XC_MethodReplacement() {             @Override             protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {                 Activity mainActivity = (Activity) param.thisObject;                 AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity);                 builder.setTitle("Dexposed sample")                         .setMessage("The dialog is shown from patch apk!")                         .setPositiveButton("ok", new DialogInterface.OnClickListener() {                             public void onClick(DialogInterface dialog, int whichButton) {                             }                         }).create().show();                 return null;                              }         });     }  }

            最后宿主apk通過調(diào)用patchloader函數(shù)庫(kù)提供的PatchMain.load函數(shù)來動(dòng)態(tài)加載下載到的補(bǔ)丁apk,加載代碼如下所示:

            // Run patch apk public void runPatchApk(View view) {     Log.d("dexposed", "runPatchApk button clicked.");     if (isLDevice) {         showLog("dexposed", "It doesn't support this function on L device.");         return;     }     if (!isSupport) {         Log.d("dexposed", "This device doesn't support dexposed!");         return;     }     File cacheDir = getExternalCacheDir();     if(cacheDir != null){         String fullpath = cacheDir.getAbsolutePath() + File.separator + "patch.apk";         PatchResult result = PatchMain.load(this, fullpath, null);         if (result.isSuccess()) {             Log.e("Hotpatch", "patch success!");         } else {             Log.e("Hotpatch", "patch error is " + result.getErrorInfo());         }     }     showDialog(); }

            為便于理解,這里也把load函數(shù)體貼出來,更詳細(xì)內(nèi)容大家可以看源碼:

            /**  * Load a runnable patch apk.  *  * @param context the application or activity context.  * @param apkPath the path of patch apk file.  * @param contentMap the object maps that will be used by patch classes.    * @return PatchResult include if success or error detail.  */ public static PatchResult load(Context context, String apkPath, HashMap<String, Object> contentMap) {      if (!new File(apkPath).exists()) {         return new PatchResult(false, PatchResult.FILE_NOT_FOUND, "FILE not found on " + apkPath);     }      PatchResult result = loadAllCallbacks(context, apkPath,context.getClassLoader());     if (!result.isSuccess()) {         return result;     }      if (loadedPatchCallbacks.getSize() == 0) {         return new PatchResult(false, PatchResult.NO_PATCH_CLASS_HANDLE, "No patch class to be handle");     }      PatchParam lpparam = new PatchParam(loadedPatchCallbacks);             lpparam.context = context;     lpparam.contentMap = contentMap;      return PatchCallback.callAll(lpparam); }

            支持的系統(tǒng)版本

            Dexposed支持從Android2.3到4.4(除了3.0)的所有dalvid運(yùn)行時(shí)arm架構(gòu)的設(shè)備,穩(wěn)定性已經(jīng)經(jīng)過實(shí)踐檢驗(yàn)。

            支持的系統(tǒng)版本:

            • Dalvik 2.3
            • Dalvik 4.0~4.4

            不支持的系統(tǒng)版本:

            • Dalvik 3.0
            • ART 5.1
            • ART M

            測(cè)試中的系統(tǒng)版本:

            • ART 5.0

            未經(jīng)測(cè)試的系統(tǒng)版本:

            • Dalvik 2.2

            使用Dexposed的項(xiàng)目

            目前阿里系主流app例如手機(jī)淘寶,支付寶,天貓都使用了Dexposed支持在線熱更新,而開源項(xiàng)目中,在Github上面能搜到的只有一個(gè)XLog項(xiàng)目,它的主要功能是方便的打印函數(shù)調(diào)用和耗時(shí)日志,這也是一個(gè)了解Dexposed如何使用的很好的例子。

            參考資料



            文/asce1885(簡(jiǎn)書作者)
            原文鏈接:http://www.jianshu.com/p/14edcb444c51
            著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并標(biāo)注“簡(jiǎn)書作者”。
            posted on 2016-11-29 12:35 冬瓜 閱讀(525) 評(píng)論(0)  編輯 收藏 引用 所屬分類: 轉(zhuǎn)貼
            久久亚洲精品视频| 色诱久久av| 国产综合免费精品久久久| 久久婷婷国产剧情内射白浆| 2022年国产精品久久久久| 久久人妻少妇嫩草AV蜜桃| 日韩人妻无码一区二区三区久久| 天天爽天天爽天天片a久久网| 久久婷婷五月综合成人D啪| 亚洲国产精品婷婷久久| 亚洲精品无码久久久久sm| 久久精品一区二区三区中文字幕| 久久精品一本到99热免费| 国内精品伊人久久久影院| 精品久久久久久亚洲| 亚洲中文字幕久久精品无码APP| 久久成人国产精品一区二区| 久久久久99精品成人片欧美| 精品一二三区久久aaa片| 亚洲午夜无码AV毛片久久| 久久久精品波多野结衣| 亚洲一区二区三区日本久久九| 日韩av无码久久精品免费| A级毛片无码久久精品免费| 久久综合九色综合欧美就去吻| 久久精品国产亚洲沈樵| 国产精品99精品久久免费| 日本人妻丰满熟妇久久久久久| 欧美伊人久久大香线蕉综合 | 亚洲国产成人乱码精品女人久久久不卡| 99久久成人国产精品免费| 亚洲国产精品无码久久98| 伊人久久大香线蕉精品不卡| 亚洲国产天堂久久综合| 亚洲精品乱码久久久久久蜜桃| 人妻无码精品久久亚瑟影视| 日韩久久无码免费毛片软件| 日韩欧美亚洲综合久久影院Ds | 91久久九九无码成人网站| 国产亚洲欧美成人久久片| 66精品综合久久久久久久|