动态加载Android,动态加载和动态链接的区别

android 怎么动态的加载类

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

创新互联专注于北仑网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供北仑营销型网站建设,北仑网站制作、北仑网页设计、北仑网站官网定制、微信平台小程序开发服务,打造北仑网络公司原创品牌,更为您提供北仑网站排名全网营销落地服务。

转自:

?

前言:

?

? ? ? 在目前的软硬件环境下,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怎样动态加载代码技术

在开发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动态加载资源

Fruit[] data = {"apple", "banana", "orange"};

int[]  bitmaps= {R.drawable.apple,  R.drawable.banana, R.drawable.orange};

for ( int i= 0; i data.size; i ++ ){

Fruit temp= new Fruit(fruit, bitmaps[i]);

fruitList.add(temp);

}

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

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

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

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中怎么动态的加载一个布局

由于前段时间项目需要,需要在一个页面上加载根据不同的按钮加载不同的布局页面,当时想到用 tabhot 。不过美工提供的界面图完全用不上tabhot ,所以想到了动态加载的方法来解决这一需求。在这里我整理了一下,写了一个 DEMO 希望大家以后少走点弯路。

首先,我们先把界面的框架图画出来,示意图如下:

中间白色部门是一个线性布局文件,我喜欢在画图的时候用不同的颜色将一块布局标示出来,方便查看。布局文件代码如下:

分享标题:动态加载Android,动态加载和动态链接的区别
浏览路径:https://www.cdcxhl.com/article2/dsdsjic.html

成都网站建设公司_创新互联,为您提供网站制作搜索引擎优化网站改版网站营销建站公司移动网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都做网站