动态加载
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插件,如同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文件。
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文件。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories