【Android】Android动态加载Jar、APK的实现
本文介绍Android中动态加载Jar、APK的实现。而主要用到的就是DexClassLoader这个类。大家都知道Android和普通的Java虚拟机有差别,它只能加载经过处理的dex文件。而加载这个dex文件可以通过DexClassLoader 和 PathClassLoader 两个类来实现这个方法。然而PathClassLoader只能加载已经安装到Android系统中的apk文件。接下来,会介绍在Android中如何动态加载Jar、如何加载未安装的APK,如何加载已经安装的APK。
1.Android如何动态加载Jar
动态加载Jar主要是用于在APP的热更新、插件化开发方面,在进行加载之前,首先需要生成Jar文件。测试的jar包定义了一个接口和一个实现类。需要注意定义接口的步骤是必不可少的,在后面利用反射加载的时候就要利用到这个接口。
定义ILoader接口:
package com.example.interf; public interface ILoader {
public String sayHi();
}
ILoader.java
定义JarLoader类:
package com.example.interf; public class JarLoader implements ILoader {
@Override
public String sayHi() {
return "来自动态加载的Jar";
}
}
JarLoader.java
然后打包为Jar文件
这里笔者导出为Loader.jar文件。有一点需要注意,就是不要把ILoader.jar接口打包进去,因为后期可能会包错: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation ,出现这个错误的原因就是打包的Jar中有接口,然后在下面的文件中又定义了接口,出现了两个接口文件,所以报错。如果这里把ILoader接口打包进去,那么在下面的测试中就不要再定义相同的ILoader接口了。
到这里我们就把Jar文件打包成功了,接下来了需要把这个Jar文件用dx工具进行处理,dx工具在Android SDK 的tools中已经提供了,一般在android-SDK/build-tools目录下。
将上面的Loader.jar文件拷贝一份到dx同级的目录下,然后执行如下命令:
dx --dex --output=Loader_dex.jar Loader.jar
然后将生成的Loader_dex.jar文件,拷贝到手机的SD根目录下面(手机SD的根目录就是:/storage/emulated/0,读者也可以使用 Environment.getExternalStorageDirectory() 查看)
接下来就可以使用如下的代码进行加载:
其中ILoader.java接口与Loader.jar中的ILoader接口保持一直。
package com.example.test; import java.io.File; import com.example.interf.ILoader; import dalvik.system.DexClassLoader; import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.util.Log;
import android.widget.Toast; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); loadJar();
} /**
* @Title LoadJar
* @Description 项目工程中必须定义接口(包名都要一致), 而被引入的第三方jar包实现这些接口,然后进行动态加载 。
* 相当于第三方按照接口协议来开发, 使得第三方应用可以以插件的形式动态加载到应用平台中。
* @return void
*/
private void loadJar(){
File dexoutputdir = getDir("dex1",0);
String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "Loader_dex.jar";
DexClassLoader loader = new DexClassLoader(dexPath,dexoutputdir.getAbsolutePath(),null,getClassLoader()); try {
Class clz = loader.loadClass("com.example.interf.JarLoader");
ILoader iShowToast = (ILoader) clz.newInstance();
Toast.makeText(this,iShowToast.sayHi(),Toast.LENGTH_LONG).show();
} catch (Exception e){
Log.d("dd",e.toString());
}
}
}
MainActivity.java
在这个类中定义的核心方法是loadJar()
private void loadJar(){
File dexoutputdir = getDir("dex1",0);
String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "Loader_dex.jar";
DexClassLoader loader = new DexClassLoader(dexPath,dexoutputdir.getAbsolutePath(),null,getClassLoader()); try {
Class clz = loader.loadClass("com.example.interf.JarLoader");
ILoader iShowToast = (ILoader) clz.newInstance();
Toast.makeText(this,iShowToast.sayHi(),Toast.LENGTH_LONG).show();
} catch (Exception e){
Log.d("dd",e.toString());
}
}
接下来笔者解释一下上面这个方法中核心类DexClassLoader。
此处需要注意DexClassLoader的四个参数:
参数1 dexPath:待加载的dex文件路径,如果是外存路径,一定要加上读外存文件的权限( <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ),否则会报与报错,Android4.4 KitKat及以后的版本需要此权限,之前的版本不需要该权限。
参数2 optimizedDirectory:解压后的dex存放位置,此位置一定要是可读写且仅该应用可读写(安全性考虑),所以只能放在data/data下。本文getDir(“dex1”, 0)会在/data/data/**package/下创建一个名叫”app_dex1“的文件夹,其内存放的文件是自动生成Loader_dex.dex;需要注意,data/data文件夹只有在手机root之后,才看得到。
参数3 libraryPath:指向包含本地库(so)的文件夹路径,可以设为null。
参数4 parent:父级类加载器,一般可以通过Context.getClassLoader获取到,也可以通过ClassLoader.getSystemClassLoader()取到。
效果图:
到这里动态加载Jar就结束了。笔者接下来总结一下思路,首先把jar文件经过dx工具处理,然后把处理后的文件放到手机的SD根目录下面,然后利用反射加载调用方法。上面其实只是实现热更新的一半,加载的Jar文件完全可以从服务器下载手机后,然后再在手机端加载,这样可以对手机上的APP进行实时的更新以及防止反编译。
2.如何加载未安装的APK
上面介绍了如何动态加载jar文件,接下来介绍如何加载未安装的APK。
首先新建一个Android项目:
定义一个接口ISayHello.java
package com.example.loaduninstallapkdemo; public interface ISayHello {
public String sayHello();
}
ISayHello.java
然后新建Activity,实现ISayHello接口:
package com.example.loaduninstallapkdemo; import android.os.Bundle;
import android.app.Activity; public class MainActivity extends Activity implements ISayHello{ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} @Override
public String sayHello() {
return "Hello, this apk is not installed";
}
}
MainActivity.java
然后把该工程的APK拷贝到手机的SD根目录下面,
接下来就可以使用如下的代码进行动态加载了,下面的加载过程和上面的类似,只是不再需要定义接口了。
package com.example.test; import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method; import dalvik.system.DexClassLoader; import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.view.Menu;
import android.widget.Toast; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); loadUnInstallAPK();
} private void loadUnInstallAPK(){
String path = Environment.getExternalStorageDirectory() + File.separator;
String filename = "UninstallApkActivity.apk"; // 4.1以后不能够将optimizedDirectory设置到sd卡目录, 否则抛出异常.
File optimizedDirectoryFile = getDir("dex", 0) ;
DexClassLoader classLoader = new DexClassLoader(path + filename, optimizedDirectoryFile.getAbsolutePath(),
null, getClassLoader()); try {
// 通过反射机制调用, 包名为com.example.loaduninstallapkdemo, 类名为MainActivity
Class mLoadClass = classLoader.loadClass("com.example.loaduninstallapkdemo.MainActivity");
Constructor constructor = mLoadClass.getConstructor(new Class[] {});
Object testActivity = constructor.newInstance(new Object[] {}); // 获取sayHello方法
Method helloMethod = mLoadClass.getMethod("sayHello", null);
helloMethod.setAccessible(true);
Object content = helloMethod.invoke(testActivity, null);
Toast.makeText(MainActivity.this, content.toString(), Toast.LENGTH_LONG).show(); } catch (Exception e) {
e.printStackTrace();
}
}
}
MainActivity.java
效果图:
3.如何加载已经安装的APK
在介绍了如何动态加载jar,加载未安装的APK后,接下来介绍如何加载已经安装的APK,
首先将制作一个简单的APK,然后把它安装的手机上面。
package com.example.installapkdemo; import android.os.Bundle;
import android.app.Activity; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
MainActivity.java
将该APK安装到手机后,接下来就可以进行加载了。
同样和加载未安装的APK类似,项目中也不需要定义接口。
package com.example.test; import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast; public class MainActivity extends Activity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadInstalledApk();
}
private void loadInstalledApk(){
try {
String pkgName = "com.example.installapkdemo";
Context context = createPackageContext(pkgName,
Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE) ; // 获取动态加载得到的资源
Resources resources = context.getResources() ;
// 获取该apk中的字符串资源"hello_world", 并且toast出来,apk换肤的实现就是这种原理
String toast = resources.getString(resources.getIdentifier("hello_world", "string", pkgName) ) ;
Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show() ; Class cls = context.getClassLoader().loadClass(pkgName + ".MainActivity");
// 跳转到该Activity
startActivity(new Intent(context, cls)) ;
} catch (NameNotFoundException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
Log.d("", e.toString()) ;
}
}
}
MainActivity.java
效果图:
原文链接:
【Android】Android动态加载Jar、APK的实现的更多相关文章
- Android动态加载jar、apk的实现
前段时间到阿里巴巴参加支付宝技术分享沙龙,看到支付宝在Android使用插件化的技术,挺好奇的.正好这几天看到了农民伯伯的相关文章,因此简单整理了下,有什么错误希望大神指正. 核心类 1.1 ...
- Android动态加载jar/dex
前言 在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优 ...
- 某APK中使用了动态注册BroadcastReceiver,Launcher中动态加载此APK出现java.lang.SecurityException异常的解决方法
在某APK中,通过如下方法动态注册了一个BroadcastReceiver,代码参考如下: @Override protected void onAttachedToWindow() { super. ...
- Java_Java中动态加载jar文件和class文件
转自:http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...
- JAVA动态加载JAR包的实现
如何动态的加载这些驱动!不可能把所有的数据库驱动都集成到JAR包中吧?!于是动态加载驱动的JAR包就产生了!其实这些在做系统基础代码时,经常用到,只是一般我们没有机会去搞而已. 动态加载JAR包,使用 ...
- Java动态加载jar及class文件
经常碰到需要动态加载jar及class文件的场景.Java类由于需要加载和编译字节码,动态加载class文件较为麻烦,但JDK仍提供了一整套方法来动态加载jar文件和class文件. 一.动态加载ja ...
- [转载] Java中动态加载jar文件和class文件
转载自http://blog.csdn.net/mousebaby808/article/details/31788325 概述 诸如tomcat这样的服务器,在启动的时候会加载应用程序中lib目录下 ...
- java动态加载jar包,并运行其中的类和方法
动态加载jar包,在实际开发中经常会需要用到,尤其涉及平台和业务的关系的时候,业务逻辑部分可以独立出去交给业务方管理,业务方只需要提供jar包,就能在平台上运行. 下面通过一个实例来直观演示: 第一: ...
- java动态加载jar文件
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, Invo ...
随机推荐
- 测试数据——有效范围(2)
测试数据库搞好,学习了一下逾期率的官方定义: • 对于某支标,如果某一期没有正常还款,则悲观逾期率=所有未还本金/借款本金: • 对于一批标,悲观逾期率=当前逾期标的所有未还本金/借款本金: • 以3 ...
- java 流媒体服务器Red5 FQA
原文链接:http://www.cnblogs.com/zhuzhao/archive/2008/08/12/1265661.html red5 FQA red5 FQA 引自:http://hi ...
- XE6入门(二)项目中的文件
XE6中项目文件为DPR,查看方法请参考一下以前写过的博文: "Delphi项目构成之项目文件DPR" 项目文件DPR 通过主菜单[Project | View Source],就 ...
- pdo_mysql安装不了或是安装后用不了的修复教程
目前发现wdOS及lanmp_wdcp的RPM包安装在部分系统下安装后,在安装pdo_mysql时无法安装或安装后无法使用的问题 如在安装时提示下如下 regenerate PHP parsers.c ...
- uni - 使用npm
一.使用 1. 在当前根目录初始化package.json npm init -y 2. 安装(自动生成node_modules文件夹) npm i packageName yarn add pack ...
- 变址values(, %edi, 4)和间址4(%edi)
<汇编语言程序设计>Richard Blum著:5.2.4 在内存和寄存器之间传送数据 使用变址的内存位置: 可以在一个命令中指定把多个值存放到内存中: values: .in ...
- 转:初探nginx架构(二)
From:http://tengine.taobao.org/book/chapter_02.html 上篇文章讲了很多关于nginx的进程模型,接下来,我们来看看nginx的是如何处理事件的. 有人 ...
- web前端开发,如何提高页面性能优化?
内容方面: 1.减少 HTTP 请求 (Make Fewer HTTP Requests) 2.减少 DOM 元素数量 (Reduce the Number of DOM Elements) 3.使得 ...
- JVM的结构
参考:http://blog.csdn.net/tonytfjing/article/details/44278233 JVM的结构 一般认为,JVM分为四大部分: 1.类加载器(ClassL ...
- openerp 7.0邮件接收中文附件乱码问题解决办法
openerp 7.0邮件接收中文附件乱码问题解决办法: 修改文件\addons\mail\mail_thread.py #1064 line插入代码: h=email.Header.Header(n ...