android classloader插件开发,【Android高级】DexClassloader和PathClassloader动态加载插件的实现...

(一)DexClassloader

一、基本概念:

在Android中可以跟java一样实现动态加载jar,但是Android使用德海Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik byte code。

Android在API中给出可动态加载的有:DexClassLoader 和 PathClassLoader。

DexClassLoader:可加载jar、apk和dex,可以从SD卡中加载(本文使用这种方式)

PathClassLoader:只能加载已经安装搭配Android系统中的apk文件

二、实施

编写接口:Dynamic

package com.smilegames.dynamic.interfaces;

public interface Dynamic {

public String helloWorld();

public String smileGames();

public String fyt();

}

编写实现类:DynamicTest

package com.smilegames.dynamic.impl;

import com.smilegames.dynamic.interfaces.Dynamic;

public class DynamicTest implements Dynamic {

@Override

public String helloWorld() {

return "Hello Word!";

}

@Override

public String smileGames() {

return "Smile Games";

}

@Override

public String fyt() {

return "fengyoutian";

}

}

三、打包并编译成dex

将接口打包成jar:dynamic.jar(

只打包这Dynamic.java这一个接口

)

将实现类打包成jar:dynamic_test.jar(

只打包DynamicTest.java这一个实现类

)

将打包后的实现类(dynamic_test.jar)编译成dex:dynamic_impl.jar

1、将dynamic_test.jar拷贝到SDK安装目录android-sdk-windows\platform-tools下(

ps:如果platform-tools没有dx.bat,可拷贝到build-tools目录下有dx.bat的子目录

)

2、执行以下命令:

dx --dex --output=dynamic_impl.jar dynamic_test.jar

3、将dynamic.jar引入测试实例

4、将dynamic_impl.jar放到模拟器或真机的sdcard

四、修改onCreate例子

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// 或许activity按钮

helloWorld = (Button) findViewById(R.id.helloWorld);

smileGames = (Button) findViewById(R.id.smileGames);

fyt = (Button) findViewById(R.id.fyt);

/*使用DexCkassLoader方式加载类*/

// dex压缩文件的路径(可以是apk,jar,zip格式)

String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "dynamic_impl.jar";

// dex解压释放后的目录

String dexOutputDirs = Environment.getExternalStorageDirectory().toString();

// 定义DexClassLoader

// 第一个参数:是dex压缩文件的路径

// 第二个参数:是dex解压缩后存放的目录

// 第三个参数:是C/C++依赖的本地库文件目录,可以为null

// 第四个参数:是上一级的类加载器

DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDirs,null,getClassLoader());

Class libProvierClazz = null;

// 使用DexClassLoader加载类

try {

libProvierClazz = dexClassLoader.loadClass("com.smilegames.dynamic.impl.DynamicTest");

// 创建dynamic实例

dynamic = (Dynamic) libProvierClazz.newInstance();

} catch (Exception e) {

e.printStackTrace();

}

helloWorld.setOnClickListener(new HelloWorldOnClickListener());

smileGames.setOnClickListener(new SmileGamesOnClickListener());

fyt.setOnClickListener(new FytOnClickListener());

}

private final class HelloWorldOnClickListener implements View.OnClickListener {

@Override

public void onClick(View v) {

if (null != dynamic) {

Toast.makeText(getApplicationContext(), dynamic.helloWorld(), 1500).show();

} else {

Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();

}

}

}

private final class SmileGamesOnClickListener implements View.OnClickListener {

@Override

public void onClick(View v) {

if (null != dynamic) {

Toast.makeText(getApplicationContext(), dynamic.smileGames(), 1500).show();

} else {

Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();

}

}

}

1、运行这段代码时4.0.1以上版本会报:

java.lang.IllegalArgumentException: optimizedDirectory not readable/writable: /storage/sdcard0

可以通过这个授权解决:

2、授权之后又会报:

java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks.

这个问题的原因是:在4.1系统由于This class loader requires an application-private, writable directory to cache optimized classes为了防止一下问题:

External storage does not provide access controls necessary to protect your application from code injection attacks.

所以加了一个判断Libcore.os.getuid() != Libcore.os.stat(parent).st_uid判断两个程序是不是同一个uid

private DexFile(String sourceName, String outputName, int flags) throws IOException {

if (outputName != null) {

try {

String parent = new File(outputName).getParent();

if (Libcore.os.getuid() != Libcore.os.stat(parent).st_uid) {

throw new IllegalArgumentException("Optimized data directory " + parent

+ " is not owned by the current user. Shared storage cannot protect"

+ " your application from code injection attacks.");

}

} catch (ErrnoException ignored) {

// assume we'll fail with a more contextual error later

}

}

mCookie = openDexFile(sourceName, outputName, flags);

mFileName = sourceName;

guard.open("close");

//System.out.println("DEX FILE cookie is " + mCookie);

}

解决方法是:指定dexoutputpath为APP自己的缓存目录

File dexOutputDir = context.getDir("dex", 0);

DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDir.getAbsolutePath(),null,getClassLoader());

ps:第四步的问题最终是2导致的,所以只需用2的解决方案即可,不需要1的授权。

执行结果自行尝试。。。

(二)PathClassloader

public void dexLoad() {

/** 使用DexClassLoader方式加载类 */

// dex压缩文件的路径(可以是apk,jar,zip格式)

String dexPath = Environment.getExternalStorageDirectory().toString()

+ File.separator + "dynamic_temp.jar";

// dex解压释放后的目录

// String dexOutputDir = getApplicationInfo().dataDir;

// String dexOutputDirs = Environment.getExternalStorageDirectory()

// .toString();

// 定义DexClassLoader

// 第一个参数:是dex压缩文件的路径

// 第二个参数:是dex解压缩后存放的目录

// 第三个参数:是C/C++依赖的本地库文件目录,可以为null

// 第四个参数:是上一级的类加载器

File dexOutputDir = getApplicationContext().getDir("dex", 0);

DexClassLoader cl = new DexClassLoader(dexPath,

dexOutputDir.getAbsolutePath(), null, getClassLoader());

Log.i("tag", dexOutputDir.getAbsolutePath());

Class libProviderClazz;

try {

libProviderClazz = cl.loadClass("com.dynamic.impl.Dynamic");

// dynamic = (IDynamic) libProviderClazz.newInstance();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

// MyInterface dynamic;

Class dynamic;

public void pathLoad() {

/** 使用PathClassLoader方法加载类 */

// 创建一个意图,用来找到指定的apk:这里的"com.dynamic.impl是指定apk中在AndroidMainfest.xml文件中定义的

Intent intent = new Intent(

"com.example.androidendyeartest.MainActivity", null);

// 获得包管理器

PackageManager pm = getPackageManager();

List resolveinfoes = pm.queryIntentActivities(intent, 0);

// 获得指定的activity的信息

ActivityInfo actInfo = resolveinfoes.get(0).activityInfo;

// 获得apk的目录或者jar的目录

String apkPath = actInfo.applicationInfo.sourceDir;

// native代码的目录

String libPath = actInfo.applicationInfo.nativeLibraryDir;

// 创建类加载器,把dex加载到虚拟机中

// 第一个参数:是指定apk安装的路径,这个路径要注意只能是通过actInfo.applicationInfo.sourceDir来获取

// 第二个参数:是C/C++依赖的本地库文件目录,可以为null

// 第三个参数:是上一级的类加载器

PathClassLoader pcl = new PathClassLoader(apkPath, libPath,

this.getClassLoader());

// 加载类

try {

dynamic = pcl.loadClass("com.test.bean.MyBean");

} catch (Exception exception) {

exception.printStackTrace();

}

}应用:

@OnClick(R.id.b1)

public void t1(View v) {

if (dynamic != null) {

// dynamic.show1(getApplicationContext());

Method method;

try {

method = dynamic.getMethod("show1",Context.class);

//Context context=getApplicationContext();

method.invoke(dynamic.newInstance(), getApplicationContext());

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

} else {

Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值