Gradle脚本打包AndroidStudio依赖库的问题

  古人云:“以铜为镜,能够正衣冠;以古为镜,能够知兴替;以人为镜,能够明得失;而以法为镜,能够断曲直。”  java

  目前,国内大多数(99%)渠道在提供给CP渠道SDK时,都会有eclipse的接入方式。但毕竟如今google爸爸已经弃用了eclipse开发方式,AndroidStudio是官方指定使用的开发工具,有一些渠道,特别是刚开始作SDK的一些小渠道,为了方便快捷,使用AndroidStudio进行开发,且提供给CP接入方式也仅仅有一个AndroidStudio接入方式,这个问题对于目前的聚合SDK接入就是一个比较麻烦的问题。好比U8,quick,主要都是eclipse方式的聚合SDK。AndroidStudio对于咱们这种eclipse接入方式的最大问题是什么呢。是由于AndroidStuido使用的是gradle自动构建工具,使用的第三方依赖库,直接在项目中gradle中依赖便可,并且依赖库麻烦的是内部依赖,一个依赖库可能内部依赖有其余第三方库。这就使咱们获取jar包有必定的难度了。谁知道什么依赖库下依赖着什么呢。python

  问题解决,当我碰见这第一个渠道的时候,思考,当前渠道提供给个人有什么,而我须要什么,还缺乏什么。在互联网的知识海里遨游,但愿寻找到遇到一样困境并已解决的知己伙伴。果然,在随着技术进步的发展下,更多人也喜欢使用AndroidStudio的方式进行开发,那么有人也提出了一些解决办法。如U8解决方案:在AndroidStudio正常接入,该依赖的依赖,该配置的配置,该写的代码写上,而后将整个项目编译成一个apk,反编译,获取里面的资源文件,而后,将项目打包成jar,提供给咱们的聚合SDK依赖。没错,这的确是一个好办法,可是对于个人需求来讲,算是比较麻烦,首先,我维护更新渠道SDK都得AndroidStudio下,那么就须要Eclipse和AndroidStudio两个开发工具下跑,增长了本身开发维护成本和之后工做交接后,同事在背后的种种谴责;其次,在咱们大陆,有幸的隔离了纷纷扰扰的墙外,咱们渠道SDK也无需用到google服务等能够经过AndroidStudio特殊打包方式编译的,因此在这个方案的前提下,思考既然能够将项目打包成jar,那我是否是能够将我如今缺乏的渠道SDK提供过来的依赖库生成jar给我聚合SDK进行依赖呢。而后就又是一顿遨游。例如gradle打包jar,gradle打包依赖库等等。果然,结合各类答案,在亲测下,找到一个符合我本身目前需求的解决办法。讲了那么多废话,下面开始正题:react

  使用shadow插件进行依赖库打包android

  首先,仍是正常将第三方库依赖,同步跑起来,将第三方库的文件都下载到本地,项目无报错。而后修改本身的gradle,怎样修改呢,以下:git

apply plugin: 'java' apply plugin: 'com.github.johnrengelman.shadow' buildscript { repositories { jcenter() } dependencies { classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1' } } repositories { mavenCentral() flatDir { dirs'libs' } } shadowJar { baseName = 'library' classifier = null version = null dependencies { include(dependency('com.squareup.retrofit2:retrofit:2.2.0')) include(dependency('com.squareup.retrofit2:converter-gson:2.2.0')) include(dependency('com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0')) include(dependency('com.squareup.okhttp3:okhttp:3.10.0'))
        include(dependency('com.squareup.okhttp3:logging-interceptor:3.8.0')) include(dependency('io.reactivex.rxjava2:rxjava:2.0.5')) include(dependency('io.reactivex.rxjava2:rxandroid:2.0.1'))
 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // 添加Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.2.0' implementation 'com.squareup.retrofit2:converter-gson:2.2.0' implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    // 添加okhttp
    implementation 'com.squareup.okhttp3:okhttp:3.10.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.8.0'
    // RxJava
    implementation 'io.reactivex.rxjava2:rxjava:2.0.5' implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
}

  如上,这就是我当前遇到的一个只提供AndroidStudio方式提供过来的,dependencies就是渠道SDK须要依赖的第三库,而后在shadowJar方法中dependencies加入咱们须要打包的第三方依赖库,如上。OK,经过这个脚本,咱们在AndroidStudio Terminal窗口下执行”gradlew shadowJar“,咱们能够看到BUILD SUCCESSFUL,欢呼吧,雀跃吧。我以为我解决了天大的问题,是能比肩中国四大发明的创举。而后拿到build/libs下的我经过脚本生成的这个library.jar,使用压缩软件打开看下,嗯嗯,确实是每一个依赖库的代码也在,可是rxandroid这个依赖库是以aar形式进行依赖的,这须要咱们手动拷贝出来,而后将对应的依赖库classes.jar获取。行,我想如今所要用到的第三方库就齐全了吧,正常接入,跑起来。What the fuck。Ru'ntimeException,未找到okio的什么类。咦,确实是,okhttp彷佛是依赖okio使用的,可是gradle依赖无需再次引用该依赖,这就使咱们打包的时候缺失了该第三方库了。寻找解决办法,我如今须要知道全部依赖库的依赖关系,我不可能一个个依赖去查对应的资料,是否有进行依赖吧。怎样能够知道整个依赖树关系呢,gradle经过命令能够查找,可是这个命令实在繁琐,AndroidStudio有个插件叫gradle view,能够看到当前项目的依赖树关系。如图:github

  原来,我刚刚打包的jar确实是有依赖其余第三方库的,获得答案,那咱们就查缺补漏,将依赖关系树中未依赖的第三方库进行在gradlr脚本中进行依赖,而后再次执行脚本文件,一样,build成功,拿到jar替换刚刚生成的不全面的jar包,打包,运行,运行无误。json

  以上,就是我对一些需求不复杂且未使用特殊第三方库的渠道SDK的依赖库打包获取,从而获得一个完整的SDK依赖。在这里有那么几个缺点:app

  一、例如v7这些依赖,使用不少android资源的,见仁见智吧,能够不依赖,单独去sdk里面拿v7这个jar和资源,也能够依赖打包,但资源文件仍是须要去sdk里拿到的;eclipse

  二、例如rxandroid依赖的aar,咱们经过脚本打包进去的也是rxandroid的aar文件,咱们还须要将他单独拿出来,并确认aar中res下有没有资源文件,有则也须要获取。这个问题,咱们能够写个脚本,解压aar,拷贝jar,若是有资源则复制合并资源,在我印象中,依赖库除了android自己的带有资源,其余还没见到过带有res资源文件的,因此这不是一个很大的问题,本身有sdk,有脚本,须要的都是能拿到的,这个脚本后面有空再更新上来maven

  三、例如海外渠道,用到google服务的等一些特殊需求时,好比google-service.json文件,在gradle下会打包自动编译的,这些问题就相对比较麻烦,虽然是有方法集成到eclipse下,仍是建议U8的解决方案

  处理aar的工具脚本以下:

#!/usr/bin/python # -*- coding: utf-8 -*- # Desc:处理aar工具

import argparse import os import os.path import zipfile import sys import platform as platform_lib import codecs import shutil import zipfile import re import subprocess platform = 'nano_default' curDir = os.getcwd() def is_include(fpath, include_path): for ig in include_path: if ig in fpath: return True return False def un_zip(file_name): """unzip zip file""" zip_file = zipfile.ZipFile(file_name) file_path = file_name[0:-4] if os.path.isdir(file_path): pass
    else: os.mkdir(file_path) print(u'Now to unzip aar to dir::'+file_path) for names in zip_file.namelist(): zip_file.extract(names, file_path+"/") zip_file.close() def getCurrDir(): global curDir retPath = curDir if platform_lib.system() == "Windows": retPath = retPath.decode('gbk') return retPath def getFullPath(filename): if os.path.isabs(filename): return filename currdir = getCurrDir() filename = os.path.join(currdir, filename) filename = filename.replace('\\', '/') filename = re.sub('/+', '/', filename) return filename def copy_files(src, dest): if not os.path.exists(src): print(u"copy files . the src is not exists.path:"+src) return

    if os.path.isfile(src): copy_file(src, dest) return

    for f in os.listdir(src): sourcefile = os.path.join(src, f) targetfile = os.path.join(dest, f) if os.path.isfile(sourcefile): copy_file(sourcefile, targetfile) else: copy_files(sourcefile, targetfile) def copy_file(src, dest): sourcefile = getFullPath(src) destfile = getFullPath(dest) if not os.path.exists(sourcefile): return
    if not os.path.exists(destfile) or os.path.getsize(destfile) != os.path.getsize(sourcefile): destdir = os.path.dirname(destfile) if not os.path.exists(destdir): os.makedirs(destdir) destfilestream = open(destfile, 'wb') sourcefilestream = open(sourcefile, 'rb') destfilestream.write(sourcefilestream.read()) destfilestream.close() sourcefilestream.close() if __name__ == '__main__': print(u"一、Now to handle aar file.") includes = ["assets", "res", "libs", "classes.jar", "AndroidManifest.xml"] xmlList = ['strings.xml', 'styles.xml', 'colors.xml', 'dimens.xml', 'ids.xml', 'attrs.xml', 'integers.xml', 'arrays.xml', 'bools.xml', 'drawables.xml', 'values.xml'] parser = argparse.ArgumentParser(u"aar处理工具") parser.add_argument('-p', '--platform', help=u"渠道名称或渠道标识,用于修改部分res文件名称,避免冲突覆盖", default="nano_default") args = parser.parse_args() platform = args.platform print(u"二、Handle channel is " + platform) path = "." targetPath = "../aar"

    if not os.path.exists(targetPath): os.makedirs(targetPath) index = 2
    for root, dirs, files in os.walk(path): for f in files: if f.endswith(".aar"): aarName = f[:-4] index = index+1
                print(index.__str__() + u"、Now to handle aar::" + f) fpath = os.path.join(root, f) un_zip(fpath) print(u"Now to copy file to targetPath") aarDirPath = fpath[:-4] for aarRoot, aarDirs, aarFiles in os.walk(aarDirPath): for sdkFile in aarFiles: sdkFile = os.path.join(aarRoot, sdkFile) if is_include(sdkFile, includes): ftargetpath = sdkFile[len(aarDirPath):] # 修改jar文件为aar文件名称
                            if "classes.jar" in ftargetpath: ftargetpath = ftargetpath.replace('classes', aarName) # 修改xmlList列表中的文件的文件名称,避免多个aar文件名称冲突或与v7包下的资源名称冲突
                            if is_include(ftargetpath, xmlList): ftargetpathList = list(ftargetpath) ftargetpathList.insert(-4, "_"+aarName) ftargetpath = ''.join(ftargetpathList) # 修改AndroidManifest.xml文件名称
                            if "AndroidManifest.xml" in ftargetpath: ftargetpathList = list(ftargetpath) ftargetpathList.insert(-4, "_" + aarName) ftargetpath = ''.join(ftargetpathList) ftargetpath = targetPath + ftargetpath print ftargetpath copy_files(sdkFile, ftargetpath) shutil.rmtree(aarDirPath)

  例如爱奇艺渠道,就是强烈推荐使用AndroidStudio方式接入的一个渠道,SDK资源有两个aar,那么咱们能够建立一个文件夹,或者就在本身的聚合SDK接入lib文件夹下,将脚本拷入:

  

  执行脚本,在当前文件夹的父目录同级下生成一个aar文件夹,这个文件夹下就是拷贝出了aar内中全部的资源,固然拷贝的路径彻底能够本身选择,能够像我同样aar资源先统一拷贝到一个文件夹,也能够你直接就往项目里面拷,都是能够的:

  

  OK,那么咱们该拷贝的拷贝,AndroidManifest.xml这些咱们就手动一下吧,聚合SDK合并AndroidManifest仍是须要注意一点的,这里就不作简单的两个文件合并,而是在文件名后面添加对应的aar名字,自行手动合并吧。

  虽然这个方案不是一个最棒的方案,小小的特殊需求寻求小小的特殊方法解决,对于国内渠道来讲解决了公司聚合SDK接入方式困境,最主要的是方便,能统一在eclipse下维护更新SDK。

相关文章
相关标签/搜索