zoukankan      html  css  js  c++  java
  • 基于Fragment的插件化

    --《摘自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的尴尬

  • 相关阅读:
    金融的本质
    读书笔记-关键对话
    pem转pfx
    pem转cer
    Java基础学习总结——Java对象的序列化和反序列化
    Kafka学习之consumer端部署及API
    zookeeper实战:SingleWorker代码样例
    Thread.setDaemon详解
    json对象转换
    【转】Hadoop学习路线图
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/10118527.html
Copyright © 2011-2022 走看看