栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android一键生成包含,学习指南

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android一键生成包含,学习指南

public interface IMessage_one {

String showMessage(Context context);

}

ShowMessageImpl_one 实现类:

public class ShowMessageImpl_one implements IMessage_one {

@Override

public String showMessage(Context context) {

Toast.makeText(context, “dexlibrary1生成的dex文件已加载”, Toast.LENGTH_LONG).show();

return “大家好才是真的好”;

}

}

编译生成Jar及进行转化


OK,接下来要做的就是将dexlibrary1打包为jar了,这里需要注意将哪些文件进行打包,从哪里获取到需要打包的文件呢?如下图所示:

即:对应module或project目录下—>build—>intermediates—>classes—>debug下(如果没有classes目录,需要先进行编译),如下图所示:

然后我们通过在build.gradle中定义task任务进行构建,构建的方式有多种,不过都大同小异,例如:

//删除jar包任务

task clearJar(type: Delete) {

delete ‘build/libs/dexlibrary1.jar’

}

task makeJar(type: Jar) {

//指定生成的jar名

baseName ‘dexlibrary1’

//archiveName = ‘dexlibrary1’ //这样指定名称也可以

//从哪里打包class文件

from(‘build/intermediates/classes/debug/org/gaochun/dexlibrary1/’)

//将assets目录打入jar包

//from fileTree(dir: ‘src/main’, includes: [‘assets

public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)

**注:**4.1以后不能够将第二个参数 optimizedDirectory 设置到sd卡目录, 否则抛出异常,强烈建议使用内部私有存储路径(即应用的data/data/xx包名/下面创建一个app_dex文件夹),不要放到sdcard上,代码容易被注入攻击。

下面我们将编译好的含有dex文件的 dexlibrary1_dex.jar 文件放到app下的assets目录下,当然也可以通过其他手段进行加载,例如放到服务器上Download下来 等等,下面演示通过放置到assets目录进行加载:

private void loadDexClass() {

File dexOutputDir = getDir(“dex”, 0);//在data/data/xx包名/下面创建一个app_dex文件夹

String internalPath = dexOutputDir.getAbsolutePath() + File.separator + “dexlibrary1_dex.jar”;

File dexFile = new File(internalPath);

try {

if (!dexFile.exists()) {

dexFile.createNewFile();

//将assets目录下的文件copy到app/data/cache目录

FileUtils.copyFiles(this, “dexlibrary1_dex.jar”, dexFile);

}

} catch (IOException e) {

e.printStackTrace();

}

//加载dex class

DexClassLoader dexClassLoader = new DexClassLoader(internalPath, dexOutputDir.getAbsolutePath(), null, getClassLoader());

try {

//该name就是internalPath路径下的dex文件里面的ShowMessageImpl_one这个类的包名+类名

Class clz = dexClassLoader.loadClass(“org.gaochun.dexlibrary1.ShowMessageImpl_one”);

IMessage_one impl = (IMessage_one) clz.newInstance();//通过该方法得到IMessage_one类

if (impl != null) {

String value = impl.showMessage(this);//调用打开弹窗并获取值

mTextView.setText(value);

}

} catch (Exception e) {

e.printStackTrace();

}

}

划重点:Class clz = dexClassLoader.loadClass(“org.gaochun.dexlibrary1.ShowMessageImpl_one”); 这个loadClass的包名必须保持一致,即app下的包名和 dexlibrary1 组件下的包名必须保持一致,不然会出现java.lang.ClassCastException或ClassNotFoundException 等错误,所以需要保持一致,如下图所示:

这里给个这样的建议,定义了一个Common的基类Module,里面存放各种interface接口文件,然后剥离出来的组件引用了Common且都implements了对应的接口,宿主app也同样引用了Common,这样在宿主app中加载dex包时就不会出现上面转换错误或者找不到类的错误了,也让项目变得更加清晰一些,画个粗糙的图吧,绿色箭头表示依赖,红色箭头表示对打包好的dex进行加载,大致是这么个意思:

ok,加载成功前后的效果图:

         

到此我们知识点和功能也都基本完善了,按照上面的操作流程,Demo也能正常的运行起来,用着用着,因为项目的需求,独立出来的module越来越多,每个module的build.gradle文件中都有一大坨clearJar、makeJar的任务代码,看着有些碍眼,这是其一,其二就是每次都需要将编译好的jar拷贝到指定目录通过命令再生成包含dex的jar,这重复机械性的工作做多了也是有点头皮发麻,所以针对这个下面做了一些优化。

优化编译脚本


优化的目的总结下来有以下几点:

① Module统一版本管理

② 将clearJar/makeJar等任务抽离开,不要在每个module中都写一大堆

③ 通过自定义的Task一键生成包含class.dex的jar,省去手动编译重复性的工作

④ 上传到Git后确保让每个协同开发的小伙伴也能直接执行task任务进行编译,无需修改其他配置

下面分别来简单进行说明:

一、Module统一版本管理

首先可以在我们在项目的根目录创建一个 versionConfig.gradle 文件,该文件中定义的内容只做版本相关的定义和配置(也可以在根目录的build.gradle目录定义),例如:

ext {

versions = [

sdkMinVersion : 14,

sdkTargetVersion : 27,

sdkCompileSdkVersion: 27

//其他…

]

depVersion = [

appCompatVersion : “27.1.1”,

recyclerViewVersion : “27.1.1”,

constraintLayoutVersion: “1.1.0”

]

deps = [

suport: [

appcompat : “com.android.support:appcompat-v7:${depVersion.appCompatVersion}”,

recyclerview : “com.android.support:recyclerview-v7:${depVersion.recyclerViewVersion}”,

constraint_layout: “com.android.support.constraint:constraint-layout:${depVersion.constraintLayoutVersion}”

]

]

}

注意由于各个module都需要引用到该配置信息,所以该文件需要在 根目录build.gradle中apply:

接下来在各个module中使用:

apply plugin: ‘com.android.application’

android {

def versions = rootProject.ext.versions

compileSdkVersion versions.sdkCompileSdkVersion

defaultConfig {

minSdkVersion versions.sdkMinVersion

targetSdkVersion versions.sdkTargetVersion

versionCode 1

versionName “1.0”

applicationId “org.gaochun.dexlibrary”

}

buildTypes {

release {

minifyEnabled false

proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’

}

}

}

dependencies {

def dependencies = rootProject.ext.deps

implementation fileTree(dir: ‘libs’, include: [’*.jar’])

implementation dependencies.suport.appcompat

implementation dependencies.suport.constraint_layout

//implementation ‘com.android.support.constraint:constraint-layout:1.1.0’

}

二、抽离clearJar/makeJar等编译任务并自定义Task任务执行编译dex任务

同样我们单独定义个文件:makeDexJar.gradle,将上面我们编译jar所定义的 clearJar/makeJar 任务放到这个文件中,如下图所示:

这个时候问题来了,如何引用这个文件;这个给多个module引用的打包编译任务有很多公共的属性,怎么封装成方法;另外生成包含class.dex的jar编译命令怎么写;下面是优化好的代码,根据注释可以清楚每一行代码的含义及作用,供大家参考:

//------------------------- 构建Jar和包含Dex的Jar ---------------------------------

ext {

readLocalSDKPropertiesToMakeDexJar = this.&readLocalSDKPropertiesToMakeDexJar

}

def readLocalSDKPropertiesToMakeDexJar(outputDexJarName, jarName, packagePath) {

//println(“我被调用了”)

//编译工具

//def buildingToolPath = ‘D:Androidandroid-sdkbuild-tools28.0.0dx.bat’

def dxbatVersion = ‘25.0.0’ //因为项目用的是25Level,所以此处用25.0.0的版本构建

def dxbat = ‘build-tools’ + dxbatVersion + ‘dx.bat’

def buildingToolPath

//主要是为了读取local.properties文件中的sdk.dir路径,设置编译工具的位置

//这样其他成员拉取代码后打包就不用手动更改编译工具的路径了

File file = rootProject.file(‘local.properties’)

if (file.exists()) {

InputStream inputStream = rootProject.file(‘local.properties’).newDataInputStream();

Properties properties = new Properties()

properties.load(inputStream)

if (properties.containsKey(“sdk.dir”)) {

buildingToolPath = properties.getProperty(“sdk.dir”) + dxbat

}

}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/678505.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号