在本应用中打开某个未安装的apk文件中的某个界面。一个apk中,最多只能有65535个方法,因此在大型项目中,将一些次要功能做成插件形式的,是很有必要的。
做成插件有两种方式:一种是将插件通过静默安装的方式装到手机中,然后在本应用中启动插件中的某个activity,但静默安装需要root权限;另一种就是通过类加载器,加载dex文件中的某个fragment,但这种方法只能加载一个fragment,如果在该fragment中启动activity就会报activity找不到异常,这是因为插件应用中的activity并没有在本应用中进行注册。
android中显示界面有两个方法,一个是加载activity,一个是加载fragment。对于activity来说,系统在启动它时会进行一系列的初始化,而且它的生命周期也不好控制,所以为了方便选择使用fragment。
在java中,想要加载某个类,可以使用类加载器进行加载,而android本身提供了一个DexClassLoader类加载器,可以使用它加载dex文件中的某个类。使用该类加载器进行加载时,需要将dex文件移到"/data/data/包名"目录下。
在加载fragment时,该fragment可能会使用到一些资源文件,因此需要将资源文件等加载到本应用中。这里就需要通过反射使用AssetManager中的addAssetPath方法。
代码
//一个用来承载别的fragment的activity public class PluginActivity extends Activity { public static final String KEY = "apk"; private APK apk;//自定义bean private AssetManager as; private Resources resources; private Resources.Theme theme; private DexClassLoader classLoader; private String dexFilePath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_plugin); initLocalData(); } private void initLocalData() { apk = getIntent().getParcelableExtra(KEY); File tem = new File(getApplication().getFilesDir(),"dexout"); tem.mkdir(); String optimizedDirectory = tem.getAbsolutePath(); moveAPK(); /** * 第一个参数为从何处获得dex文件(apk中本身就有一个dex文件) * 第二个参数为将dex加载到哪个目录下 * 第三个为c/c++库的目录 * 第四个传入系统的classLoader即可 */ classLoader = new DexClassLoader(dexFilePath,optimizedDirectory,null,super.getClassLoader()); try { as = AssetManager.class.newInstance();//通过反射获取AssetManager的实例 as.getClass().getMethod("addAssetPath",String.class).invoke(as,apk.getPath()); resources = new Resources(as,super.getResources().getDisplayMetrics(),super.getResources().getConfiguration()); theme = resources.newTheme(); theme.setTo(super.getTheme()); //通过反射拿到要加载的fragment的实例 Fragment f = (Fragment) getClassLoader().loadClass("com.example.hufeng.pluin.InFragment").newInstance(); getFragmentManager().beginTransaction().add(R.id.root,f).commit(); } catch (Exception e) { e.printStackTrace(); } } private void moveAPK() { File parent = new File(getApplication().getFilesDir(),"plugin");//将插件移动到该目录下,plugin名字可随意 parent.mkdir(); File dex = new File(parent,apk.getName()); dexFilePath = dex.getAbsolutePath(); try { FileOutputStream out = new FileOutputStream(dex); FileInputStream in = new FileInputStream(apk.getPath()); byte[] b = new byte[1024]; int len = -1; while ((len = in.read(b))!= -1){ out.write(b,0,len); out.flush(); } } catch (Exception e) { e.printStackTrace(); } } @Override public ClassLoader getClassLoader() { return classLoader !=null?classLoader:super.getClassLoader(); } @Override public AssetManager getAssets() { return as == null?super.getAssets():as; } @Override public Resources getResources() { return resources == null?super.getResources():resources; } @Override public Resources.Theme getTheme() { return theme == null?super.getTheme():theme; } }