在项目研发中会遇到部分功能常常变动,常常升级app会对用户产生反感,形成体验不好。html
项目中有这样一个功能:下载到本地的视频须要在播放时加载字幕,可是某些视频咱们的服务器中不存在字幕,通过调研发现字幕库网站能经过影片名查询到相应的字幕,并下载下来。可是问题是字幕库没有公开的字幕查询接口,只能经过一些逆向分析后,对页面进行解析捕获到了字幕的下载路径,字幕下载路径是嵌套在html代码中,这样只有经过JSoup技术(不懂得能够查百度,这儿就不细说)对html页面进行动态解析,拿到字幕的下载地址,以后再下载到咱们的服务器上面,开发的都知道使用第三方的老是不稳定,都说吃人嘴软,拿人手短,仍是不如本身的,第三方的网页布局变化了,那么使用jsoup解析的地址就所有出错了,可是不能由于这个就去对app进行从新打包发布新版本,这样对用户的体验很差,那么咱们就要使用动态加载技术去改变这样的频繁打包工做。java
解决思路:把常常变化的放在服务器上面,每次启动app的时候就从服务器上面下载下来逻辑,再动态的加载到app的包里面,动态打包咱们的app的,实例化对象,若是字幕库发生变化,咱们就只须要更新服务器上面的解析代码,从新下载相应的逻辑加载到app。android
如何实现动态加载的流程?bash
第一:制做dex文件。服务器
第二:把制做的dex文件发布到服务器上面,从服务器上面下载dex文件以后动态打包到app中app
制做的工具类:ide
public class JsoupUtils {
public static String html2Url(String html) {
return "url" + html;
}
}复制代码
①、制做dex文件函数
而后编译以后再androidstudio的build/intermediates/classes/debug/ 下面会看到你的包名生成的字节码,以后使用Java打包命令:jar -cvf把指定的字节码打包成jar文件,以下:工具
出现这个表示打包成功,布局
而后再把jar文件打包成dex文件,如今就要使用dex命令,dx.bat文件,在build目录下,或者配置环境变量:
出现如下表示打包成功:
至此dex文件打包成功
②、把制做的dex文件发布到服务器上面,从服务器上面下载dex文件以后动态打包到app中
在此演示则不去服务器下载,省略下载的步骤,直接放在assets目录下面:
先把assets目录下的utils_dex.jar拷贝到sd卡上面
如下就是类加载器:
使用反射与类加载器
android中的类加载器主要有三个:
只能用于加载jar文件,可是因为dalvik不能直接识别jar文件,因此android中没法使用这个类加载器
它只能加载已经安装的apk,由于PathClassLoader只会读取/data/dalvik-cache/目录下的dex文件,
例如安装一个apk的时候,就会在这个目录下面的x86目录下生成每一个apk对应的dex文件:
使用PathClassLoader加载apk时,它就会在这个目录下面去查找对应的DEX文件,若是apk没有安装,则会报错,ClassNotFoundException
是最理想的加载器,它的构造函数包括四个参数
一、dexPath:目标类所在的APK或jar文件的路径,类加载器将从该路径中寻找指定的目标类,该类必须是apk或者jar的全路径,若是包含多个路径,路径之间必须使用特定的分隔符分隔,特定的分隔符可使用System.getProperty("path.separtor")得到;
二、dexOutputDir:因为dex文件被包含在apk或者jar文件中,所以在装载目标类以前须要先从apk或jar文件中解压出dex文件,该参数就是定制解压出的dex文件存放的路径,在Android系统中,一个应用程序通常对应一个Linux用户,应用程序仅对属于本身的数据目录路径有写的权限,所以,该参数可使用该程序的数据路径
三、libPath:指目标类中使用的C/C++库存放的路径
四、classload是指该装载器的父装载器,通常为当前执行类的装载器
直接上代码:
package com.parse.dex;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import java.io.File;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initLoadDex();
}
private void initLoadDex() {
FileUtils.copyAssetsJarToFile(this, "utils_dex.jar", "utils_dex.jar");
File file = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "utils_dex.jar");
File optimizedDexOutputPath = getDir("dex", Context.MODE_PRIVATE);
DexClassLoader dexClassLoader = new DexClassLoader(file.getAbsolutePath(), optimizedDexOutputPath.getAbsolutePath(), null, getClassLoader());
try {
Class loadClass = dexClassLoader.loadClass("com.parse.dex.JsoupUtils");
Method html2Url = loadClass.getMethod("html2Url", String.class);
String s = (String) html2Url.invoke(loadClass, "解析html文件");
Toast.makeText(this, s, Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
}复制代码
整个动态加载类的流程就是这样的