關於利用java反射實現,Android 工程插件化的理解。個人認爲並不是上下文的注入,而是調用。

最近在研究Android 工程的插件化,也可是說是熱更新,讓項目插件化,功能模塊放到插件工程中,編譯爲插件apk。已實現在不重新安裝開發包的基礎上,達到更新項目功能模塊的效果。當然也可以用於換膚

了很多網上的資料和視頻,感覺有些地方還是很誤導讀者。所以在這裏整理一下,關於java反射使Android 項目插件化的原理。

實際上是利用了java反射的原理和DexClassLoader 這個類,載入apk中的class類和資源。

  dexClassLoader = new DexClassLoader(dexpath,dexOutputPath, null, mContext.getClassLoader());
                AssetManager assetManager = AssetManager.class.newInstance();
                Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
                addAssetPath.invoke(assetManager, dexpath);
                resources = new Resources(assetManager,
                        mContext.getResources().getDisplayMetrics(),
                        mContext.getResources().getConfiguration());

執行了上面的代碼,就可以得到兩個類 一個是 Resources類型,一個是DexClassLoader類型

而關於很多文章中提到的,上下文注入,這裏就誤導了讀者。實際上並不是把代理Activity的上下文注入到了插件apk中加載進來

Activity ,而只是代理Activity調用了插件apk中的接口。所以插件包中的apk實際上可以直接寫成一個接口而不需繼承Activity。這樣也避免了代碼的冗餘。而且也免於覆蓋很多沒有必要處理的抽象類。寫成下面的形式是完全沒有問題的。實際上它只是一個接口而並不是一個Activity。所以其實很多開源的,在插件中繼承Actitity是不對的。因爲代碼只是給代理Activity調用而已。只需要實現代理Activity中調用的接口就可以了。下面的代碼中也把代理Activity的源代碼放進來(BaseContainerActivity)。並沒有完全覆蓋公共接口中的所有方法,只是一個簡單的示例。實際上還應該在代理的相應方法中調用公共接口的方法,以保證插件類中實現的方法被調用。關於公共接口,在插件工程和子工程中使用一模一樣的包名和類名就可以了。

service 等等 BroadcastReceiver等其他的類型原理也是相同的就不累述了。實際上可以用於實現任何類中的代碼的動態更改(可以隨時從服務器是下載相應的apk,不需要重新安裝)。個人覺得非常的靈活方便。

public  class   OpenActivity   implements OpenInterface {
    protected Activity activity;


    @Override
    public void onOpenStart() {

    }

    @Override
    public void onOpenResume() {

    }

    @Override
    public void onOpenPause() {

    }

    @Override
    public void onOpenStop() {

    }

    @Override
    public void onOpenDestroy() {

    }

    @Override
    public void onOpenCreate(Bundle savedInstanceState) {
        activity.setContentView(R.layout.activity_open);
        activity.findViewById(R.id.click).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(activity,"djfkjd",Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public void setProxy(Activity proxyActivity) {
        this.activity=proxyActivity;
    }

}
public class BaseContainerActivity extends Activity {
    private static String classname;

    public static void setClassname(String classname) {
        BaseContainerActivity.classname = classname;
    }

    public static final String CLASSNAME="classname";
    private OpenInterface openInterface;
    LoaderPluginManager loaderPluginManager;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.e("classname",classname);
            loaderPluginManager = LoaderPluginManager.create(this, classname);
        try {
            DexClassLoader loader = loaderPluginManager.getClassLoader();
            Class clazz = loader.loadClass(classname);
            Constructor<?> localConstructor = clazz.getConstructor(new Class[]{});
            openInterface = (OpenInterface) localConstructor.newInstance(new Object[]{});

            openInterface.setProxy(this);

            openInterface.onOpenCreate(savedInstanceState);

        } catch (Exception e) {
            Log.e("載入class出錯",e.getMessage());
            e.printStackTrace();
        }



    }

    @Override
    public ClassLoader getClassLoader() {

        if (loaderPluginManager == null) {
            return super.getClassLoader();
        } else {
            return loaderPluginManager.getClassLoader();
        }

    }

    @Override
    public Resources getResources() {
        if(loaderPluginManager==null){
            return  super.getResources();
        }else{
            return loaderPluginManager.getResource();
        }
    }

}

 

發佈了26 篇原創文章 · 獲贊 0 · 訪問量 8036
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章