您的位置:首页 > 移动开发 > Android开发

动态加载

2016-04-13 09:47 357 查看
动态jar插件的创建相对比静态jar多了一个步骤,因为Android虚拟机是基于dex的,所以我们的class不能简单的调用jar命令压缩就可以了,而是需要使用sdk\platform-tools目录下的dx工具来进行类型转换。下面演示单一类文件AddFunc.java的生成jar插件使用命令行的过程。

1、  使用javac命令编译生成AddFunc.class文件

2、  由于AddFunc类的包目录为com.demo.jar,所以需要将AddFunc.class文件拷贝至sdk\platform-tools\com\demo\jar文件夹下面

3、  使用dx命令,生成jar插件文件,上述步骤的命令如下

C:\>javac AddFunc.java

 

C:\>dx --dex --output=AddFunc.jar com/demo/jar/AddFunc.class

 

C:\>jar tvf D:\Android\sdk\platform-tools\AddFunc.jar

    72 Fri Sep 23 14:28:48 CST 2011 META-INF/MANIFEST.MF

   964 Fri Sep 23 14:28:50 CST 2011 classes.dex

通过上述步骤在sdk\platform-tools上面就有一个AddFunc.jar文件了。在这里需要说明的是AddFunc.class必须按照包名放置,否则生成jar会报错。

其实假如本身就在Android工程下面,那么可以现在eclipse下面先编译程序,后在Bin目录下面,自然就按包名,放置了class文件,再将需要的class文件包含文件目录拷贝至sdk\platform-tools目录下就好了。假设我们需要打包两个class,就可以通过如下命令实现

C:\>dx --dex --output=AddFunc.jar com/demo/jar/AddFunc.class com/demo/jar/GameView.class

经过查询,明显比上面这个classes.dex文件大多了,具体如下

C:\>jar tvf D:\Android\sdk\platform-tools\AddFunc.jar

    72 Fri Sep 23 14:38:06 CST 2011 META-INF/MANIFEST.MF

  1752 Fri Sep 23 14:38:06 CST 2011 classes.dex

好了,我们需要的动态jar插件就形成了。


动态jar插件的使用

如上所述,我们生成了需要动态jar插件,如同java中动态加载使用ClassLoader类动态加载一样,在Android我们需要使用DexClassLoader来通过反射机制动态加载。

相对来说假如动态jar插件创建正确,这一步就没什么难题了,就全交给代码来说明吧。

AddFunc的源代码为

package com.demo.jar;

 

import android.util.Log;

 

public class AddFunc

{

    public AddFunc()

    {

           Log.i("AddFunc",
"AddFunc class Init");

    }

   

    public int Add(int a, int b)

    {

           int c  =
a + b;

           Log.i("AddFunc",
"Add result is "+c);

           return c;

    }

}

以上的代码生成动态jar插件后,在模拟器通过cmd如下命令,将jar插件放置到运行坏境中,本文是将其放置在模拟器的sdcard根目录,具体如下

C:\>adb push addFunc.jar sdcard/

6 KB/s (1149 bytes in 0.187s)

这样,就可以在代码中使用反射机制调用jar中的方法了,具体的Android工程中动态调用jar插件的代码如下

package com.demo.jar.runloadjarDemo;

 

import java.io.File;

import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

import android.app.Activity;

import android.os.Bundle;

 

public class RunLoadJarDemoActivity extends Activity

{

    /** Called when the activity is first created. */

       Class myClass  =null;

      

    @Override

    public void onCreate(Bundle savedInstanceState)

    {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        try

        {

               File
file = new File("/sdcard/AddFunc.jar");

//              File
file = new File("/sdcard/drawView.apk");

               if(file.exists())

               { 

                     DexClassLoader
cl = new DexClassLoader(file.toString(), getFilesDir().getAbsolutePath(), null, ClassLoader.getSystemClassLoader().getParent());  

                      myClass
= cl.loadClass("com.demo.jar.AddFunc");

                      Object
obj = myClass.newInstance();

                      Class[]
params = new Class[2];

                      params[0]
= Integer.TYPE;

                      params[1]
= Integer.TYPE;

                      Method
action = myClass.getMethod("Add", params);

                      int
ret = (Integer)action.invoke(obj, 15, 20);                     

               } 

        }

        catch (Exception ex)

        {     

               ex.printStackTrace();

        }

    }

}

其实从原理上来说,如上生成的动态jar插件,跟apk的生成是同一个道理,所以假设我们不想通过繁琐的dx工具,那么也可以直接由eclipse生成apk,然后通过动态加载的方法来使用apk中的类和方法,本人测试过也是可行的。

好了,至此将Android app中加载jar插件的问题就介绍到这里,如果大家觉得如上的代码调用反射太过繁琐,那么可以通过设计接口的方法来将反射变得简单,至于这个话题就不在本文进行阐述了。

Android虚拟机的动态加载技术:

Android虚拟机的动态加载技术分为两种:一种是加载基于NDK的so库;另一种是加载用java语言开发的zip包。我今天主要讨论后者。

    先简单说明一下so库加载。NDK的执行效率很高,加密性很好,但同时开发入门难度大,一般用于加解密、数学运算等场合。so的加载很简单,如果APK发布时已经携带了so文件,只需要在加载时调用System.loadLibrary(libName)方法即可。由于软件的安装目录中存放so的目录是没有写权限的,开发者不能更改该目录的内容,所以如果要动态加载存放在其他地方的so文件,用System.load(pathName)方法即可。

    现在我们重点来看一下如何用java开发android的动态包。之所以前面称之为zip包,是因为jar和apk其实都是zip格式的。android虚拟机支持这两种文件后缀的包。android虚拟机支持加载zip包中的dex格式的代码文件。所以我们要用到一个很重要的类DexClassLoader,这个类是动态加在技术的关键。提到动态加载,还需要用到的一个就是java的反射技术,下面就举一个调用伪代码:

    DexClassLoader dcl = new DexClassLoader(zip文件所在绝对路径, zip文件所在目录, 默认加载so所在目录,Context包含的classLoader);
    Class<?> clazz = dcl.loadClass(想要加载类的完整包名);
    Method getInstance = clazz .getMethod(方法名, 参数类);
    getInstance.invoke(null, 参数); //第一个参数为null表示是静态方法

    这样就把zip文件中的类加载起来了,图片等资源也可以放在zip包中加载。有了这个技术,很多的执行逻辑可以通过网络下载的方式动态调整,实现了应用的多样性。

    不过动态加载是有一些限制的,比如zip包中的Activity、Service类是不能动态加载的,因为缺少声明;即使你在Manifest文件中进行了声明,系统默认也是到安装apk所在的路径中去寻找类,所以你会遇到一个ClassNotFound的异常。插件里你可以用apk中先前放入的layout、strings等资源。但是插件中自带的界面只能用纯代码进行编写,插件中是不能加载zip包中的xml作为layout等资源使用的。所以在开发上一些特效会比较困难些,建议预先植入apk中。

    对于Activity、Service的动态加载,有一种变通的方法来解决,那就是在apk开发的时候预留各种Activity、Service的包装类,定义最常用的处理event的方法,然后在事件处理的时候调用插件内定义的方法即可。

    zip插件的制作:将代码和资源导出成jar文件。通常在这一步的基础上我们用混淆器对代码进行一次混淆。最后通过命令行dx命令将jar中的class文件转换成dex文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 动态加载