初学android插件化开发小记

来源:互联网 发布:简述网络整合营销理论 编辑:程序博客网 时间:2024/05/17 01:45

插件开发可以提高一款软件的可扩展性,我个人认为他就是一种化整为零的思想。就跟电脑的主板上留了不同的接口一样,只要插上相应的硬件就可以实现具体的功能。

Android插件开发要具备以下的基本功

这里写图片描述

sharedUserId就是主程序和插件程序进行通讯的唯一标识UID.

下面是我做的一个模拟更换皮肤的案例,首先创建一个主程序和两个插件程序如下图:
这里写图片描述
然后找三张不同的图片分别放入三个应用对应的目录下,保证三张图片的命名和尺寸完全相同(我的在mipmap-xxhdpi目录下)。

接下来在三个应用的清单文件中配置相同的sharedUserId:
这里写图片描述

一般插件程序都是没有启动图标的,所以我们在manifest文件中做如下配置:
将LAUNCHER改为DEFAULT

<category android:name="android.intent.category.DEFAULT"/>

准备工作做完就开始上代码了,主要代码如下:

/** *@author jiangrongtao *  *csdn:http://blog.csdn.net/jiang_rong_tao *  *github:https://github.com/jiangrongtao/jiangrongtao.github.io * * created at 2016/6/6 15:58*/public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, View.OnClickListener {    private static final String TAG = "MainActivity";    private MainActivity mContext;    private PopupWindow mPopupWindow;    private ArrayList<Map<String, String>> mPluginList;    private ImageView mImage;    private Button mGetPlugin;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        init();    }    private void init() {        mContext=this;        mImage= (ImageView) findViewById(R.id.iv_image);        mGetPlugin=(Button)findViewById(R.id.bt_getPlugin);        mGetPlugin.setOnClickListener(mContext);    }    /**     *  获取插件列表     * @return 返回插件集合     */    private List<Map<String,String>> findPluginList() {        //创建存放插件的集合        mPluginList=new ArrayList<Map<String,String>>();        //获取包管理者        PackageManager packageManager = mContext.getPackageManager();        //获取所有的安装包        List<PackageInfo> installedPackages = packageManager.getInstalledPackages(PackageManager.GET_ACTIVITIES);        try {            //获取当前安装包信息            PackageInfo currentPackageInfo = packageManager.getPackageInfo(getPackageName(), 0);          for(PackageInfo installedPackage: installedPackages){              String packageName = installedPackage.packageName;              String sharedUserId = installedPackage.sharedUserId;//分享的唯一标识              Log.e(TAG, "packageName: "+packageName);              Log.e(TAG, "sharedUserId: "+sharedUserId);              if(null==sharedUserId||!sharedUserId.equals(currentPackageInfo.sharedUserId)||packageName.equals(getPackageName())){                  //以上条件不属于当前应用的插件                  continue;              }              Map<String ,String > pluginMap=new HashMap<String,String>();              String label = installedPackage.applicationInfo.loadLabel(packageManager).toString();              pluginMap.put("packageName",packageName);              pluginMap.put("label",label);              mPluginList.add(pluginMap);          }        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        }        return mPluginList;    }    @Override    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {        Map<String, String> map = mPluginList.get(position);        Drawable drawable = getPluginRes(map);        if(drawable==null){            Toast.makeText(mContext, "资源不存在", Toast.LENGTH_SHORT).show();            return;        }        mImage.setImageDrawable(drawable);        mPopupWindow.dismiss();    }    /**     * 获取插件资源     * @param map     * @return     */    private Drawable getPluginRes(Map<String, String> map) {        Drawable drawable=null;        String packageName = map.get("packageName");        try {            //通过包名获取插件上下文            Context mPluginContext = mContext.createPackageContext(packageName, CONTEXT_IGNORE_SECURITY);            int resId=findPluginRes(mPluginContext,packageName);            drawable = mPluginContext.getResources().getDrawable(resId);        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        }        return drawable;    }    /**     * 查找插件中对应资源的Id     * @param mPluginContext     * @param packageName     * @return     */    private int findPluginRes(Context mPluginContext, String packageName) {        int resid=R.mipmap.main_bg;//默认为当前值        /**         * PathClassLoader 一个类加载器  PathClassLoader extends BaseDexClassLoader         * mPluginContext.getPackageResourcePath()获取包资源路径         */        PathClassLoader pathClassLoader=new PathClassLoader(mPluginContext.getPackageResourcePath(),PathClassLoader.getSystemClassLoader());        try {            /**             * R$mipmap  在字节码文件中表示drawable是R的内部类             * 反射获取到了mipmap字节码文件             */            Class<?> aClass = Class.forName(packageName + ".R$mipmap", true, pathClassLoader);            //获取字段 getDeclaredFields获取所有的字段            Field[] declaredFields = aClass.getDeclaredFields();            for (Field field:declaredFields) {                //获取字段名                String name = field.getName();                if ("main_bg".equals(name)){                    try {                        //获取该字段的值                        resid = field.getInt(name);                        break;                    } catch (IllegalAccessException e) {                        e.printStackTrace();                    }                }            }        } catch (ClassNotFoundException e) {            e.printStackTrace();        }        return resid;    }    /**     * 获取皮肤     * @param v     */    @Override    public void onClick(View v) {        if(mPopupWindow==null){            View contentView = LayoutInflater.from(mContext).inflate(R.layout.popup_layout, null);            mPopupWindow=new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);            mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));//设置背景            //查找插件列表            List<Map<String, String>> pluginList = findPluginList();            Log.e(TAG, "pluginList: "+pluginList.size() );            if(pluginList==null||pluginList.size()==0){                Toast.makeText(mContext, "没有可用插件", Toast.LENGTH_SHORT).show();                return;            }            ListView pluginListView= (ListView) contentView.findViewById(R.id.list_item);            SimpleAdapter adapter=new SimpleAdapter(mContext,pluginList,android.R.layout.simple_dropdown_item_1line,new String[]{"label"},new int[]{android.R.id.text1});            //显示插件列表            pluginListView.setAdapter(adapter);            pluginListView.setOnItemClickListener(mContext);            mPopupWindow.setWidth(600);            mPopupWindow.setHeight(150*pluginList.size());            mPopupWindow.setFocusable(true);            mPopupWindow.setOutsideTouchable(true);            mPopupWindow.showAsDropDown(v,0,20);            mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {                @Override                public void onDismiss() {                    if (mPopupWindow!=null){                        mPopupWindow=null;                    }                }            });        }    }}

点击切换皮肤会去检查是否具有当前应用的插件,如果有弹出一个popupWindow,我们可以选择不同的选项进行切换,具体效果如下:
选择插件A
这里写图片描述
选择插件B
这里写图片描述

上面有这个么一个类PathClassLoader,具体可以参考
https://m.oschina.net/blog/308583

通过上面的小案例我们应该有举一反三地能力,只要对PathClassLoader,PackageManager这两个api熟悉,对java的反射机制足够熟悉,那么其他的资源我们也可以获取和更换,说不定还可以给自己的微信开发一些小表情呢。

注:在我调试的时候打印了手机中所有应用的sharedUserId,看到很多系统的应用都有对应的sharedUserId,还有一部分其他的 APP也提供了相应的sharedUserId,所以我觉得用log跟踪+UI分析+反编译+反射是不是可以做一些其它有意义的事呢?只是个人想法而已
源码访问密码 9156

0 0
原创粉丝点击