安卓插件化开发-插桩式

插件化开发:

插件化开发已经是从热门开始走向衰落的地步了,但是其中的技术点还是很值的我们学习的,简单记录下学习之路。

插件化开发的好处:

1.不需要将apk安装到手机,下载就可以使用;
2.可以动态更新插件apk;
3.对于游戏sdk行业,插件化开发sdk可以减轻cp的接入与更新,通过动态或者逆向替换插件实现sdk的更新;
4.等等。

插件化开发的缺点:

1.开发难度大,适配各个版本,各种问题;
2.安卓高版本api的限制;
3.等等;

通过插桩式来实现加载插件

实现的效果:

宿主apk页面

插件Activity1
插件Activity2在这里插入图片描述

要解决的问题:

  1. 我们的插件apk是没有安装到手机的,四大组件不具备上下文对象,没有生命周期,类没有被加载,资源没有被加载。

开发实现:

1.没有上下文,生命周期,通过宿主apk传递,通过代理模式,代理生命周期。首先实现一个Activity的代理对象。

public interface PluginActivityInterface {

    public void attachActivity(Activity activity);
    public void onCreate(Bundle savedInstanceState);
    public void onResume();
    public void onPause();
    public void onStart();
    public void onRestart();
    public void onStop();
    public void onDestroy();
    public void onSaveInstanceState(Bundle outState);
    public boolean onTouchEvent(MotionEvent event);
    public void onBackPressed();
    
}

在插件中实现一个BaseActivity实现那这套接口,重写需要上下调用的方法,让他在宿主apk实现,代码中加了很多 plugActivity是否为null的情况是为了让插件apk也能够单独的运行。

public class BaseActivity extends Activity implements PluginActivityInterface {

    Activity plugActivity;
    public static final String TAG ="BaseActivity";

    @Override
    public void attachActivity(Activity activity) {
        plugActivity = activity;
        Log.d(TAG,"attachActivity plugActivity="+plugActivity);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.d(TAG,"onCreate");
        if (plugActivity == null) {
            super.onCreate(savedInstanceState);
        }
    }

    @Override
    public void setContentView(int layoutResID) {
        if (plugActivity != null) {
            plugActivity.setContentView(layoutResID);
        }else {
            super.setContentView(layoutResID);
        }
    }

    @Override
    public ClassLoader getClassLoader() {
        if (plugActivity != null) {
            return plugActivity.getClassLoader();
        }
        return super.getClassLoader();
    }

    @Override
    public  View findViewById(int id) {
        if (plugActivity != null) {
            return plugActivity.findViewById(id);
        }
        return super.findViewById(id);
    }

    @Override
    public void startActivity(Intent intent) {
        if (plugActivity != null){
            plugActivity.startActivity(intent);
        }else {
            super.startActivity(intent);
        }
    }

    @Override
    public void setContentView(View view) {
        if (plugActivity != null) {
            plugActivity.setContentView(view);
        }else {
            super.setContentView(view);
        }
    }

    @Override
    public Window getWindow() {
        if (plugActivity != null) {
            return plugActivity.getWindow();
        }
        return super.getWindow();
    }


    @Override
    public WindowManager getWindowManager() {
        if (plugActivity != null) {
            return plugActivity.getWindowManager();
        }
        return super.getWindowManager();
    }

    @Override
    public Intent getIntent() {
        if(plugActivity!=null){
            return plugActivity.getIntent();
        }
        return super.getIntent();
    }

    @NonNull
    @Override
    public LayoutInflater getLayoutInflater() {
        if(plugActivity!=null) {
            return plugActivity.getLayoutInflater();
        }
        return super.getLayoutInflater();
    }

    @Override
    public ApplicationInfo getApplicationInfo() {
        if(plugActivity!=null) {
            return plugActivity.getApplicationInfo();
        }
        return super.getApplicationInfo();
    }

    @Override
    public void onResume() {
        Log.d(TAG, "onResume");
        if (plugActivity == null) {
            super.onResume();
        }
    }

    @Override
    public void onPause() {
        Log.d(TAG, "onPause");
        if (plugActivity == null) {
            super.onPause();
        }
    }

    @Override
    public void onStart() {
        Log.d(TAG, "onStart");
        if (plugActivity == null) {
            super.onStart();
        }
    }

    @Override
    public void onRestart() {
        Log.d(TAG, "onRestart");
        if (plugActivity == null) {
            super.onRestart();
        }
    }

    @Override
    public void onStop() {
        Log.d(TAG, "onStop");
        if (plugActivity == null) {
            super.onStop();
        }
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        if (plugActivity == null) {
            super.onDestroy();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {

    }
}

然后让插件中的MainActivity去继承BaseActivity,

public class MainActivity extends BaseActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setOnclick();
    }

    private void setOnclick() {
        View show = findViewById(R.id.btn_show);
        View start = findViewById(R.id.btn_start);
        show.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (plugActivity != null) {
                    Log.d("lkx_debug", "插件show");
                    Toast.makeText(plugActivity, "哈哈", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "哈哈", Toast.LENGTH_SHORT).show();
                }
            }
        });

        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (plugActivity != null) {
                    Log.d("lkx_debug", "插件start");
                    startActivity(new Intent(plugActivity, TopActivity.class));
                } else {
                    startActivity(new Intent(MainActivity.this, TopActivity.class));
                }
            }
        });
    }
}

然后编译打包生成一个apk,我们将其拷贝到宿主的assets目录下

2.加载插件,根据插件路径,创建加载apk的ClassesLoader,和Resource对象

 /**
     * 加载插件
     */
    public void loadPlugin(Context context, String plugPath) {
        this.context = context;
        this.plugPath = plugPath;
        PackageManager packageManager = context.getPackageManager();
        packageInfo = packageManager.getPackageArchiveInfo(plugPath, PackageManager.GET_ACTIVITIES);

        File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
        dexClassLoader = new DexClassLoader(plugPath, dexOutFile.getAbsolutePath()
                , null, context.getClassLoader());
        try {
            AssetManager assetManager = AssetManager.class.newInstance();
            Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
            addAssetPath.invoke(assetManager, plugPath);
            resources = new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.在宿主apk中创建一个代理Activity

public class ProxyActivity extends AppCompatActivity {

    private String activityName;
    PluginActivityInterface pluginActivityInterface;
    private static final String TAG = "ProxyActivity";

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityName = getIntent().getStringExtra("activity_name");
        try {
            Class activityClass = getClassLoader().loadClass(activityName);
            Constructor constructor = activityClass.getConstructor(new Class[]{});
            Object instance = constructor.newInstance(new Object[]{});

            pluginActivityInterface = (PluginActivityInterface) instance;
            pluginActivityInterface.attachActivity(this);
            Bundle bundle = new Bundle();
            pluginActivityInterface.onCreate(bundle);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     *ProxyActivity的启动模式必须为standard
     * @param intent
     */
    @Override
    public void startActivity(Intent intent) {
        Intent intent1 = new Intent(this,ProxyActivity.class);
        intent1.putExtra("activity_name",intent.getComponent().getClassName());
        Log.d("lkx_debug","intent.getComponent().getClassName()="+intent.getComponent().getClassName());
        super.startActivity(intent1);
    }

    @Override
    public ClassLoader getClassLoader() {
        return PluginManager.getInstance().getClassLoader();
    }

    @Override
    public Resources getResources() {
        return PluginManager.getInstance().getResources();
    }


    @Override
    protected void onStart() {
        super.onStart();
        pluginActivityInterface.onStart();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        pluginActivityInterface.onDestroy();
    }

    @Override
    protected void onPause() {
        super.onPause();
        pluginActivityInterface.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        pluginActivityInterface.onResume();
    }

    @Override
    protected void onStop() {
        super.onStop();
        pluginActivityInterface.onStop();
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        pluginActivityInterface.onRestart();
    }
}

当我们需要加载插件中的MainActivity的时候,我们通过新创建的ClassesLoader去反射加载插件中的MainActivity对象,将其转为PluginActivityInterface,然后在ProxyActivity的生命周期中调用对应的方法就让插件中的MainActivity获得了生命周期。setContentView调用的是ProxyActivity的方法,但是资源是用的插件中的资源。所以插件MainActivity就展示出来了。

完整代码:https://gitee.com/kabuda777/KaBuPluginOne.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值