189 8069 5689

android动态加载,android动态加载apk框架

android 怎么动态的加载类

android 如何动态的加载类----app插件技术

创新互联建站自2013年创立以来,先为萍乡等服务建站,萍乡等地企业,进行企业商务咨询服务。为萍乡企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。

转自:

?

前言:

?

? ? ? 在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势。现如今很多项目要求需要采用类似于微信或Q游这样的插件化开发模式越来越多,本文就是阐述android的动态加载技术来满足插件化开发模式的文章。

?

1.基本概念

1.1??在Android中可以动态加载,但无法像Java中那样方便动态加载jar。

Android的虚拟机(DalvikVM)是不认识Java打出jar的byte code,需要通过dx工具来优化转换成Dalvikbyte code才行。这一点在咱们Android项目打包的apk中可以看出:引入其他Jar的内容都被打包进了classes.dex。即android要加载的java类必须dex格式的代码文件.

1.2??在Android中可以加载基于NDK的so库。

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

1.3??在Android中支持动态加载dex文件的两种方式:

DexClassLoader:这个可以加载jar/apk/dex,也可以从SD卡中加载,也是本文的重点

PathClassLoader:只能加载已经安装到Android系统中的apk文件。也就是 /data/app 目录下的 apk 文件。其它位置的文件加载的时候都会出现 ClassNotFoundException.因为 PathClassLoader 会去读取 /data/dalvik-cache 目录下的经过 Dalvik 优化过的 dex 文件,这个目录的 dex 文件是在安装 apk 包的时候由 Dalvik 生成的。

?

2.注意

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

2.2?大家可以看看DexClassLoader的API文档,里面不提倡从SD卡加载,不安全

3.如何制作插件

3.1 把工程导出为jar包

3.2 执行SDK安装目录android-sdk-windows\platform-tools下的dx命令,把jar包转换为dex格式

dx?--dex?--output=dex名 jar包名

4.如何做到启动未安装的apk中的activity?

采用反射机制,把主apk中的activity的context传递到插件的activity中,然后采用反射进行回调插件activity的方法。不足之出就是,插件中的activity并不是真正的activity,它只是运行在主activity中。比如:点击返回直接退出当前activity而不是回到主activity。实例如下:

?

这是调用的Activity:

?

[java]?view plaincopy ? ?

package?com.beyondsoft.activity;??

??

import?java.lang.reflect.Constructor;??

import?java.lang.reflect.InvocationTargetException;??

import?java.lang.reflect.Method;??

??

import?dalvik.system.DexClassLoader;??

import?android.app.Activity;??

import?android.content.pm.PackageInfo;??

import?android.os.Bundle;??

import?android.util.Log;??

??

public?class?PlugActivity?extends?Activity?{??

??

????private?Class?mActivityClass;??

????private?Object?mActivityInstance;??

????Class?localClass;??

????private?Object?instance;??

??

????@Override??

????protected?void?onCreate(Bundle?savedInstanceState)?{??

????????super.onCreate(savedInstanceState);??

??

????????Bundle?paramBundle?=?new?Bundle();??

????????paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY",?true);??

????????paramBundle.putString("str",?"PlugActivity");??

????????String?dexpath?=?"/sdcard/FragmentProject.apk";??

????????String?dexoutputpath?=?"/mnt/sdcard/";??

????????LoadAPK(paramBundle,?dexpath,?dexoutputpath);??

????}??

??

????@Override??

????protected?void?onStart()?{??

????????super.onStart();??

????????Method?start;??

????????try?{??

????????????start?=?localClass.getMethod("onStart");??

????????????????start.invoke(instance);??

????????}?catch?(Exception?e)?{??

????????????//?TODO?Auto-generated?catch?block??

????????????e.printStackTrace();??

????????}??

????}??

??

????@Override??

????protected?void?onResume()?{??

????????//?TODO?Auto-generated?method?stub??

????????super.onResume();??

????????Method?resume;??

????????try?{??

????????????resume?=?localClass.getMethod("onResume");??

????????????resume.invoke(instance);??

????????}?catch?(Exception?e)?{??

????????????//?TODO?Auto-generated?catch?block??

????????????e.printStackTrace();??

????????}??

????}??

??

????@Override??

????protected?void?onPause()?{??

????????super.onPause();??

????????Method?pause;??

????????try?{??

????????????pause?=?localClass.getMethod("onPause");??

????????????pause.invoke(instance);??

????????}?catch?(Exception?e)?{??

????????????e.printStackTrace();??

????????}??

????}??

??

????@Override??

????protected?void?onStop()?{??

????????super.onStop();??

????????try?{??

????????????Method?stop?=?localClass.getMethod("onStop");??

????????????stop.invoke(instance);??

????????}?catch?(Exception?e)?{??

????????????e.printStackTrace();??

????????}??

????}??

??

????@Override??

????protected?void?onDestroy()?{??

????????//?TODO?Auto-generated?method?stub??

????????super.onDestroy();??

????????try?{??

????????????Method?des?=?localClass.getMethod("onDestroy");??

????????????des.invoke(instance);??

????????}?catch?(Exception?e)?{??

????????????//?TODO?Auto-generated?catch?block??

????????????e.printStackTrace();??

????????}??

????}??

Android动态加载dex技术初步了解

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

参数1 dexPath:待加载的dex文件路径,如果是外存路径,一定要加上读外存文件的权限(uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/ ),否则会报与上面一样的错误,这点参考文章2中说这个权限可有可无是错误的。(更正下:Android4.4 KitKat及以后的版本需要此权限,之前的版本不需要权限)

Android怎样动态加载代码技术

在开发Android App的过程当中,可能希望实现插件式软件架构,将一部分代码以另外一个APK的形式单独发布,而在主程序中加载并执行这个APK中的代码。

实现这个任务的一般方法是:

复制代码

// 加载类cls

Context pluginContext = mainContext.createPackageContext(PLUGIN_PKG, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);

ClassLoader loader = pluginContext.getClassLoader();

Class? cls = loader.loadClass(CLASS_NAME);

// 通过反射技术,调用cls中的方法,下面是一个示例,实际代码因情况而定

Object obj = cls.newInstance();

Method method = cls.getDeclaredMethod("someMethod");

method.invoke(obj);

复制代码

但是,这个方法在Android 4.1及之后的系统中存在一些问题:对于收费应用,Google Play会将其安装在一个加密目录之下(具体就是/data/app-asec),而不是一个普通目录之下(具体就是/data/app);安装在加密目录中的应用,我们是无法使用上述方法来加载并执行代码的;而实际情况是,我们经常就是依靠插件应用来收费的。

解决上述问题的一个方案是:将插件的二进制代码拷贝到SD卡中,主程序从SD卡中加载并执行其代码。

实现这个任务的具体方法是:

复制代码

Class? cls = null;

try {

// 尝试第一种方法

cls = loadClass1(mainContext, pkg, entryCls);

} catch (Exception e) {

// 尝试第二种方法

cls = loadClass2(mainContext, pkg, entryCls);

}

// 示例代码

Object obj = cls.newInstance();

Method method = cls.getDeclaredMethod("someMethod");

method.invoke(obj);

// 第一种加载方法

private Class? loadClass1(Context mainContext, String pkg, String entryCls) throws Exception {

Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);

ClassLoader loader = pluginContext.getClassLoader();

return loader.loadClass(entryCls);

}

// 第二种加载方法

private Class? loadClass2(Context mainContext, String pkg, String entryCls) throws Exception {

Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);

String path = generatePluginDexPath(mainContext, pkg);

ensureFileExist(pluginContext, pkg, path);

// cacheDir必须是主程序的私有目录,否则DexClassLoader可能会拒绝加载

String cacheDir = mainContext.getApplicationInfo().dataDir;

ClassLoader parentLoader = pluginContext.getClassLoader();

DexClassLoader loader = new DexClassLoader(path, cacheDir, null, parentLoader);

return loader.loadClass(entryCls);

}

// 获取程序版本号

private int getVersionCode(Context context, String pkg) {

PackageInfo info = null;

int versionCode = 0;

try {

info = context.getPackageManager().getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);

versionCode = info.versionCode;

} catch (Exception e) {}

return versionCode;

}

// 获取插件二进制代码的存储位置,注意做好版本控制;路径必须是以.dex结束,否则加载会出问题

private String generatePluginDexPath(Context context, String pkg) {

int version = getVersionCode(context, pkg);

String path = getMyAppPath() + ".classes/" + pkg + version + ".dex";

return path;

}

// 主程序在SD卡上的数据目录

private String getMyAppPath() {

return Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyApp/";

}

// 拷贝插件的二进制代码到SD卡

private void ensureFileExist(Context pluginContext, String pkg, String path) throws Exception {

File file = new File(path);

if(file.exists()) return;

file.getParentFile().mkdirs();

Resources res = pluginContext.getResources();

int id = res.getIdentifier("classes", "raw", pkg);

InputStream in = res.openRawResource(id);

FileOutputStream out = new FileOutputStream(file);

try {

byte[] buffer = new byte[1024 * 1024];

int n = 0;

while((n = in.read(buffer)) 0) {

out.write(buffer, 0, n);

} out.flush();

} catch (IOException e) {

in.close();

out.close();

}

}

复制代码

插件工程这边也需要做相应的修改:

1.编译插件工程;

2.将bin目录之下的classes.dex拷贝到/res/raw目录之下;

3.重新编译插件工程;

4.发布插件APK。

android和标准linux 动态加载差异和分析

之前的分析android和标准linux在编译器的差别,那么是标准编译器编译的bin文件是否可以在andorid上运行的。

下面一个android bin为例,通过readelf -l命令,

再看标准的linux加载器

通过INTERP 段的区别,android使用linker程序,而标准Linux使用的是ld-linux.so这个so,内核分析它的.interp段,也就是linker或者ld-linux.so,将动态连接器映射到进程的地址空间,然后将控制权交给动态链接。

将arm-linux-gnueabi下的ld-linux.so.3和libc.so.6 copy到android平台的/lib下

root@sp9820e_2h10:/lib # ls

ld-linux.so.3

libc.so.6

运行helloworld,添加/lib到LD_LIBRARY_PATH,运行成功

android的动态加载和静态加载的区别

两者区别:

一,静态库的使用需要:

1

包含一个对应的头文件告知编译器lib文件里面的具体内容

2

设置lib文件允许编译器去查找已经编译好的二进制代码

二,动态库的使用:

程序运行时需要加载动态库,对动态库有依赖性,需要手动加入动态库

三,依赖性:

静态链接表示静态性,在编译链接之后,

lib库中需要的资源已经在可执行程序中了,

也就是静态存在,没有依赖性了

动态,就是实时性,在运行的时候载入需要的资源,那么必须在运行的时候提供

需要的

动态库,有依赖性,

运行时候没有找到库就不能运行了

四,区别:

简单讲,静态库就是直接将需要的代码连接进可执行程序;动态库就是在需要调用其中的函数时,根据函数映射表找到该函数然后调入堆栈执行。

做成静态库可执行文件本身比较大,但不必附带动态库

做成动态库可执行文件本身比较小,但需要附带动态库

五:

首先纠正所谓“静态连接就是把需要的库函数放进你的exe之中”的说法。在真实世界中,有三个概念:Use

static

libary,

static

linked

DLL,

dynamic

linked

DLL.

多数人混淆了static

libary

static

linked

DLL的概念,当然他们有似是而非的“相似之处”,比如都用到.lib,下面具体说明。

使用静态库(Use

static

libary)是把.lib和其他.obj一起build在目标文件中,目标文件可以是.exe,也可以是.dll或.oxc等。一般情况下,可以根本就没有“对应的”.dll

文件,如C

Run

Time(CRT)库。一个例子就是,写一个main(){},build出来并不是只有几个字节,当然有人会说那还有exe文件头呢?是,即使加上文件头的尺寸,build出的执行文件仍然“莫名的大”。实际上那多出来的部分就是CRT静态库。姑且可以把静态库.lib理解成外部程序的obj文件比较合理,它包含了函数的实现。


分享名称:android动态加载,android动态加载apk框架
文章URL:http://cdxtjz.cn/article/phsjoc.html

其他资讯