-
DynamicLib
动态加载接口库和接口实现库,并且被打成Dex
文件,如:dynamic_dex.jar
-
app
把dynamic_dex.jar
放到app的assets
目录下,实现动态加载dex
文件。 -
动态加载普通的类
-
动态加载Fragment
-
DynamicActivityInterface
动态加载接口库 -
DynamicActivityDemo
动态加载接口实现module
, 运行这个module
生成DynamicActivityDemo.apk
文件。然后把这个APK
文件放到SD卡
的根目录. -
DynamicActivityHost
动态加载手机SD卡根目录的DynamicActivityDemo.apk
文件。
1、把需要动态加载的类打成Jar
包。比如:dynamic.jar
2、把打成的Jar
包打成dex
包,注意最后打成的dex包也是以.jar
结尾的。比如:dynamic.jar
-> dynamic_dex.jar
3、把dex
包放到可以实现动态加载的工程的assets
目录下。
- 在工程的
build.gradle
里面添加
task getJar(type: Copy) {
delete 'build/libs/dynamic.jar'
from('build/intermediates/bundles/release/')
into('build/libs/')
include('classes.jar')
rename('classes.jar', 'dynamic.jar')
}
//终端输入 gradlew :DynamicLib:getJar 生成jar
getJar.dependsOn(build)
- 在命令行终端输入
gradlew :DynamicLib:getJar
就会看到在module
的build/libs
看到dynamic.jar
dx --dex --output=dynamic_dex.jar dynamic.jar
最后在这个目录生成的dynamic_dex.jar
就是我们需要的dex文件。
我们就以dynamic_dex.jar
dex包为例,把它解压,可以看到两个文件
由于我们最终动态加载的是Dex
文件,但是Dex
包中的类都不能直接调用。那么怎么调用我们需要的类呢?通常有两种方法,如下
- 用Java反射 ----- Java 反射 使用总结
- 面向接口编程 ---- 本工程就是使用的这个方法
可以看到在例子中,我们用了两个接口Dynamic
,FragmentI
。DynamicImpl
实现Dynamic
接口;SetFragment
实现FragmentI
接口。
不过这里需要注意的是:DynamicLib
中的接口,需要在app
工程里面重新写一份,并且包名要相同,都是com.dyncmic.lib
。
操作方法详见:Android 反射-换一种方式编程
具体到本工程的代码如下:
package com.dynamic.lib;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by ${zhaoyanjun} on 2017/3/3.
*/
public class SetFragment extends Fragment implements FragmentI {
@Override
public Fragment getFragment() {
return new SetFragment() ;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//通过反射获取
int layoutID = getId( getContext() , "layout" , "fragment_main") ;
View view = inflater.inflate( layoutID , container , false ) ;
return view ;
}
public static int getId(Context context , String className , String name ){
return context.getResources().getIdentifier( name , className , context.getPackageName() ) ;
}
}
- ClassLoader
在java
中,有个概念叫做"类加载器"
(ClassLoader
),它的作用就是动态的装载Class
文件。标准的java sdk
中有一个ClassLoader
类,借助这个类可以装载想要的Class
文件,每个ClassLoader
对象在初始化时必须制定Class
文件的路径。
- DexClassLoader
dex
文件是一种经过android
打包工具优化后的Class
文件,因此加载这样特殊的Class
文件就需要特殊的类装载器,所以android中提供了DexClassLoader
类。这个类加载器用来从.jar
和.apk
类型的文件内部加载classes.dex
文件。通过这种方式可以用来执行非安装的程序代码,作为程序的一部分进行运行。
DexClassLoader 继承关系
DexClassLoader
继承BaseDexClassLoader
。继承关系如下图:
ClassLoader
-- BaseDexClassLoader
-- DexClassLoader 、 PathClassLoader
DexClassLoader 构造函数
DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)
-
参数1:待加载的
dex
文件路径,如果是外存路径,一定要加上读外存文件的权限(<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> )
。 -
参数2:解压后的
dex
存放位置,此位置一定要是可读写且仅该应用可读写(安全性考虑),所以只能放在data/data
下。 -
参数3:指向包含本地库
(so)
的文件夹路径,可以设为null
. -
参数4:父级类加载器,一般可以通过
Context.getClassLoader
获取到,也可以通过ClassLoader.getSystemClassLoader()
取到。
PathClassLoader
和DexClassLoader
的区别
-
PathClassLoader
不能主动从zip
包中释放出dex
,因此只支持直接操作dex
格式文件,或者已经安装的apk
(因为已经安装的apk在cache中存在缓存的dex文件)。 -
DexClassLoader
可以支持.apk
、.jar
和.dex
文件,并且会在指定的outpath
路径释放出dex
文件。
动态加载本身是插件开发里面的知识,本项目只是动态加载DEX
文件。动态加载也是可以加载APK
文件的,这里不做说明,感兴趣的自己可以搜索资料学习一下。