android插件化开发activity篇

来源:互联网 发布:淘宝水印怎么设置大小 编辑:程序博客网 时间:2024/06/05 07:33

现在都使用过支付宝吧,支付宝里边有好多功能就是采用插件化开发的,例如支付宝里边的小黄车,小蓝车等功能,都是采用的插件化做的。一开始我认为是WebView做的,

后来打开手机里的显示边框布局发现不是WebView,是原生的。他采用的是插件化加载第三方应用。

采用插件化开发的好处有宿主app与插件进行分开编译;并发开发节约时间和成本;按需要下载模块,但是第一次加载比较慢。

下边我们来讲解一下这个插件化开发:在进行插件化开发的时候要,有个桥梁将宿主APP和插件进行互相连接,这个连接也就是一个标准,那么这个标准是什么呢,就是一个

接口,让宿主app和插件都使用这套标准接口,这个接口就写有关activity的生命周期的方法。

/** * 两端都使用的标准 * Created by yzl on 2017/10/10. */public interface PayInterface {    public void onCreate(Bundle savedInstance);    public void onStart();    public void onResume();    public void onPause();    public void onStop();    public void onDestroy();    public void onSaveInstanceState(Bundle outState);    public boolean onTouchEvent(MotionEvent event);    public void onBackPressed();    public void attach(Activity activity);}
标准接口写好了,然后进行写插件,我们这里写简单的插件,两个activity的展示。
在插件里我们要抽出一个基类activity,BaseActivity。让她继承activity并实现PayInterface. 插件中的主activity(mainactivity)继承BaseActivity。
现在mainactivity中要查找控件,使用this是不可以的,因为MainActivity.this是系统注入如入的上下文,插件只可以使用宿主注入的上下文也就是这里的that(所以
在接口中写里attach(Activity,activity),这个拿到的上下文就是宿主APP注入的上下文。
** * * 重写关于上下文的父方法 * Created by yzl on 2017/10/10. */public class BaseActivty extends Activity implements PayInterface{    protected Activity that;    @Override    public void setContentView(@LayoutRes int layoutResID) {        if(that == null){            super.setContentView(layoutResID);        }else {            that.setContentView(layoutResID);        }    }    @Override    public <T extends View> T findViewById(int id) {        if(that == null){            return super.findViewById(id);        }else {            return that.findViewById(id);        }    }    @Override    public ClassLoader getClassLoader() {        if(that == null){            return super.getClassLoader();        }else {            return that.getClassLoader();        }    }    @NonNull    @Override    public LayoutInflater getLayoutInflater() {        if(that == null){            return super.getLayoutInflater();        }else {            return that.getLayoutInflater();        }    }    @Override    public Window getWindow() {        if(that == null){            return super.getWindow();        }else {            return that.getWindow();        }    }    @Override    public WindowManager getWindowManager() {        if(that == null){            return super.getWindowManager();        }else {            return that.getWindowManager();        }    }    @Override    public void onCreate(Bundle savedInstance) {    }    @Override    public void onStart() {    }    @Override    public void onResume() {    }    @Override    public void onPause() {    }    @Override    public void onStop() {    }    @Override    public void onDestroy() {    }    @Override    public void onSaveInstanceState(Bundle outState) {            }    @Override    public void attach(Activity activity) {        this.that = activity;    }
}
public class MainActivity extends BaseActivty implements View.OnClickListener {    private TextView tv_chajian;    @Override    public void onCreate(Bundle savedInstance) {        super.onCreate(savedInstance);        setContentView(R.layout.activity_main);        tv_chajian = that.findViewById(R.id.tv_chajian);    }}

现在插件写好了,在说说宿主App。宿主app就是先加载在内存卡里存放的pak,然后找到要加载的activity的classname然后加载对应的class文件,在加载对应资源文件。
这里加载很明显分为四步,apk文件,classname,class文件,资源文件。加载apk我们简单现在不说明,现在说说其余三个。现在我们写一个插件管理器pluginManger
工具类,将类设计为单利的,保证对象的唯一性。找加载的第一个activity的classname,
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);dexClassLoader = new DexClassLoader(path,dexOutFile.getAbsolutePath(),null,context.getClassLoader());PackageManager packageManager = context.getPackageManager();PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,packageManager.GET_ACTIVITIES);entryActivityName = packageInfo.activities[0].name;
接下来在准备资源,利用反射的方法zhao dao
AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = AssetManager.class.getMethod("addAssetPath",String.class);addAssetPath.invoke(assetManager,path);resources = new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());
pluginManger工具类的代码如下:
/** * Created by yzl on 2017/10/10. */public class pluginManger {    private Context context;    private DexClassLoader dexClassLoader;    private Resources resources;    private String entryActivityName;    private static final pluginManger ourInstance = new pluginManger();    public static pluginManger getInstance() {        return ourInstance;    }    private pluginManger() {    }    public void loadpath(String path){        File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);        dexClassLoader = new DexClassLoader(path,dexOutFile.getAbsolutePath(),null,context.getClassLoader());        PackageManager packageManager = context.getPackageManager();        PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,packageManager.GET_ACTIVITIES);        entryActivityName = packageInfo.activities[0].name;        Log.i("插件activity的classname",entryActivityName);        try {            AssetManager assetManager = AssetManager.class.newInstance();            Method addAssetPath = AssetManager.class.getMethod("addAssetPath",String.class);            addAssetPath.invoke(assetManager,path);            resources = new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());        } catch (Exception e) {            e.printStackTrace();        }    }    public DexClassLoader getDexClassLoader() {        return dexClassLoader;    }    public Resources getResources() {        return resources;    }    public String getEntryActivityName() {        return entryActivityName;    }    public void setContext(Context context) {        this.context = context.getApplicationContext();    }}

现在要加载的class文件的名字与资源都准备好了,接下来去加载对应的class文件,并显示。name宿主APP显示一个界面,那就必须的有容器去放置这个内容,那么我们去
插件一个宿主activity(ProxyActivity
    className = getIntent().getStringExtra("classname");//得到要加载的全类名    Class activityClazz = getClassLoader().loadClass(className);    Constructor constructor = activityClazz.getConstructor(new Class[]{});//创建构造方法    Object instance = constructor.newInstance(new Object[]{});    payInterface = (PayInterface) instance;//将得到的实力转化为标准的接口类型    payInterface.attach(this);    Bundle bundle = new Bundle();//创建bundle对象便于传递参数    payInterface.onCreate(bundle);//创建
这样界面还是空白的因为她还没有生命周期,我们要将对应的生命周期加上就完美了。
/** * Created by yzl on 2017/10/10. */public class ProxyActivity extends Activity {    private String className;//要跳转的activity    private PayInterface payInterface;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        className = getIntent().getStringExtra("classname");//得到要加载的全类名        Log.i("传递过来的class名称为",className);        try {            Class activityClazz = getClassLoader().loadClass(className);            Constructor constructor = activityClazz.getConstructor(new Class[]{});            Object instance = constructor.newInstance(new Object[]{});            payInterface = (PayInterface) instance;            payInterface.attach(this);            Bundle bundle = new Bundle();            payInterface.onCreate(bundle);        } catch (Exception e) {            e.printStackTrace();        }        //利用反射加载class对应的class文件        //因为插件不在art虚拟机里安装的,所以直接使用凡事是找不到class文件的,        //这个类所加载的class都是插件中的class,不能是宿主中的class,所以要重写getclassloader();    }    @Override    protected void onStart() {        super.onStart();        payInterface.onStart();    }    @Override    protected void onResume() {        super.onResume();        payInterface.onResume();    }    @Override    protected void onPause() {        super.onPause();        payInterface.onPause();    }    @Override    protected void onStop() {        super.onStop();        payInterface.onStop();    }    @Override    protected void onDestroy() {        super.onDestroy();        payInterface.onDestroy();    }    @Override    public ClassLoader getClassLoader() {        return pluginManger.getInstance().getDexClassLoader();        //由于super是系统注入的上下文(也就是本宿主的上下文)xi    }    @Override    public Resources getResources() {        return pluginManger.getInstance().getResources();    }    @Override    public void startActivity(Intent intent) {        String twoActivityClass = intent.getStringExtra("className");        Intent intent1 = new Intent(this,ProxyActivity.class);        intent1.putExtra("classname",twoActivityClass);        super.startActivity(intent1);    }}



原创粉丝点击