--《摘自android插件化开发指南》

1.有些项目,整个app只有一个Activity,切换页面全靠Fragment,盛行过一时,但有点极端

2.Activity切换fragment页面

第一步:FragmentLoaderActivity作为Fragment的承载容器

<activity android:name=".FragmentLoaderActivity">
<intent-filter>
<action android:name="jianqiang.com.hostapp.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
public class BaseHostActivity extends Activity {
private AssetManager mAssetManager;
private Resources mResources;
private Theme mTheme; protected String mDexPath;
protected ClassLoader dexClassLoader; protected void loadClassLoader() {
File dexOutputDir = this.getDir("dex", Context.MODE_PRIVATE);
final String dexOutputPath = dexOutputDir.getAbsolutePath();
dexClassLoader = new DexClassLoader(mDexPath,
dexOutputPath, null, getClassLoader());
}
protected void loadResources() {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, mDexPath);
mAssetManager = assetManager;
} catch (Exception e) {
e.printStackTrace();
}
Resources superRes = super.getResources();
mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),
superRes.getConfiguration());
mTheme = mResources.newTheme();
mTheme.setTo(super.getTheme());
} @Override
public AssetManager getAssets() {
return mAssetManager == null ? super.getAssets() : mAssetManager;
} @Override
public Resources getResources() {
return mResources == null ? super.getResources() : mResources;
} @Override
public Theme getTheme() {
return mTheme == null ? super.getTheme() : mTheme;
}
}
public class FragmentLoaderActivity extends BaseHostActivity {

    private String mClass;

    @Override
protected void onCreate(Bundle savedInstanceState) {
mDexPath = getIntent().getStringExtra(AppConstants.EXTRA_DEX_PATH);
mClass = getIntent().getStringExtra(AppConstants.EXTRA_CLASS); super.onCreate(savedInstanceState); setContentView(R.layout.activity_fragment_loader); loadClassLoader();
loadResources(); try {
//反射出插件的Fragment对象
Class<?> localClass = dexClassLoader.loadClass(mClass);
Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});
Object instance = localConstructor.newInstance(new Object[] {});
Fragment f = (Fragment) instance;
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.container, f);
ft.commit();
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}

第二步:

MainActivity跳转到FragmentLoaderActivity,传两个参数(dexPath和fragment的名称),FragmentLoaderActivity根据参数加载对应的Fragment

Intent intent = new Intent(AppConstants.ACTION);
intent.putExtra(AppConstants.EXTRA_DEX_PATH, mPluginItems.get(position).pluginPath);
intent.putExtra(AppConstants.EXTRA_CLASS, mPluginItems.get(position).packageInfo.packageName + ".Fragment1");
startActivity(intent);

3.插件内部的Fragment跳转

public class BaseFragment extends Fragment {
private int containerId; public int getContainerId() {
return containerId;
} public void setContainerId(int containerId) {
this.containerId = containerId;
}
}
public class Fragment2 extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment2, container, false); if (getArguments() != null) {
String username = getArguments().getString("username");
TextView tv = (TextView)view.findViewById(R.id.label);
tv.setText(username);
} view.findViewById(R.id.btnReturn).setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View arg0) {
//从栈中将当前fragment推出
getFragmentManager().popBackStack();
}
}); return view;
}
}
public class Fragment1 extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false); view.findViewById(R.id.load_fragment2_btn).setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View arg0) { Fragment2 fragment2 = new Fragment2();
Bundle args = new Bundle();
args.putString("username", "baobao");
fragment2.setArguments(args); getFragmentManager()
.beginTransaction()
.addToBackStack(null) //将当前fragment加入到返回栈中
.replace(Fragment1.this.getContainerId(), fragment2).commit();
}
}); return view;
}
}

其实就是利用FragmentManager动态切换Fragment技术来实现

4.插件Fragment跳转插件外部的Fragment(包括宿主中的,另一个插件中的)

第一步:把宿主和插件的资源都合并到一起,这样就能想用哪个资源就用哪个资源

public class PluginManager {
public final static List<PluginItem> plugins = new ArrayList<PluginItem>(); //正在使用的Resources
public static volatile Resources mNowResources; //原始的application中的BaseContext,不能是其他的,否则会内存泄漏
public static volatile Context mBaseContext; //ContextImpl中的LoadedAPK对象mPackageInfo
private static Object mPackageInfo = null; public static void init(Application application) {
//初始化一些成员变量和加载已安装的插件
mPackageInfo = RefInvoke.getFieldObject(application.getBaseContext(), "mPackageInfo"); mBaseContext = application.getBaseContext();
mNowResources = mBaseContext.getResources(); try {
AssetManager assetManager = application.getAssets();
String[] paths = assetManager.list(""); ArrayList<String> pluginPaths = new ArrayList<String>();
for(String path : paths) {
if(path.endsWith(".apk")) {
String apkName = path; PluginItem item = generatePluginItem(apkName);
plugins.add(item); Utils.extractAssets(mBaseContext, apkName); pluginPaths.add(item.pluginPath);
}
} reloadInstalledPluginResources(pluginPaths); } catch (Exception e) {
e.printStackTrace();
}
} private static PluginItem generatePluginItem(String apkName) {
File file = mBaseContext.getFileStreamPath(apkName);
PluginItem item = new PluginItem();
item.pluginPath = file.getAbsolutePath();
item.packageInfo = DLUtils.getPackageInfo(mBaseContext, item.pluginPath); return item;
} private static void reloadInstalledPluginResources(ArrayList<String> pluginPaths) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, mBaseContext.getPackageResourcePath()); for(String pluginPath: pluginPaths) {
addAssetPath.invoke(assetManager, pluginPath);
} Resources newResources = new Resources(assetManager,
mBaseContext.getResources().getDisplayMetrics(),
mBaseContext.getResources().getConfiguration()); RefInvoke.setFieldObject(mBaseContext, "mResources", newResources);
//这是最主要的需要替换的,如果不支持插件运行时更新,只留这一个就可以了
RefInvoke.setFieldObject(mPackageInfo, "mResources", newResources); //清除一下之前的resource的数据,释放一些内存
//因为这个resource有可能还被系统持有着,内存都没被释放
//clearResoucesDrawableCache(mNowResources); mNowResources = newResources;
//需要清理mtheme对象,否则通过inflate方式加载资源会报错
//如果是activity动态加载插件,则需要把activity的mTheme对象也设置为null
RefInvoke.setFieldObject(mBaseContext, "mTheme", null);
} catch (Throwable e) {
e.printStackTrace();
}
}
}

第二步:把所有的插件的ClassLoader都放进一个集合MyClassLoaders,在FragmentLoaderActivity中,使用MyClassLoaders来加载相应插件的Fragment

public class MyClassLoaders {
public static final HashMap<String, DexClassLoader> classLoaders = new HashMap<String, DexClassLoader>();
}
public class FragmentLoaderActivity extends Activity {
private DexClassLoader classLoader; @Override
protected void onCreate(Bundle savedInstanceState) {
//String pluginName = getIntent().getStringExtra(AppConstants.EXTRA_PLUGIN_NAME);
String mClass = getIntent().getStringExtra(AppConstants.EXTRA_CLASS);
String mDexPath = getIntent().getStringExtra(AppConstants.EXTRA_DEX_PATH); classLoader = MyClassLoaders.classLoaders.get(mDexPath); super.onCreate(savedInstanceState); FrameLayout rootView = new FrameLayout(this);
rootView.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
rootView.setId(android.R.id.primary);
setContentView(rootView); BaseFragment fragment = null;
try {
if(classLoader == null) {
fragment = (BaseFragment) getClassLoader().loadClass(mClass).newInstance();
} else {
fragment = (BaseFragment) classLoader.loadClass(mClass).newInstance();
} fragment.setContainerId(android.R.id.primary);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(android.R.id.primary, fragment);
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
} @Override
public Resources getResources() {
return PluginManager.mNowResources;
}
}

fragment插件化的好处避开了Activity必须要面对AMS的尴尬

基于Fragment的插件化的更多相关文章

  1. 基于AOP的插件化(扩展)方案

    在项目迭代开发中经常会遇到对已有功能的改造需求,尽管我们可能已经预留了扩展点,并且尝试通过接口或扩展类完成此类任务.可是,仍然有很多难以预料的场景无法通过上述方式解决.修改原有代码当然能够做到,但是这 ...

  2. Android基于代理的插件化思路分析

    前言 正常的App开发流程基本上是这样的:开发功能-->测试--->上线,上线后发现有大bug,紧急修复---->发新版本---->用户更新----->bug修复.从发现 ...

  3. android 基于dex的插件化开发

    安卓里边可以用DexClassLoader实现动态加载dex文件,通过访问dex文件访问dex中封装的方法,如果dex文件本身还调用了native方法,也就间接实现了runtime调用native方法 ...

  4. JavaScript插件化开发

    大熊君JavaScript插件化开发 一,开篇分析 Hi,大家好!大熊君又和大家见面了,还记得昨天的那篇文章吗------这个系列的开篇(第一季).主要讲述了以“jQuery的方式如何开发插件”, 那 ...

  5. 热门前沿知识相关面试问题-android插件化面试问题讲解

    插件化由来: 65536/64K[技术层面上]随着代码越来越大,业务逻辑越来繁杂,所以很容易达到一个65536的天花板,其65536指的是整个项目中的方法总数如果达到这个数量时则不无法创建新的方法了, ...

  6. 基于.NET MVC的高性能IOC插件化架构(一)

    最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博文是我的各大IOC框架的性能测试:http:/ ...

  7. 基于.NET MVC的高性能IOC插件化架构

    基于.NET MVC的高性能IOC插件化架构 最近闲下来,整理了下最近写的代码,先写写架构,后面再分享几个我自己写的插件 最近经过反复对比,IOC框架选择了Autofac,原因很简单,性能出众,这篇博 ...

  8. 基于EasyNVR实现RTSP/Onvif监控摄像头Web无插件化直播监控

    前言 随着互联网的发展,尤其是移动互联网,基于H5.微信的应用越来越多,企业也更多地想基于H5.微信公众号来快速开发和运营自己的产品,那么传统的安防IPC所输出的各种RTSP.GB28181.SDK视 ...

  9. 基于EasyNVR二次开发实现自己的摄像机IPC/NVR无插件化直播解决方案

    在之前的博客中<基于EasyNVR实现RTSP/Onvif监控摄像头Web无插件化直播监控>,我们已经比较多的描述EasyNVR所实现的功能,这些也在方案地址:http://www.eas ...

随机推荐

  1. 从 Confluence 5.3 及其早期版本中恢复空间

    如果你需要从 Confluence 5.3 及其早期版本中的导出文件恢复到晚于 Confluence 5.3 的 Confluence 中的话.你可以使用临时的 Confluence 空间安装,然后将 ...

  2. AD9361框图

    1. Fir滤波器的阶数为64或128 而内插或抽取因子为:1.2或4. HB1和HB2的内插或抽取因子为1或2而HB3的因子为1.2或3 BB_LPF为:三阶巴特沃斯低通滤波器,3dB点频率可编程, ...

  3. JSTL 标准标签库 (JavaServer Pages Standard Tag library, JSTL)

    JSP标准标签库(JavaServer Pages Standard Tag Library,JSTL)是一个定制标签库的集合,用来解决 像遍历Map或集合.条件测试.XML处理,甚至数据 库访问和数 ...

  4. poj1185 状态压缩经典题

    状态压缩的好题,直接求会爆内存,先把所有可能的状态求出来存在stk里,然后f[i][k][t]表示i行状态为t,i-1状态为k,由i-1状态来推出i状态即可 注意要打好边际条件的状态,并且某个可行状态 ...

  5. codeforce 240E 最小树形图+路径记录更新

    最小树形图的路径是在不断建立新图的过程中更新的,因此需要开一个结构体cancle记录那些被更新的边,保存可能会被取消的边和边在旧图中的id 在朱刘算法最后添加了一个从后往前遍历新建边的循环,这可以理解 ...

  6. 10进制 VS 2进制

    10进制 VS 2进制 时间限制: 1 Sec  内存限制: 32 MB 题目描述 样例输出 623 #include<stdio.h> #include<string.h> ...

  7. linux 压缩和解压缩

    压缩 tar -cvf jpg.tar *.jpg //将目录里所有jpg文件打包成tar.jpg tar -czf jpg.tar.gz *.jpg   //将目录里所有jpg文件打包成jpg.ta ...

  8. POJ 1064 Cable master (二分法+精度控制)

    Cable master Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 65358   Accepted: 13453 De ...

  9. ES6中const、let与var的对比详解

    对比 声明方式 变量提升 作用域 初始值 重复定义const 否   块级 需要 不允许let   否     块级 不需要 不允许var 是    函数级 不需要 允许 变量提升:const 和 l ...

  10. [转] 一张图理解prototype、proto和constructor的三角关系

    前面的话 javascript里的关系又多又乱.作用域链是一种单向的链式关系,还算简单清晰:this机制的调用关系,稍微有些复杂:而关于原型,则是prototype.proto和constructor ...