昨日使用 Android Studio 途中彈了 Android Studio 3 的更新通知,那時因為知道升級會有 breaking change,擔心升級要大改 Gradle 設定檔。所以延後了一天才更新。今日試了將現有的 project 更新,暫時未遇到問題,應該算是完成了。

Android Studio 3 的更新通知

Android Studio 升級完成後,開啟 project。Android Studio 會提示你升級 build tool 之類的東西,按照指示進行。Android Studio 會改動你的 Gradle wrapper,build tool 版本。但之後 Gradle project sync 時可能會有一大堆 error。這時可以試試以下的方法:

檢查 app/build.gradle 的 buildToolsVersion 是否太舊。在根目錄的 build.gradle 的 ext 內加入 compileSdkVersion 和 buildToolsVersion:

ext {
    compileSdkVersion = 26
    buildToolsVersion = '26.0.2'
    // ...
}

之後將 app/build.gradle 的 compileSdkVersion 和 buildToolsVersion 換成 ext 的 property:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    // ...
}

然後在根目錄的 build.gradle 加入以下的內容。用意是將 subproject 的 Android 或 Android library project 的 compileSdkVersion 和 buildToolsVersion 強行設定成 ext 所訂的版本。如果你的 project 是 React Native 的話,應該會加入不少 subproject,而每個 subproject 都會有自己的 compileSdkVersion 和 buildToolsVersion,如果版本不一致的話是不能 build 的。你可以手動逐個更新 build.gradle,但之後用 NPM update 過那些 React Native component 之後你又要再手動改一次,非常麻煩。所以都是用下面的方法比較好:

subprojects { subproject ->
    afterEvaluate {
        if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
            android {
                compileSdkVersion rootProject.ext.compileSdkVersion
                buildToolsVersion rootProject.ext.buildToolsVersion
            }
        }
    }
}

之後試試 build project,可能已經成功。如果不能的話,就要繼續改其他地方。例如我的 project 有自訂 buildTypes,現在需要補回 matchingFallbacks 確保其他 Android 或 Android library subproject 的 buildTypes 能配對到你的 buildTypes。

android {
    // ...
    buildTypes {
        debug {
            debuggable true
            minifyEnabled false
            shrinkResources false
            applicationIdSuffix devAppIdSuffix
            versionNameSuffix devVersionNameSuffix
            signingConfig signingConfigs.debug
            manifestPlaceholders = [
                    // ...
            ]
            ext.alwaysUpdateBuildId = false
        }
        upload {
            minifyEnabled true
            shrinkResources true
            signingConfig signingConfigs.upload
            manifestPlaceholders = [
                    // ...
            ]
            matchingFallbacks = ['release']
        }
        // ...
    }
    // ...
}

每個 productFlavors 都要加 dimension,之前無做就要補回。

android {
    // ...
    flavorDimensions "api"
    productFlavors {
        development {
            dimension "api"
            buildConfigField "String", "ENVIRONMENT", "\"development\""
            buildConfigField "String", "API_ENDPOINT", "\"foo.com\""
        }
        production {
            dimension "api"
            buildConfigField "String", "ENVIRONMENT", "\"production\""
            buildConfigField "String", "API_ENDPOINT", "\"bar.com\""
        }
    }
    // ...
}

加入 Google Maven repository。現在 Google 會將以下最新版本的 library 經這個 repository 發布。

  • Android Support Library
  • Architecture Components Library
  • Constraint Layout Library
  • Android Test Support Library
  • Databinding Library
  • Android Instant App Library
  • Google Play services
  • Firebase

如果之前有加過 maven { url 'https://maven.google.com' } 的話,要將它刪走。

buildscript {
    repositories {
        google()
        jcenter()
        // ...
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        // ...
    }
}

我的 project 到了現在已經能夠 build 到,如果不能的話要再查看其他文檔。

之後可以再將 dependency 設定更新到 Gradle 3.4 的新設定方式。compile 已經在 Gradle 3.4 開始 deprecated,要改用 implementation 或 api。provided 要轉用 compileOnly。跟據文檔的講法,應該要盡量使用 implementation。因為它會限制這個 dependency 的使用範圍,所以可以加快因改動 module 後再 build 的速度。如果出現 build error 的話,可以試試將 implementation 換成 api,api 的功能其實和之前的 compile 一樣。debugCompile 換成 debugImplementation;testCompile 換成 testImplementation……以下是一些轉用 implementation 例子:

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"
    
    implementation project(':react-native-camera')
    
    implementation "com.android.support:appcompat-v7:$androidSupportLibVersion"
    implementation "com.google.dagger:dagger:$daggerVersion"
    kapt "com.google.dagger:dagger-compiler:$daggerVersion"
    
    debugImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
    debugLeakCanaryImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
    releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"

    androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
        exclude group: 'com.google.code.findbugs', module: 'jsr305'
    })
    testImplementation 'junit:junit:4.12'
    testImplementation "org.robolectric:robolectric:3.4.2"
}

註:kapt 是 Kotlin annotation processing tool,如果是 Java project 應該用 annotationProcessor。

參考