Android 外掛框架機制之預熱篇

mymdeep發表於2017-02-25

關於Android外掛框架機制的介紹,我打算分幾章來介紹,這是第一篇也就是預熱篇。
Android 外掛框架機制系列文章:
Android 外掛框架機制之Small
Android 外掛框架機制之DroidPlugin

為什麼使用外掛化

隨著應用的模組化的不斷增加,APK的體積不斷增長,方法數很可能會引發64K問題(解決方案),谷歌提供的方案並不完美,而且APK的啟動速度會受影響。

  • 提高工程的執行速度,每個模組作為一個獨立的外掛進行開發和除錯。
  • 提高應用的啟動速度,應用啟動時可以選擇只載入必須的模組,其他模組使用時再載入
  • 多團隊並行開發
  • 線上動態載入或更新模組
  • 靈活的功能配置

基礎知識

機制

外掛化的根本思路就是讓你的應用呼叫未安裝的apk,jar,dex檔案中的方法。
在Android中,系統提供了兩個API可供選擇:

  • PathClassLoader:只能載入已經安裝到Android系統中的APK檔案,這個是另一種載入思路,但是跟外掛化沒關係,這裡不提。
  • DexClassLoader:支援載入外部的APK,Jar,或Dex檔案。

    基礎示例

    我們先寫一個外掛APK,新建一個工程,修改MainActivity:
public class MainActivity extends Activity {

    private Activity otherActivity;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        boolean b = false;
        if (savedInstanceState != null) {
            b = savedInstanceState.getBoolean("KEY_START_FROM_OTHER_ACTIVITY", false);
            if (b) {
                this.otherActivity.setContentView(new TBSurfaceView(
                        this.otherActivity));
            }
        }
        if (!b) {
            super.onCreate(savedInstanceState);
            // setContentView(R.layout.main);
            setContentView(new TBSurfaceView(this));
        }
    }

    public void setActivity(Activity paramActivity) {
        this.otherActivity = paramActivity;
    }
}複製程式碼

在被載入的Activity中是不是識別和載入資原始檔的,所以不能用佈局檔案,只能用一個View。

public class TBSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
    private SurfaceHolder sfh;
    private Thread th;
    private Canvas canvas;
    private Paint paint;

    public TBSurfaceView(Context context) {
        super(context);
        th = new Thread(this);
        sfh = this.getHolder();
        sfh.addCallback(this);
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        this.setKeepScreenOn(true);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        th.start();
    }

    private void draw() {
        try {
            canvas = sfh.lockCanvas();
            if (canvas != null) {
                canvas.drawColor(Color.WHITE);
                canvas.drawText("Time: " + System.currentTimeMillis(), 100,
                        100, paint);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (canvas != null) {
                sfh.unlockCanvasAndPost(canvas);
            }
        }
    }

    public void run() {
        while (true) {
            draw();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
                               int height) {
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
    }
}複製程式碼

然後將生成的apk,不要安裝在手機,而是存入手機,比如我們就存入sd卡的根目錄,可以使用adb命令:

adb push /Users/xxxxx/file/source/loadActivity/app/build/outputs/apk/app-debug.apk /sdcard/複製程式碼

然後我們新建一個應用,寫一個載入的方法:

 public void dex() {


        String apkPath = "/sdcard/app-debug.apk";
        String optPath = "/mnt/sdcard/";
//        String libPath = info.activityInfo.applicationInfo.nativeLibraryDir;
        File dexOutputDir = getDir("dex", 0);

        DexClassLoader clsLoader = new DexClassLoader(apkPath, dexOutputDir.getAbsolutePath(),
                null, this.getClass().getClassLoader());
        try {

            Class localClass = clsLoader
                    .loadClass("deep.loadactivity.MainActivity");
            mActivityClass = localClass;
            Constructor localConstructor = localClass.getConstructor(new Class[] {});
            instance = localConstructor.newInstance(new Object[] {});
            mActivityInstance = instance;

            Method localMethodSetActivity = localClass.getDeclaredMethod(
                    "setActivity", new Class[] { Activity.class });
            localMethodSetActivity.setAccessible(true);
            localMethodSetActivity.invoke(instance, new Object[] { this });

            Method methodonCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });
            methodonCreate.setAccessible(true);
            Bundle paramBundle = new Bundle();
            paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY", true);
            paramBundle.putString("str", "MainActivity");
            methodonCreate.invoke(instance, new Object[] { paramBundle });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }複製程式碼

下面主要說一下DexClassLoader:

DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)複製程式碼

dexPath:被解壓的apk路徑,不能為空。
optimizedDirectory:解壓後的.dex檔案的儲存路徑,不能為空。
libraryPath:庫檔案的的搜尋路徑,一般來說是 .so 庫檔案的路徑,也可以指明多個路徑。
parent:父親載入器,一般為ClassLoader.getSystemClassLoader()。

說明

在上面的例子中我們其實是演示了一下動態載入Activity的方法,Activity被動態載入後,是沒有生命週期的,只是當做一個類來做處理,由上面程式可以看出,我們是手動呼叫onCreate方法的。
當然,我們也可以寫一些其他類或方法進行呼叫,不一定非要用Activity。

開源框架

說完了外掛機制,我們也認識到了外掛機制的一些不足,就比如剛才說的,Activity生命週期沒有了,需要手動呼叫各個方法,那給我們的開發帶來了很多不方便,但是網上的一些開源框架,解決了這些不方便,將呼叫的方法,順序封裝好,我們只管呼叫即可。
這些框架,我會在後面的文章中詳細演示。

android-pluginmgr

利用DexMaker的動態熱部署功能來實現Activity。

  • 優點:
    1.外掛app不需要任何規則和限制
    2.技術方法相對成熟穩定
  • 缺點:
    1.oom問題突出
    2.只支援Activity,不支援其它元件。

dynamic-load-apk

這是基於代理的方式實現外掛框架的,需要按照一定的規則來開發外掛APK。

  • 優點:
    1.外掛需要遵循一些規則,更加可控
    2.方案簡單
  • 缺點:
    1.不支援this呼叫元件中的方法,需要that,有些難理解
    2.不支援隱式呼叫APK內部的Activity
    3.相容性問題較多

DynamicAPK

這是攜程實現的一種多APK/DEX載入的外掛框架。

  • 優點:
    1.很少修改即可實現改造
    2.提升工程編譯速度
    3.可實現熱更新
    4.提高App的啟動速度
  • 缺點:
    1.不支援so庫
    2.不支援aar,maven遠端倉庫的依賴

DroidPlugin

這是360實現的一種外掛框架,他可以直接執行第三方獨立得APK。完全不需要對APK進行修改或安裝。

  • 優點:
    1.支援四大元件
    2.支援所有系統API
    3.外掛與外掛之間,外掛與宿主之間的程式碼和資源完全隔離
    4.實現了程式管理,佔用記憶體低
  • 缺點:
    1.不支援自定義資源的Notification
    2.不支援IntentFilter
    3.缺乏對Native層的Hook操作
    4.由於外掛與外掛,外掛與宿主之間完全隔離,因此,如果需要通訊,需要Android系統級別的通訊方式。

Small

這是一個跨平臺的外掛化框架。

  • 優點:
    1.外掛編碼與資原始檔的使用與普通開發無差別
    2.通過設定URI,宿主可以方便地與外掛間進行通訊
    3.支援Android IOS HTML5
    *缺點:
    不支援Service的動態註冊。

總結

以上是我對外掛調研的一個總結,在後面的演示中,我會將主流的外掛化框架進行詳細說明。

相關文章