古人云:「以銅爲鏡,能夠正衣冠;以古爲鏡,能夠知興替;以人爲鏡,能夠明得失;而以法爲鏡,能夠斷曲直。」 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。