├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bintray.gradle ├── build.gradle ├── clean-git.sh ├── fastdex-build-lib ├── .gitignore ├── build.gradle └── src │ ├── main │ └── java │ │ └── fastdex │ │ └── build │ │ └── lib │ │ ├── aapt │ │ ├── AaptResourceCollector.java │ │ ├── AaptUtil.java │ │ ├── Constant.java │ │ ├── DefaultFileCopyProcessor.java │ │ ├── FakeRDotTxtEntry.java │ │ ├── FileCopyException.java │ │ ├── FileUtil.java │ │ ├── Generator.java │ │ ├── JavaXmlUtil.java │ │ ├── ObjectUtil.java │ │ ├── PatchUtil.java │ │ ├── RDotTxtEntry.java │ │ ├── ResourceDirectory.java │ │ ├── ResourceEntry.java │ │ └── StringUtil.java │ │ ├── fd │ │ ├── Communicator.java │ │ ├── ILogger.java │ │ ├── NullLogger.java │ │ └── ServiceCommunicator.java │ │ └── snapshoot │ │ ├── api │ │ ├── DiffInfo.java │ │ ├── DiffResultSet.java │ │ ├── Node.java │ │ ├── STSerializable.java │ │ ├── Snapshoot.java │ │ └── Status.java │ │ ├── file │ │ ├── BaseDirectorySnapshoot.java │ │ ├── DirectorySnapshoot.java │ │ ├── FileDiffInfo.java │ │ ├── FileNode.java │ │ ├── FileSuffixFilter.java │ │ ├── Options.java │ │ └── ScanFilter.java │ │ ├── res │ │ └── AndManifestDirectorySnapshoot.java │ │ ├── sourceset │ │ ├── JavaDirectoryDiffResultSet.java │ │ ├── JavaDirectorySnapshoot.java │ │ ├── JavaFileDiffInfo.java │ │ ├── PathInfo.java │ │ ├── SourceSetDiffResultSet.java │ │ └── SourceSetSnapshoot.java │ │ └── string │ │ ├── BaseStringSnapshoot.java │ │ ├── StringDiffInfo.java │ │ ├── StringNode.java │ │ └── StringSnapshoot.java │ └── test │ └── java │ └── snapshoot │ ├── AndManifestDirectorySnapshootTest.java │ ├── BuildConfigAndRDiffTest.java │ ├── ResultSetTest.java │ ├── SourceSetSnapshootTest.java │ └── StringSnapshootTest.java ├── fastdex-common ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── fastdex │ └── common │ ├── ShareConstants.java │ ├── fd │ └── ProtocolConstants.java │ └── utils │ ├── DigestUtils.java │ ├── FileUtils.java │ └── SerializeUtils.java ├── fastdex-gradle ├── .gitignore ├── build.gradle └── src │ └── main │ ├── groovy │ └── fastdex │ │ └── build │ │ ├── FastdexPlugin.groovy │ │ ├── extension │ │ └── FastdexExtension.groovy │ │ ├── task │ │ ├── FastdexCleanTask.groovy │ │ ├── FastdexCreateMaindexlistFileTask.groovy │ │ ├── FastdexCustomJavacTask.groovy │ │ ├── FastdexIgnoreTask.groovy │ │ ├── FastdexInstantRunMarkTask.groovy │ │ ├── FastdexInstantRunTask.groovy │ │ ├── FastdexManifestTask.groovy │ │ ├── FastdexPatchTask.groovy │ │ ├── FastdexPrepareTask.groovy │ │ ├── FastdexResourceIdTask.groovy │ │ └── FastdexScanAptOutputTask.groovy │ │ ├── transform │ │ ├── FastdexDexBuilderTransform.groovy │ │ ├── FastdexDexMergerTransform.groovy │ │ ├── FastdexDexTransform.groovy │ │ ├── FastdexJarMergingTransform.groovy │ │ ├── FastdexPreDexTransform.groovy │ │ └── TransformProxy.groovy │ │ ├── util │ │ ├── ClassInject.groovy │ │ ├── Constants.groovy │ │ ├── DexOperation.groovy │ │ ├── FastdexBuildListener.groovy │ │ ├── FastdexInstantRun.groovy │ │ ├── FastdexRuntimeException.groovy │ │ ├── FastdexUtils.groovy │ │ ├── FindDexFileVisitor.groovy │ │ ├── FixPublicSymbolLogger.groovy │ │ ├── FixPublicSymbolLoggerWrapper.java │ │ ├── GradleUtils.groovy │ │ ├── JarOperation.groovy │ │ ├── JumpException.groovy │ │ ├── LibDependency.groovy │ │ ├── MetaInfo.groovy │ │ ├── ProjectSnapshoot.groovy │ │ └── ReflectUtils.groovy │ │ └── variant │ │ ├── FastdexBuilder.groovy │ │ └── FastdexVariant.groovy │ └── resources │ ├── META-INF │ └── gradle-plugins │ │ ├── com.dx168.fastdex.properties │ │ ├── com.github.typ0520.fastdex.properties │ │ └── fastdex.app.properties │ ├── fastdex-dex-merge.jar │ ├── fastdex-dx │ ├── fastdex-dx.jar │ ├── fastdex-runtime.dex │ └── fastdex-studio-info-macos.sh ├── fastdex-idea-plugin ├── .gitignore ├── build.gradle ├── find-build-variants.sh └── src │ └── main │ ├── java │ └── fastdex │ │ └── idea │ │ ├── actions │ │ ├── BaseAction.java │ │ ├── FastdexRunAction.java │ │ └── UpdateAction.java │ │ ├── icons │ │ └── PluginIcons.java │ │ ├── models │ │ ├── ArtifactDependencyModelWrapper.java │ │ ├── Constant.java │ │ ├── FastdexConfiguration.java │ │ ├── FastdexStatus.java │ │ ├── GetServerCallback.java │ │ └── GradleDependencyEntity.java │ │ ├── utils │ │ ├── DialogUtil.java │ │ ├── DocumentUtil.java │ │ ├── FastdexUtil.java │ │ ├── GradleUtil.java │ │ ├── GroovyFileUil.java │ │ ├── LogUtil.java │ │ ├── NotificationUtils.java │ │ ├── StreamUtil.java │ │ └── Utils.java │ │ └── views │ │ ├── CheckUpdateDialog.form │ │ ├── CheckUpdateDialog.java │ │ ├── DeviceChooserDialog.java │ │ ├── FastdexTerminal.java │ │ ├── FastdexToolWindowFactory.java │ │ ├── ImageJPanel.java │ │ ├── JTitle.java │ │ └── MulLabel.java │ └── resources │ ├── META-INF │ └── plugin.xml │ └── icons │ ├── bg_update.png │ ├── debug.png │ ├── debug@2x.png │ ├── gradlesync.png │ ├── gradlesync@2x.png │ ├── icon.png │ └── icon@2x.png ├── fastdex-runtime ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── fastdex │ └── runtime │ ├── AntilazyLoad.java │ ├── Constants.java │ ├── Fastdex.java │ ├── FastdexApplication.java │ ├── FastdexReceiver.java │ ├── FastdexService.java │ ├── FastdexUncaughtExceptionHandler.java │ ├── MiddlewareActivity.java │ ├── RuntimeMetaInfo.java │ ├── fd │ ├── ApplicationPatch.java │ ├── Logging.java │ ├── Restarter.java │ └── Server.java │ ├── loader │ ├── MonkeyPatcher.java │ ├── MultiDex.java │ ├── MultiDexExtractor.java │ ├── ResourcePatcher.java │ └── SystemClassLoaderAdder.java │ └── utils │ ├── ShareFileLockHelper.java │ ├── SharePatchFileUtil.java │ ├── ShareReflectUtil.java │ ├── Utils.java │ └── ZipUtil.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── install.sh ├── sample ├── .gitignore ├── app │ ├── build.gradle │ ├── libs │ │ └── fm-sdk-2.1.2.jar │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── java │ │ │ └── fastdex │ │ │ └── sample │ │ │ └── Debug.java │ │ ├── flavor1 │ │ └── java │ │ │ └── fastdex │ │ │ └── sample │ │ │ └── Flavor1.java │ │ ├── flavor1debug │ │ └── java │ │ │ └── fastdex │ │ │ └── sample │ │ │ └── Flavor1Debug.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ ├── a.txt │ │ ├── font │ │ │ ├── cn │ │ │ │ └── haha.txt │ │ │ └── xx.txt │ │ └── html │ │ │ └── index.html │ │ ├── java │ │ └── fastdex │ │ │ └── sample │ │ │ ├── CustomView.java │ │ │ ├── ImplementTest.java │ │ │ ├── InterfaceTest.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ParentClass.java │ │ │ └── SampleApplication.java │ │ └── res │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── my_view.xml │ │ └── view_custom.xml │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── common-group │ └── common2 │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── fastdex │ │ │ └── sample │ │ │ └── common2 │ │ │ ├── Common2Utils.java │ │ │ └── Common2Utils2.java │ │ └── res │ │ └── values │ │ └── strings.xml ├── common │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── a.txt │ │ ├── java │ │ └── fastdex │ │ │ └── sample │ │ │ └── common │ │ │ ├── CommonUtils.java │ │ │ ├── CommonUtils2.java │ │ │ └── CommonView.java │ │ └── res │ │ ├── layout │ │ ├── common_view.xml │ │ └── common_view2.xml │ │ └── values │ │ └── strings.xml ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── javalib │ ├── .gitignore │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── fastdex │ │ └── sample │ │ └── javalib │ │ ├── JavaLib.java │ │ └── Main.java ├── keystore.jks ├── kotlinlib │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── fastdex │ │ │ └── sample │ │ │ └── kotlinlib │ │ │ └── KotlinHello.kt │ │ └── res │ │ └── values │ │ └── strings.xml └── settings.gradle └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build 3 | # Ignore Gradle GUI config 4 | gradle-app.setting 5 | 6 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 7 | !gradle-wrapper.jar 8 | 9 | # Cache of project 10 | .gradletasknamecache 11 | 12 | .DS_Store 13 | node_modules 14 | 15 | # Built application files 16 | *.apk 17 | *.ap_ 18 | 19 | # Java class files 20 | *.class 21 | 22 | # Generated files 23 | bin/ 24 | gen/ 25 | 26 | # Gradle files 27 | .gradle/ 28 | *.iml 29 | /*/*.iml 30 | .idea 31 | /*/.idea/ 32 | 33 | # Local configuration file (sdk path, etc) 34 | local.properties 35 | 36 | # Proguard folder generated by Eclipse 37 | proguard/ 38 | 39 | # Log Files 40 | *.log 41 | 42 | /*/.idea 43 | .idea 44 | .test 45 | sample/.idea/ 46 | runtime/build/ 47 | 48 | build-lib/build/ 49 | gradle-plugin/build/ 50 | sample/build/ 51 | 52 | sample/.gradle/ 53 | fastdex-build/ 54 | buildSrc/ 55 | 56 | fastdex-idea-plugin/build/ 57 | sample/common/build/ 58 | sample/common-group/common2/build/ 59 | sample/common-group/common3/build/ 60 | sample/javalib/build/ 61 | sample/javalib-group/javalib2/build/ 62 | sample/javalib-group/javalib3/build/ 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /bintray.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.jfrog.bintray' 2 | apply plugin: 'maven-publish' 3 | 4 | def projectName = project.hasProperty("MAVEN_ARTIFACT_ID") ? project.MAVEN_ARTIFACT_ID : project.name 5 | def mavenDesc = projectName 6 | def baseUrl = 'https://github.com/typ0520/fastdex' 7 | def siteUrl = baseUrl 8 | def gitUrl = "${baseUrl}.git" 9 | def issueUrl = "${baseUrl}/issues" 10 | 11 | def licenseIds = ['Apache-2.0'] 12 | def licenseNames = ['The Apache Software License, Version 2.0'] 13 | def licenseUrls = ['http://www.apache.org/licenses/LICENSE-2.0.txt'] 14 | def inception = '2017' 15 | 16 | Properties props = new Properties() 17 | try { 18 | props.load(project.rootProject.file('local.properties').newDataInputStream()) 19 | } catch (Throwable e) { 20 | e.printStackTrace() 21 | } 22 | 23 | def bintray_user = props.getProperty("BINTRAY_USER","") 24 | def bintray_key = props.getProperty("BINTRAY_KEY","") 25 | 26 | install { 27 | repositories { 28 | mavenInstaller { 29 | pom.project { 30 | // Description 31 | name projectName 32 | description mavenDesc 33 | url siteUrl 34 | 35 | // Archive 36 | groupId project.group 37 | artifactId archivesBaseName 38 | version project.version 39 | 40 | // License 41 | inceptionYear inception 42 | licenses { 43 | licenseNames.eachWithIndex { ln, li -> 44 | license { 45 | name ln 46 | url licenseUrls[li] 47 | } 48 | } 49 | } 50 | developers { 51 | developer { 52 | name bintray_user 53 | } 54 | } 55 | scm { 56 | connection gitUrl 57 | developerConnection gitUrl 58 | url siteUrl 59 | } 60 | } 61 | } 62 | } 63 | } 64 | 65 | task sourcesJar(type: Jar) { 66 | from sourceSets.main.allJava 67 | classifier = 'sources' 68 | } 69 | 70 | 71 | task javadocJar(type: Jar, dependsOn: javadoc) { 72 | from javadoc.destinationDir 73 | classifier = 'javadoc' 74 | } 75 | 76 | artifacts { 77 | archives javadocJar 78 | archives sourcesJar 79 | } 80 | 81 | tasks['javadoc'].enabled = false 82 | 83 | bintray { 84 | user = bintray_user 85 | key = bintray_key 86 | configurations = ['archives'] 87 | pkg { 88 | repo = 'maven' 89 | name = "${project.groupId}:${projectName}" 90 | desc = mavenDesc 91 | websiteUrl = siteUrl 92 | issueTrackerUrl = issueUrl 93 | vcsUrl = gitUrl 94 | labels = ['android', 'plugin', 'gradle', 'fastdex'] 95 | licenses = licenseIds 96 | publish = true 97 | publicDownloadNumbers = true 98 | } 99 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /// Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | maven { url 'https://maven.google.com' } 7 | maven { url 'http://dl.bintray.com/jetbrains/intellij-plugin-service' } 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:2.2.0' 12 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 13 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 14 | classpath 'com.github.dcendents:android-maven-plugin:1.2' 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | jcenter() 21 | maven { url 'https://maven.google.com' } 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } -------------------------------------------------------------------------------- /clean-git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -d '.git' ];then 4 | echo "$(pwd) not a git repository" 5 | exit -1 6 | fi 7 | git status | grep 'nothing to commit' > /dev/null 8 | if [ $? != 0 ];then 9 | echo "Cannot clean: You have unstaged changes." 10 | exit -1 11 | fi 12 | 13 | IFS=$'\n' 14 | 15 | #prepare delete directory 16 | PRE_DEL_DIR_ARR=('DevSample' 'Sample' 'fastdex-build' 'buildSrc' 'app' 'sample') 17 | 18 | #return: 1: yes 0: no 19 | is_mapping() { 20 | if [ "$1" == '' ];then 21 | return 0 22 | fi 23 | for pattern in ${PRE_DEL_DIR_ARR[@]} 24 | do 25 | echo $1 | grep "^${pattern}" > /dev/null 26 | if [ $? == 0 ];then 27 | return 1 28 | fi 29 | done 30 | return 0 31 | } 32 | 33 | processed_count=0 34 | 35 | for row in $(git rev-list --objects --all) 36 | do 37 | obj_hash=$(echo ${row} | awk '{print $1}') 38 | relative_path=$(echo ${row} | awk '{print $2}') 39 | is_mapping ${relative_path} 40 | if [ $? == 1 ];then 41 | echo "process: ${relative_path}" 42 | #remove object 43 | git filter-branch --force --index-filter "git rm --cached --ignore-unmatch ${relative_path}" --prune-empty --tag-name-filter cat -- --all 44 | 45 | if [ $? == 0 ];then 46 | let processed_count=processed_count+1 47 | fi 48 | else 49 | if [ "${relative_path}" == '' ];then 50 | echo "relative path is empty,just ignore. hash value: ${obj_hash}" 51 | else 52 | echo "skip ${relative_path}" 53 | fi 54 | 55 | if [ "${relative_path}" != '' ];then 56 | echo "skip ${relative_path}" 57 | fi 58 | fi 59 | done 60 | 61 | 62 | echo "processed count: ${processed_count}" 63 | 64 | if [ $processed_count > 0 ];then 65 | if [ ! -f '.gitignore' ];then 66 | touch .gitignore 67 | fi 68 | 69 | echo '' >> .gitignore 70 | for pattern in ${PRE_DEL_DIR_ARR[@]} 71 | do 72 | echo .gitignore | grep "^${pattern}$" > /dev/null 73 | if [ $? != 0 ];then 74 | echo $pattern >> .gitignore 75 | fi 76 | echo .gitignore | grep "^${pattern}/$" > /dev/null 77 | if [ $? != 0 ];then 78 | echo "${pattern}/" >> .gitignore 79 | fi 80 | done 81 | 82 | echo '' 83 | echo '=========Execute the following command to complete the cleanup=========' 84 | echo "git add ." 85 | echo "git commit -m 'clean git repository'" 86 | echo "git push origin --force --all" 87 | echo "git push origin --force --tags" 88 | echo '==================' 89 | fi -------------------------------------------------------------------------------- /fastdex-build-lib/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /fastdex-build-lib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'maven' 3 | 4 | sourceCompatibility = 1.7 5 | targetCompatibility = 1.7 6 | 7 | [compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8' 8 | 9 | dependencies { 10 | compile project(':fastdex-common') 11 | compile 'org.apache.ant:ant:1.8.2' 12 | compile 'com.google.guava:guava:18.0' 13 | compile 'com.android.tools.ddms:ddmlib:22.0' 14 | 15 | testCompile 'junit:junit:4.12' 16 | testCompile 'com.google.code.gson:gson:2.3.1' 17 | } 18 | 19 | sourceSets { 20 | main { 21 | java { 22 | srcDir 'src/main/java' 23 | } 24 | 25 | resources { 26 | srcDir 'src/main/resources' 27 | } 28 | } 29 | } 30 | 31 | apply from: rootProject.file('bintray.gradle') 32 | 33 | 34 | def generated = new File("${project.buildDir}/generated/java") 35 | sourceSets { 36 | main { 37 | java { 38 | srcDir generated 39 | } 40 | } 41 | } 42 | 43 | task generateVersionConstantsJava { 44 | inputs.property("version", version) 45 | ext.versionFile = new File(generated, "com/github/typ0520/fastdex/Version.java") 46 | outputs.file(versionFile) 47 | } 48 | 49 | generateVersionConstantsJava << { 50 | versionFile.parentFile.mkdirs() 51 | versionFile.text = """ 52 | package com.github.typ0520.fastdex; 53 | 54 | public final class Version { 55 | private Version() {} 56 | public static final String FASTDEX_BUILD_VERSION = "$version"; 57 | } 58 | """ 59 | } 60 | 61 | tasks.compileJava.dependsOn generateVersionConstantsJava -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/DefaultFileCopyProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.FileOutputStream; 22 | import java.io.InputStream; 23 | import java.io.OutputStream; 24 | 25 | public class DefaultFileCopyProcessor implements FileUtil.FileCopyProcessor { 26 | 27 | /** 28 | * copyFileToFileProcess 29 | * 30 | * @param from,maybe directory 31 | * @param to,maybe directory 32 | * @param isFile,maybe directory or file 33 | * @return boolean, if true keep going copy,only active in directory so far 34 | */ 35 | public boolean copyFileToFileProcess(final String from, final String to, final boolean isFile) { 36 | try { 37 | if (isFile) { 38 | String fromFile = new File(from).getAbsolutePath(); 39 | String toFile = new File(to).getAbsolutePath(); 40 | if (fromFile.equals(toFile)) { 41 | toFile = toFile + "_copy"; 42 | } 43 | FileUtil.createFile(toFile); 44 | InputStream inputStream = new FileInputStream(fromFile); 45 | OutputStream outputStream = new FileOutputStream(toFile); 46 | try { 47 | byte[] buffer = new byte[Constant.Capacity.BYTES_PER_KB]; 48 | int length = -1; 49 | while ((length = inputStream.read(buffer, 0, buffer.length)) != -1) { 50 | outputStream.write(buffer, 0, length); 51 | outputStream.flush(); 52 | } 53 | } finally { 54 | if (inputStream != null) { 55 | inputStream.close(); 56 | } 57 | if (outputStream != null) { 58 | outputStream.close(); 59 | } 60 | } 61 | } else { 62 | FileUtil.createDirectory(to); 63 | } 64 | } catch (Exception e) { 65 | throw new FileCopyException(e); 66 | } 67 | return true; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/FakeRDotTxtEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | /** 20 | * An {@link RDotTxtEntry} with fake {@link #idValue}, useful for comparing two resource entries for 21 | * equality, since {@link RDotTxtEntry#compareTo(RDotTxtEntry)} ignores the id value. 22 | */ 23 | public class FakeRDotTxtEntry extends RDotTxtEntry { 24 | 25 | private static final String FAKE_ID = "0x00000000"; 26 | 27 | public FakeRDotTxtEntry(IdType idType, RType type, String name) { 28 | super(idType, type, name, FAKE_ID); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/FileCopyException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | public class FileCopyException extends RuntimeException { 20 | 21 | /** 22 | * serialVersionUID 23 | */ 24 | private static final long serialVersionUID = -6670157031514003361L; 25 | 26 | /** 27 | * @param message 28 | */ 29 | public FileCopyException(String message) { 30 | super(message); 31 | } 32 | 33 | /** 34 | * @param cause 35 | */ 36 | public FileCopyException(Throwable cause) { 37 | super(cause); 38 | } 39 | 40 | /** 41 | * @param message 42 | * @param cause 43 | */ 44 | public FileCopyException(String message, Throwable cause) { 45 | super(message, cause); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/Generator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | import java.io.FileInputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.security.MessageDigest; 23 | 24 | public final class Generator { 25 | 26 | private static final char[] characters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; 27 | private static final String FONT_FAMILY_TIMES_NEW_ROMAN = "Times New Roman"; 28 | 29 | /** 30 | * md5 file 31 | * 32 | * @param fullFilename 33 | * @return String 34 | */ 35 | public static String md5File(String fullFilename) { 36 | String result = null; 37 | if (fullFilename != null) { 38 | try { 39 | result = md5File(new FileInputStream(fullFilename)); 40 | } catch (Exception e) { 41 | throw new RuntimeException(e); 42 | } 43 | } 44 | return result; 45 | } 46 | 47 | /** 48 | * md5 file 49 | * 50 | * @param inputStream 51 | * @return String 52 | */ 53 | public static String md5File(final InputStream inputStream) { 54 | String result = null; 55 | if (inputStream != null) { 56 | try { 57 | MessageDigest md = MessageDigest.getInstance("MD5"); 58 | byte[] buffer = new byte[Constant.Capacity.BYTES_PER_KB]; 59 | int readCount = 0; 60 | while ((readCount = inputStream.read(buffer, 0, buffer.length)) != -1) { 61 | md.update(buffer, 0, readCount); 62 | } 63 | result = StringUtil.byteToHexString(md.digest()); 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | } finally { 67 | try { 68 | inputStream.close(); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | } 74 | return result; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/ObjectUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | /** 20 | * reflect the object property and invoke the method 21 | * 22 | * @author Dandelion 23 | * @since 2008-04-?? 24 | */ 25 | public final class ObjectUtil { 26 | 27 | private ObjectUtil() { 28 | } 29 | 30 | /** 31 | * when object is null return blank,when the object is not null it return object; 32 | * 33 | * @param object 34 | * @return Object 35 | */ 36 | public static Object nullToBlank(Object object) { 37 | if (object == null) { 38 | return StringUtil.BLANK; 39 | } 40 | return object; 41 | } 42 | 43 | /** 44 | * equal 45 | * 46 | * @param a 47 | * @param b 48 | * @return boolean 49 | */ 50 | public static boolean equal(Object a, Object b) { 51 | return a == b || (a != null && a.equals(b)); 52 | } 53 | 54 | /** 55 | * field name to method name 56 | * 57 | * @param methodPrefix 58 | * @param fieldName 59 | * @return methodName 60 | */ 61 | public static String fieldNameToMethodName(String methodPrefix, String fieldName) { 62 | return fieldNameToMethodName(methodPrefix, fieldName, false); 63 | } 64 | 65 | /** 66 | * field name to method name 67 | * 68 | * @param methodPrefix 69 | * @param fieldName 70 | * @param ignoreFirstLetterCase 71 | * @return methodName 72 | */ 73 | public static String fieldNameToMethodName(String methodPrefix, String fieldName, boolean ignoreFirstLetterCase) { 74 | String methodName = null; 75 | if (fieldName != null && fieldName.length() > 0) { 76 | if (ignoreFirstLetterCase) { 77 | methodName = methodPrefix + fieldName; 78 | } else { 79 | methodName = methodPrefix + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1); 80 | } 81 | } else { 82 | methodName = methodPrefix; 83 | } 84 | return methodName; 85 | } 86 | 87 | /** 88 | * method name to field name 89 | * 90 | * @param methodPrefix 91 | * @param methodName 92 | * @return fieldName 93 | */ 94 | public static String methodNameToFieldName(String methodPrefix, String methodName) { 95 | return methodNameToFieldName(methodPrefix, methodName, false); 96 | } 97 | 98 | /** 99 | * method name to field name 100 | * 101 | * @param methodPrefix 102 | * @param methodName 103 | * @param ignoreFirstLetterCase 104 | * @return fieldName 105 | */ 106 | public static String methodNameToFieldName(String methodPrefix, String methodName, boolean ignoreFirstLetterCase) { 107 | String fieldName = null; 108 | if (methodName != null && methodName.length() > methodPrefix.length()) { 109 | int front = methodPrefix.length(); 110 | if (ignoreFirstLetterCase) { 111 | fieldName = methodName.substring(front, front + 1) + methodName.substring(front + 1); 112 | } else { 113 | fieldName = methodName.substring(front, front + 1).toLowerCase() + methodName.substring(front + 1); 114 | } 115 | } 116 | return fieldName; 117 | } 118 | } -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/ResourceDirectory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | import java.util.Arrays; 20 | import java.util.HashSet; 21 | import java.util.Set; 22 | 23 | public class ResourceDirectory { 24 | 25 | public String directoryName = null; 26 | public String resourceFullFilename = null; 27 | public Set resourceEntrySet = new HashSet(); 28 | 29 | public ResourceDirectory(String directoryName, String resourceFullFilename) { 30 | this.directoryName = directoryName; 31 | this.resourceFullFilename = resourceFullFilename; 32 | } 33 | 34 | public int hashCode() { 35 | return Arrays.hashCode(new Object[]{this.directoryName, this.resourceFullFilename}); 36 | } 37 | 38 | 39 | public boolean equals(Object object) { 40 | if (!(object instanceof ResourceDirectory)) { 41 | return false; 42 | } 43 | ResourceDirectory that = (ResourceDirectory) object; 44 | return ObjectUtil.equal(this.directoryName, that.directoryName) && ObjectUtil.equal(this.resourceFullFilename, that.resourceFullFilename); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/aapt/ResourceEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014-present Facebook, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | package fastdex.build.lib.aapt; 18 | 19 | import java.util.Arrays; 20 | 21 | public class ResourceEntry { 22 | 23 | public String name = null; 24 | public String value = null; 25 | 26 | public ResourceEntry(String name, String value) { 27 | this.name = name; 28 | this.value = value; 29 | } 30 | 31 | public int hashCode() { 32 | return Arrays.hashCode(new Object[]{this.name}); 33 | } 34 | 35 | 36 | public boolean equals(Object object) { 37 | if (!(object instanceof ResourceEntry)) { 38 | return false; 39 | } 40 | ResourceEntry that = (ResourceEntry) object; 41 | return ObjectUtil.equal(this.name, that.name); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/fd/Communicator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.build.lib.fd; 18 | 19 | 20 | import java.io.DataInputStream; 21 | import java.io.DataOutputStream; 22 | import java.io.IOException; 23 | 24 | public abstract class Communicator { 25 | 26 | public abstract T communicate(DataInputStream input, DataOutputStream output) throws IOException; 27 | 28 | int getTimeout() { 29 | return 2000; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/fd/ILogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.build.lib.fd; 18 | 19 | import java.util.Formatter; 20 | 21 | public interface ILogger { 22 | 23 | /** 24 | * Prints an error message. 25 | * 26 | * @param t is an optional {@link Throwable} or {@link Exception}. If non-null, its 27 | * message will be printed out. 28 | * @param msgFormat is an optional error format. If non-null, it will be printed 29 | * using a {@link Formatter} with the provided arguments. 30 | * @param args provides the arguments for errorFormat. 31 | */ 32 | void error( Throwable t, String msgFormat, Object... args); 33 | 34 | /** 35 | * Prints a warning message. 36 | * 37 | * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. 38 | * @param args provides the arguments for warningFormat. 39 | */ 40 | void warning( String msgFormat, Object... args); 41 | 42 | /** 43 | * Prints an information message. 44 | * 45 | * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. 46 | * @param args provides the arguments for msgFormat. 47 | */ 48 | void info( String msgFormat, Object... args); 49 | 50 | /** 51 | * Prints a verbose message. 52 | * 53 | * @param msgFormat is a string format to be used with a {@link Formatter}. Cannot be null. 54 | * @param args provides the arguments for msgFormat. 55 | */ 56 | void verbose( String msgFormat, Object... args); 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/fd/NullLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.build.lib.fd; 18 | 19 | /** 20 | *

21 | * Dummy implementation of an {@link ILogger}. 22 | *

23 | * Use {@link #getLogger()} to get a default instance of this {@link NullLogger}. 24 | */ 25 | public class NullLogger implements ILogger { 26 | 27 | private static final ILogger sThis = new NullLogger(); 28 | 29 | public static ILogger getLogger() { 30 | return sThis; 31 | } 32 | 33 | @Override 34 | public void error( Throwable t, String errorFormat, Object... args) { 35 | // ignore 36 | } 37 | 38 | @Override 39 | public void warning(String warningFormat, Object... args) { 40 | // ignore 41 | } 42 | 43 | @Override 44 | public void info(String msgFormat, Object... args) { 45 | // ignore 46 | } 47 | 48 | @Override 49 | public void verbose(String msgFormat, Object... args) { 50 | // ignore 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/api/DiffInfo.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.api; 2 | 3 | /** 4 | * 目录对比,file.length或者file.lastModified不一样时判定文件发生变化 5 | * Created by tong on 17/3/29. 6 | */ 7 | public class DiffInfo { 8 | public Status status; 9 | public String uniqueKey; 10 | public T now;//如果是删除此值为null 11 | public T old; 12 | 13 | public DiffInfo() { 14 | } 15 | 16 | public DiffInfo(Status status, String uniqueKey, T now, T old) { 17 | this.status = status; 18 | this.uniqueKey = uniqueKey; 19 | 20 | this.now = now; 21 | this.old = old; 22 | 23 | if (this.uniqueKey == null || this.uniqueKey.length() == 0) { 24 | throw new IllegalStateException("UniqueKey can not be null or epmty!!"); 25 | } 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "DiffInfo{" + 31 | "status=" + status + 32 | ", uniqueKey='" + uniqueKey + '\'' + 33 | ", now=" + now + 34 | ", old=" + old + 35 | '}'; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object o) { 40 | if (this == o) return true; 41 | if (o == null || getClass() != o.getClass()) return false; 42 | 43 | DiffInfo that = (DiffInfo) o; 44 | 45 | if (status != that.status) return false; 46 | return uniqueKey != null ? uniqueKey.equals(that.uniqueKey) : that.uniqueKey == null; 47 | 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | int result = status != null ? status.hashCode() : 0; 53 | result = 31 * result + (uniqueKey != null ? uniqueKey.hashCode() : 0); 54 | return result; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/api/Node.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.api; 2 | 3 | /** 4 | * Created by tong on 17/3/29. 5 | */ 6 | public abstract class Node { 7 | /** 8 | * 获取索引值 9 | * @return 10 | */ 11 | public abstract String getUniqueKey(); 12 | /** 13 | * 如果没有发生变化返回true,反之false 14 | * @param anNode 15 | * @return 16 | */ 17 | public boolean diffEquals(Node anNode) { 18 | return equals(anNode); 19 | } 20 | 21 | @Override 22 | public final boolean equals(Object o) { 23 | if (super.equals(o)) return true; 24 | if (o == null || getClass() != o.getClass()) return false; 25 | 26 | Node node = (Node) o; 27 | 28 | String uniqueKey = getUniqueKey(); 29 | String anUniqueKey = node.getUniqueKey(); 30 | return uniqueKey != null ? uniqueKey.equals(anUniqueKey) : anUniqueKey == null; 31 | } 32 | 33 | @Override 34 | public final int hashCode() { 35 | String uniqueKey = getUniqueKey(); 36 | return uniqueKey != null ? uniqueKey.hashCode() : 0; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/api/STSerializable.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.api; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | /** 7 | * Created by tong on 17/3/30. 8 | */ 9 | public interface STSerializable { 10 | void serializeTo(OutputStream outputStream) throws IOException; 11 | } 12 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/api/Status.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.api; 2 | 3 | /** 4 | * Created by tong on 17/3/29. 5 | */ 6 | public enum Status { 7 | NOCHANGED, ADDED, DELETEED, MODIFIED 8 | } 9 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/file/DirectorySnapshoot.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.file; 2 | 3 | /** 4 | 5 | 当前 6 | com/dx168/fastdex/sample/MainActivity.java 7 | 老的 8 | com/dx168/fastdex/sample/MainActivity.java 9 | com/dx168/fastdex/sample/MainActivity2.java 10 | 删除的是 11 | com/dx168/fastdex/sample/MainActivity2.java 12 | 13 | 假如 14 | com/dx168/fastdex/sample/MainActivity.java 15 | com/dx168/fastdex/sample/MainActivity2.java 16 | 老的 17 | com/dx168/fastdex/sample/MainActivity.java 18 | 新增的是 19 | com/dx168/fastdex/sample/MainActivity2.java 20 | 21 | 当前的 22 | com/dx168/fastdex/sample/MainActivity.java 23 | com/dx168/fastdex/sample/MainActivity2.java 24 | com/dx168/fastdex/sample/MainActivity3.java 25 | 老的 26 | com/dx168/fastdex/sample/MainActivity.java 27 | com/dx168/fastdex/sample/MainActivity2.java 28 | com/dx168/fastdex/sample/MainActivity4.java 29 | 新增的是 30 | com/dx168/fastdex/sample/MainActivity3.java 31 | 删除的是 32 | com/dx168/fastdex/sample/MainActivity4.java 33 | 34 | 除了删除的和新增的就是所有需要进行扫描的SourceSetInfo 35 | com/dx168/fastdex/sample/MainActivity.java 36 | com/dx168/fastdex/sample/MainActivity2.java 37 | */ 38 | 39 | import java.io.File; 40 | import java.io.IOException; 41 | 42 | /** 43 | * Created by tong on 17/3/29. 44 | */ 45 | public final class DirectorySnapshoot extends BaseDirectorySnapshoot { 46 | public DirectorySnapshoot() { 47 | } 48 | 49 | public DirectorySnapshoot(BaseDirectorySnapshoot snapshoot) { 50 | super(snapshoot); 51 | } 52 | 53 | public DirectorySnapshoot(File directory) throws IOException { 54 | super(directory); 55 | } 56 | 57 | public DirectorySnapshoot(File directory, ScanFilter scanFilter) throws IOException { 58 | super(directory, scanFilter); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/file/FileDiffInfo.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.file; 2 | 3 | import fastdex.build.lib.snapshoot.api.DiffInfo; 4 | import fastdex.build.lib.snapshoot.api.Status; 5 | 6 | /** 7 | * 目录对比,file.length或者file.lastModified不一样时判定文件发生变化 8 | * Created by tong on 17/3/29. 9 | */ 10 | public class FileDiffInfo extends DiffInfo { 11 | public FileDiffInfo() { 12 | } 13 | 14 | public FileDiffInfo(Status status, FileNode now, FileNode old) { 15 | super(status, (now != null ? now.getUniqueKey() : old.getUniqueKey()), now, old); 16 | } 17 | 18 | @Override 19 | public boolean equals(Object o) { 20 | if (!super.equals(o)) return false; 21 | if (o == null || getClass() != o.getClass()) return false; 22 | 23 | FileDiffInfo that = (FileDiffInfo) o; 24 | 25 | if (status != that.status) return false; 26 | if (uniqueKey != null ? !uniqueKey.equals(that.uniqueKey) : that.uniqueKey != null) return false; 27 | 28 | if (now != null && !now.diffEquals(that.now)) { 29 | return false; 30 | } 31 | 32 | if (old != null && !old.diffEquals(that.old)) { 33 | return false; 34 | } 35 | return true; 36 | 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | int result = super.hashCode(); 42 | int nowLastModified = 0; 43 | 44 | if (now != null && now.md5 == null) { 45 | nowLastModified = (int) (now.lastModified ^ (now.lastModified >>> 32)); 46 | } 47 | 48 | int oldLastModified = 0; 49 | if (old != null && old.md5 == null) { 50 | oldLastModified = (int) (old.lastModified ^ (old.lastModified >>> 32)); 51 | } 52 | 53 | int nowFileLength = 0; 54 | int oldFileLength = 0; 55 | if (now != null) { 56 | nowFileLength = (int) (now.fileLength ^ (now.fileLength >>> 32)); 57 | } 58 | if (old != null) { 59 | oldFileLength = (int) (old.fileLength ^ (old.fileLength >>> 32)); 60 | } 61 | result = 31 * result + (now != null && now.md5 != null ? (now.md5.hashCode() + nowLastModified + nowFileLength) : 0); 62 | result = 31 * result + (old != null && old.md5 != null ? (old.md5.hashCode() + oldLastModified + oldFileLength) : 0); 63 | return result; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/file/FileNode.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.file; 2 | 3 | import fastdex.build.lib.snapshoot.api.Node; 4 | import fastdex.common.utils.DigestUtils; 5 | import fastdex.common.utils.FileUtils; 6 | import java.io.File; 7 | 8 | /** 9 | * Created by tong on 17/3/29. 10 | */ 11 | public class FileNode extends Node { 12 | public String nodePath; 13 | public String md5; 14 | public long lastModified; 15 | public long fileLength; 16 | 17 | @Override 18 | public String getUniqueKey() { 19 | return nodePath; 20 | } 21 | 22 | @Override 23 | public boolean diffEquals(Node anNode) { 24 | if (!super.diffEquals(anNode)) 25 | return false; 26 | 27 | FileNode fileNode = (FileNode) anNode; 28 | if (fileLength != fileNode.fileLength) 29 | return false; 30 | 31 | if (md5 != null) { 32 | return md5.equals(((FileNode) anNode).md5); 33 | } 34 | else { 35 | return lastModified == fileNode.lastModified; 36 | } 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "FileNode{" + 42 | "nodePath='" + nodePath + '\'' + 43 | ", lastModified=" + lastModified + 44 | ", fileLength=" + fileLength + 45 | ", md5=" + md5 + 46 | '}'; 47 | } 48 | 49 | public static FileNode create(File rootDir, File file, boolean useMd5,boolean useRelativePath) { 50 | //相对路径作为key 51 | FileNode fileInfo = new FileNode(); 52 | //fileInfo.absolutePath = file.getAbsolutePath(); 53 | if (useRelativePath) { 54 | if (rootDir != null) { 55 | fileInfo.nodePath = rootDir.toPath().relativize(file.toPath()).toString(); 56 | } 57 | } 58 | else { 59 | fileInfo.nodePath = file.getAbsolutePath(); 60 | } 61 | 62 | fileInfo.lastModified = file.lastModified(); 63 | fileInfo.fileLength = file.length(); 64 | 65 | if (useMd5) { 66 | try { 67 | fileInfo.md5 = DigestUtils.md5DigestAsHex(FileUtils.readContents(file)); 68 | } catch (Throwable e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | return fileInfo; 73 | } 74 | 75 | public static FileNode create(File rootDir, File file, boolean useMd5) { 76 | return create(rootDir,file,useMd5,true); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/file/FileSuffixFilter.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.file; 2 | 3 | import java.io.File; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | 7 | /** 8 | * Created by tong on 17/3/29. 9 | */ 10 | public class FileSuffixFilter implements ScanFilter { 11 | private final Set suffixList = new HashSet<>(); 12 | 13 | public FileSuffixFilter() { 14 | } 15 | 16 | public FileSuffixFilter(String ...suffixs) { 17 | for (String suffix : suffixs) { 18 | addSuffix(suffix); 19 | } 20 | } 21 | 22 | public FileSuffixFilter(Set suffixList) { 23 | for (String suffix : suffixList) { 24 | addSuffix(suffix); 25 | } 26 | } 27 | 28 | public void addSuffix(String suffix) { 29 | if (suffix == null || suffix.length() == 0) { 30 | throw new IllegalArgumentException("suffix can not be epmty!!"); 31 | } 32 | this.suffixList.add(suffix); 33 | 34 | if (this.suffixList.isEmpty()) { 35 | throw new IllegalArgumentException("suffix list can not be epmty!!"); 36 | } 37 | } 38 | 39 | public Set getSuffixList() { 40 | return suffixList; 41 | } 42 | 43 | @Override 44 | public boolean preVisitFile(File file) { 45 | for (String suffix : suffixList) { 46 | if (file.getName().endsWith(suffix)) { 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/file/Options.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.file; 2 | 3 | import fastdex.build.lib.snapshoot.api.Status; 4 | 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * Created by tong on 17/3/30. 10 | */ 11 | public class Options { 12 | private final Set suffixList = new HashSet<>(); 13 | private Status[] focusStatus = null; 14 | 15 | public static class Builder { 16 | private final Options options = new Options(); 17 | 18 | public Builder addSuffix(String suffix) { 19 | options.suffixList.add(suffix); 20 | return this; 21 | } 22 | 23 | public Builder focusStatus(Status ...focusStatus) { 24 | if (focusStatus != null) { 25 | Set set = new HashSet(); 26 | for (Status status : focusStatus) { 27 | set.add(status); 28 | } 29 | if (set.size() < focusStatus.length) { 30 | throw new IllegalStateException("Content can not be repeated !"); 31 | } 32 | } 33 | 34 | if (focusStatus.length == 0) { 35 | options.focusStatus = null; 36 | } 37 | else { 38 | options.focusStatus = focusStatus; 39 | } 40 | return this; 41 | } 42 | 43 | public Options build() { 44 | return options; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/file/ScanFilter.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.file; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Created by tong on 17/3/29. 7 | */ 8 | public interface ScanFilter { 9 | /** 10 | * 如果返回true处理这个文件,反之忽略 11 | * @param file 12 | * @return 13 | */ 14 | boolean preVisitFile(File file); 15 | } 16 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/res/AndManifestDirectorySnapshoot.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.res; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Path; 6 | import fastdex.build.lib.snapshoot.file.BaseDirectorySnapshoot; 7 | import fastdex.build.lib.snapshoot.file.FileDiffInfo; 8 | import fastdex.build.lib.snapshoot.file.FileNode; 9 | import fastdex.build.lib.snapshoot.file.FileSuffixFilter; 10 | 11 | /** 12 | * Created by tong on 17/8/22. 13 | */ 14 | public class AndManifestDirectorySnapshoot extends BaseDirectorySnapshoot { 15 | private static final FileSuffixFilter SUFFIX_FILTER = new FileSuffixFilter(".xml"); 16 | 17 | public AndManifestDirectorySnapshoot() throws IOException { 18 | } 19 | 20 | public AndManifestDirectorySnapshoot(AndManifestDirectorySnapshoot snapshoot) { 21 | super(snapshoot); 22 | } 23 | 24 | @Override 25 | public void addFile(File file) { 26 | addFile(file,SUFFIX_FILTER); 27 | } 28 | 29 | @Override 30 | protected FileNode createNode(String path, Path filePath) { 31 | File rootDir = null; 32 | if (path != null) { 33 | rootDir = new File(path); 34 | } 35 | return FileNode.create(rootDir,filePath.toFile(), useMd5,false); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/sourceset/JavaDirectoryDiffResultSet.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.sourceset; 2 | 3 | import fastdex.build.lib.snapshoot.api.DiffResultSet; 4 | 5 | /** 6 | * Created by tong on 17/3/29. 7 | */ 8 | public class JavaDirectoryDiffResultSet extends DiffResultSet { 9 | public String projectPath; 10 | 11 | public JavaDirectoryDiffResultSet() { 12 | } 13 | 14 | public JavaDirectoryDiffResultSet(JavaDirectoryDiffResultSet resultSet) { 15 | super(resultSet); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/sourceset/JavaDirectorySnapshoot.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.sourceset; 2 | 3 | import fastdex.build.lib.snapshoot.file.BaseDirectorySnapshoot; 4 | import fastdex.build.lib.snapshoot.file.FileNode; 5 | import fastdex.build.lib.snapshoot.api.DiffInfo; 6 | import fastdex.build.lib.snapshoot.file.FileSuffixFilter; 7 | import com.google.gson.annotations.Expose; 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | /** 12 | * Created by tong on 17/3/30. 13 | */ 14 | public class JavaDirectorySnapshoot extends BaseDirectorySnapshoot { 15 | private static final FileSuffixFilter JAVA_SUFFIX_FILTER = new FileSuffixFilter(".java",".kt"); 16 | @Expose 17 | public String projectPath; 18 | 19 | public JavaDirectorySnapshoot() { 20 | } 21 | 22 | public JavaDirectorySnapshoot(JavaDirectorySnapshoot snapshoot) { 23 | super(snapshoot); 24 | } 25 | 26 | public JavaDirectorySnapshoot(File directory) throws IOException { 27 | super(directory, JAVA_SUFFIX_FILTER); 28 | } 29 | 30 | public JavaDirectorySnapshoot(File directory, boolean useMd5) throws IOException { 31 | super(directory,JAVA_SUFFIX_FILTER, useMd5); 32 | } 33 | 34 | public JavaDirectorySnapshoot(File directory,FileSuffixFilter filter, boolean useMd5) throws IOException { 35 | super(directory,filter, useMd5); 36 | } 37 | 38 | public JavaDirectorySnapshoot(File directory,boolean useMd5, String ...childPath) throws IOException { 39 | super(directory, toFileList(childPath),JAVA_SUFFIX_FILTER,useMd5); 40 | } 41 | 42 | @Override 43 | protected JavaDirectoryDiffResultSet createEmptyResultSet() { 44 | JavaDirectoryDiffResultSet javaDirectoryDiffResultSet = new JavaDirectoryDiffResultSet(); 45 | javaDirectoryDiffResultSet.projectPath = projectPath; 46 | return javaDirectoryDiffResultSet; 47 | } 48 | 49 | @Override 50 | protected DiffInfo createEmptyDiffInfo() { 51 | return new JavaFileDiffInfo(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/sourceset/JavaFileDiffInfo.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.sourceset; 2 | 3 | import fastdex.build.lib.snapshoot.api.Status; 4 | import fastdex.build.lib.snapshoot.file.FileDiffInfo; 5 | import fastdex.build.lib.snapshoot.file.FileNode; 6 | import fastdex.common.ShareConstants; 7 | 8 | /** 9 | * 目录对比,file.length或者file.lastModified不一样时判定文件发生变化 10 | * Created by tong on 17/3/29. 11 | */ 12 | public class JavaFileDiffInfo extends FileDiffInfo { 13 | public String path; 14 | 15 | public JavaFileDiffInfo() { 16 | } 17 | 18 | public JavaFileDiffInfo(Status status, FileNode now, FileNode old) { 19 | super(status, now, old); 20 | } 21 | 22 | public String getClassRelativePath() { 23 | String classRelativePath = null; 24 | if (uniqueKey.endsWith(ShareConstants.JAVA_SUFFIX)) { 25 | classRelativePath = uniqueKey.substring(0, uniqueKey.length() - ShareConstants.JAVA_SUFFIX.length()); 26 | } 27 | else if (uniqueKey.endsWith(ShareConstants.KT_SUFFIX)) { 28 | classRelativePath = uniqueKey.substring(0, uniqueKey.length() - ShareConstants.KT_SUFFIX.length()); 29 | } 30 | 31 | return classRelativePath; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/sourceset/PathInfo.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.sourceset; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * Created by tong on 17/4/6. 7 | */ 8 | public class PathInfo { 9 | public String path; 10 | public File absoluteFile; 11 | public String relativePath; 12 | 13 | public PathInfo(File absoluteFile, String relativePath) { 14 | this.absoluteFile = absoluteFile; 15 | this.relativePath = relativePath; 16 | } 17 | 18 | public PathInfo(String path, File absoluteFile, String relativePath) { 19 | this.path = path; 20 | this.absoluteFile = absoluteFile; 21 | this.relativePath = relativePath; 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | 29 | PathInfo pathInfo = (PathInfo) o; 30 | 31 | if (absoluteFile != null ? !absoluteFile.equals(pathInfo.absoluteFile) : pathInfo.absoluteFile != null) 32 | return false; 33 | return relativePath != null ? relativePath.equals(pathInfo.relativePath) : pathInfo.relativePath == null; 34 | 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | int result = absoluteFile != null ? absoluteFile.hashCode() : 0; 40 | result = 31 * result + (relativePath != null ? relativePath.hashCode() : 0); 41 | return result; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "PathInfo{" + 47 | "absoluteFile=" + absoluteFile + 48 | ", nodePath='" + relativePath + '\'' + 49 | '}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/string/BaseStringSnapshoot.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.string; 2 | 3 | import fastdex.build.lib.snapshoot.api.DiffInfo; 4 | import fastdex.build.lib.snapshoot.api.DiffResultSet; 5 | import fastdex.build.lib.snapshoot.api.Snapshoot; 6 | import fastdex.build.lib.snapshoot.api.Status; 7 | 8 | import java.io.IOException; 9 | import java.util.Set; 10 | 11 | /** 12 | * Created by tong on 17/3/31. 13 | */ 14 | public class BaseStringSnapshoot extends Snapshoot { 15 | 16 | public BaseStringSnapshoot() { 17 | } 18 | 19 | public BaseStringSnapshoot(BaseStringSnapshoot snapshoot) { 20 | super(snapshoot); 21 | } 22 | 23 | public BaseStringSnapshoot(Set strings) throws IOException { 24 | for (String str : strings) { 25 | addNode((NODE) StringNode.create(str)); 26 | } 27 | } 28 | 29 | public BaseStringSnapshoot(String ...strings) throws IOException { 30 | for (String str : strings) { 31 | addNode((NODE) StringNode.create(str)); 32 | } 33 | } 34 | 35 | @Override 36 | protected DiffInfo createEmptyDiffInfo() { 37 | return new StringDiffInfo(); 38 | } 39 | 40 | @Override 41 | protected void diffNode(DiffResultSet diffInfos, Snapshoot otherSnapshoot, NODE now, NODE old) { 42 | //不需要对比变化 43 | addDiffInfo(diffInfos,createDiffInfo(Status.NOCHANGED,now,old)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/string/StringDiffInfo.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.string; 2 | 3 | import fastdex.build.lib.snapshoot.api.DiffInfo; 4 | import fastdex.build.lib.snapshoot.api.Status; 5 | 6 | /** 7 | * Created by tong on 17/3/31. 8 | */ 9 | public class StringDiffInfo extends DiffInfo { 10 | public StringDiffInfo() { 11 | } 12 | 13 | public StringDiffInfo(Status status, StringNode now, StringNode old) { 14 | super(status, (now != null ? now.getUniqueKey() : old.getUniqueKey()), now, old); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/string/StringNode.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.string; 2 | 3 | import fastdex.build.lib.snapshoot.api.Node; 4 | 5 | /** 6 | * Created by tong on 17/3/31. 7 | */ 8 | public class StringNode extends Node { 9 | private String string; 10 | 11 | public StringNode() { 12 | } 13 | 14 | public StringNode(String string) { 15 | this.string = string; 16 | } 17 | 18 | public void setString(String string) { 19 | this.string = string; 20 | } 21 | 22 | public String getString() { 23 | return string; 24 | } 25 | 26 | @Override 27 | public String getUniqueKey() { 28 | return string; 29 | } 30 | 31 | public static StringNode create(String string) { 32 | return new StringNode(string); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "StringNode{" + 38 | "string='" + string + '\'' + 39 | '}'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/main/java/fastdex/build/lib/snapshoot/string/StringSnapshoot.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.lib.snapshoot.string; 2 | 3 | import java.io.IOException; 4 | import java.util.Set; 5 | 6 | /** 7 | * Created by tong on 17/3/31. 8 | */ 9 | public final class StringSnapshoot extends BaseStringSnapshoot { 10 | public StringSnapshoot() { 11 | } 12 | 13 | public StringSnapshoot(StringSnapshoot snapshoot) { 14 | super(snapshoot); 15 | } 16 | 17 | public StringSnapshoot(Set strings) throws IOException { 18 | super(strings); 19 | } 20 | 21 | public StringSnapshoot(String... strings) throws IOException { 22 | super(strings); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/test/java/snapshoot/BuildConfigAndRDiffTest.java: -------------------------------------------------------------------------------- 1 | package snapshoot; 2 | 3 | import fastdex.build.lib.snapshoot.api.DiffResultSet; 4 | import fastdex.build.lib.snapshoot.file.FileSuffixFilter; 5 | import fastdex.build.lib.snapshoot.file.ScanFilter; 6 | import fastdex.build.lib.snapshoot.sourceset.JavaDirectorySnapshoot; 7 | import fastdex.build.lib.snapshoot.sourceset.SourceSetSnapshoot; 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | 11 | import junit.framework.TestCase; 12 | import org.junit.Test; 13 | 14 | /** 15 | * Created by tong on 17/3/31. 16 | */ 17 | public class BuildConfigAndRDiffTest extends TestCase { 18 | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); 19 | 20 | @Test 21 | public void test() throws Throwable { 22 | // SourceSetSnapshoot sourceSetSnapshoot = new SourceSetSnapshoot(new File("/Users/tong/Projects/fastdex/sample/app"),"/Users/tong/Projects/fastdex/sample/app/src/main/java"); 23 | // 24 | // final File rDir = new File("/Users/tong/Projects/fastdex/sample/app/build/generated/source/r/debug"); 25 | // JavaDirectorySnapshoot rSnapshoot = new JavaDirectorySnapshoot(rDir){ 26 | // @Override 27 | // protected void walkFileTree(File directory, ScanFilter scanFilter) throws IOException { 28 | // visitFile(new File("/Users/tong/Projects/fastdex/sample/app/build/generated/source/r/debug/com/dx168/fastdex/sample/R.java").toPath(),null,scanFilter); 29 | // } 30 | // }; 31 | // 32 | // File buildConfigDir = new File("/Users/tong/Projects/fastdex/sample/app/build/generated/source/buildConfig/debug"); 33 | // JavaDirectorySnapshoot buildConfigSnapshoot = new JavaDirectorySnapshoot(buildConfigDir){ 34 | // @Override 35 | // protected void walkFileTree(File directory, ScanFilter scanFilter) throws IOException { 36 | // visitFile(new File("/Users/tong/Projects/fastdex/sample/app/build/generated/source/buildConfig/debug/com/dx168/fastdex/sample/BuildConfig.java").toPath(),null,scanFilter); 37 | // } 38 | // }; 39 | // sourceSetSnapshoot.addJavaDirectorySnapshoot(rSnapshoot); 40 | // sourceSetSnapshoot.addJavaDirectorySnapshoot(buildConfigSnapshoot); 41 | // 42 | // SourceSetSnapshoot oldSourceSetSnapshoot = (SourceSetSnapshoot) SourceSetSnapshoot.load(new File("/Users/tong/Projects/fastdex/sample/app/build/fastdex/Debug/sourceSets.json"),SourceSetSnapshoot.class); 43 | // DiffResultSet diffResultSet = sourceSetSnapshoot.diff(oldSourceSetSnapshoot); 44 | // 45 | // System.out.println(diffResultSet); 46 | // DiffResultSet diffResultSet2 = sourceSetSnapshoot.diff(oldSourceSetSnapshoot); 47 | } 48 | 49 | 50 | @Test 51 | public void test2() throws Throwable { 52 | // JavaDirectorySnapshoot snapshoot = new JavaDirectorySnapshoot(new File("/Users/tong/Projects/fastdex/sample/app/build/intermediates/classes/debug"),new FileSuffixFilter(".class")); 53 | // JavaDirectorySnapshoot oldSnapshoot = (JavaDirectorySnapshoot) JavaDirectorySnapshoot.load(new File("/Users/tong/Desktop/snapshoot.json"),JavaDirectorySnapshoot.class); 54 | // 55 | // DiffResultSet diffResultSet = snapshoot.diff(oldSnapshoot); 56 | // System.out.println(GSON.toJson(diffResultSet.changedDiffInfos)); 57 | // 58 | // snapshoot.serializeTo(new FileOutputStream("/Users/tong/Desktop/snapshoot.json")); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /fastdex-build-lib/src/test/java/snapshoot/ResultSetTest.java: -------------------------------------------------------------------------------- 1 | package snapshoot; 2 | 3 | /** 4 | * Created by tong on 17/3/31. 5 | */ 6 | public class ResultSetTest { 7 | } 8 | -------------------------------------------------------------------------------- /fastdex-common/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /fastdex-common/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'maven' 3 | 4 | sourceCompatibility = 1.7 5 | targetCompatibility = 1.7 6 | 7 | [compileJava, compileTestJava, javadoc]*.options*.encoding = 'UTF-8' 8 | 9 | dependencies { 10 | compile fileTree(dir: 'libs', include: ['*.jar']) 11 | compile 'com.google.code.gson:gson:2.3.1' 12 | 13 | testCompile 'junit:junit:4.12' 14 | testCompile 'com.google.code.gson:gson:2.3.1' 15 | } 16 | 17 | apply from: rootProject.file('bintray.gradle') 18 | -------------------------------------------------------------------------------- /fastdex-common/src/main/java/fastdex/common/ShareConstants.java: -------------------------------------------------------------------------------- 1 | package fastdex.common; 2 | 3 | /** 4 | * Created by tong on 17/4/28. 5 | */ 6 | public interface ShareConstants { 7 | String JAVA_SUFFIX = ".java"; 8 | String KT_SUFFIX = ".kt"; 9 | String CLASS_SUFFIX = ".class"; 10 | String DEX_SUFFIX = ".dex"; 11 | String CLASSES = "classes"; 12 | String CLASSES_DEX = CLASSES + DEX_SUFFIX; 13 | String META_INFO_FILENAME = "fastdex-meta-info.json"; 14 | String RESOURCE_APK_FILE_NAME = "resources.apk"; 15 | String MERGED_PATCH_DEX = "merged-patch.dex"; 16 | String PATCH_DEX = "patch.dex"; 17 | String RES_SPLIT_STR = "__"; 18 | long MESSAGE_TOKEN = 0x19910520L; 19 | } 20 | -------------------------------------------------------------------------------- /fastdex-common/src/main/java/fastdex/common/fd/ProtocolConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.common.fd; 18 | 19 | /** 20 | * Constants shared between Android Studio and the Instant Run runtime. 21 | */ 22 | public interface ProtocolConstants { 23 | 24 | /** 25 | * Magic (random) number used to identify the protocol 26 | */ 27 | long PROTOCOL_IDENTIFIER = 0x35107124L; 28 | 29 | /** 30 | * Version of the protocol 31 | */ 32 | int PROTOCOL_VERSION = 1; 33 | 34 | /** 35 | * Message: sending patches 36 | */ 37 | int MESSAGE_PATCHES = 1; 38 | 39 | /** 40 | * Message: ping, send ack back 41 | */ 42 | int MESSAGE_PING = 2; 43 | 44 | /** 45 | * Message: look up a very quick checksum of the given path; this 46 | * may not pick up on edits in the middle of the file but should be a 47 | * quick way to determine if a path exists and some basic information 48 | * about it. 49 | *

50 | * Currently disabled.. Tied to using extracted resource 51 | * directories (controlled by FileManager#USE_EXTRACTED_RESOURCES). 52 | */ 53 | int MESSAGE_PATH_EXISTS = 3; 54 | 55 | /** 56 | * Message: query whether the app has a given file and if so return 57 | * its checksum. (This is used to determine whether the app can receive 58 | * a small delta on top of a (typically resource ) file instead of resending the whole 59 | * file over again.) 60 | *

61 | * Currently disabled.. Tied to using extracted resource 62 | * directories (controlled by FileManager#USE_EXTRACTED_RESOURCES). 63 | */ 64 | int MESSAGE_PATH_CHECKSUM = 4; 65 | 66 | /** 67 | * Message: restart activities 68 | */ 69 | int MESSAGE_RESTART_ACTIVITY = 5; 70 | 71 | /** 72 | * Message: show toast 73 | */ 74 | int MESSAGE_SHOW_TOAST = 6; 75 | 76 | /** 77 | * Done transmitting 78 | */ 79 | int MESSAGE_EOF = 7; 80 | 81 | /** 82 | * Message: ask the run-as server to copy a file as the app userid. 83 | */ 84 | int MESSAGE_SEND_FILE = 8; 85 | 86 | /** 87 | * Message: ask the run-as server to execute a shell command as the app userid. 88 | */ 89 | int MESSAGE_SHELL_COMMAND = 9; 90 | 91 | int MESSAGE_PING_AND_SHOW_TOAST = 11; 92 | 93 | /** 94 | * No updates 95 | */ 96 | int UPDATE_MODE_NONE = 0; 97 | 98 | /** 99 | * Patch changes directly, keep app running without any restarting 100 | */ 101 | int UPDATE_MODE_HOT_SWAP = 1; 102 | 103 | /** 104 | * Patch changes, restart activity to reflect changes 105 | */ 106 | int UPDATE_MODE_WARM_SWAP = 2; 107 | 108 | /** 109 | * Store change in app directory, restart app 110 | */ 111 | int UPDATE_MODE_COLD_SWAP = 3; 112 | } 113 | -------------------------------------------------------------------------------- /fastdex-common/src/main/java/fastdex/common/utils/DigestUtils.java: -------------------------------------------------------------------------------- 1 | package fastdex.common.utils; 2 | 3 | import java.security.MessageDigest; 4 | import java.security.NoSuchAlgorithmException; 5 | 6 | /** 7 | * Created by jianjun.lin on 2016/11/17. 8 | */ 9 | public abstract class DigestUtils { 10 | 11 | private static final String MD5_ALGORITHM_NAME = "MD5"; 12 | 13 | private static final char[] HEX_CHARS = 14 | {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 15 | 16 | 17 | /** 18 | * Calculate the MD5 digest of the given bytes. 19 | * 20 | * @param bytes the bytes to calculate the digest over 21 | * @return the digest 22 | */ 23 | public static byte[] md5Digest(byte[] bytes) { 24 | return digest(MD5_ALGORITHM_NAME, bytes); 25 | } 26 | 27 | 28 | /** 29 | * Return a hexadecimal string representation of the MD5 digest of the given bytes. 30 | * 31 | * @param bytes the bytes to calculate the digest over 32 | * @return a hexadecimal digest string 33 | */ 34 | public static String md5DigestAsHex(byte[] bytes) { 35 | return digestAsHexString(MD5_ALGORITHM_NAME, bytes); 36 | } 37 | 38 | public static String md5DigestAsHex(String s) { 39 | return md5DigestAsHex(s.getBytes()); 40 | } 41 | 42 | /** 43 | * Append a hexadecimal string representation of the MD5 digest of the given 44 | * bytes to the given {@link StringBuilder}. 45 | * 46 | * @param bytes the bytes to calculate the digest over 47 | * @param builder the string builder to append the digest to 48 | * @return the given string builder 49 | */ 50 | public static StringBuilder appendMd5DigestAsHex(byte[] bytes, StringBuilder builder) { 51 | return appendDigestAsHex(MD5_ALGORITHM_NAME, bytes, builder); 52 | } 53 | 54 | /** 55 | * Create a new {@link MessageDigest} with the given algorithm. 56 | * Necessary because {@code MessageDigest} is not thread-safe. 57 | */ 58 | private static MessageDigest getDigest(String algorithm) { 59 | try { 60 | return MessageDigest.getInstance(algorithm); 61 | } catch (NoSuchAlgorithmException ex) { 62 | throw new IllegalStateException("Could not find MessageDigest with algorithm \"" + algorithm + "\"", ex); 63 | } 64 | } 65 | 66 | private static byte[] digest(String algorithm, byte[] bytes) { 67 | return getDigest(algorithm).digest(bytes); 68 | } 69 | 70 | private static String digestAsHexString(String algorithm, byte[] bytes) { 71 | char[] hexDigest = digestAsHexChars(algorithm, bytes); 72 | return new String(hexDigest); 73 | } 74 | 75 | private static StringBuilder appendDigestAsHex(String algorithm, byte[] bytes, StringBuilder builder) { 76 | char[] hexDigest = digestAsHexChars(algorithm, bytes); 77 | return builder.append(hexDigest); 78 | } 79 | 80 | private static char[] digestAsHexChars(String algorithm, byte[] bytes) { 81 | byte[] digest = digest(algorithm, bytes); 82 | return encodeHex(digest); 83 | } 84 | 85 | private static char[] encodeHex(byte[] bytes) { 86 | char chars[] = new char[32]; 87 | for (int i = 0; i < chars.length; i = i + 2) { 88 | byte b = bytes[i / 2]; 89 | chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf]; 90 | chars[i + 1] = HEX_CHARS[b & 0xf]; 91 | } 92 | return chars; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /fastdex-common/src/main/java/fastdex/common/utils/SerializeUtils.java: -------------------------------------------------------------------------------- 1 | package fastdex.common.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import java.io.*; 6 | 7 | /** 8 | * Created by tong on 17/3/30. 9 | */ 10 | public class SerializeUtils { 11 | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); 12 | 13 | public static T load(InputStream inputStream,Class type) throws IOException { 14 | String json = new String(FileUtils.readStream(inputStream)); 15 | return GSON.fromJson(json,type); 16 | } 17 | 18 | public static void serializeTo(OutputStream outputStream,Object obj) throws IOException { 19 | String json = GSON.toJson(obj); 20 | try { 21 | outputStream.write(json.getBytes()); 22 | outputStream.flush(); 23 | } finally { 24 | if (outputStream != null) { 25 | outputStream.close(); 26 | } 27 | } 28 | } 29 | 30 | public static void serializeTo(File file,Object obj) throws IOException { 31 | String json = GSON.toJson(obj); 32 | FileOutputStream outputStream = null; 33 | FileUtils.ensumeDir(file.getParentFile()); 34 | try { 35 | outputStream = new FileOutputStream(file); 36 | outputStream.write(json.getBytes()); 37 | outputStream.flush(); 38 | } finally { 39 | if (outputStream != null) { 40 | outputStream.close(); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /fastdex-gradle/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /fastdex-gradle/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | apply plugin: 'maven' 3 | 4 | dependencies { 5 | compile gradleApi() 6 | compile localGroovy() 7 | compile project(':fastdex-build-lib') 8 | compile 'com.android.tools.build:gradle:2.0.0' 9 | } 10 | 11 | repositories { 12 | jcenter() 13 | } 14 | 15 | apply from: rootProject.file('bintray.gradle') 16 | 17 | tasks['install'].doLast { 18 | String launchTaskName = project.gradle.startParameter.taskRequests.get(0).args.get(0).toString() 19 | 20 | println("launchTaskName: " + launchTaskName) 21 | if ("install".equals(launchTaskName)) { 22 | project.file('build/libs').deleteDir() 23 | } 24 | } 25 | 26 | tasks['bintrayUpload'].doLast { 27 | project.file('build/libs').deleteDir() 28 | } -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/extension/FastdexExtension.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.extension 2 | 3 | /** 4 | * Created by tong on 17/10/3. 5 | */ 6 | class FastdexExtension { 7 | /** 8 | * 是否可用 9 | */ 10 | //default true (可以用来区分是开发机器调试,还是jenkins机器打包,如果是开发机器就在local.properties加fastdex.enabled=true) 11 | //Properties localProperties = new Properties() 12 | //localProperties.load(project.rootProject.file('local.properties').newDataInputStream()) 13 | //fastdexEnable = Boolean.valueOf(localProperties.getProperty("fastdex.enabled", "false")) 14 | boolean fastdexEnable = true 15 | 16 | /** 17 | * debug模式下打印的日志稍微多一些 18 | */ 19 | boolean debug = false 20 | 21 | /** 22 | * 是否换成fastdex的编译方式 23 | */ 24 | boolean useCustomCompile = false 25 | 26 | /** 27 | * 每次都参与dex生成的class 28 | */ 29 | String[] hotClasses = [] 30 | 31 | /** 32 | * 当变化的java文件数量大于等于这个值时触发dex merge(随着变化的java文件的增多,补丁打包会越来越慢,dex merge以后当前的状态相当于全量打包以后的状态) 33 | */ 34 | int dexMergeThreshold = 3 35 | 36 | /** 37 | * 当发送的补丁中包含dex时会调用 'adb shell am force-stop' 强制重启app 38 | */ 39 | boolean restartAppByCmd = true 40 | 41 | /** 42 | * 进hook debug这个build type 43 | */ 44 | boolean onlyHookDebug = false 45 | } -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexCleanTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import fastdex.build.util.FastdexUtils 4 | import fastdex.build.variant.FastdexVariant 5 | import org.gradle.api.DefaultTask 6 | import org.gradle.api.tasks.TaskAction 7 | 8 | /** 9 | * 清空指定variantName的缓存,如果variantName == null清空所有缓存 10 | * Created by tong on 17/3/12. 11 | */ 12 | class FastdexCleanTask extends DefaultTask { 13 | FastdexVariant fastdexVariant 14 | 15 | FastdexCleanTask() { 16 | group = 'fastdex' 17 | } 18 | 19 | @TaskAction 20 | def clean() { 21 | if (fastdexVariant == null) { 22 | FastdexUtils.cleanAllCache(project) 23 | } 24 | else { 25 | FastdexUtils.cleanCache(project,variantName) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexCreateMaindexlistFileTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import fastdex.common.utils.FileUtils 4 | import fastdex.build.variant.FastdexVariant 5 | import org.gradle.api.DefaultTask 6 | import org.gradle.api.tasks.TaskAction 7 | 8 | /** 9 | * transformClassesWithMultidexlistFor${variantName}的作用是计算哪些类必须放在第一个dex里面,由于fastdex使用替换Application的方案隔离了项目代码的dex, 10 | * 所以这个任务就没有存在的意义了,禁止掉这个任务以提高打包速度,但是transformClassesWithDexFor${variantName}会使用这个任务输出的txt文件,所以需要生成一个空文件防止报错 11 | * Created by tong on 17/3/12. 12 | */ 13 | class FastdexCreateMaindexlistFileTask extends DefaultTask { 14 | FastdexVariant fastdexVariant 15 | 16 | FastdexCreateMaindexlistFileTask() { 17 | group = 'fastdex' 18 | } 19 | 20 | @TaskAction 21 | def createFile() { 22 | if (fastdexVariant.androidVariant != null) { 23 | File maindexlistFile = fastdexVariant.androidVariant.getVariantData().getScope().getMainDexListFile() 24 | File parentFile = maindexlistFile.getParentFile() 25 | FileUtils.ensumeDir(parentFile) 26 | 27 | if (!FileUtils.isLegalFile(maindexlistFile)) { 28 | maindexlistFile.createNewFile() 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexIgnoreTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import com.android.build.gradle.api.ApplicationVariant 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.tasks.TaskAction 6 | 7 | /** 8 | * Created by tong on 17/12/12. 9 | */ 10 | class FastdexIgnoreTask extends DefaultTask { 11 | ApplicationVariant androidVariant 12 | boolean proguardEnable 13 | boolean ignoreBuildType 14 | 15 | FastdexIgnoreTask() { 16 | group = 'fastdex' 17 | } 18 | 19 | @TaskAction 20 | def instantRun() { 21 | String buildTypeName = androidVariant.getBuildType().buildType.getName() 22 | project.logger.error("--------------------fastdex--------------------") 23 | if (ignoreBuildType) { 24 | project.logger.error("onlyHookDebug = true, build-type = ${buildTypeName}, just ignore") 25 | } 26 | else if (proguardEnable) { 27 | project.logger.error("fastdex android.buildTypes.${buildTypeName}.minifyEnabled=true, just ignore") 28 | } 29 | project.logger.error("--------------------fastdex--------------------") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexInstantRunMarkTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import fastdex.build.variant.FastdexVariant 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.tasks.TaskAction 6 | 7 | /** 8 | * Created by tong on 17/3/12. 9 | */ 10 | class FastdexInstantRunMarkTask extends DefaultTask { 11 | FastdexVariant fastdexVariant 12 | 13 | FastdexInstantRunMarkTask() { 14 | group = 'fastdex' 15 | } 16 | 17 | @TaskAction 18 | def mark() { 19 | fastdexVariant.fastdexInstantRun.fromFastdexInstantRun = true 20 | project.logger.error("==fastdex fromFastdexInstantRun: true") 21 | 22 | new Thread(new Runnable() { 23 | @Override 24 | void run() { 25 | try { 26 | fastdexVariant.fastdexInstantRun.preparedDevice(true) 27 | } catch (Throwable e) { 28 | 29 | } 30 | } 31 | }).start() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexInstantRunTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import fastdex.build.util.FastdexInstantRun 4 | import fastdex.build.util.FastdexRuntimeException 5 | import fastdex.build.variant.FastdexVariant 6 | import org.gradle.api.DefaultTask 7 | import org.gradle.api.tasks.TaskAction 8 | 9 | /** 10 | * Created by tong on 17/3/12. 11 | */ 12 | class FastdexInstantRunTask extends DefaultTask { 13 | FastdexVariant fastdexVariant 14 | 15 | FastdexInstantRunTask() { 16 | group = 'fastdex' 17 | } 18 | 19 | @TaskAction 20 | def instantRun() { 21 | FastdexInstantRun fastdexInstantRun = fastdexVariant.fastdexInstantRun 22 | 23 | if (!fastdexInstantRun.isInstallApk()) { 24 | return 25 | } 26 | 27 | fastdexInstantRun.preparedDevice() 28 | def targetVariant = fastdexVariant.androidVariant 29 | project.logger.error("==fastdex normal run ${fastdexVariant.variantName}") 30 | //安装app 31 | File apkFile = targetVariant.outputs.first().getOutputFile() 32 | project.logger.error("adb -s ${fastdexInstantRun.device.getSerialNumber()} install -r ${apkFile}") 33 | 34 | try { 35 | fastdexInstantRun.device.installPackage(apkFile.absolutePath,true) 36 | } catch (Throwable e) { 37 | throw new FastdexRuntimeException(e) 38 | } 39 | fastdexInstantRun.startBootActivity() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexManifestTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import fastdex.build.util.GradleUtils 4 | import fastdex.build.variant.FastdexVariant 5 | import groovy.xml.Namespace 6 | import groovy.xml.QName 7 | import org.gradle.api.DefaultTask 8 | import org.gradle.api.tasks.TaskAction 9 | 10 | /** 11 | * 替换项目的Application为fastdex.runtime.FastdexApplication 12 | * 并且在Manifest文件里中添加下面的节点 13 | * 14 | * 15 | * Created by tong on 17/3/11. 16 | */ 17 | class FastdexManifestTask extends DefaultTask { 18 | static final String FASTDEX_ORIGIN_APPLICATION_CLASSNAME = "FASTDEX_ORIGIN_APPLICATION_CLASSNAME" 19 | static final String FASTDEX_BOOT_ACTIVITY_CLASSNAME = "FASTDEX_BOOT_ACTIVITY_CLASSNAME" 20 | static final String MIDDLEWARE_ACTIVITY = "fastdex.runtime.MiddlewareActivity" 21 | static final String FASTDEX_SERVICE = "fastdex.runtime.FastdexService" 22 | 23 | FastdexVariant fastdexVariant 24 | 25 | FastdexManifestTask() { 26 | group = 'fastdex' 27 | } 28 | 29 | @TaskAction 30 | def updateManifest() { 31 | def ns = new Namespace("http://schemas.android.com/apk/res/android", "android") 32 | 33 | def xml = GradleUtils.parseXml(fastdexVariant.manifestPath) 34 | 35 | def application = xml.application[0] 36 | if (application) { 37 | QName nameAttr = new QName("http://schemas.android.com/apk/res/android", 'name', 'android') 38 | def applicationName = application.attribute(nameAttr) 39 | if (applicationName == null || applicationName.isEmpty()) { 40 | applicationName = "android.app.Application" 41 | } 42 | application.attributes().put(nameAttr, "fastdex.runtime.FastdexApplication") 43 | def metaDataTags = application['meta-data'] 44 | 45 | // remove any old FASTDEX_ORIGIN_APPLICATION_CLASSNAME elements 46 | metaDataTags.findAll { 47 | it.attributes()[ns.name].equals(FASTDEX_ORIGIN_APPLICATION_CLASSNAME) 48 | }.each { 49 | it.parent().remove(it) 50 | } 51 | // Add the new FASTDEX_ORIGIN_APPLICATION_CLASSNAME element 52 | application.appendNode('meta-data', [(ns.name): FASTDEX_ORIGIN_APPLICATION_CLASSNAME, (ns.value): applicationName]) 53 | 54 | 55 | String bootActivityName = GradleUtils.getBootActivityByXmlNode(xml) 56 | // remove any old FASTDEX_BOOT_ACTIVITY_CLASSNAME elements 57 | metaDataTags.findAll { 58 | it.attributes()[ns.name].equals(FASTDEX_BOOT_ACTIVITY_CLASSNAME) 59 | }.each { 60 | it.parent().remove(it) 61 | } 62 | // Add the new FASTDEX_BOOT_ACTIVITY_CLASSNAME element 63 | application.appendNode('meta-data', [(ns.name): FASTDEX_BOOT_ACTIVITY_CLASSNAME, (ns.value): bootActivityName]) 64 | 65 | application['activity'].findAll { 66 | it.attributes()[ns.name].equals(MIDDLEWARE_ACTIVITY) 67 | }.each { 68 | it.parent().remove(it) 69 | } 70 | 71 | application.appendNode('activity', [(ns.name): MIDDLEWARE_ACTIVITY]) 72 | 73 | application['service'].findAll { 74 | it.attributes()[ns.name].equals(FASTDEX_SERVICE) 75 | }.each { 76 | it.parent().remove(it) 77 | } 78 | 79 | application.appendNode('service', [(ns.name): FASTDEX_SERVICE,(ns.process): ":fastdex"]) 80 | 81 | // Write the manifest file 82 | def printer = new XmlNodePrinter(new PrintWriter(fastdexVariant.manifestPath, "utf-8")) 83 | printer.preserveWhitespace = true 84 | printer.print(xml) 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/task/FastdexPrepareTask.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.task 2 | 3 | import fastdex.build.variant.FastdexVariant 4 | import org.gradle.api.DefaultTask 5 | import org.gradle.api.tasks.TaskAction 6 | 7 | /** 8 | * 准备上下文环境 9 | * Created by tong on 17/4/18. 10 | */ 11 | class FastdexPrepareTask extends DefaultTask { 12 | FastdexVariant fastdexVariant 13 | 14 | FastdexPrepareTask() { 15 | group = 'fastdex' 16 | } 17 | 18 | @TaskAction 19 | def prepareContext() { 20 | fastdexVariant.prepareEnv() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/transform/FastdexDexBuilderTransform.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.transform 2 | 3 | import com.android.build.api.transform.Transform 4 | import com.android.build.api.transform.TransformException 5 | import com.android.build.api.transform.TransformInvocation 6 | import fastdex.build.util.Constants 7 | import fastdex.build.util.JarOperation 8 | import fastdex.build.variant.FastdexVariant 9 | import fastdex.common.utils.FileUtils 10 | 11 | /** 12 | * Created by tong on 17/10/31. 13 | */ 14 | class FastdexDexBuilderTransform extends TransformProxy { 15 | 16 | FastdexDexBuilderTransform(Transform base,File streamOutputFolder, FastdexVariant fastdexVariant) { 17 | super(base,streamOutputFolder,fastdexVariant) 18 | } 19 | 20 | @Override 21 | void transform(TransformInvocation transformInvocation) throws TransformException, IOException, InterruptedException { 22 | if (fastdexVariant.hasDexCache) { 23 | project.logger.error("\n==fastdex patch transform start,we will generate dex file") 24 | if (fastdexVariant.projectSnapshoot.diffResultSet.isJavaFileChanged()) { 25 | FileUtils.deleteDir(streamOutputFolder) 26 | File patchJar = new File(streamOutputFolder,Constants.PATCH_JAR) 27 | //生成补丁jar 28 | JarOperation.generatePatchJar(fastdexVariant,transformInvocation,patchJar) 29 | } 30 | else { 31 | project.logger.error("==fastdex no java files have changed, just ignore") 32 | } 33 | } 34 | else { 35 | fastdexBuilder.injectInputAndSaveClassPath(transformInvocation) 36 | base.transform(transformInvocation) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/transform/FastdexDexMergerTransform.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.transform 2 | 3 | import com.android.build.api.transform.* 4 | import fastdex.build.util.Constants 5 | import fastdex.build.variant.FastdexVariant 6 | import org.gradle.api.file.FileCollection 7 | import java.lang.reflect.Constructor 8 | 9 | /** 10 | * Created by tong on 17/10/31. 11 | */ 12 | @Deprecated 13 | class FastdexDexMergerTransform extends TransformProxy { 14 | 15 | FastdexDexMergerTransform(Transform base,File streamOutputFolder, FastdexVariant fastdexVariant) { 16 | super(replaceBaseTransform(base,fastdexVariant),streamOutputFolder,fastdexVariant) 17 | } 18 | 19 | @Override 20 | void transform(TransformInvocation transformInvocation) throws TransformException, IOException, InterruptedException { 21 | if (fastdexVariant.hasDexCache) { 22 | if (fastdexVariant.projectSnapshoot.diffResultSet.isJavaFileChanged()) { 23 | File patchJar = new File(fastdexVariant.dexBuilderOutputFolder,Constants.PATCH_JAR) 24 | fastdexBuilder.patchBuild(base,patchJar,streamOutputFolder) 25 | } 26 | else { 27 | project.logger.error("==fastdex no java files have changed, just ignore") 28 | } 29 | } 30 | else { 31 | project.logger.error("\n==fastdex normal transform start") 32 | fastdexBuilder.invokeNormalBuildTransform(base,transformInvocation) 33 | project.logger.error("==fastdex normal transform end\n") 34 | } 35 | } 36 | 37 | static Transform replaceBaseTransform(Transform base, FastdexVariant fastdexVariant) { 38 | //multiDexEnabled true, minSdkVersion 15 39 | // base.dexingType: LEGACY_MULTIDEX 40 | // base.mainDexListFile: file collection 41 | // base.errorReporter: com.android.build.gradle.internal.ExtraModelInfo@5b238f1d 42 | // base.dexMerger: DX 43 | // base.minSdkVersion: 15 44 | // base.isDebuggable: true 45 | // 46 | 47 | //multiDexEnabled true, minSdkVersion 21 48 | // base.dexingType: NATIVE_MULTIDEX 49 | // base.mainDexListFile: null 50 | // base.errorReporter: com.android.build.gradle.internal.ExtraModelInfo@3c9d924 51 | // base.dexMerger: DX 52 | // base.minSdkVersion: 21 53 | // base.isDebuggable: true 54 | 55 | //com.android.build.gradle.internal.transforms.DexMergerTransform 56 | 57 | Class dexingTypeClass = Class.forName("com.android.builder.dexing.DexingType") 58 | Object[] values = dexingTypeClass.getMethod("values").invoke(null,null) 59 | 60 | Constructor[] constructors = base.getClass().getConstructors() 61 | Constructor targetConstructor = constructors[0] 62 | 63 | Transform result = 64 | targetConstructor.newInstance(values.find { it.isMultiDex() && it.isPreDex() } 65 | ,(FileCollection)null 66 | , base.errorReporter 67 | ,base.dexMerger 68 | ,21 69 | ,true) 70 | return result 71 | } 72 | } -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/transform/FastdexJarMergingTransform.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.transform 2 | 3 | import com.android.build.api.transform.Transform 4 | import com.android.build.api.transform.TransformException 5 | import com.android.build.api.transform.TransformInvocation 6 | import fastdex.build.util.Constants 7 | import fastdex.build.util.GradleUtils 8 | import fastdex.build.util.JarOperation 9 | import fastdex.build.variant.FastdexVariant 10 | import fastdex.common.utils.FileUtils 11 | 12 | /** 13 | * 拦截transformClassesWithJarMergingFor${variantName}任务, 14 | * Created by tong on 17/27/3. 15 | */ 16 | class FastdexJarMergingTransform extends TransformProxy { 17 | 18 | FastdexJarMergingTransform(Transform base,File streamOutputFolder, FastdexVariant fastdexVariant) { 19 | super(base,streamOutputFolder,fastdexVariant) 20 | } 21 | 22 | @Override 23 | void transform(TransformInvocation transformInvocation) throws TransformException, IOException, InterruptedException { 24 | if (fastdexVariant.hasDexCache) { 25 | if (fastdexVariant.projectSnapshoot.diffResultSet.isJavaFileChanged()) { 26 | FileUtils.cleanDir(streamOutputFolder) 27 | 28 | //补丁jar 29 | File patchJar = new File(streamOutputFolder,Constants.PATCH_JAR) 30 | //生成补丁jar 31 | JarOperation.generatePatchJar(fastdexVariant,transformInvocation,patchJar) 32 | } 33 | else { 34 | fastdexVariant.project.logger.error("==fastdex no java files have changed, just ignore") 35 | } 36 | } 37 | else { 38 | fastdexBuilder.injectInputAndSaveClassPath(transformInvocation) 39 | 40 | if (GradleUtils.getAndroidGradlePluginVersion().compareTo(Constants.MIN_BUILD_CACHE_ENABLED_VERSION) >= 0) { 41 | //不做合并时为了使用build-cache 42 | fastdexVariant.transformInvocation = transformInvocation 43 | } 44 | else { 45 | base.transform(transformInvocation) 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/transform/FastdexPreDexTransform.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.transform 2 | 3 | import com.android.build.api.transform.Transform 4 | import com.android.build.api.transform.TransformException 5 | import com.android.build.api.transform.TransformInvocation 6 | import fastdex.build.util.Constants 7 | import fastdex.build.util.FastdexUtils 8 | import fastdex.build.util.JarOperation 9 | import fastdex.build.variant.FastdexVariant 10 | import fastdex.common.utils.FileUtils 11 | 12 | /** 13 | * Created by tong on 17/11/2. 14 | */ 15 | class FastdexPreDexTransform extends TransformProxy { 16 | 17 | FastdexPreDexTransform(Transform base,File streamOutputFolder, FastdexVariant fastdexVariant) { 18 | super(base,streamOutputFolder,fastdexVariant) 19 | } 20 | 21 | @Override 22 | void transform(TransformInvocation transformInvocation) throws TransformException, IOException, InterruptedException { 23 | if (fastdexVariant.hasDexCache) { 24 | project.logger.error("\n==fastdex patch transform start,we will generate dex file") 25 | 26 | if (base.dexingType.isMultiDex() && base.dexingType.isPreDex()) { 27 | if (fastdexVariant.projectSnapshoot.diffResultSet.isJavaFileChanged()) { 28 | //生成补丁jar包 29 | File patchJar = new File(FastdexUtils.getBuildDir(project,variantName),Constants.PATCH_JAR) 30 | JarOperation.generatePatchJar(fastdexVariant,transformInvocation,patchJar) 31 | fastdexBuilder.patchBuild(base,patchJar,streamOutputFolder) 32 | } 33 | else { 34 | project.logger.error("==fastdex no java files have changed, just ignore") 35 | } 36 | } 37 | else { 38 | if (fastdexVariant.projectSnapshoot.diffResultSet.isJavaFileChanged()) { 39 | FileUtils.deleteDir(streamOutputFolder) 40 | File patchJar = new File(streamOutputFolder,Constants.PATCH_JAR) 41 | //生成补丁jar 42 | JarOperation.generatePatchJar(fastdexVariant,transformInvocation,patchJar) 43 | } 44 | else { 45 | project.logger.error("==fastdex no java files have changed, just ignore") 46 | } 47 | } 48 | } 49 | else { 50 | if (base.dexingType.isMultiDex() && base.dexingType.isPreDex()) { 51 | fastdexBuilder.injectInputAndSaveClassPath(transformInvocation) 52 | fastdexBuilder.invokeNormalBuildTransform(base,transformInvocation) 53 | } 54 | else { 55 | fastdexBuilder.injectInputAndSaveClassPath(transformInvocation) 56 | base.transform(transformInvocation) 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/transform/TransformProxy.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.transform 2 | 3 | import com.android.build.api.transform.QualifiedContent 4 | import com.android.build.api.transform.Transform 5 | import com.android.build.gradle.api.ApplicationVariant 6 | import fastdex.build.variant.FastdexBuilder 7 | import fastdex.build.variant.FastdexVariant 8 | import org.gradle.api.Project 9 | 10 | /** 11 | * Created by tong on 17/10/3. 12 | */ 13 | class TransformProxy extends Transform { 14 | final Transform base 15 | final File streamOutputFolder 16 | final FastdexVariant fastdexVariant 17 | final FastdexBuilder fastdexBuilder 18 | final Project project 19 | final String variantName 20 | final ApplicationVariant androidVariant 21 | 22 | TransformProxy(Transform base,File streamOutputFolder,FastdexVariant fastdexVariant) { 23 | this.base = base 24 | this.streamOutputFolder = streamOutputFolder 25 | this.fastdexVariant = fastdexVariant 26 | this.fastdexBuilder = fastdexVariant.fastdexBuilder 27 | this.project = fastdexVariant.project 28 | this.variantName = fastdexVariant.variantName 29 | this.androidVariant = fastdexVariant.androidVariant 30 | } 31 | 32 | @Override 33 | String getName() { 34 | return base.getName() 35 | } 36 | 37 | @Override 38 | Set getInputTypes() { 39 | return base.getInputTypes() 40 | } 41 | 42 | @Override 43 | Set getScopes() { 44 | return base.getScopes() 45 | } 46 | 47 | @Override 48 | boolean isIncremental() { 49 | return base.isIncremental() 50 | } 51 | } -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/Constants.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util 2 | 3 | import fastdex.common.ShareConstants 4 | 5 | /** 6 | * Created by tong on 17/3/14. 7 | */ 8 | interface Constants extends ShareConstants { 9 | String BUILD_DIR = "fastdex" 10 | String R_TXT = "R.txt" 11 | String RESOURCE_PUBLIC_XML = "public.xml" 12 | String RESOURCE_IDX_XML = "idx.xml" 13 | String RUNTIME_DEX_FILENAME = "fastdex-runtime.dex" 14 | String DEPENDENCIES_FILENAME = "dependencies.json" 15 | String SOURCESET_SNAPSHOOT_FILENAME = "sourceSets.json" 16 | String LAST_DIFF_RESULT_SET_FILENAME = "lastDiffResultSet.json" 17 | String CLASSPATH_FILENAME = "classpath.json" 18 | String ANDROID_MANIFEST_FILENAME = "android_manifest.json" 19 | String ERROR_REPORT_FILENAME = "last-build-error-report.txt" 20 | String DEFAULT_LIBRARY_VARIANT_DIR_NAME = "release" 21 | String DEX_MERGE_JAR_FILENAME = "fastdex-dex-merge.jar" 22 | String STUDIO_INFO_SCRIPT_MACOS = "fastdex-studio-info-macos-%s.sh" 23 | String MIN_BUILD_CACHE_ENABLED_VERSION = "2.2.2" 24 | String PATCH_JAR = "patch.jar" 25 | } 26 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/DexOperation.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util 2 | 3 | import com.android.build.api.transform.Transform 4 | import fastdex.build.variant.FastdexVariant 5 | import fastdex.common.utils.FileUtils 6 | import org.apache.tools.ant.taskdefs.condition.Os 7 | 8 | /** 9 | * dex操作 10 | * Created by tong on 17/11/4. 11 | */ 12 | class DexOperation { 13 | /** 14 | * 生成补丁dex 15 | * @param fastdexVariant 16 | * @param base 17 | * @param patchJar 18 | * @param patchDex 19 | */ 20 | static final void generatePatchDex(FastdexVariant fastdexVariant, Transform base,File patchJar,File patchDex) { 21 | FileUtils.deleteFile(patchDex) 22 | FileUtils.ensumeDir(patchDex.parentFile) 23 | 24 | long start = System.currentTimeMillis() 25 | List cmdArgs = new ArrayList<>() 26 | 27 | //TODO 补丁的方法数也有可能超过65535个,最好加上使dx生成多个dex的参数,但是一般补丁不会那么大所以暂时不处理 28 | if (Os.isFamily(Os.FAMILY_WINDOWS) || fastdexVariant.project.projectDir.absolutePath.contains(" ")) { 29 | //调用dx命令 30 | cmdArgs.add(FastdexUtils.getDxCmdPath(fastdexVariant.project)) 31 | cmdArgs.add("--dex") 32 | cmdArgs.add("--output=${patchDex}") 33 | cmdArgs.add(patchJar.absolutePath) 34 | } 35 | else { 36 | File dxJarFile = new File(FastdexUtils.getBuildDir(fastdexVariant.project),"fastdex-dx.jar") 37 | File dxCommandFile = new File(FastdexUtils.getBuildDir(fastdexVariant.project),"fastdex-dx") 38 | 39 | if (!FileUtils.isLegalFile(dxJarFile)) { 40 | FileUtils.copyResourceUsingStream("fastdex-dx.jar",dxJarFile) 41 | } 42 | 43 | if (!FileUtils.isLegalFile(dxCommandFile)) { 44 | FileUtils.copyResourceUsingStream("fastdex-dx",dxCommandFile) 45 | } 46 | 47 | cmdArgs.add("sh") 48 | cmdArgs.add(dxCommandFile.absolutePath) 49 | cmdArgs.add("--dex") 50 | cmdArgs.add("--no-optimize") 51 | cmdArgs.add("--force-jumbo") 52 | cmdArgs.add("--output=${patchDex}") 53 | cmdArgs.add(patchJar.absolutePath) 54 | } 55 | 56 | //调用dx命令 57 | FastdexUtils.runCommand(fastdexVariant.project, cmdArgs) 58 | long end = System.currentTimeMillis() 59 | fastdexVariant.project.logger.error("==fastdex patch transform generate dex success. use: ${end - start}ms") 60 | } 61 | 62 | /** 63 | * 合并dex 64 | * @param fastdexVariant 65 | * @param outputDex 输出的dex路径 66 | * @param patchDex 补丁dex路径 67 | * @param cachedDex 68 | */ 69 | static void mergeDex(FastdexVariant fastdexVariant,File outputDex,File patchDex,File cachedDex) { 70 | long start = System.currentTimeMillis() 71 | def project = fastdexVariant.project 72 | File dexMergeJar = new File(FastdexUtils.getBuildDir(project),Constants.DEX_MERGE_JAR_FILENAME) 73 | if (!FileUtils.isLegalFile(dexMergeJar)) { 74 | FileUtils.copyResourceUsingStream(Constants.DEX_MERGE_JAR_FILENAME,dexMergeJar) 75 | } 76 | 77 | List cmdArgs = new ArrayList<>() 78 | cmdArgs.add(FastdexUtils.getJavaCmdPath()) 79 | cmdArgs.add("-jar") 80 | cmdArgs.add(dexMergeJar.absolutePath) 81 | cmdArgs.add(outputDex.absolutePath) 82 | cmdArgs.add(patchDex.absolutePath) 83 | cmdArgs.add(cachedDex.absolutePath) 84 | 85 | FastdexUtils.runCommand(fastdexVariant.project, cmdArgs) 86 | 87 | long end = System.currentTimeMillis() 88 | project.logger.error("==fastdex merge dex success. use: ${end - start}ms") 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/FastdexRuntimeException.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util 2 | 3 | /** 4 | * Created by tong on 17/4/18. 5 | */ 6 | class FastdexRuntimeException extends RuntimeException { 7 | 8 | FastdexRuntimeException() { 9 | } 10 | 11 | FastdexRuntimeException(String var1) { 12 | super(var1) 13 | } 14 | 15 | FastdexRuntimeException(String var1, Throwable var2) { 16 | super(var1, var2) 17 | } 18 | 19 | FastdexRuntimeException(Throwable var1) { 20 | super(var1) 21 | } 22 | 23 | FastdexRuntimeException(String var1, Throwable var2, boolean var3, boolean var4) { 24 | super(var1, var2, var3, var4) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/FindDexFileVisitor.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util 2 | 3 | import java.nio.file.FileVisitResult 4 | import java.nio.file.Path 5 | import java.nio.file.SimpleFileVisitor 6 | import java.nio.file.attribute.BasicFileAttributes 7 | import fastdex.common.ShareConstants 8 | 9 | /** 10 | * Created by tong on 17/9/24. 11 | */ 12 | class FindDexFileVisitor extends SimpleFileVisitor { 13 | public boolean hasDex 14 | 15 | @Override 16 | FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 17 | if (file.toString().endsWith(ShareConstants.DEX_SUFFIX)) { 18 | hasDex = true 19 | return FileVisitResult.TERMINATE 20 | } 21 | return FileVisitResult.CONTINUE 22 | } 23 | } -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/FixPublicSymbolLoggerWrapper.java: -------------------------------------------------------------------------------- 1 | package fastdex.build.util; 2 | 3 | /** 4 | * Created by tong on 17/11/3. 5 | */ 6 | 7 | public class FixPublicSymbolLoggerWrapper { 8 | } 9 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/JumpException.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util 2 | 3 | /** 4 | * Created by tong on 17/5/2. 5 | */ 6 | class JumpException extends RuntimeException { 7 | JumpException() { 8 | } 9 | 10 | JumpException(String s) { 11 | super(s) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/MetaInfo.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util 2 | 3 | import fastdex.common.utils.SerializeUtils 4 | import fastdex.build.variant.FastdexVariant 5 | import com.google.gson.Gson 6 | import fastdex.common.utils.FileUtils 7 | import org.gradle.api.Project 8 | 9 | /** 10 | * Created by tong on 17/4/18. 11 | */ 12 | class MetaInfo { 13 | /** 14 | * 全量编译时的工程路径 15 | */ 16 | public String projectPath 17 | 18 | public String rootProjectPath 19 | 20 | public String fastdexVersion 21 | /** 22 | * 全量编译完成后输出的dex个数 23 | */ 24 | public int dexCount 25 | 26 | /** 27 | * 全量编译完成的时间 28 | */ 29 | public long buildMillis 30 | 31 | public String variantName 32 | 33 | public int mergedDexVersion 34 | 35 | public int patchDexVersion 36 | 37 | public int resourcesVersion 38 | 39 | public boolean active = true 40 | 41 | /** 42 | * 是否移动了工程目录 43 | * @param project 44 | * @return 45 | */ 46 | def isRootProjectDirChanged(String curRootProjectPath) { 47 | return !curRootProjectPath.equals(rootProjectPath) 48 | } 49 | 50 | def save(FastdexVariant fastdexVariant) { 51 | File metaInfoFile = FastdexUtils.getMetaInfoFile(fastdexVariant.project,fastdexVariant.variantName) 52 | SerializeUtils.serializeTo(new FileOutputStream(metaInfoFile),this) 53 | } 54 | 55 | @Override 56 | String toString() { 57 | return "MetaInfo{" + 58 | "buildMillis=" + buildMillis + 59 | ", variantName='" + variantName + '\'' + 60 | ", mergedDexVersion=" + mergedDexVersion + 61 | ", patchDexVersion=" + patchDexVersion + 62 | ", resourcesVersion=" + resourcesVersion + 63 | '}' 64 | } 65 | 66 | static MetaInfo load(Project project,String variantName) { 67 | File metaInfoFile = FastdexUtils.getMetaInfoFile(project,variantName) 68 | return new Gson().fromJson(new String(FileUtils.readContents(metaInfoFile)),MetaInfo.class) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/groovy/fastdex/build/util/ReflectUtils.groovy: -------------------------------------------------------------------------------- 1 | package fastdex.build.util; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | /** 6 | * Created by tong on 17/11/3. 7 | */ 8 | class ReflectUtils { 9 | static Field getFieldByName(Class aClass, String name) { 10 | Class currentClass = aClass 11 | while (currentClass != null) { 12 | try { 13 | return currentClass.getDeclaredField(name) 14 | } catch (NoSuchFieldException e) { 15 | // ignored. 16 | } 17 | currentClass = currentClass.getSuperclass() 18 | } 19 | return null 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/META-INF/gradle-plugins/com.dx168.fastdex.properties: -------------------------------------------------------------------------------- 1 | implementation-class=fastdex.build.FastdexPlugin -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/META-INF/gradle-plugins/com.github.typ0520.fastdex.properties: -------------------------------------------------------------------------------- 1 | implementation-class=fastdex.build.FastdexPlugin -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/META-INF/gradle-plugins/fastdex.app.properties: -------------------------------------------------------------------------------- 1 | implementation-class=fastdex.build.FastdexPlugin -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/fastdex-dex-merge.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-gradle/src/main/resources/fastdex-dex-merge.jar -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/fastdex-dx: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright (C) 2007 The Android Open Source Project 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Set up prog to be the path of this script, including following symlinks, 18 | # and set up progdir to be the fully-qualified pathname of its directory. 19 | prog="$0" 20 | while [ -h "${prog}" ]; do 21 | newProg=`/bin/ls -ld "${prog}"` 22 | newProg=`expr "${newProg}" : ".* -> \(.*\)$"` 23 | if expr "x${newProg}" : 'x/' >/dev/null; then 24 | prog="${newProg}" 25 | else 26 | progdir=`dirname "${prog}"` 27 | prog="${progdir}/${newProg}" 28 | fi 29 | done 30 | oldwd=`pwd` 31 | progdir=`dirname "${prog}"` 32 | cd "${progdir}" 33 | progdir=`pwd` 34 | prog="${progdir}"/`basename "${prog}"` 35 | cd "${oldwd}" 36 | 37 | jarfile=fastdex-dx.jar 38 | libdir=$(dirname $0) 39 | 40 | if [ ! -r "$libdir/$jarfile" ]; then 41 | # set dx.jar location for the SDK case 42 | libdir="$libdir/lib" 43 | fi 44 | 45 | 46 | if [ ! -r "$libdir/$jarfile" ]; then 47 | # set dx.jar location for the Android tree case 48 | libdir=`dirname "$progdir"`/framework 49 | fi 50 | 51 | if [ ! -r "$libdir/$jarfile" ]; then 52 | echo `basename "$prog"`": can't find $jarfile" 53 | exit 1 54 | fi 55 | 56 | # By default, give dx a max heap size of 1 gig. This can be overridden 57 | # by using a "-J" option (see below). 58 | defaultMx="-Xmx1024M" 59 | 60 | # The following will extract any initial parameters of the form 61 | # "-J" from the command line and pass them to the Java 62 | # invocation (instead of to dx). This makes it possible for you to add 63 | # a command-line parameter such as "-JXmx256M" in your scripts, for 64 | # example. "java" (with no args) and "java -X" give a summary of 65 | # available options. 66 | 67 | javaOpts="" 68 | 69 | while expr "x$1" : 'x-J' >/dev/null; do 70 | opt=`expr "x$1" : 'x-J\(.*\)'` 71 | javaOpts="${javaOpts} -${opt}" 72 | if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then 73 | defaultMx="no" 74 | fi 75 | shift 76 | done 77 | 78 | if [ "${defaultMx}" != "no" ]; then 79 | javaOpts="${javaOpts} ${defaultMx}" 80 | fi 81 | 82 | if [ "$OSTYPE" = "cygwin" ]; then 83 | # For Cygwin, convert the jarfile path into native Windows style. 84 | jarpath=`cygpath -w "$libdir/$jarfile"` 85 | else 86 | jarpath="$libdir/$jarfile" 87 | fi 88 | 89 | exec java $javaOpts -jar "$jarpath" "$@" -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/fastdex-dx.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-gradle/src/main/resources/fastdex-dx.jar -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/fastdex-runtime.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-gradle/src/main/resources/fastdex-runtime.dex -------------------------------------------------------------------------------- /fastdex-gradle/src/main/resources/fastdex-studio-info-macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #输入gradle进程id 4 | 5 | #输出以下信息 6 | #是否是在studio上触发的构建 7 | 8 | #如果是在studio上触发的构建在获取以下信息 9 | #android studio 是否开启了instant run 10 | #android studio版本号 11 | 12 | debug=0 13 | 14 | function debug_log { 15 | if [ $debug != 0 ];then 16 | echo $@ 17 | fi 18 | } 19 | 20 | gradle_pid=$1 21 | if [ "${gradle_pid}" == "" ];then 22 | echo "please input gradle pid" 23 | exit -1 24 | fi 25 | ps_gradle_result=$(ps -ef | grep "${gradle_pid}" | head -1) 26 | debug_log "ps_gradle_result: ${ps_gradle_result}" 27 | 28 | echo $ps_gradle_result | grep "$0 ${gradle_pid}" > /dev/null 29 | if [ $? == 0 ] || [ "${ps_gradle_result}" == "" ];then 30 | echo "process not found id: ${gradle_pid}" 31 | exit -1 32 | fi 33 | 34 | gradle_ppid=$(echo $ps_gradle_result | awk '{print $3}') 35 | 36 | ps_studio_result=$(ps -ef | grep 'MacOS/studio' | head -1) 37 | debug_log "ps_studio_result: ${ps_studio_result}" 38 | echo $ps_studio_result | grep 'Contents/MacOS/studio' > /dev/null 39 | 40 | from_studio=true 41 | if [ $? != 0 ] || [ "${ps_studio_result}" == "" ];then 42 | from_studio=false 43 | 44 | echo "from_studio=${from_studio}" 45 | if [ "$2" != "" ];then 46 | echo 'from_studio=false' > $2 47 | fi 48 | exit 0 49 | fi 50 | 51 | studio_pid=$(echo $ps_studio_result | awk '{print $2}') 52 | studio_home="$(echo $ps_studio_result | awk '{print $8}')" 53 | temp_dir=$(echo $ps_studio_result | awk '{print $9}') 54 | if [ "${temp_dir}" != "" ];then 55 | studio_home="${studio_home} ${temp_dir}" 56 | fi 57 | 58 | studio_home=${studio_home%/MacOS/studio*} 59 | debug_log $studio_home 60 | 61 | info_plist="${studio_home}/Info.plist" 62 | debug_log $info_plist 63 | line_num=$(cat -n "${info_plist}" | grep 'CFBundleShortVersionString' | awk '{print $1}') 64 | let line_num=$line_num+1 65 | debug_log $line_num 66 | studio_version=$(sed -n "${line_num},${line_num}p" "${info_plist}") 67 | studio_version=${studio_version#*} 68 | studio_version=${studio_version%*} 69 | 70 | if [ "${gradle_ppid}" != "${studio_pid}" ];then 71 | from_studio=false 72 | fi 73 | 74 | instant_run_disabled=false 75 | instant_run_config="${HOME}/Library/Preferences/AndroidStudio2.2/options/instant-run.xml" 76 | if [ -f "${instant_run_config}" ];then 77 | cat ${instant_run_config} | grep 'false' > /dev/null 78 | if [ $? == 0 ];then 79 | instant_run_disabled=true 80 | fi 81 | fi 82 | 83 | echo "from_studio=${from_studio}" 84 | #echo "gradle_pid=${gradle_pid}" 85 | #echo "gradle_ppid=${gradle_ppid}" 86 | #echo "studio_pid=${studio_pid}" 87 | echo "studio_home=${studio_home}" 88 | echo "studio_version=${studio_version}" 89 | echo "info_plist=${info_plist}" 90 | echo "instant_run_disabled=${instant_run_disabled}" 91 | echo "instant_run_config=${instant_run_config}" 92 | 93 | if [ "$2" != "" ];then 94 | echo "from_studio=${from_studio}" > $2 95 | echo "gradle_pid=${gradle_pid}" >> $2 96 | echo "gradle_ppid=${gradle_ppid}" >> $2 97 | echo "studio_pid=${studio_pid}" >> $2 98 | echo "studio_home=${studio_home}" >> $2 99 | echo "studio_version=${studio_version}" >> $2 100 | echo "info_plist=${info_plist}" >> $2 101 | echo "instant_run_disabled=${instant_run_disabled}" >> $2 102 | echo "instant_run_config=${instant_run_config}" >> $2 103 | fi 104 | 105 | exit 0 -------------------------------------------------------------------------------- /fastdex-idea-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .git 3 | *.iml 4 | out 5 | *.jar -------------------------------------------------------------------------------- /fastdex-idea-plugin/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "org.jetbrains.intellij" version "0.2.4" 3 | } 4 | 5 | apply plugin: 'org.jetbrains.intellij' 6 | apply plugin: 'java' 7 | 8 | version = '1.0.4' 9 | 10 | compileJava { 11 | sourceCompatibility = 1.6 12 | targetCompatibility = 1.6 13 | } 14 | 15 | intellij { 16 | version 'IC-2016.2.5' 17 | pluginName 'Fastdex Plugin' 18 | 19 | plugins = ['android', 'gradle', 'Groovy', 'terminal'] 20 | // Uncomment to test against Android Studio 21 | // intellij.alternativeIdePath = '/Applications/Android Studio.app' 22 | } 23 | 24 | 25 | project.tasks['patchPluginXml'].doLast { 26 | // 27 | //=> 28 | // 29 | 30 | File xmlFile = project.file('build/patchedPluginXmlFiles/plugin.xml') 31 | 32 | def ideaPlugin = new XmlParser().parse(new InputStreamReader(new FileInputStream(xmlFile.absolutePath), "utf-8")) 33 | 34 | if (ideaPlugin) { 35 | def ideaVersions = ideaPlugin['idea-version'] 36 | if (ideaVersions) { 37 | ideaVersions.findAll { 38 | it.parent().remove(it) 39 | } 40 | } 41 | 42 | ideaPlugin.appendNode('idea-version', ["since-build": "141.0"]) 43 | 44 | def versions = ideaPlugin['version'] 45 | if (versions) { 46 | versions.findAll { 47 | it.parent().remove(it) 48 | } 49 | } 50 | ideaPlugin.appendNode('version', project.version) 51 | 52 | // Write the manifest file 53 | def printer = new XmlNodePrinter(new PrintWriter(xmlFile.absolutePath, "utf-8")) 54 | printer.preserveWhitespace = true 55 | printer.print(ideaPlugin) 56 | 57 | println("parse: ${xmlFile} \n \n=> \n") 58 | } 59 | } -------------------------------------------------------------------------------- /fastdex-idea-plugin/find-build-variants.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #寻找包含Build Variants的idea插件在那个jar包里 4 | #https://android.googlesource.com/platform/tools/adt/idea 5 | 6 | cd /Applications/Android\ Studio.app/Contents 7 | for jar in $(find . | grep .jar$);do 8 | unzip -l $jar | grep META-INF/plugin.xml 9 | 10 | if [ $? == 0 ];then 11 | if [ -d TEMP-META-INF ];then 12 | rm -rf TEMP-META-INF 13 | fi 14 | echo "scan idea plugin jar: ${jar}" 15 | 16 | unzip $jar "META-INF/*.xml" -d TEMP-META-INF/ > /dev/null 17 | 18 | for xml in $(ls TEMP-META-INF/META-INF/ | grep .xml$);do 19 | echo "scan xml: ${xml}" 20 | 21 | cat TEMP-META-INF/META-INF/${xml} | grep 'Build Variants' > /dev/null 22 | if [ $? == 0 ];then 23 | echo "find it /Applications/Android\ Studio.app/Contents/TEMP-META-INF/META-INF/${xml}" 24 | echo "find it /Applications/Android\ Studio.app/Contents/${jar}" 25 | exit 0 26 | fi 27 | done 28 | fi 29 | done -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/actions/BaseAction.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.actions; 2 | 3 | import com.intellij.openapi.actionSystem.AnAction; 4 | import com.intellij.openapi.actionSystem.AnActionEvent; 5 | import com.intellij.openapi.actionSystem.DataKeys; 6 | import com.intellij.openapi.application.ApplicationManager; 7 | import com.intellij.openapi.project.Project; 8 | import fastdex.idea.utils.DocumentUtil; 9 | import javax.swing.*; 10 | import java.io.File; 11 | 12 | /** 13 | * Created by pengwei on 16/9/11. 14 | */ 15 | public abstract class BaseAction extends AnAction { 16 | protected Project currentProject; 17 | protected File projectDir; 18 | protected AnActionEvent anActionEvent; 19 | 20 | public BaseAction(Icon icon) { 21 | super(icon); 22 | } 23 | 24 | @Override 25 | public final void actionPerformed(AnActionEvent anActionEvent) { 26 | DocumentUtil.saveDocument(); 27 | this.anActionEvent = anActionEvent; 28 | this.currentProject = DataKeys.PROJECT.getData(anActionEvent.getDataContext()); 29 | this.projectDir = new File(currentProject.getBasePath()); 30 | actionPerformed(); 31 | } 32 | 33 | public abstract void actionPerformed(); 34 | 35 | /** 36 | * 异步执行 37 | * 38 | * @param runnable 39 | */ 40 | protected void asyncTask(Runnable runnable) { 41 | ApplicationManager.getApplication().executeOnPooledThread(runnable); 42 | } 43 | 44 | protected void invokeLater(Runnable runnable) { 45 | ApplicationManager.getApplication().invokeLater(runnable); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/icons/PluginIcons.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.icons; 2 | 3 | import com.intellij.icons.AllIcons; 4 | import com.intellij.openapi.util.IconLoader; 5 | import javax.swing.*; 6 | 7 | /** 8 | * Created by pengwei on 16/9/15. 9 | */ 10 | public class PluginIcons { 11 | 12 | public static final Icon FastdexIcon = load("/icons/icon.png"); 13 | public static final Icon Suspend = intellijLoad("/actions/suspend.png"); 14 | public static final Icon GC = intellijLoad("/actions/gc.png"); 15 | public static final Icon GradleSync = load("/icons/gradlesync.png"); 16 | 17 | private static Icon load(String path) { 18 | try { 19 | return IconLoader.getIcon(path, PluginIcons.class); 20 | } catch (IllegalStateException e) { 21 | return null; 22 | } 23 | } 24 | 25 | private static Icon intellijLoad(String path) { 26 | return IconLoader.getIcon(path, AllIcons.class); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/models/ArtifactDependencyModelWrapper.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.models; 2 | 3 | import com.android.tools.idea.gradle.dsl.model.dependencies.ArtifactDependencyModel; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * Created by pengwei on 16/9/14. 10 | * 兼容android studio不同版本 11 | * 2.1.2之前版本name()方法返回string, 之后返回GradleNullableValue 12 | */ 13 | public class ArtifactDependencyModelWrapper { 14 | 15 | private ArtifactDependencyModel model; 16 | 17 | public ArtifactDependencyModelWrapper(ArtifactDependencyModel model) { 18 | this.model = model; 19 | } 20 | 21 | @NotNull 22 | public String name() { 23 | Object value = getArtifactDependencyModelMethod("name"); 24 | if (value != null) { 25 | return getGradleNotNullValue(value); 26 | } 27 | return ""; 28 | } 29 | 30 | @Nullable 31 | public String group() { 32 | Object value = getArtifactDependencyModelMethod("group"); 33 | if (value != null) { 34 | return getGradleNullableValue(value); 35 | } 36 | return ""; 37 | } 38 | 39 | @Nullable 40 | public String version() { 41 | Object value = getArtifactDependencyModelMethod("version"); 42 | if (value != null) { 43 | return getGradleNullableValue(value); 44 | } 45 | return ""; 46 | } 47 | 48 | public String configurationName() { 49 | return model.configurationName(); 50 | } 51 | 52 | /** 53 | * 反射调用ArtifactDependencyModel方法 54 | * 55 | * @param methodName 56 | * @return 57 | */ 58 | private Object getArtifactDependencyModelMethod(String methodName) { 59 | try { 60 | Class ArtifactDependencyModelClass = Class.forName("com.android.tools.idea.gradle.dsl.model.dependencies.ArtifactDependencyModel"); 61 | Method groupMethod = ArtifactDependencyModelClass.getMethod(methodName); 62 | Object value = groupMethod.invoke(model); 63 | return value; 64 | } catch (Exception e) { 65 | return null; 66 | } 67 | } 68 | 69 | private String getGradleNullableValue(Object value) { 70 | return getGradleValue("GradleNullableValue", value); 71 | } 72 | 73 | private String getGradleNotNullValue(Object value) { 74 | return getGradleValue("GradleNotNullValue", value); 75 | } 76 | 77 | private String getGradleValue(String className, Object value) { 78 | if (value instanceof String) { 79 | return (String) value; 80 | } 81 | try { 82 | Class classType = Class.forName("com.android.tools.idea.gradle.dsl.model.values." + className); 83 | if (value.getClass().equals(classType)) { 84 | Method valueMethod = classType.getMethod("value"); 85 | value = valueMethod.invoke(value); 86 | if (value instanceof String) { 87 | return (String) value; 88 | } 89 | } 90 | } catch (Exception e) { 91 | } 92 | return ""; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/models/Constant.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.models; 2 | 3 | /** 4 | * Created by pengwei on 2016/10/22. 5 | */ 6 | public interface Constant { 7 | boolean DEBUG_MODE = true; 8 | 9 | // groupId 10 | String FASTDEX_CLASSPATH_GROUP = "com.github.typ0520"; 11 | // artifactId 12 | String FASTDEX_CLASSPATH_ARTIFACT = "fastdex-gradle"; 13 | // plugin name 14 | String FASTDEX_PLUGIN_ID = "fastdex.app"; 15 | 16 | // gradle tool 17 | String ANDROID_GRADLE_TOOL_GROUP_NAME = "com.android.tools.build"; 18 | 19 | //插件依赖的最低的fastdex版本 20 | String MIN_FASTDEX_VERSION = "0.3"; 21 | 22 | //最低支持多个设备连接的fastdex版本 23 | String MIN_SUPPORT_MULTIPLE_DEVICE_FASTDEX_VERSION = "0.4"; 24 | } 25 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/models/FastdexConfiguration.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.models; 2 | 3 | import com.intellij.openapi.components.*; 4 | import com.intellij.util.xmlb.XmlSerializerUtil; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | /** 8 | * Created by huangyong on 17/2/14. 9 | */ 10 | @State( 11 | name = "FastdexConfigurationStorage", 12 | storages = @Storage(file = "fastdex-configuration.xml", roamingType = RoamingType.DISABLED) 13 | ) 14 | public class FastdexConfiguration implements PersistentStateComponent { 15 | @Nullable 16 | @Override 17 | public FastdexConfiguration getState() { 18 | return this; 19 | } 20 | 21 | @Override 22 | public void loadState(FastdexConfiguration fastdexConfiguration) { 23 | XmlSerializerUtil.copyBean(fastdexConfiguration, this); 24 | } 25 | 26 | public static FastdexConfiguration getInstance() { 27 | return ServiceManager.getService(FastdexConfiguration.class); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/models/GetServerCallback.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.models; 2 | 3 | /** 4 | * Created by pengwei on 16/9/14. 5 | */ 6 | public interface GetServerCallback { 7 | void onSuccess(GradleDependencyEntity entity); 8 | void onFailure(String errMsg); 9 | } 10 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/DialogUtil.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import com.intellij.openapi.ui.DialogBuilder; 4 | import com.intellij.openapi.ui.Messages; 5 | import javax.swing.*; 6 | 7 | /** 8 | * Created by pengwei on 2016/11/2. 9 | */ 10 | public final class DialogUtil { 11 | 12 | /** 13 | * 创建普通对话框 14 | * @param message 15 | * @param okText 16 | * @param cancelText 17 | * @return 18 | */ 19 | public static boolean createDialog(String message, String okText, String cancelText) { 20 | DialogBuilder builder = new DialogBuilder(); 21 | builder.setTitle("Dialog Message"); 22 | builder.resizable(false); 23 | builder.setCenterPanel(new JLabel(message, Messages.getInformationIcon(), SwingConstants.CENTER)); 24 | builder.addOkAction().setText(okText); 25 | builder.addCancelAction().setText(cancelText); 26 | builder.setButtonsAlignment(SwingConstants.CENTER); 27 | return builder.show() == 0; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/DocumentUtil.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import com.intellij.codeInsight.actions.ReformatCodeAction; 4 | import com.intellij.codeInsight.actions.ReformatCodeProcessor; 5 | import com.intellij.openapi.application.ApplicationManager; 6 | import com.intellij.openapi.fileEditor.FileDocumentManager; 7 | import com.intellij.openapi.project.Project; 8 | import com.intellij.openapi.vfs.VirtualFile; 9 | import com.intellij.psi.PsiFile; 10 | 11 | /** 12 | * Created by pengwei on 2016/11/1. 13 | */ 14 | public class DocumentUtil { 15 | 16 | /** 17 | * 保存文档和设置 18 | */ 19 | public static void saveDocument() { 20 | FileDocumentManager.getInstance().saveAllDocuments(); 21 | ApplicationManager.getApplication().saveSettings(); 22 | } 23 | 24 | /** 25 | * 格式化代码 26 | * 27 | * @param project 28 | * @param virtualFiles 29 | */ 30 | public static void reformatCode(final Project project, final VirtualFile virtualFiles) { 31 | ApplicationManager.getApplication().runWriteAction(new Runnable() { 32 | @Override 33 | public void run() { 34 | PsiFile[] psiFiles = ReformatCodeAction.convertToPsiFiles(new VirtualFile[]{virtualFiles}, project); 35 | if (psiFiles != null && psiFiles.length == 1 && psiFiles[0] != null) { 36 | new ReformatCodeProcessor(project, psiFiles[0], null, false).run(); 37 | } 38 | } 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/GroovyFileUil.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.collect.Iterables; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; 8 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; 9 | import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; 10 | import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; 11 | 12 | import java.util.Arrays; 13 | import java.util.Iterator; 14 | 15 | /** 16 | * Created by pengwei on 2016/10/31. 17 | */ 18 | public class GroovyFileUil { 19 | 20 | /** 21 | * 获取最后一个插件的表达式 22 | * 23 | * @param buildScript 24 | * @return 25 | */ 26 | public static GrExpression getLastPlugin(GroovyFile buildScript) { 27 | Iterator var2 = getMethodCalls(buildScript, "apply").iterator(); 28 | GrExpression expression = null; 29 | while (var2.hasNext()) { 30 | GrMethodCall methodCall = (GrMethodCall) var2.next(); 31 | expression = methodCall.getInvokedExpression(); 32 | } 33 | return expression; 34 | } 35 | 36 | public static Iterable getMethodCalls(@NotNull GrStatementOwner parent) { 37 | return Iterables.filter(Arrays.asList(parent.getStatements()), GrMethodCall.class); 38 | } 39 | 40 | public static Iterable getMethodCalls(@NotNull GrStatementOwner parent, @NotNull final String methodName) { 41 | return Iterables.filter(getMethodCalls(parent), new Predicate() { 42 | public boolean apply(@Nullable GrMethodCall input) { 43 | return input != null && methodName.equals(getMethodCallName(input)); 44 | } 45 | }); 46 | } 47 | 48 | public static String getMethodCallName(@NotNull GrMethodCall gmc) { 49 | GrExpression expression = gmc.getInvokedExpression(); 50 | return expression.getText() != null ? expression.getText() : ""; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/LogUtil.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import fastdex.idea.models.Constant; 4 | 5 | /** 6 | * Created by pengwei on 2016/11/2. 7 | */ 8 | public class LogUtil { 9 | public static void d(String info) { 10 | if (!Constant.DEBUG_MODE) { 11 | return; 12 | } 13 | System.out.println(info); 14 | } 15 | 16 | public static void d(String info, Object...args) { 17 | if (!Constant.DEBUG_MODE) { 18 | return; 19 | } 20 | System.out.println(String.format(info, args)); 21 | } 22 | 23 | public static void d(Object info) { 24 | if (!Constant.DEBUG_MODE) { 25 | return; 26 | } 27 | System.out.println(info); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import com.intellij.notification.Notification; 4 | import com.intellij.notification.NotificationGroup; 5 | import com.intellij.notification.NotificationType; 6 | import com.intellij.notification.Notifications; 7 | import com.intellij.openapi.application.ApplicationManager; 8 | import com.intellij.openapi.ui.Messages; 9 | import com.intellij.openapi.util.SystemInfo; 10 | 11 | public class NotificationUtils { 12 | 13 | private static final String TITLE = "Fastdex Plugin"; 14 | private static final NotificationGroup NOTIFICATION_GROUP = NotificationGroup.balloonGroup(TITLE); 15 | 16 | /** 17 | * show a Notification 18 | * 19 | * @param message 20 | * @param type 21 | */ 22 | public static void showNotification(final String message, 23 | final NotificationType type) { 24 | ApplicationManager.getApplication().invokeLater(new Runnable() { 25 | @Override 26 | public void run() { 27 | Notification notification = 28 | NOTIFICATION_GROUP.createNotification(TITLE, message, type, null); 29 | Notifications.Bus.notify(notification); 30 | } 31 | }); 32 | } 33 | 34 | /** 35 | * show a error Notification 36 | * 37 | * @param message 38 | */ 39 | public static void errorNotification(final String message) { 40 | showNotification(message, NotificationType.ERROR); 41 | } 42 | 43 | /** 44 | * error message dialog 45 | * @param message 46 | */ 47 | public static void errorMsgDialog(String message) { 48 | Messages.showMessageDialog(message, "Error", Messages.getInformationIcon()); 49 | } 50 | 51 | /** 52 | * show a info Notification 53 | * 54 | * @param message 55 | */ 56 | public static void infoNotification(final String message) { 57 | showNotification(message, NotificationType.INFORMATION); 58 | } 59 | 60 | /** 61 | * 提示 62 | */ 63 | public static void gradleWrapperNotFound() { 64 | String tip = null; 65 | if (SystemInfo.isWindows) { 66 | tip = "'gradlew.bat' not found. "; 67 | } 68 | else { 69 | tip = "'gradlew' not found. "; 70 | } 71 | errorNotification(tip); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/StreamUtil.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | /** 8 | * Created by pengwei on 16/9/11. 9 | */ 10 | public class StreamUtil { 11 | /** 12 | * InputStream 转 byte[] 13 | * @param inStream 14 | * @return 15 | * @throws IOException 16 | */ 17 | public static final byte[] inputStream2byte(InputStream inStream) throws IOException { 18 | ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); 19 | byte[] buff = new byte[100]; 20 | int rc = 0; 21 | while ((rc = inStream.read(buff, 0, 100)) > 0) { 22 | swapStream.write(buff, 0, rc); 23 | } 24 | byte[] in2b = swapStream.toByteArray(); 25 | return in2b; 26 | } 27 | 28 | /** 29 | * InputStream 转 string 30 | * @param inStream 31 | * @return 32 | * @throws IOException 33 | */ 34 | public static final String inputStream2String(InputStream inStream) throws IOException { 35 | return new String(inputStream2byte(inStream)); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.utils; 2 | 3 | import com.android.ddmlib.AndroidDebugBridge; 4 | import com.intellij.openapi.module.Module; 5 | import com.intellij.openapi.util.SystemInfo; 6 | import org.jetbrains.android.facet.AndroidFacet; 7 | import org.jetbrains.jps.android.model.impl.JpsAndroidModuleProperties; 8 | import java.awt.*; 9 | import java.awt.event.KeyEvent; 10 | import java.io.IOException; 11 | import java.lang.reflect.Field; 12 | import java.net.URI; 13 | import java.net.URISyntaxException; 14 | 15 | /** 16 | * Created by pengwei on 16/9/11. 17 | */ 18 | public final class Utils { 19 | 20 | public static final String BREAK_LINE = System.getProperty("line.separator"); 21 | 22 | public static boolean notEmpty(String text) { 23 | return (text != null && text.trim().length() != 0); 24 | } 25 | 26 | /** 27 | * 打开浏览器 28 | * 29 | * @param url 30 | */ 31 | public static void openUrl(String url) { 32 | if (SystemInfo.isWindows) { 33 | try { 34 | Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); 35 | } catch (IOException e) { 36 | e.printStackTrace(); 37 | } 38 | } else { 39 | try { 40 | URI uri = new URI(url); 41 | Desktop desktop = null; 42 | if (Desktop.isDesktopSupported()) { 43 | desktop = Desktop.getDesktop(); 44 | } 45 | if (desktop != null) 46 | desktop.browse(uri); 47 | } catch (IOException ioe) { 48 | ioe.printStackTrace(); 49 | } catch (URISyntaxException e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | } 54 | 55 | /** 56 | * 模拟键盘输入 57 | * @param r 58 | * @param key 59 | */ 60 | public static void keyPressWithCtrl(Robot r, int key) { 61 | if (r == null) { 62 | return; 63 | } 64 | r.keyPress(KeyEvent.VK_CONTROL); 65 | r.keyPress(key); 66 | r.keyRelease(key); 67 | r.keyRelease(KeyEvent.VK_CONTROL); 68 | r.delay(100); 69 | } 70 | 71 | public static String getBuildVariantName(Module module) { 72 | //see com.android.tools.idea.gradle.variant.view.BuildVariantView; line:232 73 | 74 | AndroidFacet androidFacet = AndroidFacet.getInstance(module); 75 | JpsAndroidModuleProperties facetProperties = androidFacet.getProperties(); 76 | String variantName = facetProperties.SELECTED_BUILD_VARIANT; 77 | return variantName; 78 | } 79 | 80 | public static boolean hasInitAndroidDebugBridge() { 81 | try { 82 | Field field = AndroidDebugBridge.class.getDeclaredField("sInitialized"); 83 | field.setAccessible(true); 84 | Boolean val = (Boolean) field.get(null); 85 | if (val != null && val == Boolean.TRUE) { 86 | return true; 87 | } 88 | } catch (Exception e) { 89 | 90 | } 91 | return false; 92 | } 93 | 94 | public static String formatPath(String path) { 95 | if (!SystemInfo.isWindows) { 96 | return path.replaceAll(" ","\\\\ "); 97 | } 98 | return path; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/views/CheckUpdateDialog.form: -------------------------------------------------------------------------------- 1 | 2 |

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
65 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/views/DeviceChooserDialog.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.views; 2 | 3 | import com.android.ddmlib.IDevice; 4 | import com.android.sdklib.IAndroidTarget; 5 | import com.android.tools.idea.run.DeviceChooser; 6 | import com.android.tools.idea.run.DeviceChooserListener; 7 | import com.google.common.base.Predicate; 8 | import com.intellij.openapi.ui.DialogWrapper; 9 | import com.intellij.openapi.util.Disposer; 10 | import org.jetbrains.android.facet.AndroidFacet; 11 | import org.jetbrains.android.util.AndroidBundle; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | import javax.swing.*; 15 | 16 | /** 17 | * Created by tong on 17/9/11. 18 | */ 19 | public class DeviceChooserDialog extends DialogWrapper { 20 | private final DeviceChooser myDeviceChooser; 21 | private DeviceChooserListener deviceChooserListener; 22 | private boolean closed; 23 | 24 | public DeviceChooserDialog(@NotNull AndroidFacet facet, 25 | @NotNull IAndroidTarget projectTarget, 26 | boolean multipleSelection, 27 | @Nullable String[] selectedSerials, 28 | @Nullable Predicate filter) { 29 | super(facet.getModule().getProject(), true); 30 | setTitle(AndroidBundle.message("choose.device.dialog.title")); 31 | 32 | getOKAction().setEnabled(false); 33 | 34 | myDeviceChooser = new DeviceChooser(multipleSelection, getOKAction(), facet, projectTarget, filter); 35 | Disposer.register(myDisposable, myDeviceChooser); 36 | myDeviceChooser.addListener(new DeviceChooserListener() { 37 | @Override 38 | public void selectedDevicesChanged() { 39 | updateOkButton(); 40 | 41 | if (deviceChooserListener != null) { 42 | deviceChooserListener.selectedDevicesChanged(); 43 | } 44 | } 45 | }); 46 | 47 | init(); 48 | myDeviceChooser.init(selectedSerials); 49 | } 50 | 51 | private void updateOkButton() { 52 | IDevice[] devices = getSelectedDevices(); 53 | boolean enabled = devices.length > 0; 54 | for (IDevice device : devices) { 55 | if (!device.isOnline()) { 56 | enabled = false; 57 | } 58 | } 59 | getOKAction().setEnabled(enabled); 60 | } 61 | 62 | @Override 63 | public JComponent getPreferredFocusedComponent() { 64 | return myDeviceChooser.getPreferredFocusComponent(); 65 | } 66 | 67 | @Override 68 | protected void doOKAction() { 69 | myDeviceChooser.finish(); 70 | super.doOKAction(); 71 | } 72 | 73 | @Override 74 | public void doCancelAction() { 75 | this.deviceChooserListener = null; 76 | 77 | closed = true; 78 | super.doCancelAction(); 79 | } 80 | 81 | @Override 82 | protected String getDimensionServiceKey() { 83 | return "AndroidDeviceChooserDialog"; 84 | } 85 | 86 | @Override 87 | protected JComponent createCenterPanel() { 88 | return myDeviceChooser.getPanel(); 89 | } 90 | 91 | public IDevice[] getSelectedDevices() { 92 | return myDeviceChooser.getSelectedDevices(); 93 | } 94 | 95 | public DeviceChooser getMyDeviceChooser() { 96 | return myDeviceChooser; 97 | } 98 | 99 | public void setDeviceChooserListener(DeviceChooserListener deviceChooserListener) { 100 | this.deviceChooserListener = deviceChooserListener; 101 | } 102 | 103 | public DeviceChooserListener getDeviceChooserListener() { 104 | return deviceChooserListener; 105 | } 106 | 107 | public boolean isClosed() { 108 | return closed; 109 | } 110 | } -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/views/FastdexToolWindowFactory.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.views; 2 | 3 | import com.intellij.openapi.project.DumbAware; 4 | import com.intellij.openapi.project.Project; 5 | import com.intellij.openapi.wm.ToolWindow; 6 | import com.intellij.openapi.wm.ToolWindowFactory; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Created by pengwei on 16/9/16. 11 | */ 12 | public class FastdexToolWindowFactory implements ToolWindowFactory, DumbAware { 13 | 14 | public static final String TOOL_WINDOW_ID = "Fastdex"; 15 | 16 | public FastdexToolWindowFactory() { 17 | } 18 | 19 | @Override 20 | public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { 21 | FastdexTerminal.getInstance(project).initTerminal(toolWindow); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/views/ImageJPanel.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.views; 2 | 3 | 4 | import javax.imageio.ImageIO; 5 | import javax.swing.*; 6 | import java.awt.*; 7 | import java.awt.image.BufferedImage; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | 11 | /** 12 | * Created by pengwei on 16/9/14. 13 | */ 14 | public class ImageJPanel extends JPanel { 15 | 16 | private BufferedImage image; 17 | 18 | public void setImagePath(String path) { 19 | try { 20 | InputStream is = this.getClass().getClassLoader().getResourceAsStream(path); 21 | if (is != null) { 22 | image = ImageIO.read(is); 23 | } 24 | } catch (IOException e) { 25 | 26 | } 27 | } 28 | 29 | @Override 30 | public void paint(Graphics g) { 31 | super.paint(g); 32 | g.drawImage(image, 0, 0, getWidth(), getHeight(), null, null); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/views/JTitle.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.views; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | /** 7 | * Created by pengwei on 16/9/14. 8 | */ 9 | public class JTitle extends JLabel { 10 | 11 | public JTitle(String text) { 12 | super(text); 13 | setFont(new Font("微软雅黑", Font.BOLD, 15)); 14 | setBorder(BorderFactory.createEmptyBorder(10,0,0,0)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/java/fastdex/idea/views/MulLabel.java: -------------------------------------------------------------------------------- 1 | package fastdex.idea.views; 2 | 3 | import javax.swing.*; 4 | 5 | /** 6 | * Created by pengwei on 16/9/14. 7 | */ 8 | public class MulLabel extends JLabel { 9 | 10 | public MulLabel() { 11 | this(""); 12 | } 13 | 14 | public MulLabel(String text) { 15 | this(text, false); 16 | } 17 | 18 | public MulLabel(String text, boolean html) { 19 | super(text); 20 | if (html) { 21 | setHtml(text); 22 | } 23 | setBorder(BorderFactory.createEmptyBorder(3,0,3,0)); 24 | } 25 | 26 | public void setHtml(String text) { 27 | setText("" + text + ""); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/bg_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/bg_update.png -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/debug.png -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/debug@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/debug@2x.png -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/gradlesync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/gradlesync.png -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/gradlesync@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/gradlesync@2x.png -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/icon.png -------------------------------------------------------------------------------- /fastdex-idea-plugin/src/main/resources/icons/icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/fastdex-idea-plugin/src/main/resources/icons/icon@2x.png -------------------------------------------------------------------------------- /fastdex-runtime/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /fastdex-runtime/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) 5 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 6 | 7 | defaultConfig { 8 | //minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) 9 | 10 | minSdkVersion 8 11 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) 12 | 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | lintOptions { 25 | abortOnError false 26 | } 27 | } 28 | 29 | dependencies { 30 | compile project(':fastdex-common') 31 | } 32 | 33 | //如果runtime代码有变化,生成fastdex-runtime.dex放进fastdex-gradle/src/main/resources/fastdex-runtime.dex 34 | project.afterEvaluate { 35 | android.libraryVariants.all { variant -> 36 | def variantName = variant.name.capitalize() 37 | if (!"Release".equals(variantName)) { 38 | return 39 | } 40 | Task generateDexTask = project.tasks.create("generateRuntimeDexFor${variantName}") 41 | generateDexTask.group = 'fastdex' 42 | generateDexTask.dependsOn variant.javaCompile 43 | generateDexTask.doLast { 44 | File inputDirectory = project.file("build/intermediates/classes/release") 45 | new File(inputDirectory,"com").deleteDir() 46 | project.copy { 47 | from project.rootProject.file("fastdex-common/build/classes/main/fastdex") 48 | into project.file("build/intermediates/classes/release/fastdex") 49 | } 50 | 51 | String sdkDirectory = project.android.getSdkDirectory() 52 | if (sdkDirectory.contains("\\")) { 53 | sdkDirectory = sdkDirectory.replace("\\", "/"); 54 | } 55 | String dxcmd = "" 56 | File dx = new File(sdkDirectory,"build-tools${File.separator}${project.android.getBuildToolsVersion()}${File.separator}dx") 57 | dxcmd = dx.absolutePath 58 | if (org.apache.tools.ant.taskdefs.condition.Os.isFamily(org.apache.tools.ant.taskdefs.condition.Os.FAMILY_WINDOWS)) { 59 | dxcmd = "${dxcmd}.bat" 60 | } 61 | project.file("build/outputs/fastdex/${variantName.toLowerCase()}").mkdirs() 62 | File outDex = project.file("build/outputs/fastdex/${variantName.toLowerCase()}/fastdex-runtime.dex") 63 | dxcmd = "${dxcmd} --dex --output=${outDex} ${inputDirectory}" 64 | 65 | println("==dxcmd: \n${dxcmd}") 66 | def process = dxcmd.execute() 67 | int status = process.waitFor() 68 | process.destroy() 69 | if (status != 0) { 70 | throw new GradleException("generate fastdex runtime dex fail!") 71 | } 72 | 73 | project.copy { 74 | from outDex 75 | into project.rootProject.file('fastdex-gradle/src/main/resources') 76 | } 77 | 78 | project.file("build/intermediates/classes/release").deleteDir() 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /fastdex-runtime/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/tong/Applications/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/AntilazyLoad.java: -------------------------------------------------------------------------------- 1 | package fastdex.runtime; 2 | 3 | /** 4 | * Created by tong on 17/3/15. 5 | */ 6 | public class AntilazyLoad { 7 | public static String str = ""; 8 | } -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/Constants.java: -------------------------------------------------------------------------------- 1 | package fastdex.runtime; 2 | 3 | import fastdex.common.ShareConstants; 4 | 5 | /** 6 | * Created by tong on 17/4/28. 7 | */ 8 | public interface Constants extends ShareConstants { 9 | String FASTDEX_DIR = "fastdex"; 10 | String PATCH_DIR = "patch"; 11 | String TEMP_DIR = "temp"; 12 | String DEX_DIR = "dex"; 13 | String OPT_DIR = "opt"; 14 | String RES_DIR = "res"; 15 | } 16 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/FastdexReceiver.java: -------------------------------------------------------------------------------- 1 | package fastdex.runtime; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.text.TextUtils; 7 | import fastdex.runtime.fd.Restarter; 8 | 9 | /** 10 | * Created by tong on 17/10/14. 11 | */ 12 | public class FastdexReceiver extends BroadcastReceiver { 13 | public static final String FASTDEX_RECEIVER_ACTION = "android.intent.action.FastdexReceiver"; 14 | 15 | private static final String EXTAR_CMD = "EXTAR_CMD"; 16 | private static final String EXTAR_TEXT = "EXTAR_TEXT"; 17 | private static final String EXTAR_UPDATE_MODE = "EXTAR_UPDATE_MODE"; 18 | private static final String EXTAR_HASDEX = "EXTAR_HASDEX"; 19 | private static final String EXTAR_HAS_RESOURCES = "EXTAR_HAS_RESOURCES"; 20 | private static final String EXTAR_PREPARED_PATCH_PATH = "EXTAR_PREPARED_PATCH_PATH"; 21 | private static final String EXTAR_RESOURCES_APKP_ATH = "EXTAR_RESOURCES_APKP_ATH"; 22 | private static final String EXTAR_TOAST = "EXTAR_TOAST"; 23 | private static final String EXTAR_RESTART_APP_BY_CMD = "EXTAR_RESTART_APP_BY_CMD"; 24 | 25 | private static final int CMD_SHOW_TOAST = 1; 26 | private static final int CMD_SHOW_RESTART = 2; 27 | 28 | @Override 29 | public void onReceive(Context context, Intent intent) { 30 | int cmd = intent.getIntExtra(EXTAR_CMD,0); 31 | if (cmd == CMD_SHOW_TOAST) { 32 | String text = intent.getStringExtra(EXTAR_TEXT); 33 | if (!TextUtils.isEmpty(text)) { 34 | Restarter.showToast(text); 35 | } 36 | } 37 | else if (cmd == CMD_SHOW_RESTART) { 38 | int updateMode = intent.getIntExtra(EXTAR_UPDATE_MODE,0); 39 | boolean hasDex = intent.getBooleanExtra(EXTAR_HASDEX,false); 40 | boolean hasResources = intent.getBooleanExtra(EXTAR_HAS_RESOURCES,false); 41 | String preparedPatchPath = intent.getStringExtra(EXTAR_PREPARED_PATCH_PATH); 42 | String resourcesApkPath = intent.getStringExtra(EXTAR_RESOURCES_APKP_ATH); 43 | boolean toast = intent.getBooleanExtra(EXTAR_TOAST,false); 44 | boolean restartAppByCmd = intent.getBooleanExtra(EXTAR_RESTART_APP_BY_CMD,false); 45 | Restarter.restart(updateMode,hasDex,hasResources,preparedPatchPath,resourcesApkPath,toast,restartAppByCmd); 46 | } 47 | } 48 | 49 | public static void showToast(String text) { 50 | Intent intent = new Intent(); 51 | intent.setAction(FASTDEX_RECEIVER_ACTION); 52 | intent.addCategory(Fastdex.get().getApplicationContext().getPackageName()); 53 | intent.putExtra(EXTAR_CMD,CMD_SHOW_TOAST); 54 | intent.putExtra(EXTAR_TEXT,text); 55 | Fastdex.get().getApplicationContext().sendBroadcast(intent); 56 | } 57 | 58 | public static void restart(int updateMode, boolean hasDex, boolean hasResources, String preparedPatchPath, String resourcesApkPath, boolean toast, boolean restartAppByCmd) { 59 | Intent intent = new Intent(); 60 | intent.setAction(FASTDEX_RECEIVER_ACTION); 61 | intent.addCategory(Fastdex.get().getApplicationContext().getPackageName()); 62 | intent.putExtra(EXTAR_CMD,CMD_SHOW_RESTART); 63 | intent.putExtra(EXTAR_UPDATE_MODE,updateMode); 64 | intent.putExtra(EXTAR_HASDEX,hasDex); 65 | intent.putExtra(EXTAR_HAS_RESOURCES,hasResources); 66 | intent.putExtra(EXTAR_PREPARED_PATCH_PATH,preparedPatchPath); 67 | intent.putExtra(EXTAR_RESOURCES_APKP_ATH,resourcesApkPath); 68 | intent.putExtra(EXTAR_TOAST,toast); 69 | intent.putExtra(EXTAR_RESTART_APP_BY_CMD,restartAppByCmd); 70 | Fastdex.get().getApplicationContext().sendBroadcast(intent); 71 | } 72 | } -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/FastdexService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.runtime; 18 | 19 | import android.app.Service; 20 | import android.content.Intent; 21 | import android.os.IBinder; 22 | import android.util.Log; 23 | 24 | import fastdex.runtime.fd.Logging; 25 | import fastdex.runtime.fd.Server; 26 | 27 | /** 28 | * Service which starts the Instant Run server; started by the IDE via 29 | * adb shell am startservice pkg/service 30 | */ 31 | public class FastdexService extends Service { 32 | 33 | private Server server; 34 | 35 | @Override 36 | public int onStartCommand(Intent intent, int flags, int startId) { 37 | return START_STICKY; 38 | } 39 | 40 | @Override 41 | public IBinder onBind(Intent intent) { 42 | // Don't allow anyone to bind to this service. 43 | return null; 44 | } 45 | 46 | @Override 47 | public void onCreate() { 48 | super.onCreate(); 49 | Log.d(Logging.LOG_TAG, "Starting Fastdex Server for " + getPackageName()); 50 | server = Server.create(this); 51 | } 52 | 53 | @Override 54 | public void onDestroy() { 55 | if (server != null) { 56 | Log.d(Logging.LOG_TAG, "Stopping Fastdex Server for " + getPackageName()); 57 | server.shutdown(); 58 | } 59 | super.onDestroy(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/FastdexUncaughtExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package fastdex.runtime; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import fastdex.runtime.fd.Logging; 6 | 7 | /** 8 | * Created by tong on 17/9/6. 9 | */ 10 | public class FastdexUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { 11 | private Context applicationContext; 12 | private final Thread.UncaughtExceptionHandler ueh; 13 | 14 | public FastdexUncaughtExceptionHandler(Context applicationContext) { 15 | ueh = Thread.getDefaultUncaughtExceptionHandler(); 16 | this.applicationContext = applicationContext; 17 | } 18 | 19 | @Override 20 | public void uncaughtException(Thread thread, Throwable ex) { 21 | if (applicationContext != null) { 22 | Fastdex fastdex = Fastdex.get(); 23 | if (fastdex != null) { 24 | RuntimeMetaInfo runtimeMetaInfo = fastdex.readRuntimeMetaInfoFromFile(); 25 | if (runtimeMetaInfo != null && (runtimeMetaInfo.getPatchDexVersion() > 0 || runtimeMetaInfo.getMergedDexVersion() > 0)) { 26 | Throwable root = getRootThrowable(ex); 27 | if (root instanceof IncompatibleClassChangeError) { 28 | //重新触发安装 29 | runtimeMetaInfo.setBuildMillis(-1); 30 | Fastdex.get().saveRuntimeMetaInfo(runtimeMetaInfo); 31 | 32 | String msg = "修改了抽象类导致的错误,Fastdex已经为你自动恢复状态,重新执行一次fastdex" + runtimeMetaInfo.getVariantName() + "任务就可以解决这个问题了"; 33 | ueh.uncaughtException(thread, new RuntimeException(msg,ex)); 34 | Log.e(Logging.LOG_TAG,msg); 35 | return; 36 | } 37 | } 38 | } 39 | } 40 | 41 | ueh.uncaughtException(thread, ex); 42 | } 43 | 44 | Throwable getRootThrowable(Throwable throwable) { 45 | if (throwable == null) { 46 | return null; 47 | } 48 | Throwable cause = throwable.getCause(); 49 | if (cause == null) { 50 | return throwable; 51 | } 52 | if (cause == throwable) { 53 | return throwable; 54 | } 55 | return getRootThrowable(throwable.getCause()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/MiddlewareActivity.java: -------------------------------------------------------------------------------- 1 | package fastdex.runtime; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.os.Process; 8 | import android.os.SystemClock; 9 | import android.util.Log; 10 | import android.widget.TextView; 11 | 12 | public class MiddlewareActivity extends Activity { 13 | private static final String TAG = "Fastdex.MiddlewareAct"; 14 | private static final Handler HANDLER = new Handler(Looper.getMainLooper()); 15 | private static final long RESET_WAIT = 1000L; 16 | private long createTime; 17 | private boolean ready; 18 | private int back; 19 | 20 | private final Runnable reset = new Runnable() { 21 | public void run() { 22 | Log.d(TAG, "kill process: " + Process.myPid()); 23 | Process.killProcess(Process.myPid()); 24 | } 25 | }; 26 | 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | TextView tv = new TextView(this); 30 | tv.setGravity(17); 31 | tv.setText("restart app..."); 32 | setContentView(tv); 33 | this.createTime = SystemClock.uptimeMillis(); 34 | 35 | this.ready = getIntent().getBooleanExtra("reset", false); 36 | if (this.ready) { 37 | reset(); 38 | } 39 | } 40 | 41 | protected void onDestroy() { 42 | HANDLER.removeCallbacks(this.reset); 43 | super.onDestroy(); 44 | } 45 | 46 | public void reset() { 47 | this.ready = true; 48 | HANDLER.removeCallbacks(this.reset); 49 | long d = SystemClock.uptimeMillis() - this.createTime; 50 | if (d > RESET_WAIT) { 51 | HANDLER.postDelayed(this.reset, 100L); 52 | } else { 53 | HANDLER.postDelayed(this.reset, RESET_WAIT - d); 54 | } 55 | } 56 | 57 | public void onBackPressed() { 58 | if (this.back++ > 0) { 59 | if (this.ready) { 60 | this.reset.run(); 61 | } 62 | super.onBackPressed(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/fd/ApplicationPatch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package fastdex.runtime.fd; 17 | 18 | import java.io.DataInputStream; 19 | import java.io.IOException; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import android.util.Log; 23 | 24 | // This class is used in both the Android runtime and in the IDE. 25 | // Technically we only need the write protocol on the IDE side and the 26 | // read protocol on the Android app size, but keeping it all together and 27 | // in sync right now. 28 | public class ApplicationPatch { 29 | public final String path; 30 | public final byte[] data; 31 | 32 | public ApplicationPatch(String path, byte[] data) { 33 | this.path = path; 34 | this.data = data; 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return "ApplicationPatch{" + 40 | "path='" + path + '\'' + 41 | ", data.length='" + data.length + '\'' + 42 | '}'; 43 | } 44 | 45 | // Only needed on the Android side 46 | public static List read(DataInputStream input) throws IOException { 47 | int changeCount = input.readInt(); 48 | 49 | Log.d(Logging.LOG_TAG, "Receiving " + changeCount + " changes"); 50 | List changes = new ArrayList(changeCount); 51 | for (int i = 0; i < changeCount; i++) { 52 | String path = input.readUTF(); 53 | int size = input.readInt(); 54 | byte[] bytes = new byte[size]; 55 | input.readFully(bytes); 56 | changes.add(new ApplicationPatch(path, bytes)); 57 | 58 | Log.d(Logging.LOG_TAG, "Receiving path: " + path); 59 | } 60 | 61 | return changes; 62 | } 63 | 64 | 65 | public String getPath() { 66 | return path; 67 | } 68 | 69 | 70 | public byte[] getBytes() { 71 | return data; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/fd/Logging.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.runtime.fd; 18 | 19 | /** 20 | * Instant Run runtime logging related code 21 | */ 22 | public class Logging { 23 | 24 | /** Log tag used by instant run runtime */ 25 | public static final String LOG_TAG = "Fastdex"; 26 | } 27 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/utils/ShareFileLockHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Tinker available. 3 | * 4 | * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in 7 | * compliance with the License. You may obtain a copy of the License at 8 | * 9 | * https://opensource.org/licenses/BSD-3-Clause 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 12 | * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 13 | * either express or implied. See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.runtime.utils; 18 | 19 | import android.util.Log; 20 | import java.io.Closeable; 21 | import java.io.File; 22 | import java.io.FileOutputStream; 23 | import java.io.IOException; 24 | import java.nio.channels.FileLock; 25 | 26 | /** 27 | * Created by zhangshaowen on 16/6/3. 28 | */ 29 | public class ShareFileLockHelper implements Closeable { 30 | public static final int MAX_LOCK_ATTEMPTS = 3; 31 | public static final int LOCK_WAIT_EACH_TIME = 10; 32 | private static final String TAG = "Fastdex.FileLockHelper"; 33 | private final FileOutputStream outputStream; 34 | private final FileLock fileLock; 35 | 36 | private ShareFileLockHelper(File lockFile) throws IOException { 37 | outputStream = new FileOutputStream(lockFile); 38 | 39 | int numAttempts = 0; 40 | boolean isGetLockSuccess; 41 | FileLock localFileLock = null; 42 | //just wait twice, 43 | Exception saveException = null; 44 | while (numAttempts < MAX_LOCK_ATTEMPTS) { 45 | numAttempts++; 46 | try { 47 | localFileLock = outputStream.getChannel().lock(); 48 | isGetLockSuccess = (localFileLock != null); 49 | if (isGetLockSuccess) { 50 | break; 51 | } 52 | //it can just sleep 0, afraid of cpu scheduling 53 | Thread.sleep(LOCK_WAIT_EACH_TIME); 54 | 55 | } catch (Exception e) { 56 | // e.printStackTrace(); 57 | saveException = e; 58 | Log.e(TAG, "getInfoLock Thread failed time:" + LOCK_WAIT_EACH_TIME); 59 | } 60 | } 61 | 62 | if (localFileLock == null) { 63 | throw new IOException("Fastdex Exception:FileLockHelper lock file failed: " + lockFile.getAbsolutePath(), saveException); 64 | } 65 | fileLock = localFileLock; 66 | } 67 | 68 | public static ShareFileLockHelper getFileLock(File lockFile) throws IOException { 69 | return new ShareFileLockHelper(lockFile); 70 | } 71 | 72 | @Override 73 | public void close() throws IOException { 74 | try { 75 | if (fileLock != null) { 76 | fileLock.release(); 77 | } 78 | } finally { 79 | if (outputStream != null) { 80 | outputStream.close(); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/utils/SharePatchFileUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making Tinker available. 3 | * 4 | * Copyright (C) 2016 THL A29 Limited, a Tencent company. All rights reserved. 5 | * 6 | * Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in 7 | * compliance with the License. You may obtain a copy of the License at 8 | * 9 | * https://opensource.org/licenses/BSD-3-Clause 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is 12 | * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 13 | * either express or implied. See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package fastdex.runtime.utils; 18 | 19 | import android.content.Context; 20 | import android.content.pm.ApplicationInfo; 21 | import java.io.File; 22 | import fastdex.common.ShareConstants; 23 | import fastdex.runtime.Constants; 24 | 25 | public class SharePatchFileUtil { 26 | /** 27 | * data dir, such as /data/data/com.github.typ0520.fastdex.sample/fastdex 28 | * @param context 29 | * @return 30 | */ 31 | public static File getFastdexDirectory(Context context) { 32 | ApplicationInfo applicationInfo = context.getApplicationInfo(); 33 | if (applicationInfo == null) { 34 | // Looks like running on a test Context, so just return without patching. 35 | return null; 36 | } 37 | 38 | return new File(applicationInfo.dataDir, Constants.FASTDEX_DIR); 39 | } 40 | 41 | /** 42 | * 获取上一次源码发生变化的日期 43 | * @param context 44 | * @return 45 | */ 46 | public static long getLastSourceModified(Context context) { 47 | ApplicationInfo applicationInfo = context.getApplicationInfo(); 48 | if (applicationInfo == null) { 49 | return 0; 50 | } 51 | 52 | return new File(applicationInfo.sourceDir).lastModified(); 53 | } 54 | 55 | 56 | /** 57 | * data dir, such as /data/data/com.github.typ0520.fastdex.sample/fastdex/patch 58 | * @param context 59 | * @return 60 | */ 61 | public static File getPatchDirectory(Context context) { 62 | return new File(getFastdexDirectory(context), Constants.PATCH_DIR); 63 | } 64 | 65 | public static File getPatchTempDirectory(Context context) { 66 | return new File(getFastdexDirectory(context), Constants.TEMP_DIR); 67 | } 68 | 69 | /** 70 | * change the jar file path as the makeDexElements do 71 | * 72 | * @param path 73 | * @param optimizedDirectory 74 | * @return 75 | */ 76 | public static String optimizedPathFor(File path, File optimizedDirectory) { 77 | String fileName = path.getName(); 78 | if (!fileName.endsWith(ShareConstants.DEX_SUFFIX)) { 79 | int lastDot = fileName.lastIndexOf("."); 80 | if (lastDot < 0) { 81 | fileName += ShareConstants.DEX_SUFFIX; 82 | } else { 83 | StringBuilder sb = new StringBuilder(lastDot + 4); 84 | sb.append(fileName, 0, lastDot); 85 | sb.append(ShareConstants.DEX_SUFFIX); 86 | fileName = sb.toString(); 87 | } 88 | } 89 | 90 | File result = new File(optimizedDirectory, fileName); 91 | return result.getPath(); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /fastdex-runtime/src/main/java/fastdex/runtime/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package fastdex.runtime.utils; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.util.Log; 6 | import java.io.FileInputStream; 7 | import fastdex.runtime.fd.Logging; 8 | 9 | /** 10 | * Created by tong on 17/10/14. 11 | */ 12 | public class Utils { 13 | private static String processName = null; 14 | 15 | public static boolean isMainProcess(Context context) { 16 | String pkgName = context.getPackageName(); 17 | String processName = getProcessName(context); 18 | if (processName == null || processName.length() == 0) { 19 | processName = ""; 20 | } 21 | return pkgName.equals(processName); 22 | } 23 | 24 | /** 25 | * add process name cache 26 | * 27 | * @param context 28 | * @return 29 | */ 30 | public static String getProcessName(final Context context) { 31 | if (processName != null) { 32 | return processName; 33 | } 34 | //will not null 35 | processName = getProcessNameInternal(context); 36 | return processName; 37 | } 38 | 39 | private static String getProcessNameInternal(final Context context) { 40 | int myPid = android.os.Process.myPid(); 41 | 42 | if (context == null || myPid <= 0) { 43 | return ""; 44 | } 45 | 46 | ActivityManager.RunningAppProcessInfo myProcess = null; 47 | ActivityManager activityManager = 48 | (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 49 | 50 | try { 51 | for (ActivityManager.RunningAppProcessInfo process : activityManager.getRunningAppProcesses()) { 52 | if (process.pid == myPid) { 53 | myProcess = process; 54 | break; 55 | } 56 | } 57 | } catch (Exception e) { 58 | Log.e(Logging.LOG_TAG, "getProcessNameInternal exception:" + e.getMessage()); 59 | } 60 | 61 | if (myProcess != null) { 62 | return myProcess.processName; 63 | } 64 | 65 | byte[] b = new byte[128]; 66 | FileInputStream in = null; 67 | try { 68 | in = new FileInputStream("/proc/" + myPid + "/cmdline"); 69 | int len = in.read(b); 70 | if (len > 0) { 71 | for (int i = 0; i < len; i++) { // lots of '0' in tail , remove them 72 | if (b[i] > 128 || b[i] <= 0) { 73 | len = i; 74 | break; 75 | } 76 | } 77 | return new String(b, 0, len); 78 | } 79 | 80 | } catch (Exception e) { 81 | e.printStackTrace(); 82 | } finally { 83 | try { 84 | if (in != null) { 85 | in.close(); 86 | } 87 | } catch (Exception e) { 88 | } 89 | } 90 | 91 | return ""; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | org.gradle.daemon=true 21 | 22 | groupId=com.github.typ0520 23 | version=0.8.8 24 | 25 | ANDROID_BUILD_MIN_SDK_VERSION=14 26 | ANDROID_BUILD_TARGET_SDK_VERSION=22 27 | ANDROID_BUILD_SDK_VERSION=23 28 | ANDROID_BUILD_TOOLS_VERSION=24.0.0 -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri May 06 15:07:13 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf ~/.m2/repository/com/dx168/fastdex 4 | 5 | sh gradlew :runtime:generateRuntimeDexForRelease 6 | sh gradlew install -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /build 3 | # Ignore Gradle GUI config 4 | gradle-app.setting 5 | 6 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 7 | !gradle-wrapper.jar 8 | 9 | # Cache of project 10 | .gradletasknamecache 11 | 12 | .DS_Store 13 | node_modules 14 | 15 | # Built application files 16 | *.apk 17 | *.ap_ 18 | 19 | # Java class files 20 | *.class 21 | 22 | # Generated files 23 | bin/ 24 | gen/ 25 | 26 | # Gradle files 27 | .gradle/ 28 | *.iml 29 | /*/*.iml 30 | .idea 31 | /*/.idea/ 32 | 33 | # Local configuration file (sdk path, etc) 34 | local.properties 35 | 36 | # Proguard folder generated by Eclipse 37 | proguard/ 38 | 39 | # Log Files 40 | *.log 41 | 42 | /*/.idea 43 | .idea 44 | 45 | app/build/ 46 | buildSrc/build/ 47 | 48 | build/ 49 | 50 | 51 | common/build/ 52 | common-group/common2/build/ 53 | common-group/common3/build/ 54 | javalib/build/ 55 | javalib-group/javalib2/build/ 56 | javalib-group/javalib3/build/ 57 | common4 58 | 59 | output.txt -------------------------------------------------------------------------------- /sample/app/libs/fm-sdk-2.1.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typ0520/fastdex/91c1c4e82c983d03c33f271ce4a110ea74c44a91/sample/app/libs/fm-sdk-2.1.2.jar -------------------------------------------------------------------------------- /sample/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/galen/Tools/android-sdk-macosx/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -renamesourcefileattribute SourceFile 20 | -keepattributes SourceFile,LineNumberTable -------------------------------------------------------------------------------- /sample/app/src/debug/java/fastdex/sample/Debug.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | /** 4 | * Created by tong on 17/8/25. 5 | */ 6 | public class Debug { 7 | } 8 | -------------------------------------------------------------------------------- /sample/app/src/flavor1/java/fastdex/sample/Flavor1.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | /** 4 | * Created by tong on 17/8/25. 5 | */ 6 | public class Flavor1 { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /sample/app/src/flavor1debug/java/fastdex/sample/Flavor1Debug.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | /** 4 | * Created by tong on 17/8/25. 5 | */ 6 | public class Flavor1Debug { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /sample/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /sample/app/src/main/assets/a.txt: -------------------------------------------------------------------------------- 1 | wwwwwww -------------------------------------------------------------------------------- /sample/app/src/main/assets/font/cn/haha.txt: -------------------------------------------------------------------------------- 1 | haha -------------------------------------------------------------------------------- /sample/app/src/main/assets/font/xx.txt: -------------------------------------------------------------------------------- 1 | xxx 2 | -------------------------------------------------------------------------------- /sample/app/src/main/assets/html/index.html: -------------------------------------------------------------------------------- 1 | index.html -------------------------------------------------------------------------------- /sample/app/src/main/java/fastdex/sample/CustomView.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.RelativeLayout; 6 | import android.widget.TextView; 7 | import com.github.typ0520.fastdex.sample.R; 8 | import butterknife.BindView; 9 | import butterknife.ButterKnife; 10 | 11 | /** 12 | * Created by tong on 17/3/12. 13 | */ 14 | public class CustomView extends RelativeLayout { 15 | @BindView(R.id.tv) TextView tv; 16 | 17 | 18 | public CustomView(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | inflate(context, R.layout.view_custom,this); 21 | ButterKnife.bind(this); 22 | 23 | tv.setText(getResources().getString(R.string.s3) + " -00"); 24 | MainActivity.aa(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/app/src/main/java/fastdex/sample/ImplementTest.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | /** 4 | * Created by tong on 17/11/14. 5 | */ 6 | public class ImplementTest extends ParentClass implements InterfaceTest { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /sample/app/src/main/java/fastdex/sample/InterfaceTest.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | /** 4 | * Created by tong on 17/11/14. 5 | */ 6 | public interface InterfaceTest { 7 | default String test() { 8 | System.out.println("默认方法"); 9 | return "默认方法"; 10 | } 11 | 12 | static String staticMethod() { 13 | System.out.println("静态方法"); 14 | return "静态方法"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sample/app/src/main/java/fastdex/sample/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | import com.github.typ0520.fastdex.sample.R; 7 | 8 | public class LoginActivity extends Activity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_login); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sample/app/src/main/java/fastdex/sample/ParentClass.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | /** 4 | * Created by tong on 17/11/14. 5 | */ 6 | public class ParentClass { 7 | } 8 | -------------------------------------------------------------------------------- /sample/app/src/main/java/fastdex/sample/SampleApplication.java: -------------------------------------------------------------------------------- 1 | package fastdex.sample; 2 | 3 | import android.content.Context; 4 | import android.support.multidex.MultiDex; 5 | 6 | /** 7 | * Created by tong on 17/10/3. 8 | */ 9 | public class SampleApplication extends android.app.Application { 10 | 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | } 15 | 16 | @Override 17 | protected void attachBaseContext(Context base) { 18 | super.attachBaseContext(base); 19 | MultiDex.install(base); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sample/app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /sample/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 18 | 19 | 20 |