初学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
- 初学android插件化开发小记
- jQuery插件开发小记
- Chrome 插件初学开发
- Eclipse插件下载地址小记(android开发)
- Android小记:svn 插件下载
- Android Studio自用插件小记
- android NDK开发小记
- Android开发小记
- MTK android开发小记
- Android开发小记一
- Android开发细节小记
- Android开发小记
- android开发 混淆小记
- android插件化开发
- Android插件化开发
- Android插件化开发
- Android 插件化开发
- Android插件化开发
- json学习小结
- ModBuS协议校验码问题
- [转]高效Hash构建方法
- Spring Boot使用Redis进行消息的发布订阅
- 深入浅出动态代理
- 初学android插件化开发小记
- SnackBar 笔记(二)样式美化
- ios界面开发之UIControl事件
- 一些Objective-C的编码规范
- Android广告平台
- 【刷题之路】零钱凑整问题
- 程序员常用软件清单曝光
- 考研经验总结
- iOS添加测试设备与调试