dl插件框架 - 《Andriod必备技能》

来源:互联网 发布:淘宝数据采集器破解版 编辑:程序博客网 时间:2024/06/05 00:12

DLTest

百度dl框架研究学习demo

github:https://github.com/andoop/DLTest

关于dl框架

  1. 插件加载框架
  2. 与DLoad(动态加载,我的另一个工程)原理一样,但是封装处理的很好,Dload在此
  3. 在插件(动态包中)支持R访问资源(在Dload中,我们是通过流来获取图片资源的)
  4. 可维护多个插件(动态包),Dload中,我们只做了加载一个动态包的功能
  5. 实际开发中,可直接在dl框架上修改使用即可,还是比较实用的
  6. 虽然也可以加载还有.so文件的插件(动态包,下面都统一叫做“插件”),但是会有好多问题的

先上demo,一睹为快

首页

demo首页,显示插件列表,有两个插件:main_plugin_a和main_plugin_b

#
首页

进入main_plugin_a中

#
首页

开启main_plugin_a中的service

#

首页

关闭main_plugin_a中的service

#
首页

打开main_plugin_a中另一个activity

#

首页

进入插件main_plugin_b


#

首页

在插件b中调用宿主方法


#
首页

在宿主中调用插件main_plugin_b中的方法


#

分析一下demo代码

看一下宿主工程

MainActivity 的initData方法

     private void initData() {            //得到插件存放的位置,demo中用到的插件存放到了sdcard的dltest文件夹下            String pluginFolder = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+"dltest";            File file = new File(pluginFolder);            File[] plugins = file.listFiles();            if (plugins == null || plugins.length == 0) {                mNoPluginTextView.setVisibility(View.VISIBLE);                return;            }            //遍历dltest文件夹下所有插件            for (File plugin : plugins) {                //PluginItem是我自己定义的类                PluginItem item = new PluginItem();                item.pluginPath = plugin.getAbsolutePath();                //使用DlUtils获取插件包信息                item.packageInfo = DLUtils.getPackageInfo(this, item.pluginPath);                if (item.packageInfo.activities != null && item.packageInfo.activities.length > 0) {                    //得到插件的主activity                    item.launcherActivityName = item.packageInfo.activities[0].name;                }                if (item.packageInfo.services != null && item.packageInfo.services.length > 0) {                    item.launcherServiceName = item.packageInfo.services[0].name;                }                mPluginItems.add(item);                //加载插件,第二个参数为false,标识没有.so文件                DLPluginManager.getInstance(this).loadApk(item.pluginPath,false);            }            mListView.setAdapter(mPluginAdapter);            mListView.setOnItemClickListener(this);            mPluginAdapter.notifyDataSetChanged();        }

点击ListView中对应条目(会调起插件)

  @Override    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {        PluginItem item = mPluginItems.get(position);        DLPluginManager pluginManager = DLPluginManager.getInstance(this);        //打开插件的中的Activity        pluginManager.startPluginActivity(this, new DLIntent(item.packageInfo.packageName, item.launcherActivityName));    }

看看main_plugin_a插件中
//继承dl框架中的DLBasePluginActivitypublic class MainActivity extends DLBasePluginActivity implements View.OnClickListener {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //正常使用布局文件        setContentView(R.layout.activity_main);        findViewById(R.id.btn_openSecond).setOnClickListener(this);        findViewById(R.id.btn_openService).setOnClickListener(this);        findViewById(R.id.btn_stopService).setOnClickListener(this);    }    //开启插件中另一个Activity    public void openSecond(){        startPluginActivity(new DLIntent(getPackageName(),SecondTestActivity.class));    }    //开启服务    private void openService() {        startPluginService(new DLIntent(getPackageName(),TestService.class));    }    //停止服务    private void stopService() {        stopPluginService(new DLIntent(getPackageName(),TestService.class));    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn_openSecond:                openSecond();                break;            case R.id.btn_openService:                openService();                break;            case R.id.btn_stopService:                stopService();                break;        }    }}

在插件中,所有的Activity都要继承dl中的DLBasePluginActivity,所有的FragmentActivity都要继承dl中的DLBasePluginFragmentActivity,所有的service都要继承dl中的DLBasePluginService,具体使用请看demo即可

开启Activity使用startPluginActivity(DLIntent);开启服务使用startPluginService(DLIntent);停止服务使用stopPluginService(DLIntent),当然也可以绑定服务的,有相关方法


看看main_plugin_b,插件b实现了与宿主的互相调用

插件b中

public class MainActivity extends DLBasePluginFragmentActivity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        findViewById(R.id.btn_plugin_b).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //得到宿主实现的接口对象                IHost host = InterfaceManager.getHost();                if(host==null){                    Toast.makeText(MainActivity.this.that,"hello", Toast.LENGTH_SHORT).show();                    return;                }                //调用宿主方法                String hostMethod = host.hostMethod(that,"在插件main_plugin_b中调用>>");                Toast.makeText(MainActivity.this.that, hostMethod, Toast.LENGTH_SHORT).show();            }        });        //传入插件b实现的接口对象,宿主得到后可以调用其方法        InterfaceManager.setPlugin(new IPlugin() {            @Override            public String pulginMethod(String s) {                return s+"我是插件b";            }        });    }}

IHost、IPlugin和InterfaceManager没有在dl的library中,存在于我写的另一个library中,在这个类库中,主要定义插件和宿主之间调用的接口,demo中只是定义了一个简单的接口,详情请看demo中interactlib。


在宿主中调用插件b实现IPlugin接口的对象的方法

 public void doActionWithPlugin(View view){        String s = InterfaceManager.getPlugin().pulginMethod("在host中调用>>>");        Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();    }

插件打包使用

插件应该从服务端下载,demo中为了演示方便,将插件APk通过adb命令push到了sdcard的dltest文件夹下

task命令如下

def dlPath = '/sdcard/dltest'def upload = { ->        try {            //创建dltest文件夹            exec {                commandLine 'E:/android_dev/sdk/sdk/platform-tools/adb.exe', 'shell', 'mkdir', dlPath            }        }        catch (ignored) {        }        try{            //将所有工程中生成的apk上传到dltest文件夹中            exec {                commandLine 'E:/android_dev/sdk/sdk/platform-tools/adb.exe', 'push', project.name + '-debug.apk', dlPath                workingDir project.projectDir.toString() + '/build/outputs/apk/'            }        }        catch (ignored){}    }    task uploadDebug << {        upload()    }

最后执行uploadDebug任务即可

在这里不用通过dx命令再次处理插件包了,是因为这里的插件包是apk文件,apk文件中的.class文件已经被处理过了


dl原理分析

占坑
 <activity android:name="com.ryg.dynamicload.DLProxyActivity"/>    <activity android:name="com.ryg.dynamicload.DLProxyFragmentActivity"/>    <service android:name="com.ryg.dynamicload.DLProxyService"        android:enabled="true"        android:exported="true"/>

插件中的Activity,FragmentActivity,service要分别继承dl的DLBasePluginActivity、DLBasePluginFragmentActivity和DLBasePluginService

dl中的DLProxyActivity,DLProxyFragmentActivity,DLProxyService会代理插件中Activity,FragmentActivity,service的生命周期


插件中组件关联代理组件

以Activity为例:
DLProxyActivity实现了DLAttachable接口

public interface DLAttachable {    public void attach(DLPlugin proxyActivity, DLPluginManager pluginManager);}

他调用attach方法将被代理的Activity传过来(插件中继承了DLBasePluginActivity的Activity)

DLBasePluginActivity实现了DLPlugin,DLPlugin如下

public interface DLPlugin {    public void onCreate(Bundle savedInstanceState);    public void onStart();    public void onRestart();    public void onActivityResult(int requestCode, int resultCode, Intent data);    public void onResume();    public void onPause();    public void onStop();    public void onDestroy();    //调用这里的attach,将代理Activity传入到了DLBasePluginActivity中(插件中Activity)    public void attach(Activity proxyActivity, DLPluginPackage pluginPackage);    public void onSaveInstanceState(Bundle outState);    public void onNewIntent(Intent intent);    public void onRestoreInstanceState(Bundle savedInstanceState);    public boolean onTouchEvent(MotionEvent event);    public boolean onKeyUp(int keyCode, KeyEvent event);    public void onWindowAttributesChanged(LayoutParams params);    public void onWindowFocusChanged(boolean hasFocus);    public void onBackPressed();    public boolean onCreateOptionsMenu(Menu menu);    public boolean onOptionsItemSelected(MenuItem item);}

都是Activity的生命周期方法

在DLProxyActivity中

@Override    protected void onStart() {        mRemoteActivity.onStart();        super.onStart();    }    @Override    protected void onRestart() {        mRemoteActivity.onRestart();        super.onRestart();    }    @Override    protected void onResume() {        mRemoteActivity.onResume();        super.onResume();    }    @Override    protected void onPause() {        mRemoteActivity.onPause();        super.onPause();    }

摘出了部分代码,mRemoteActivity就是被代理的DLBasePluginActivity(插件中的Activity)


插件中为什么可以通过R来访问图片呢?

重写Activity中的 getAssets()和getResources()并返回自己的重写构建AssetManager,(AssetManager中对应的资源路径,改为插件路径即可)和由此生成的Resources即可,

dl中体现如下:(DLPluginManager中)

  AssetManager assetManager = createAssetManager(dexPath);  Resources resources = createResources(assetManager);

private AssetManager createAssetManager(String dexPath) {        try {            AssetManager assetManager = AssetManager.class.newInstance();            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);            addAssetPath.invoke(assetManager, dexPath);            return assetManager;        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    private Resources createResources(AssetManager assetManager) {        Resources superRes = mContext.getResources();        Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());        return resources;    }

dexPath即为插件路径,例如:/sdcard/dltest/xxx.apk


怎样实例化插件中类的呢(DLPluginManager中)
 private DexClassLoader createDexClassLoader(String dexPath) {        File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE);        dexOutputPath = dexOutputDir.getAbsolutePath();        //初始化一个dexclassloader        DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir, mContext.getClassLoader());        return loader;    }

       DexClassLoader dexClassLoader = createDexClassLoader(dexPath);        AssetManager assetManager = createAssetManager(dexPath);        Resources resources = createResources(assetManager);        // create pluginPackage        pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);        mPackagesHolder.put(packageInfo.packageName, pluginPackage);

每一个插件都会对应一个dexclassloder,生成的dexclassload会被封装到DLPluginPackage对象中,而这个对象会被放到以插件包名为键的map中,这样插件和dexclassloader就一一对应了


  protected void launchTargetActivity() {        try {            //根据类名生成一个插件中Activity的对象            Class<?> localClass = getClassLoader().loadClass(mClass);            Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});            Object instance = localConstructor.newInstance(new Object[] {});            //生成的插件中Activity的对象            mPluginActivity = (DLPlugin) instance;            //代理Activity关联插件中的Activity            ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager);            Log.d(TAG, "instance = " + instance);            // attach the proxy activity and plugin package to the mPluginActivity            //插件中Activity关联代理Activity            mPluginActivity.attach(mProxyActivity, mPluginPackage);            Bundle bundle = new Bundle();            bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL);            mPluginActivity.onCreate(bundle);        } catch (Exception e) {            e.printStackTrace();        }    }

上面是生成插件中Activity对象的过程,插件中service对象生成与关联代理的流程,是类似的,这个不过多讨论了


在插件中使用上下文时,慎用this

插件中的Activity最后是被当做普通的类,通过反射得到其实例的,系统不会管理器生命周期,通过this获取上下文已经不行了,但是dl为我们提供了关键字that,that会在不同的情况下,指向本Activity或者代理Activity,所以,在使用this行不通的情况下,可以使用that。


注意点

插件和宿主之间的桥梁其实是接口,如demo中宿主和插件b之间相互调用的接口,宿主和插件b都依赖了interactlib工程,但是插件b打包的时候,却不能包含任何接口文件,否者会报异常,demo中处理如下:

dependencies {    compile fileTree(include: ['*.jar'], dir: 'libs')    testCompile 'junit:junit:4.12'    //去除v7中的v4包,防止跟宿主v4冲突    compile('com.android.support:appcompat-v7:23.4.0') {        exclude module: 'support-v4'    }    compile project(':dllib')    //私有引用,打包时不会包含    provided files('../interactlib/build/libs/interactlib.jar')}

上面是插件b的dependencies


以上就是dl的使用方法,还有它的原理解析,具体使用还是请参考demo,

更多学习>>dl 源码地址:
https://github.com/singwhatiwanna/dynamic-load-apk
欢迎关注andoop,周一、二内容更新,干货永不断!
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 衣服上有502胶水怎么办 衣服上面粘了胶怎么办 衣服上的胶干了怎么办 凌晨4点到火车站怎么办 运管罚款没钱交怎么办 郑州地铁票没买怎么办 遇到吸毒者拦路威胁要钱怎么办 开车遇见拦路要钱的怎么办 高速上有人拦车怎么办 马路上有人拦车怎么办 苹果手机下截软件要钱怎么办 孩子在学校问同学要钱怎么办 在学校被同学要钱怎么办 把人家店砸了要怎么办 外汇出金不到账怎么办 把罚款单弄丢了怎么办 在12306买不到下铺怎么办有 地铁票买反了怎么办 香港买错特惠票怎么办 到达迪拜t3 后怎么办 海藻面膜调多了怎么办 被鸡爪子抓伤了怎么办 被鸡抓伤肿了怎么办 护士电子化没有激活码怎么办 窗帘盒螺丝掉了怎么办 窗帘的环扣掉了怎么办 门式起重吊装行车脱轨怎么办 在日本丢了东西怎么办 在日本钱包丢了怎么办 被起诉后没钱还怎么办 分期付款卖车打不起车款怎么办 地铁票买多了怎么办 工伤陪护费没有发票怎么办 工伤医疗费报销单位不盖章怎么办 家里的led灯坏了怎么办 吊顶led灯坏了怎么办 客厅空了一面墙怎么办 轨道灯的轨道不够长怎么办 奔驰大灯不亮了怎么办 led顶灯不亮了怎么办 吸顶灯led灯坏了怎么办