目录

Android黑科技动态加载(一)之Java中的ClassLoader
Android黑科技动态加载(二)之Android中的ClassLoader
Android黑科技动态加载(三)之动态加载资源
Android黑科技动态加载(四)之插件化开发

项目地址

参考

Android插件化框架系列之类加载器
Android动态加载之ClassLoader详解

Java虚拟机与Android虚拟机加载对象的不同

在Java虚拟机中加载的对象主要是字节码, 也就是class文件. 而Android虚拟机中主要加载的是dex文件, dex文件就是把class优化整合打包的结果 . 我们知道一个apk其实就是一个打包的文件, 运行的时候会释放出dex包. 当然优化后的odex是主要加载的对象, 主要优点是比dex加载地快.

ClassLoader分类

在上一篇博客中, 我们知道Java虚拟机的ClassLoader有三个:

  • BootStrap ClassLoader

  • Extension ClassLoader

  • App ClassLoader

并且ClassLoader加载类的顺序准守双亲委托机制.

在Android虚拟机中, 也有三个ClassLoader, 并且加载顺序也是准守双亲委托机制:

  • BootClassLoader : 主要用于加载系统的类,包括java和android系统的类库

  • PathClassLoader : 主要用于加载应用内中的类, 它加载的路径是固定的, 因此无法指定

  • DexClassLoader : 可以用于加载任意路径的zip,jar或者apk文件, 可以实现动态加载

Android中的ClassLoader

应用

好了, 既然我们现在知道DexClassLoader可以加载任意路径的zip,jar或者apk文件. 那么到底有什么用呢? 回到我们的目的, 我们为什么要学习ClassLoader? 不就是实现运行时动态加载服务器上下载的临时补丁或者插件么?

假设我们现在有一个服务, 用于提供用户信息管理的. 而我们的用户信息是托管在第三方后端云上, 现在由于某些因素, 需要更换后端云. 所以我们也需要重新去实现我们的用户信息管理类. 在不发布新版本的情况下, 我们可以使用上面讲到的技术去实现.

IUserService.java

package top.august.i;public interface IUserService {    String login(String username, String password);    String logout();}

该接口定义了用户服务的标准, 我们导出jar包UserService.jar

UserPlugin

我们新建Android Studio工程UserPlugin, 然后引入jar包.

然后我们对IUserService进行实现

public class UserService implements IUserService {    private String username;    @Override    public String login(String s, String s1) {        if (s.equals(s1)) {            username = s;            return "后端云A:登录成功!";        }        return "后端云A:登录失败...";    }    @Override    public String logout() {        if (TextUtils.isEmpty(username)) {            return "后端云A:未登录...";        }        username = null;        return "后端云A:注销成功!";    }}

这里我们用户验证就简单地对比一下用户名跟密码是否一样...

然后我们输出APKUserPlugin.apk

HostApp

接下来我们新建Android Studio工程HostApp

  • 然后把UserPlugin.apk放到assets目录下

  • UserService.jar放到libs下面并且引用

其实这里UserPlugin.apk可以放到任意路径下. 因为这才是我们服务器的情况, 把插件APK放到任意位置. 这里只是为了方便读写, 所以才放到assets目录下

PluginUtil

然后我们新建工具类

package com.example.hostapp;import android.content.Context;import com.example.i.IUserService;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import dalvik.system.DexClassLoader;/** * Created by August on 2017/3/18. */public class PluginUtil {    private static final String PLUGIN_NAME = "UserPlugin.apk";    /**     * 加载插件APP的类     *     * @param context     * @return     * @throws IOException     */    public static IUserService load(Context context) throws IOException {        String src = context.getFilesDir().getAbsolutePath() + File.separator + PLUGIN_NAME;    // 插件APK存放的位置        String dest = context.getFilesDir().getAbsolutePath() + File.separator + "plugin" + File.separator; // 插件APK转换为dex的文件夹, 也就是DexClassLoader加载的classPath        // 创建dest目录        File destFile = new File(dest);        if (!destFile.exists()) {            destFile.mkdir();        } else if (!destFile.isDirectory()) {            destFile.delete();            destFile.mkdir();        }        try {            copyPlugin(context, src);   // 把插件从assets拷贝出来, 如果是从网络下载下来的 那么就可以从下载文件的路径拷贝过来            DexClassLoader classLoader = new DexClassLoader(src, dest, null, context.getClassLoader()); // 构造自己的DexClassLoader            Class<?> cls = classLoader.loadClass("com.example.userplugin.impl.UserService");    // 加载插件APK中的UserService类            IUserService service = (IUserService) cls.newInstance();    // 返回实例            return service;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * 拷贝插件APP到插件目录     *     * @param context     * @param dest     * @throws IOException     */    private static void copyPlugin(Context context, String dest) throws IOException {        InputStream is = null;        FileOutputStream fos = null;        is = context.getAssets().open(PLUGIN_NAME);        fos = new FileOutputStream(dest);        int len;        byte[] buf = new byte[1024];        while ((len = is.read(buf)) != -1) {            fos.write(buf, 0, len);        }        if (is != null) {            is.close();        }        if (fos != null) {            fos.close();        }    }}

MainActivity

package com.example.hostapp;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Toast;import com.example.i.IUserService;import java.io.IOException;public class MainActivity extends AppCompatActivity {    private IUserService mService;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void load(View v) {        try {            mService = PluginUtil.load(this);            if (mService != null) {                Toast.makeText(this, "加载插件APP成功", Toast.LENGTH_SHORT).show();                return;            }        } catch (IOException e) {            e.printStackTrace();        }        Toast.makeText(this, "加载插件APP失败", Toast.LENGTH_SHORT).show();    }    public void login(View v) {        if (mService != null) {            Toast.makeText(this, mService.login("August1996", "August1996"), Toast.LENGTH_SHORT).show();        }    }    public void logout(View v) {        if (mService != null) {            Toast.makeText(this, mService.logout(), Toast.LENGTH_SHORT).show();        }    }}

activity_main

    

效果图

Android 5.0

这是在Android 5上面的效果 实现了我们的目标->动态加载.

但是在Android4.1上面会报错. Android插件化框架系列之类加载器文章中指出了原因是因为Android虚拟机加载Class的差异, 把插件应用中的compile改成provided方式就可以了, 具体区别看Android Studi添加依赖那些事.

Android 4.1.gif

)

文章有不足的地方, 希望大家帮忙纠正.[拜托][拜托][拜托]

更多相关文章

  1. Android面试基础
  2. 使用html,javascript,css,phonegap创建开发android应用程序
  3. android系统定制从听说到入门二
  4. 在eclipse的android工程里引用android sdk之外的类和方法
  5. 深度解析Android中字体设置
  6. Android(安卓)插件安装 和MyEclipse的Windows下面没有Android(安
  7. 基于Android官方Paging Library的RecyclerView分页加载框架
  8. Android(安卓)弹出窗口 PopupWindow
  9. Android(安卓)网格视图(GirdView)简易适配器的使用

随机推荐

  1. android 学习示例
  2. Android(安卓)判断SD卡是否存在及容量查
  3. Android中listview中的button
  4. 系出名门Android(8)
  5. Android新增一个音频类型及双音频输出的
  6. Android(安卓)安装位置 - installLocatio
  7. android的init.rc文件的语法
  8. 【转】android 有效解决achartengine在sc
  9. Android培训班(53)
  10. android通过读取系统属性设置字体缩放的