dl插件框架 - 《Andriod必备技能》
来源:互联网 发布:淘宝数据采集器破解版 编辑:程序博客网 时间:2024/06/05 00:12
DLTest
百度dl框架研究学习demo
github:https://github.com/andoop/DLTest
关于dl框架
- 插件加载框架
- 与DLoad(动态加载,我的另一个工程)原理一样,但是封装处理的很好,Dload在此
- 在插件(动态包中)支持R访问资源(在Dload中,我们是通过流来获取图片资源的)
- 可维护多个插件(动态包),Dload中,我们只做了加载一个动态包的功能
- 实际开发中,可直接在dl框架上修改使用即可,还是比较实用的
- 虽然也可以加载还有.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,周一、二内容更新,干货永不断!
- dl插件框架 - 《Andriod必备技能》
- DroidPlugin插件框架 - 《Android必备技能》
- DL插件化框架
- Java 反射,开发框架必备技能
- Android 使用动态加载框架DL进行插件化开发
- Android--使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- Android 使用动态加载框架DL进行插件化开发
- 【GCC调试程序C语言问题】对‘ceil’未定义的引用/已经包含头文件仍然提示未定义的引用
- 征程,出发,成长,实现
- desSlideshow.js——tab图片幻灯片(竖)
- Android LRecyclerView实现Item侧滑菜单、长按拖拽Item、滑动删除Item等功能
- 2D 道具提示 HUD
- dl插件框架 - 《Andriod必备技能》
- javaSE javaEE javaME的区别
- ExtJs中Radio和radiogroup获取值
- php数组下标不为不连续的数字的问题
- C++拷贝构造函数的使用时机 笔试题
- mysql_config_editor的用法
- 利用ServletContextListener 获取spring上下文
- GeometryService方法研究之intersect
- volley工具包的第二种简介的方法