Bleoo

志同道合,一杯酒。

  • 主页
  • 随笔
  • Android

Bleoo

志同道合,一杯酒。

  • 主页
  • 随笔
  • Android

Android动态加载Dex文件入门

2017-04-26

Android使用Dalvik虚拟机加载可执行程序,所以不能直接加载基于class的jar,而是需要将class转化为dex字节码,从而执行代码。优化后的字节码文件可以存在一个*.jar中,只要其内部存放的是*.dex即可使用。

综述

将class的jar包转化为dex需要用到命令dx(在\android-sdk\build-tools\version[23.0.1] 或 \android-sdk\platform-tools下能找到);命令使用方式为:dx –dex –output=output.jar origin.jar,该命令将包含class的origin.jar转化为包含dex的output.jar文件。

Android支持动态加载的两种方式是:DexClassLoader和PathClassLoader,DexClassLoader可加载jar/apk/dex,且支持从SD卡加载。PathClassLoader 的限制要更多一些,它只能加载已经安装到 Android 系统中的 apk 文件,也就是 /data/app 目录下的 apk 文件。其它位置的文件加载的时候都会出现 ClassNotFoundException。

实验步骤

1.定义一个接口IShowToast.Java

1
2
3
4
5
6
7
package com.example.testdextoast;
import android.content.Context;
public interface IShowToast {
public int showToast(Context context);
}

2.定义一个简单实现ShowToastImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.testdextoast;
import android.content.Context;
import android.widget.Toast;
public class ShowToastImpl implements IShowToast {
@Override
public int showToast(Context context) {
Toast.makeText(context, "我来自另一个dex文件", Toast.LENGTH_LONG).show();
return 100;
}
}

3.将且仅将ShowToastImpl.java导出为jar(Eclipse工程右键->Export->Java->Jar file),这时候我们得到一个包含class文件的jar,命名为origin.jar。

4.在命令行下,进入dx所在目录,将jar文件拷到该目录下,执行dx –dex –output=output.jar origin.jar,得到内含class.dex的output.jar

5.将output.jar文件放到SD卡下adb push output.jar sdcard/output.jar(或者将jar内的dex抽出来放到SD卡亦可)

6.开始编写调用代码

7.新建调用的Android工程,将IShowToast.java放入该工程,注意:此处一定不能改变此文件的包路径,否则dex中的实现类找不到接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.example.testdextoast;
import java.io.File;
import dalvik.system.DexClassLoader;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
File dexOutputDir = getDir("dex1", 0);
String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "output.jar";
DexClassLoader loader = new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, getClassLoader());
try {
Class clz = loader.loadClass("com.example.testdextoast.ShowToastImpl");
IShowToast impl = (IShowToast) clz.newInstance();
impl.showToast(this);
} catch (Exception e) {
Log.d("MainActivity", "error happened", e);
}
}
}

注意

此处需要注意DexClassLoader的四个参数:

  • dexPath:待加载的dex文件路径,如果是外存路径,一定要加上读外存文件的权限(<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> )。
  • optimizedDirectory:解压后的dex存放位置,此位置一定要是可读写且仅该应用可读写(安全性考虑),所以只能放在data/data下。本文getDir(“dex1”, 0)会在/data/data/**package/下创建一个名叫”app_dex1“的文件夹,其内存放的文件是自动生成output.dex。
  • libraryPath:指向包含本地库(so)的文件夹路径,可以设为null。
  • parent:父级类加载器,一般可以通过Context.getClassLoader获取到,也可以通过ClassLoader.getSystemClassLoader()取到。
  • Android

扫一扫,分享到微信

微信分享二维码
Android动态加载Apk资源
Android运行时权限库PermissionsDispatcher
© 2017 Bleoo
Hexo Theme Yilia by Litten