Android插件框架部分问题及解决思路探索
插件框架需要的功能有:插件的集成与发现,插件的安装、升级、卸载管理,获取插件的描述和功能信息,调用插件特定Activity以及方法,宿主和插件互通消息等。
1.1.1 插件的集成与发现
android下,默认的情况是,每个apk是相互独立的,基本上每个应用都是一个dalvik虚拟机,都有一个uid,再配合上linux本身的权限机制,使得apk互通很难直接进行。通过主程序和插件共用sharedUserID,一个独立应用可以集成多个apk,可以并为一个单独的dalvik虚拟机,直观地反应给开发人员就是shell下列出进程,那几个apk同时加载后,会一个进程存在。直观地反应给用户人员的就是,当你从主程序进入插件activity,这个时候你点home键,再返回到主程序,你看到的仍然是插件activity的界面,而不是主程序进入插件的界面,这样多个apk对用户是透明的,用户看到的,是一个单独的app。在调用系统api获取到安装软件包列表后,可以通过sharedUserID筛选本程序的插件,并且通过反射机制或者读取插件的描述文件,得到插件的功能信息,在主程序通过启动进入插件的入口。
1.1.2 插件的安装、升级、卸载管理
在获取到插件列表后,得到其安装包对象,进一步可以读取版本信息,并且通过反射的方式,调用插件里面的特定接口,获取到该插件的唯一标志,和服务器进行对比,决定是否需要升级。对需要升级的插件,从服务器下载插件后,提示用户安装插件。系统在安装完插件后,会发送Intent.ACTION_PACKAGE_ADDED广播,主程序监听到这一广播后,刷新已经接入功能列表,完成对某一个插件的添加或者升级处理。同时,用户可以选择卸载某插件,系统在卸载apk后,会发送Intent.ACTION_PACKAGE_REMOVED广播,主程序监听到这一广播后,刷新接入功能列表,将某一个插件卸除。
1.1.3 获取插件的描述和功能信息
在我的工作页面,对于每一个插件,会提供ICON、名字、未读数目等信息,其中的未读数目是动态的,需要在插件里面赋值。在实现上,定义一个描述类,定义需要展现的信息,比如ICON的resID、标题、未读数目等,在插件里面,通过插件接口返回该描述类,主程序通过java反射机制获取到具体的值,并且显示给用户。
对于一个插件,需要提供给外部调用的接口,入口有:1)点击了首页的某一条消息,进而进入到插件,2)点击了我的工作里面的入口按钮,进入插件等情况。第一种情况通过插件约定的消息接受接口来完成,第二种在插件的描述文档返回启动入口页面需要的action名字。
1.1.4 调用特定Activity以及方法
在获取到插件的功能描述信息后,就得到了插件里面可以调用的activity,以及相关方法的完整路径。在调用插件里面的activity的时候,通过描述文件,构造目标intent,在主程序里面直接startActivity(intent)就可以了,并且可以携带初始数据,比如案件ID、移动考勤本人的patrolID等,在插件里面获取intent,得到数据,再进行处理。
当需要调用插件里面的特定方法的时候,通过获取到的插件包对象,获取插件的ClassLoader,通过loadClass()方法,获得插件里面某一个类的实例,利用java的反射特性,通过java.lang.reflect.Method方法,获取到目标方法的句柄,再利用invoke()方法,就可以调用目标方法,并且可以把当前context等参数传过去。
1.1.5 宿主和插件互通消息
定义一个消息类,利用java 1.5的特性完成可变长度参数的传递。定义一个消息接受和响应的接口,以一个插件为粒度,实现该接口。这样当宿主程序需要向某一个插件发送消息的时候,只需调用相应接口方法,插件实现方法就可以了。
当插件需要向主程序发送消息的时候,调用给某一个插件的发送消息的方法,同时把自身的句柄传递进去,当插件处理完获取反应后,获取宿主的句柄,利用其接受消息的接口,向宿主发送消息。
另外,也可以采用广播来实现宿主与插件、插件与插件的通信