android插件化开发--修改携程插件支持aidl,dependencies
2017-03-16 14:35
351 查看
一.携程插件修改后的使用方式:
1.设置local.properties文件下solidMode,当等于false时,可以在主工程添加依赖,运行常规开发模式;
三.修改后的build.gradle
1.主工程build.gradle
apply plugin:'com.android.application'
2.插件工程
3.sub-project-build.gradle
四.使用方式:
1.设置local.properties文件下solidMode,当等于false时,可以在主工程添加依赖,运行常规开发模式;
当等于true时作为插件运行,许用命令行执行打包操作 2.设置apk_module_config.xml 修改要添加的插件包名和资源id(0x01系统资源id,0x7f默认资源id) 3.将插件build.gradle文件设置和CallmePlugin项目下build.gradle一样 4.打包命令:gradle assembleRelease repackAll 5.安装Release APK in /build-outputs/***-release-final.apk
二.打包流程:
三.修改后的build.gradle
1.主工程build.gradle
apply plugin:'com.android.application'
project.ext { build_Type='' } android { compileSdkVersion 25 buildToolsVersion "25.0.0" signingConfigs { demo { keyAlias 'demo' keyPassword '123456' storePassword '123456' storeFile file('../demo.jks') } } defaultConfig { applicationId "ctrip.android.sample" versionCode 1 versionName "1.0" minSdkVersion 14 targetSdkVersion 23 } dexOptions { javaMaxHeapSize "4g" // preDexLibraries = false } buildTypes { debug { debuggable true minifyEnabled false signingConfig signingConfigs.demo build_Type='debug' } release { minifyEnabled false signingConfig signingConfigs.demo } } } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile project(':pluginbase') if (!solidMode) { compile project(':CallmePlugin') } } //打包后产出物复制到build-outputs目录。apk、manifest、mapping task copyReleaseOutputs(type:Copy){ from ("$buildDir/outputs/apk/app-release.apk") { rename 'app-release.apk', 'demo-base-release.apk' } from "$buildDir/intermediates/manifests/full/release/AndroidManifest.xml" from ("$buildDir/outputs/mapping/release/mapping.txt") { rename 'mapping.txt', 'demo-base-mapping.txt' } into new File(rootDir, 'build-outputs') } //assembleRelease<<{ // copyReleaseOutputs.execute() //} clean { delete buildDir delete "${rootDir}/build-outputs/demo-base-release.apk" delete "${rootDir}/build-outputs/AndroidManifest.xml" delete "${rootDir}/build-outputs/demo-base-mapping.txt" delete "${rootDir}/build-outputs/demo-mapping-final.txt" delete "${rootDir}/build-outputs/demo-release-reloaded.apk" delete "${rootDir}/build-outputs/demo-release-resigned.apk" delete "${rootDir}/build-outputs/demo-release-repacked.apk" delete "${rootDir}/build-outputs/demo-release-final.apk" } import org.apache.tools.ant.taskdefs.condition.Os def getZipAlignPath(){ def zipAlignPath = "${android.sdkDirectory}/build-tools/${android.buildToolsVersion}/zipalign" if(Os.isFamily(Os.FAMILY_WINDOWS)){ zipAlignPath += '.exe' } assert (new File(zipAlignPath)).exists() : '没有找到zipalign应用程序!' return zipAlignPath } import java.util.zip.ZipEntry import java.util.zip.ZipFile import java.util.zip.ZipOutputStream // 打包过程中很多手工zip过程: // 1,为了压缩resources.arsc文件而对标准产出包重新压缩 // 2,以及各子apk的纯手打apk包 // 但对于音频等文件,压缩会导致资源加载报异常 // 重新打包方法,使用STORED过滤掉不应该压缩的文件们 // 后缀名列表来自于android源码 def repackApk(originApk, targetApk){ def noCompressExt = [".jpg", ".jpeg", ".png", ".gif", ".wav", ".mp2", ".mp3", ".ogg", ".aac", ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".amr", ".awb", ".wma", ".wmv"] ZipFile zipFile = new ZipFile(originApk) ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(targetApk))) zipFile.entries().each{ entryIn -> if(entryIn.directory){ println "${entryIn.name} is a directory" } else{ def entryOut = new ZipEntry(entryIn.name) def dotPos = entryIn.name.lastIndexOf('.') def ext = (dotPos >= 0) ? entryIn.name.substring(dotPos) : "" def isRes = entryIn.name.startsWith('res/') if(isRes && ext in noCompressExt){ entryOut.method = ZipEntry.STORED entryOut.size = entryIn.size entryOut.compressedSize = entryIn.size entryOut.crc = entryIn.crc } else{ entryOut.method = ZipEntry.DEFLATED } zos.putNextEntry(entryOut) zos << zipFile.getInputStream(entryIn) zos.closeEntry() } } zos.finish() zos.close() zipFile.close() } // multidex默认会把manifest中注册的所有组件以及它们的直接引用类放在主dex里, // 以保证至少在查找组件的时候涉及到的类加载正确。 // 但第一级+第二级已经会导致主dex超标。 // 所以在此hack修改CreateManifestKeepList类,让它不要顾忌activity、service、receiver // 以保障主dex足够小不至于爆掉 def patchKeepSpecs() { def taskClass = "com.android.build.gradle.internal.tasks.multidex.CreateManifestKeepList"; def clazz = this.class.classLoader.loadClass(taskClass) def keepSpecsField = clazz.getDeclaredField("KEEP_SPECS") keepSpecsField.setAccessible(true) def keepSpecsMap = (Map) keepSpecsField.get(null) /* if (keepSpecsMap.remove("activity") != null) { // println "KEEP_SPECS patched: removed 'activity' root" } else { // println "Failed to patch KEEP_SPECS: no 'activity' root found" } if (keepSpecsMap.remove("service") != null) { // println "KEEP_SPECS patched: removed 'service' root" } else { // println "Failed to patch KEEP_SPECS: no 'service' root found" } if (keepSpecsMap.remove("receiver") != null) { // println "KEEP_SPECS patched: removed 'receiver' root" } else { // println "Failed to patch KEEP_SPECS: no 'receiver' root found" }*/ } patchKeepSpecs() //解析完任务 // dex命令默认保障方法数索引不超过65535, // 但在编译期pass掉第一关的dex,有可能在运行期卡在dexopt上, // 所以指定最大index数50000,远小于65535,安全第一。 afterEvaluate { // println tasks.withType(com.android.build.gradle.tasks.Dex) tasks.matching { it.name.startsWith('dex') }.each { dx -> // println "found dex task $dx.name, add parameters" if (dx.additionalParameters == null) { dx.additionalParameters = [] } // dx.additionalParameters += '--minimal-main-dex' dx.additionalParameters += '--set-max-idx-number=50000' } try{ //打包完成执行copy操作 tasks.getByName("assembleRelease"){ it.doLast{ copyReleaseOutputs.execute() } } }catch (Exception e){ //打包完成执行copy操作 tasks.getByName("assembleDebug"){ it.doLast{ copyReleaseOutputs.execute() } } } } //base apk的assets中填充各子apk //输入:Ctrip-base-release.apk //输出:Ctrip-release-reloaded.apk task reload(type:Zip){ inputs.file "$rootDir/build-outputs/demo-base-release.apk" inputs.files fileTree(new File(rootDir,'build-outputs')).include('*.so') outputs.file "$rootDir/build-outputs/demo-release-reloaded.apk" into 'assets/baseres/',{ from fileTree(new File(rootDir,'build-outputs')).include('*.so') } from zipTree("$rootDir/build-outputs/demo-base-release.apk"), { exclude('**/META-INF/*.SF') exclude('**/META-INF/*.RSA') } destinationDir file("$rootDir/build-outputs/") archiveName 'demo-release-reloaded.apk' } //对apk重新压缩,调整各文件压缩比到正确 //输入:Ctrip-release-reloaded.apk //输出:Ctrip-release-repacked.apk task repack (dependsOn: 'reload') { inputs.file "$rootDir/build-outputs/demo-release-reloaded.apk" outputs.file "$rootDir/build-outputs/demo-release-repacked.apk" doLast{ println "release打包之后,重新压缩一遍,以压缩resources.arsc" def oldApkFile = file("$rootDir/build-outputs/demo-release-reloaded.apk") assert oldApkFile != null : "没有找到release包!" def newApkFile = new File(oldApkFile.parentFile, 'demo-release-repacked.apk') //重新打包 repackApk(oldApkFile.absolutePath, newApkFile.absolutePath) assert newApkFile.exists() : "没有找到重新压缩的release包!" } } //对apk重签名 //输入:Ctrip-release-repacked.apk //输出:Ctrip-release-resigned.apk task resign(type:Exec,dependsOn: 'repack'){ inputs.file "$rootDir/build-outputs/demo-release-repacked.apk" outputs.file "$rootDir/build-outputs/demo-release-resigned.apk" workingDir "$rootDir/build-outputs" executable "${System.env.'JAVA_HOME'}/bin/jarsigner" def argv = [] argv << '-verbose' argv << '-sigalg' argv << 'SHA1withRSA' argv << '-digestalg' argv << 'SHA1' argv << '-keystore' argv << "$rootDir/demo.jks" argv << '-storepass' argv << '123456' argv << '-keypass' argv << '123456' argv << '-signedjar' argv << 'demo-release-resigned.apk' argv << 'demo-release-repacked.apk' argv << 'demo' args = argv } //重新对jar包做对齐操作 //输入:Ctrip-release-resigned.apk //输出:Ctrip-release-final.apk task realign (dependsOn: 'resign') { inputs.file "$rootDir/build-outputs/demo-release-resigned.apk" outputs.file "$rootDir/build-outputs/demo-release-final.apk" doLast{ println '重新zipalign,还可以加大压缩率!' def oldApkFile = file("$rootDir/build-outputs/demo-release-resigned.apk") assert oldApkFile != null : "没有找到release包!" def newApkFile = new File(oldApkFile.parentFile,'demo-release-final.apk') def cmdZipAlign = getZipAlignPath() def argv = [] argv << '-f' //overwrite existing outfile.zip // argv << '-z' //recompress using Zopfli argv << '-v' //verbose output argv << '4' //alignment in bytes, e.g. '4' provides 32-bit alignment argv << oldApkFile.absolutePath argv << newApkFile.absolutePath project.exec { commandLine cmdZipAlign args argv } assert newApkFile.exists() : "没有找到重新zipalign的release包!" } } /** * 用来连接文件的task */ class ConcatFiles extends DefaultTask { @InputFiles FileCollection sources @OutputFile File target @TaskAction void concat() { File tmp = File.createTempFile('concat', null, target.getParentFile()) tmp.withWriter { writer -> sources.each { file -> file.withReader { reader -> writer << reader } } } target.delete() tmp.renameTo(target) } } //合并base和所有模块的mapping文件 task concatMappings(type: ConcatFiles){ sources = fileTree(new File(rootDir,'build-outputs')).include('*mapping.txt') target = new File(rootDir,'build-outputs/demo-mapping-final.txt') } task repackAll(dependsOn: ['realign','concatMappings'])
2.插件工程
if (solidMode) { apply plugin: 'com.android.application' project.ext { packageName = 'com.gstd.ssms.plugin' apk = packageName.replace('.', '_') apkName = apk+'_cl:1_v:1' } apply from: '../sub-project-build.gradle' }else{ apply plugin: 'com.android.library' } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.google.code.gson:gson:2.5' compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.android.support:support-v4:25.0.1' } android { compileSdkVersion 25 buildToolsVersion '25.0.0' defaultConfig { minSdkVersion 14 targetSdkVersion 23 } lintOptions { abortOnError false } }
3.sub-project-build.gradle
buildscript { repositories { mavenCentral() } dependencies { classpath 'net.sf.proguard:proguard-gradle:5.2.1' } } import org.apache.tools.ant.taskdefs.condition.Os project.ext { BUILD_TOOLS_VERSION = '23.0.3' TARGET_SDK_VERSION = 23 sdk = [:] if (Os.isFamily(Os.FAMILY_WINDOWS)) { println "sub-project-build******************************************************" + sdk.aapt + sdkDir + "------" sdk.aapt = "${rootDir}/aapt_win.exe" sdk.dex = "$sdkDir/build-tools/$BUILD_TOOLS_VERSION/dx.bat" sdk.aidl = "$sdkDir\\build-tools\\$BUILD_TOOLS_VERSION\\aidl.exe" } else if (Os.isFamily(Os.FAMILY_MAC)) { sdk.aapt = "${rootDir}/aapt_mac" sdk.dex = "$sdkDir/build-tools/$BUILD_TOOLS_VERSION/dx" } else if (Os.isFamily(Os.FAMILY_UNIX)) { sdk.aapt = "${rootDir}/aapt_linux" sdk.dex = "$sdkDir/build-tools/$BUILD_TOOLS_VERSION/dx" } sdk.androidJar = "$sdkDir/platforms/android-$TARGET_SDK_VERSION/android.jar" if (TARGET_SDK_VERSION >= 23) { sdk.apacheJar = "$sdkDir/platforms/android-23/optional/org.apache.http.legacy.jar"; } } //初始化,确保必要目录都存在 task init << { new File(rootDir, 'build-outputs').mkdirs() buildDir.mkdirs() new File(buildDir, 'gen/r').mkdirs() new File(buildDir, 'intermediates').mkdirs() new File(buildDir, 'intermediates/classes').mkdirs() new File(buildDir, 'intermediates/classes/release').mkdirs() new File(buildDir, 'intermediates/classes-obfuscated').mkdirs() new File(buildDir, 'intermediates/res').mkdirs() new File(buildDir, 'intermediates/dex').mkdirs() new File(buildDir, 'generated').mkdir() new File(buildDir, 'generated/source').mkdir() new File(buildDir, 'generated/source/aidl').mkdir() new File(buildDir, 'generated/source/aidl/release').mkdir() } //清除build产出物 task clean(type: Delete)<<{ delete buildDir delete "${rootDir}/build-outputs/${apkName}-mapping.txt" delete "${rootDir}/build-outputs/${apkName}.so" } task aaptReleasePlugin(type: Exec, dependsOn: 'init') { inputs.file "$sdk.androidJar" // inputs.file "${rootDir}/build-outputs/demo-base-release.apk" inputs.file "$projectDir/src/main/AndroidManifest.xml" inputs.dir "$projectDir/src/main/res" inputs.dir "$projectDir/src/main/assets" // inputs.file "${rootDir}/app/build/generated/source/r/release/com/gstd/app/R.java" outputs.dir "$buildDir/gen/r" outputs.file "$buildDir/intermediates/res/resources.zip" outputs.file "$buildDir/intermediates/res/aapt-rules.txt" workingDir buildDir executable sdk.aapt def resourceId = '' def parseApkXml = (new XmlParser()).parse(new File(rootDir, 'apk_module_config.xml')) parseApkXml.Module.each { module -> if (module.@packageName == "${packageName}") { resourceId = module.@resourceId println "find packageName: " + module.@packageName + " ,resourceId:" + resourceId } else { println "----------------not font---------------" } } def argv = [] argv << 'package' //打包 argv << "-v" argv << '-f' //强制覆盖已有文件 argv << "-I" argv << "$sdk.androidJar" //添加一个已有的固化jar包 //argv << '-I' //argv << "${rootDir}/build-outputs/demo-base-release.apk" argv << '-M' argv << "$projectDir/src/main/AndroidManifest.xml" //指定manifest文件 argv << '-S' argv << "$projectDir/src/main/res" //res目录 argv << '-A' argv << "$projectDir/src/main/assets" //assets目录 argv << '-m' //make package directories under location specified by -J argv << '-J' argv << "$buildDir/generated/source/r/release/" //哪里输出R.java定义 argv << '-F' argv << "$buildDir/intermediates/res/resources-release.ap_" //指定apk的输出位置 argv << '-G' //-G A file to output proguard options into. argv << "$buildDir/intermediates/res/aapt-rules.txt" // argv << '--debug-mode' //manifest的application元素添加android:debuggable="true" argv << '--custom-package' //指定R.java生成的package包名 argv << "${packageName}" argv << '-0' //指定哪些后缀名不会被压缩 argv << 'apk' //argv << '--public-R-path' //argv << "${rootDir}/app/build/generated/source/r/release/com/gstd/app/R.java" argv << '--apk-module' argv << "$resourceId" args = argv } task copyReleaseOutputsApk(type:Copy){ from ("$buildDir/outputs/apk/${project.name}-release-unsigned.apk") { rename "${project.name}-release-unsigned.apk", "${apkName}.so" } into new File(rootDir, 'build-outputs') } afterEvaluate { tasks.getByName('compileReleaseJavaWithJavac') { it.doFirst { println 'aaptReleasePlugin -------------------' aaptReleasePlugin.execute() } } tasks.getByName("assembleRelease") { it.doFirst { copyReleaseOutputsApk.execute(); } } }
四.使用方式:
1.设置local.properties文件下solidMode,当等于false时,可以在主工程添加依赖,运行常规开发模式; 当等于true时作为插件运行,许用命令行执行打包操作 2.设置apk_module_config.xml 修改要添加的插件包名和资源id(0x01系统资源id,0x7f默认资源id) 3.将插件build.gradle文件设置和CallmePlugin项目下build.gradle一样 4.打包命令:gradle assembleRelease repackAll 5.安装Release APK in /build-outputs/***-release-final.apk
相关文章推荐
- android插件化开发--修改携程插件开发支持provider
- Android插件化开发之OpenAtlas插件适配
- Android插件化开发 第三篇 [加载插件资源]
- Android插件化开发之OpenAtlas插件适配
- Android插件化开发之OpenAtlas插件的安装与卸载、更新与回滚
- Android插件化开发-hook 系统服务(通过binder修改粘贴板服务行为)
- Android应用插件化开发中自定义View基础插件遇到的问题
- android插件化开发,1个主app,N个插件app
- Android插件化开发 第四篇 [加载插件Activity]
- Android插件化学习之路(七)之DL插件开发该注意的坑
- Android插件化开发 第三篇 [加载插件资源]
- Android插件化开发 第四篇 [加载插件Activity]
- Android插件化开发之OpenAtlas插件启动方式与插件启动广播
- Android插件化开发之OpenAtlas生成插件信息列表
- android插件化-apkplug插件开发-07
- Android插件化开发之OpenAtlas生成插件信息列表
- Android SDK开发指南(翻译)系列三:Tools(一)--使用AIDL, 设计一个远程接口
- Android开发中实现跨进程通讯的AIDL接口
- Android浏览器插件开发(二)
- Ubuntu10.04 Android 开发环境配置 SDk下载配置 ADT插件下载