android DexClassLoader动态加载技术详解

介绍

做项目到一定庞大的时候就会发现方法数太多,安装包根本就装不上去了,这个也不足为奇,我们都知道当方法数目超过65536这个数目限制的时候,挡在2.x的系统上面就会出现无法安装的情况,这个时候动态加载技术就显得非的重要了,我们的项目中为了兼容2.x的手机也是用到了android的动态加载技术,这里我会详细的讲解一下怎么去用,怎么实战,我感觉,空谈理论不如动手来得实在。

实例

下面就通过一个例子反复的说明怎么来实现动态加载,通过不同的方法来调用。

准备工作

1:新建一个java工程(我比较懒我就直接新建Android工程了) 2:然后看看包路径。 \ 我们敲入里面的代码:先是Iinterface.java


packagecom.demo.dex;
 
/**
 * 对外接口
 *
 * @author edsheng
 *
 */
publicinterfaceIinterface {
    publicvoidcall();
 
    publicString getData();
}



然后是Iclass.java

packagecom.demo.dex;
 
importandroid.content.Context;
importandroid.widget.Toast;
 
publicclassIClass implementsIinterface {
 
    privateContext context;
 
    publicIClass(Context context) {
        super();
        this.context = context;
    }
 
    @Override
    publicvoidcall() {
        Toast.makeText(context,"call method",0).show();
    }
 
    @Override
    publicString getData() {
        return"hello,i am from IClass";
    }
 
}



注意上面的实现类,在构造的时候我给它传递了以Context对象,为什么要这样呢?因为在android里面什么东西不都是通过context来获取的吗?我这里为了实验就只调用了一下toast。 既然准备工作都准备好了,那就开始下一步吧。导出为jar。工程上面右键,export->java->jar file选中src下面的这两个类就行了。  \

导出我们的jar文件我给他命名为test.jar,然后把java的class文件转换成android能识别的字节码吧。使用dx工具,可以在andorid的SKD目录下面搜索一下这个工具,以前的说在platform-tools这个目录下面但是我没有找到,相反我在build-tools里面的子目录里面找到了不同版本的dx工具,我随便找了一个版本的19.0.3然后把test.jar复制到这个目录下面, 在cmd命令里面敲入

dx --dex --output=testdex.jar test.jar



\ 然后准备工作就完成了,这样一个优化的jar或者说是dex就准备好了。

正式开始

新建一个andorid工程(我比较懒,刚才已经就是使用的andorid工程),然后把testdex.jar复制到assets目录下面,来看看我的工程目录吧。 \

然后贴出FileUtil.java的代码

packagecom.demo.utile;
 
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.InputStream;
 
importandroid.content.Context;
importandroid.os.Environment;
 
publicclassFileUtile {
    publicstaticvoid CopyAssertJarToFile(Context context, String filename,
            String des) {
        try{
 
            File file = newFile(Environment.getExternalStorageDirectory()
                    .toString() + File.separator + des);
            if(file.exists()) {
                return;
            }
 
            InputStream inputStream = context.getAssets().open(filename);
            file.createNewFile();
            FileOutputStream fileOutputStream = newFileOutputStream(file);
            bytebuffer[] = newbyte[1024];
            intlen = 0;
            while((len = inputStream.read(buffer)) != 0) {
                fileOutputStream.write(buffer,0, len);
            }
            fileOutputStream.close();
            fileOutputStream.close();
        }catch(Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}





这个类主要的作用就是把我们的jar相当于解压到一个目录下面。我这里是解压到外置设备上的其实这样做的安全性并不高,但是为了方便我就这样做了,建议是解压到包目录下面。记住别忘了给应用加权限!!!!!文件读写权限!!!! 最后我们来看看Activity里面的代码


packagecom.demo.activity;
 
importjava.io.File;
importjava.lang.reflect.Constructor;
importjava.lang.reflect.Method;
 
importandroid.app.Activity;
importandroid.content.Context;
importandroid.os.Bundle;
importandroid.os.Environment;
 
importcom.demo.utile.FileUtile;
 
importdalvik.system.DexClassLoader;
 
publicclassMainActivity extendsActivity {
    @Override
    protectedvoidonCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        FileUtile.CopyAssertJarToFile(this,"testdex.jar","testdex.jar");
 
        File file = newFile(Environment.getExternalStorageDirectory()
                .toString() + File.separator + "testdex.jar");
        finalFile optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
        DexClassLoader classLoader = newDexClassLoader(file.getAbsolutePath(),
                optimizedDexOutputPath.getAbsolutePath(),null,
                getClassLoader());
        try{
            Class<!--?--> iclass = classLoader.loadClass("com.demo.dex.IClass");
            Constructor<!--?--> istructor = iclass.getConstructor(Context.class);
            //利用反射原理去调用
            Method method = iclass.getMethod("call",null);
            String data = (String) method.invoke(istructor.newInstance(this),null);
            System.out.println(data);
        }catch(Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}



其实这样调用还有一点要注意的是我感觉这样做每次都要去调用反射,总给我感觉来说不太友好,那么这里我有给出了下面一种写法,这种写法最主要的地方是,要获得Iinterface这个接口,把Iinterface.java这个文件复制到你的工程里面,记得包名相同,调用的时候我们可以这样来调用它。

Class<!--?--> iclass = classLoader.loadClass("com.demo.dex.IClass");
            Constructor<!--?--> istructor = iclass.getConstructor(Context.class);
            Iinterface iinterface = (Iinterface) istructor.newInstance(this);
             String data = iinterface.getData();
             iinterface.call();
             Toast.makeText(this, data, 0).show();


这样是不是看起来更舒服呢? 好了今天就分享到这里。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值