Android開發日常之gradle

學無止境,有一技旁身,至少不至于孤陋寡聞。
隨著我們工作閱歷的提升,除了基本的業務開發,也需要提升我們自身在開發效能方面的一些儲備與筆記。

Android開發日常之gradle
Android開發日常之adb
Android開發日常之git
Android開發日常之shell alias

在Android開發中在項目根目錄、app或者其他module中我們都能看到build.gradle 這個文件,我們也知道Android項目的編譯和運行都是通過gradle來實現,需要引入第三方庫時就找到對應項目的build.gradle 中按照集成文檔修修改改引入即可,有可能也不會過多的去關注,這里簡單介紹下關于在Android開發中gradle的使用。

  1. 調整gradle的編譯參數(gradle.properties文件中配置)
    內存配置:

    org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

    • 堆的內存分配用-Xms和-Xmx
      1. Xms分配堆最小內存,默認為物理內存的1/64;
      2. Xmx分配最大內存,默認為物理內存的1/4。
    • 非堆內存分配用-XX:PermSize和-XX:MaxPermSize
      1. XX:PermSize分配非堆最小內存,默認為物理內存的1/64;
      2. XX:MaxPermSize分配最大內存,默認為物理內存的1/4。

    守護進程
    org.gradle.daemon=true
    并行編譯
    org.gradle.parallel=true
    開啟緩存
    android.enableBuildCache=true
    開啟孵化模式
    org.gradle.configureondemand=true
    更多配置信息,移步Gradle官方文檔

  2. 推薦引用固定版本依賴庫

     dependencies { 
       compile 'com.google.code.gson:gson:2.2.1' //推薦寫法
     // compile 'com.google.code.gson:gson:2.+' // 不推薦寫法
    }
    

    雖然帶+號的引用可以保證庫是最新的,但更新的新庫有沒有更改庫邏輯而帶來的bug是未知的。

  3. module、jar及aar的依賴方式

     implementation project(':commonlibrary')
     implementation files('libs/alipaysdk.jar')
     implementation(name: 'xpay-1.0.1', ext: 'aar')
    
    • module生成jar
      def ccLibName = 'cc-1.0.1.jar'
      task makeJar(type: Copy) {
          //刪除存在的
          delete 'build/libs/'+ccLibName
          //設置拷貝的文件
          from('build/intermediates/packaged-classes/release/')
          //打進jar包后的文件目錄
          into('build/libs/')
          //將classes.jar放入build/libs/目錄下
          //include ,exclude參數來設置過濾
          include('classes.jar')
          rename ('classes.jar',ccLibName)
      }
      makeJar.dependsOn(build)
      
    • module生成aar(默認編譯會生成aar,此處更改aar生成名稱)
      buildTypes {
       release {
           ...
           ...
           android.libraryVariants.all { variant ->
               if(variant.name.equalsIgnoreCase("release")) {
                   variant.outputs.all { output ->
                       def f = output.outputFileName
                       if (f != null && f.endsWith('.aar')) {
                           def fileName = "cc-v${defaultConfig.versionName}.aar"
                           output.outputFileName = fileName
                       }
                   }
               }
           }
       }
      }
      
  4. 用exclude關鍵字解決依賴庫沖突問題
    如我們在Android項目中集成某個第三方庫時,可能第三方庫也會引入某個本地已經引入的庫,所以就會存在引入多版本的依賴庫。

    • 移除整個組織的庫
      compile ('com.facebook.fresco:animated-webp:0.13.0') { 
             exclude group: 'com.android.support' // 僅僅寫組織名稱
      }
      
    • 精確移除某個指定庫
      compile('com.android.support:appcompat-v7:23.2.0') {
            exclude group: 'com.android.support', module: 'support-annotations' // 寫全稱
            exclude group: 'com.android.support', module: 'support-compat'
            exclude group: 'com.android.support', module: 'support-v4'
            exclude group: 'com.android.support', module: 'support-vector-drawable'
      }
      

    如何產看項目中的第三方庫的依賴關系?
    執行以下命令行:(如何你想查看某個module的依賴關系,此處的app使用你的module名來替換即可

    • Windows:
      gradlew :app:dependencies > dependencis.txt
    • MAC:
      ./gradlew :app:dependencies > dependencis.txt
  5. 設置java版本

    • 如果是在某個module中設置,那么就在對應module的build.gradle文件中配置:
      android {
         compileOptions {
             sourceCompatibility JavaVersion.VERSION_1_8
             targetCompatibility JavaVersion.VERSION_1_8
         }
      }
      
    • 如果想要做全局配置,那么就在根目錄的build.gradle中配置:
      allprojects {
         repositories {
            jcenter()
         }
        tasks.withType(JavaCompile) {
           sourceCompatibility = JavaVersion.VERSION_1_8
           targetCompatibility = JavaVersion.VERSION_1_8
        }
      }
      
  6. 在build.gradle中設置productFlavors

    android {  
          productFlavors {
              baidu {
                  manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
              }  
              xiaomi {
                  manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
              }
          }  
      }
    
  7. assemble是Gradle中的編譯打包命令

    • 打包baidu渠道的release版本
      gradlew assembleBaiduRelease
    • 打包baidu渠道的debug版本
      gradlew assembleBaiduDebug
    • 生成baidu渠道的Release和Debug版本
      gradlew assembleBaidu
    • 打包全部渠道Release版本
      gradlew assembleRelease
  8. 為各渠道設置各自的配置信息

    android {  
           productFlavors {
               baidu {
                   applicationId "com.liujc.demo.baidu"
                   manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu",
                                          APP_NAME :  "app應用01",  //app名稱
                                          ]
                   buildConfigField "String", "API_APP_ID", "\"10001\""
               }  
               xiaomi {
                   applicationId "com.liujc.demo.xiaomi"
                   manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi",
                                          APP_NAME :  "app應用02",  //app名稱
                                          ]
                   buildConfigField "String", "API_APP_ID", "\"10002\""
               }
           }  
       }
    

    如何使用?
    如上的applicationIdmanifestPlaceholders 中信息在AndroidManifest中使用如下:

      android:label="${APP_NAME}"
      <uses-permission android:name="${applicationId}.permission.JPUSH_MESSAGE" />
    

    buildConfigField 中的參數在代碼中使用BuildConfig.API_APP_ID

  9. Android Studio 為多渠道執行自定義名稱打包命令:

    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                applicationVariants.all { variant ->
                    variant.outputs.each { output ->
                        def outputFile = output.outputFile
                        if (outputFile != null && outputFile.name.endsWith('.apk')) {
                            // 計劃輸出apk名稱為App_V1.0.0_baidu.apk
                            def fileName = "App_V${variant.productFlavors.versionName}_${variant.productFlavors.name}.apk"
                            output.outputFile = new File(outputFile.parent, fileName)
                        }
                    }
                }
    

    下面是多渠道配置:

    android {  
        productFlavors {
            baidu {
                applicationId "com.liujc.demo.baidu"
                versionCode 1
                versionName "1.0.0"
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu",
                                       APP_NAME :  "app應用01",  //app名稱
                                       ]
                buildConfigField "String", "API_APP_ID", "\"10001\""
            }  
            xiaomi {
                versionCode 2
                versionName "1.0.1"
                applicationId "com.liujc.demo.xiaomi"
                manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi",
                                       APP_NAME :  "app應用02",  //app名稱
                                       ]
                buildConfigField "String", "API_APP_ID", "\"10002\""
            }
        }  
    }
    

    打包成功后你會發現包名為:App_V[1.0.0]_[baidu].apk ,這不是我想要的啊,怎么都多了個中括號呢,修改打包代碼:

     proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                 applicationVariants.all { variant ->
                     variant.outputs.each { output ->
                         def outputFile = output.outputFile
                         if (outputFile != null && outputFile.name.endsWith('.apk')) {
                             // 計劃輸出apk名稱為App_V1.0.0_baidu.apk
                             def fileName = "App_V${variant.productFlavors[0].versionName}_${variant.productFlavors[0].name}.apk"
                             output.outputFile = new File(outputFile.parent, fileName)
                         }
                     }
                 }
    

    再重新打包,成功后發現包名為:App_V1.0.0_baidu.apk OK了。

  10. 打包時相關屬性介紹

    name:build type的名字
    applicationIdSuffix:應用id后綴
    versionNameSuffix:版本名稱后綴
    debuggable:是否生成一個debug的apk
    minifyEnabled:是否混淆
    proguardFiles:混淆文件
    signingConfig:簽名配置
    manifestPlaceholders:清單占位符
    shrinkResources:是否去除未利用的資源,默認false,表示不去除。
    zipAlignEnable:是否使用zipalign工具壓縮。
    multiDexEnabled:是否拆成多個Dex
    multiDexKeepFile:指定文本文件編譯進主Dex文件中
    multiDexKeepProguard:指定混淆文件編譯進主Dex文件中


以下開發中開發中常用的gradle命令

  1. 生成Android項目的依賴文件并導出
    gradlew :app:dependencies > dependencis.txt

  2. 查看編譯日志詳細信息
    gradlew compileDebugSource --stacktrace -info

  3. 輸出項目debug編譯日志
    gradlew assembleDebug --info>log2.txt

  4. 獲取構建報告,以HTML的形式展示。
    gradle build -profile
    gradle build -x lint 在Task Execution(任務執行)中去掉lint任務,節省Gradle構建時間。
    Tips:使用Android Studio任務欄直接點擊運行按鈕是不能去掉lint任務的,所以為了方便構建后安裝應用,可以使用 gradle installDebug -x lint 命令。

  5. Clean命令

    mac:
    ./gradlew clean build --info > cleanlog.log
    
    windows:
    gradlew.bat clean build --info > cleanlog.log
    
  6. 使用help
    ./gradlew --help

  7. build 某個指定 module
    AS 推薦的結構是 multiple project 結構,即一個 project 下,管理多個 module,如果每次都要 build 全部的 project 的話,有點浪費時間,則可以使用 -p module 參數,其中 module 是你要 build 的 module:

    $ ./gradlew -p app clean build
    $ ./gradlew -p app/app_module assembledebug
    
  8. 明確指定不執行某個 task
    Gradle 的命令存在依賴,例如 build task,是依賴于一系列的其他的 task,如果想要指定不執行某個 task,則可以使用 -x task 參數,其中 task 是要忽略的那個,這個參數可以傳遞多次。

    $ ./gradlew build -x test -x lint
    
  9. 執行多模塊任務
    當你在工程根目錄下通過命令行窗口運行任務時,Gradle會找出所有模塊中的同名任務,并運行它們。比如,你有一個app模塊和一個Android Wear模塊,運行./gradlew assembleDebug將會為app模塊和Android Wear模塊都構建一個debug版本。如果你切換到模塊的目錄下,Gradle將只會運行該模塊的任務,即使你在工程根目錄中運行Gradle Wrapper。比如,在Android Wear模塊的目錄運行./gradlew assembleDebug,只會構建Android Wear模塊。

    切換目錄的方式來運行模塊的任務是比較繁瑣的。另一個方式是可以使用模塊名稱來預處理任務名稱,使運行任務只在那個模塊上運行。比如只想構建Android Wear模塊,可以使用如下命令:

    ./gradlew :wear:assembleDebug
    ./gradlew -p app/app_module assembledebug
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容