概述

面对App业务逻辑的频繁变更,如果每一次改变都对App进行一次升级,会降低App的用户体验,那么App进行模块化升级(这里与增量升级是不同的)是很好的解决方案,让用户在完全无感觉的情况下改变App中的业务逻辑。要实现这种模块化升级,动态加载字节码(jar/dex)就是实现这个需求的理论基础。

Android系统加载字节码

Android的虚拟机(Dalvik VM)无法识别普通jar包中的字节码,所以需要通过字节码转换工具将jar转换成dex,jar包中的所有字节码都会打进classes.dex,这样的字节码才能被Dalvik虚拟机识别。

实例讲解

源码下载,需要准备两个工程,一个Android工程(AndroidPractice),Android工程中去加载字节码。宁外一个普通的java工程(DexModule),下面看看工程结构。需要注意的是IDynamicLoad这个接口在两个工程中都需要,且包名一致。

字节码加载工程结构

DexModule工程讲解

DexModule这个工程中只有两个类,一个是接口IDynamicLoad,这个接口就是文章开始提到的模块升级的关键,只需要提前约定好接口,将接口发布给调用方,具体的实现对调用方完全是透明的,这样就能做到随意的更改接口的实现,宁一个是该接口的实现类DynamicLoad。实现非常简单就是返回一个字符串。

packagecom.vjson.module;publicinterfaceIDynamicLoad{publicStringdexLoad();}
<spanstyle="font-weight:normal;">packagecom.vjson.module;publicclassDynamicLoadimplementsIDynamicLoad{@OverridepublicStringdexLoad(){return"dexloadpractice";}}</span>


导出jar包

在导出jar包的时候一定要注意,不要导出IDynamicLoad这个接口文件,因为Android工程中已经有一个接口文件,不然加载字节码的时候会导致字节码冲突。

导出jar包

字节码转换

用前面提到的字节码转换工具,将jar转换为Dalvik VM认识的dex。d2j-jar2dex.sh这个命令的-o参数指定输出文件。

d2j-jar2dex.sh-omodule.jardynamicLoad.jar


然后将生成的module.jar放到手机的sdcard里面。

adbpushmodule.jar/mnt/sdcard/



加载dex

加载字节码需要用到DexClassLoader这个类,它负责从jar包中提取(解压缩的一个过程)classes.dex,并且将字节码加载到内存,接下来就通过loadClass方法加载需要的类,看下面的详细代码,注意高亮的行。

packagecom.vjson.practice;importjava.io.File;importandroid.annotation.TargetApi;importandroid.app.Activity;importandroid.os.Build;importandroid.os.Bundle;importandroid.os.Environment;importandroid.view.View;importandroid.view.View.OnClickListener;importandroid.widget.Button;importandroid.widget.Toast;importcom.vjson.dynamicload.R;importcom.vjson.module.IDynamicLoad;importdalvik.system.DexClassLoader;publicclassMainActivityextendsActivity{privateButtonmBtn;privateIDynamicLoadmDynamicLoad;@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)publicstaticfinalIDynamicLoadloadByteCode(){FilejarFile=newFile(Environment.getExternalStorageDirectory().toString()+File.separator+"module.jar");FileoptmizedPath=BaseApplication.sInstance.getDir("dex",MODE_PRIVATE);DexClassLoaderloader=newDexClassLoader(jarFile.getAbsolutePath(),optmizedPath.getAbsolutePath(),null,BaseApplication.sInstance.getClassLoader());IDynamicLoaddynaicLoad=null;try{Class<?>clazz=loader.loadClass("com.vjson.module.DynamicLoad");dynaicLoad=(IDynamicLoad)clazz.newInstance();}catch(ClassNotFoundExceptione){e.printStackTrace();}catch(InstantiationExceptione){e.printStackTrace();}catch(IllegalAccessExceptione){e.printStackTrace();}returndynaicLoad;}@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mDynamicLoad=loadByteCode();mBtn=(Button)findViewById(R.id.btn);mBtn.setOnClickListener(newOnClickListener(){@OverridepublicvoidonClick(Viewv){Stringstr=mDynamicLoad.dexLoad();Toast.makeText(getApplicationContext(),str,Toast.LENGTH_SHORT).show();}});}}


注意代码的26行,通过context的getDir(“dex”, MODE_PRIVATE)方法获取应用程序的私有目录,这是由于从Android4.1.2开始由于安全原因,防止代码注入攻击,必须将字节码放到私有目录下面也就是data/data/应用程序包名/。从jar包中提前出来的classes.dex就放在这个目录下面。

总结

本文主要介绍了,Android中的字节码加载技术,为接下来的文章Android模块化升级提供一个理论基础,其实最精髓的地方就是定义接口,通过接口调用端和实现端进行通信。在模块化升级中将会讲解jar包的完整性验证和安全性验证。


更多相关文章

  1. Android(安卓)效率开发之图片---Glide 旋转图片处理
  2. 自己动手做android热更新框架
  3. Android(安卓)实现布局动态加载
  4. Android中的MVP模式使用
  5. Android常见面试题&字节跳动、阿里、腾讯2019实习生Android岗部
  6. 基于JSON RPC的一种Android跨进程调用解决方案了解一下?
  7. Android(安卓)Glide图片加载库基础使用详解
  8. Android(安卓)AOP实现原理之字节码插桩(一)
  9. Android(安卓)Hack Retrofit 增强参数(固定参数)

随机推荐

  1. Android中MVP模式
  2. Android(安卓)学习笔记(4)—— ToggleButto
  3. AndroidX介绍及项目迁移
  4. Android(安卓)P 中的网络安全配置指南 ne
  5. 离线配置Android开发环境
  6. FusionCharts报表在Android上的实现
  7. android全格式多媒体播放器(一:ffmpeg移植)
  8. Android(安卓)Power Management 实现
  9. 改造 Android(安卓)官方架构组件 ViewMod
  10. android 中关于 activity 的一些理解