IntelliJ IDEA 搭建安卓開發(fā)環(huán)境

前言


前段時(shí)間重拾安卓開發(fā),基于 IntelliJ IDEA 搭建了開發(fā)環(huán)境,整體體驗(yàn)還不錯(cuò),就記錄下我的配置過程。

cover

本來想寫在 IntelliJ IDEA 插件和經(jīng)驗(yàn)分享 一文中,結(jié)果發(fā)現(xiàn)篇幅太長(zhǎng)了就單獨(dú)拎出來。

為什么不是 Android Studio ?


很久以前在同事的電腦上瞥過幾眼 Android Studio 的界面,有點(diǎn)丑陋而且看著不太好用的樣子。當(dāng)然,這不是主要原因,作為廣大網(wǎng)友強(qiáng)烈推薦的安卓開發(fā)工具必然有它的獨(dú)到之處,而我選擇 IntelliJ IDEA 也是有我的考量:

  • Android Studio 本來就是基于 IntelliJ IDEA 定制的( 后者 + 一堆定制插件 )。

    對(duì)比了 Android Studio ( Arctic Fox | 2020.3.1 )IntelliJ IDEA ( 2021.3 ) 之后,發(fā)現(xiàn)二者的界面功能就是一個(gè)模子刻出來的,而屬于前者獨(dú)有的功能:

  • 我在 IntelliJ IDEA 上安裝了很多插件并做了較多配置,不想再重復(fù)一遍。而且能共用一個(gè) IDE 也能節(jié)省很多磁盤空間。

  • 我在 IntelliJ IDEA 上本來就做多種語(yǔ)言的開發(fā): 前端Java 后端腳本語(yǔ)言( Bat / Shell / Python ) 等,能多一個(gè)安卓那自然是更好的。

理論上這個(gè)方案是沒問題的,我實(shí)際開發(fā)也還沒遇到過因?yàn)?IntelliJ IDEA 自身原因?qū)е碌膯栴}。所以,不要無(wú)腦聽別人說用什么就用什么,有疑惑就多試試。團(tuán)隊(duì)開發(fā)中,沒能力還是建議使用團(tuán)隊(duì)統(tǒng)一的 IDE

配置環(huán)境變量


開發(fā)時(shí)下載的依賴包、緩存、臨時(shí)配置等默認(rèn)是保存在用戶目錄( ${user.home} )下,通過配置環(huán)境變量可以將它們安排在自定義的目錄下。

# 設(shè)置 Android SDK 安裝目錄的路徑
# ref: https://developer.android.google.cn/studio/command-line/variables?hl=en
ANDROID_SDK_ROOT=E:\Android\SDK

# ANDROID_HOME 也指向 SDK 安裝目錄,但已棄用,只是為了兼容 AGP < 3.4.0
ANDROID_HOME=%ANDROID_SDK_ROOT%

# 注意和 ANDROID_SDK_ROOT 的區(qū)別,一般這個(gè)變量代表安卓模擬器配置文件目錄的父目錄
# adb 估計(jì)寫死了路徑, ${user.home}/.android/adbkey 無(wú)法根據(jù)這個(gè)變量遷移
# 按英文版文檔說明:Android Studio 4.2 起這個(gè)變量被 ANDROID_PREFS_ROOT 代替
ANDROID_SDK_HOME=E:\Android

# 修改 Gradle 的全局配置和緩存的目錄,IntelliJ IDEA 能夠自動(dòng)識(shí)別這個(gè)路徑
GRADLE_USER_HOME=E:\Android\.gradle

# 將 adb 加入到系統(tǒng)全局路徑中
Path=%ANDROID_SDK_ROOT%\platform-tools;%Path%

配置 Gradle


添加 Gradle 全局 初始化腳本 ,它們會(huì)在所有項(xiàng)目構(gòu)建開始前執(zhí)行,并可以對(duì)構(gòu)建生命周期進(jìn)行攔截,可以添加額外的配置和插件以及任務(wù)。( 參考

官網(wǎng)用法介紹

  • 管理公司內(nèi)部的配置,例如去哪里查找定制的插件。
  • 配置一些基于當(dāng)前環(huán)境( 開發(fā)環(huán)境還是持續(xù)集成環(huán)境 )的屬性。
  • 提供構(gòu)建所需要的用戶個(gè)人信息,例如倉(cāng)庫(kù)或數(shù)據(jù)庫(kù)的用戶名和密碼。
  • 定義機(jī)器的環(huán)境,例如 JDK 安裝在什么位置。
  • 注冊(cè)一些監(jiān)聽器。這對(duì)一些需要監(jiān)聽 Gradle 事件的工具來說很有用。
  • 注冊(cè)一些 Logger 。你可能希望去自定義如何輸出 Gradle 產(chǎn)生的日志信息。

下面所有腳本( 按需添加 )采用 Kotlin DSL 語(yǔ)法編寫, Gradle 5.0 起可用,如使用舊版本請(qǐng)自行改為 Groovy 語(yǔ)法。

沒有代碼提示的話,寫 Gradle 腳本比批處理還惡心。

Gradle 項(xiàng)目中打開下面的腳本才會(huì)有代碼提示。

  • .gradle/init.d/0.repos.init.gradle.kts

    /*
     * 國(guó)內(nèi)的網(wǎng)絡(luò)環(huán)境默認(rèn)不設(shè)置鏡像加速的話很大概率無(wú)法下載依賴包導(dǎo)致初始化報(bào)錯(cuò),對(duì)于剛?cè)腴T的新手來說直接勸退。
     *
     * 本腳本全局配置所有項(xiàng)目使用阿里云鏡像源進(jìn)行加速,這樣就不用每個(gè)項(xiàng)目重復(fù)設(shè)置了,新項(xiàng)目拉下來就能跑。
     *
     * 文件名以 0 開頭是為了把這個(gè)腳本的執(zhí)行順序排在前面。
     *
     * gradlew showRepos
     * gradlew switchDeleteRawRepos
     *
     * @author anyesu <https://github.com/anyesu>
     */
    
    val pluginRepositories = ArrayList<MavenArtifactRepository>()
    val CONFIG_FILE = File(gradleUserHomeDir, "init.d/.deleteRawRepos")
    
    /** 是否刪除項(xiàng)目中配置的源地址 */
    var deleteRawRepos = CONFIG_FILE.exists()
    val GROUP_NAME = "init.gradle"
    val TASK_NAME_SHOW_REPOS = "showRepos"
    val TASK_NAME_SWITCH_DELETE_RAW_REPOS = "switchDeleteRawRepos"
    
    // 源地址
    val rawRepos = listOf(
        // central
        "https://repo1.maven.org/maven2/",
        "https://repo.maven.apache.org/maven2/",
        // jcenter
        "https://jcenter.bintray.com/",
        // google
        "https://maven.google.com/",
        "https://dl.google.com/dl/android/maven2/",
        // gradle-plugin
        "https://plugins.gradle.org/m2/"
    )
    
    // 代理地址
    object Repos {
        const val MAVEN_ALIYUN = "https://maven.aliyun.com/repository/public"
        const val MAVEN_JITPACK = "https://www.jitpack.io"
        const val GOOGLE_ALIYUN = "https://maven.aliyun.com/repository/google"
        const val GRADLE_PLUGIN_ALIYUN = "https://maven.aliyun.com/repository/gradle-plugin"
        const val GRADLE_PLUGIN_ALIYUN_OLD = "https://maven.aliyun.com/nexus/content/repositories/gradle-plugin"
    }
    
    projectsLoaded {
        allprojects {
            if (deleteRawRepos) deleteRawRepos()
            configureRepos()
            tasks.register(TASK_NAME_SHOW_REPOS) {
                group = GROUP_NAME
                description = "查看當(dāng)前項(xiàng)目最終依賴的倉(cāng)庫(kù)"
                doLast { printRepositories() }
            }
        }
        rootProject {
            tasks.named(TASK_NAME_SHOW_REPOS) {
                doFirst {
                    displayScriptConfigs()
                    pluginRepositories.print("pluginManagement.repositories")
                }
            }
            tasks.register(TASK_NAME_SWITCH_DELETE_RAW_REPOS) {
                group = GROUP_NAME
                description = "切換 - 是否刪除項(xiàng)目中配置的源地址"
                doLast {
                    if (deleteRawRepos) CONFIG_FILE.delete() else CONFIG_FILE.createNewFile()
                    deleteRawRepos = !deleteRawRepos
                    displayScriptConfigs()
                }
            }
        }
    }
    
    beforeSettings {
        configurePluginManagement()
    }
    
    fun displayScriptConfigs() {
        println("[ Current Initialization Script Configs ]")
        println("  - deleteRawRepos [${if (deleteRawRepos) "enabled" else "disabled"}]")
    }
    
    fun Settings.configurePluginManagement() = pluginManagement {
        repositories {
            all {
                if (this !is MavenArtifactRepository) return@all
                if (!deleteRawRepos) {
                    pluginRepositories.add(this)
                    return@all
                }
                val path = url.toString().run { if (endsWith("/")) this else "$this/" }
                if (rawRepos.contains(path)) {
                    logger.info("removed from `settings.pluginManagement.repositories`: ${display()}")
                    remove(this)
                } else {
                    pluginRepositories.add(this)
                }
            }
            // TODO 阿里云插件新版地址有時(shí)候 jar 會(huì)下載不下來,所以同時(shí)加上舊版的地址備用。
            maven(Repos.GRADLE_PLUGIN_ALIYUN_OLD)
            maven(Repos.GRADLE_PLUGIN_ALIYUN)
        }
    }
    
    fun Project.configureRepos() {
        buildscript.repositories.configureRepos()
        repositories.configureRepos()
    }
    
    fun Project.deleteRawRepos() {
        buildscript.repositories.deleteRawRepos(name, "buildscript.repositories")
        repositories.deleteRawRepos(name, "repositories")
    }
    
    fun Project.printRepositories() {
        buildscript.repositories.print("buildscript.repositories")
        repositories.print("repositories")
    }
    
    fun RepositoryHandler.deleteRawRepos(projectName: String = "", display: String = "") {
        // TODO: all 的邏輯不僅順序執(zhí)行還會(huì)作為回調(diào)
        all {
            if (this is MavenArtifactRepository && rawRepos.contains(url.toString())) {
                logger.info("removed from `${projectName}.${display}`: ${display()}")
                remove(this)
            }
        }
    }
    
    fun RepositoryHandler.configureRepos() {
        mavenLocal()
        mavenCentral { url = uri(Repos.MAVEN_ALIYUN) }
        maven(Repos.MAVEN_ALIYUN)
        google { url = uri(Repos.GOOGLE_ALIYUN) }
        maven(Repos.MAVEN_JITPACK)
        jcenter { url = uri(Repos.MAVEN_ALIYUN) }
        if (!deleteRawRepos) {
            mavenCentral()
            google()
            jcenter()
        }
    }
    
    fun RepositoryHandler.mavenArtifactRepositories() =
        map { if (it is MavenArtifactRepository) it else null }.filterNotNull()
    
    fun RepositoryHandler.print(display: String = "") {
        mavenArtifactRepositories().print(display)
    }
    
    fun List<MavenArtifactRepository>.print(display: String = "") {
        println("\n[ ${display} ]")
        forEach { println("  - ${it.display()}") }
    }
    
    fun MavenArtifactRepository.display() = "[ ${name.padEnd(32)} ] -> $url"
    
  • .gradle/init.d/cacheToLocalMavenRepository.init.gradle.kts

    解決了原腳本的幾個(gè)問題:

    • parts[1] 可能包含 . ,不能將其替換為 / ,比如:

      org.jetbrains.intellij/org.jetbrains.intellij.gradle.plugin
      
    • 還要考慮更深的子目錄,比如:

      com.jetbrains.intellij.idea/ideaIC/2021.1.3/fef2d88b0f4771ce5ac9a9963d3717080439cf4f/ideaIC-2021.1.3
      
    /*
     * 添加一個(gè) Task 用來把 Gradle 緩存的 Jar 包復(fù)制到 Maven 的本地倉(cāng)庫(kù),這樣和 Maven 就可以共用緩存了,而且 Maven 的目錄結(jié)構(gòu)更方便查找。
     *
     * 需要先設(shè)置環(huán)境變量 M2_HOME 為 Maven 的安裝目錄,否則會(huì)復(fù)制到默認(rèn)的 ${user.home}/.m2 目錄。
     *
     * 需要手動(dòng)執(zhí)行, Gradle 原有緩存在遷移后自行手動(dòng)刪除。
     *
     * gradlew cacheToLocalMavenRepository
     *
     * @see https://blog.csdn.net/feinifi/article/details/81458639
     *
     * @author anyesu <https://github.com/anyesu>
     */
    
    val GROUP_NAME = "init.gradle"
    
    projectsLoaded {
        rootProject {
            tasks.register<Copy>("cacheToLocalMavenRepository") {
                group = GROUP_NAME
                description = "Gradle 緩存復(fù)制到 Maven 本地倉(cāng)庫(kù)"
    
                from(File(gradleUserHomeDir, "caches/modules-2/files-2.1"))
                into(repositories.mavenLocal().url)
                eachFile {
                    val parts = ArrayList(path.split("/"))
                    parts[0] = parts[0].replace('.', '/') // 路徑分割
                    if (parts.size > 3) parts.removeAt(3) // 去除隨機(jī)值
                    path = parts.joinToString("/")
                }
                duplicatesStrategy = DuplicatesStrategy.EXCLUDE // 重復(fù)文件策略
                includeEmptyDirs = false
            }
        }
    }
    
  • .gradle/init.d/dependency-graph.init.gradle.kts

    /*
     * 為所有項(xiàng)目添加 gradle-dependency-graph-generator-plugin 插件,用于生成項(xiàng)目的依賴關(guān)系圖。
     *
     * gradlew generateDependencyGraph
     * gradlew generateProjectDependencyGraph
     *
     * @author anyesu <https://github.com/anyesu>
     */
    
    val PLUGIN_ID = "com.vanniktech.dependency.graph.generator"
    
    projectsLoaded {
        allprojects {
            buildscript {
                repositories {
                    mavenCentral()
                }
    
                dependencies {
                    classpath("com.vanniktech:gradle-dependency-graph-generator-plugin:+")
                }
            }
    
            afterEvaluate {
                // 和項(xiàng)目配置沖突,如果項(xiàng)目中已經(jīng)配置了就忽略本配置
                if (!plugins.hasPlugin(PLUGIN_ID)) {
                    apply(plugin = PLUGIN_ID)
                    logger.info("project '$name' apply plugin: $PLUGIN_ID")
                }
            }
        }
    }
    
  • .gradle/init.d/dependencyUpdates.init.gradle.kts

    借助 Package Search 插件可以提供類似的功能,更直觀。

    /*
     * 依賴版本管理,查看哪些依賴可以升級(jí)( 可自定義升級(jí)規(guī)則 )。
     *
     * gradlew dependencyUpdates
     *
     * @see https://github.com/ben-manes/gradle-versions-plugin#using-a-gradle-init-script
     *
     * @author anyesu <https://github.com/anyesu>
     */
    
    import com.github.benmanes.gradle.versions.VersionsPlugin
    import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask
    
    val GROUP_NAME = "init.gradle"
    val PLUGIN_ID = "com.github.ben-manes.versions"
    
    initscript {
        repositories {
            mavenLocal()
            gradlePluginPortal()
        }
    
        dependencies {
            classpath("com.github.ben-manes:gradle-versions-plugin:+")
        }
    }
    
    projectsEvaluated {
        allprojects {
            // 和項(xiàng)目配置沖突,如果項(xiàng)目中已經(jīng)配置了就忽略本配置
            if (!plugins.hasPlugin(PLUGIN_ID)) {
                configureDependencyUpdates()
                logger.info("project '$name' apply plugin: $PLUGIN_ID")
            }
        }
    }
    
    val String.isNonStable
        get() = listOf("final", "rc", "m", "alpha", "beta", "ga").any {
            val regex = "^(?i).*[.-]${it}[.\\d-]*$".toRegex()
            regex.matches(this)
        }
    
    // 版本號(hào)以 v 開頭,比如:v1.1.1
    val String.startsWithV get() = startsWith("v")
    
    fun Project.configureDependencyUpdates() {
        apply<VersionsPlugin>()
        tasks.withType<DependencyUpdatesTask> {
            group = GROUP_NAME
            rejectVersionIf {
                !currentVersion.isNonStable && candidate.version.isNonStable ||
                    currentVersion.startsWithV && !candidate.version.startsWithV
            }
        }
    }
    

配置 SDK


  • 打開 IntelliJ IDEA 新建一個(gè)安卓項(xiàng)目

    這時(shí)因?yàn)檫€沒有安裝 Android SDK 所以無(wú)法新建項(xiàng)目,會(huì)提示我們?nèi)グ惭b( 如下圖所示: Install SDK )。我們照著提示安裝就好了,安裝路徑會(huì)自動(dòng)識(shí)別為我們前面設(shè)置的環(huán)境變量 ANDROID_SDK_ROOT

    新建安卓項(xiàng)目 - Android SDK 未安裝
    新建安卓項(xiàng)目 - Android SDK 安裝完成

    安裝完成就可以繼續(xù)新建項(xiàng)目了( 隨便建一個(gè) Basic Activity 的手機(jī)項(xiàng)目 )。

    注意:

    Android 31 依賴:( 參考

    • com.android.tools.build:gradle:7.x
    • Minimum supported Gradle version is 7.0.2.
    • IntelliJ IDEA (or Android Studio) with version 2020.3.1 or newer.

    當(dāng)然,也可以進(jìn)設(shè)置頁(yè)面自定義選擇 SDK 版本進(jìn)行安裝( 參考 ), API 19 + 可以兼容目前大部分的項(xiàng)目和手機(jī)。

    Android SDK 設(shè)置

    新版本 IntelliJ IDEA 真的很方便,不像 N 年前用 Eclipse 開發(fā)時(shí)要自己去網(wǎng)上東找西找地下載各種 SDK 然后配置。

  • 打開新建的項(xiàng)目

    打開項(xiàng)目后會(huì)自動(dòng)識(shí)別為 Gradle 項(xiàng)目,開始自動(dòng)下載對(duì)應(yīng)的 Gradle wrapper 和依賴包。剛開始使用的話因?yàn)楸镜貨]緩存所以下載會(huì)比較久,用一段時(shí)間以后攢夠了緩存,之后再開任何項(xiàng)目就會(huì)比較快了。

    一般來說不用像 Maven 一樣下載一個(gè)全局的 Gradle ,因?yàn)轫?xiàng)目外基本用不上,而項(xiàng)目?jī)?nèi)每個(gè)項(xiàng)目設(shè)置的 Gradle 版本基本上都不一樣,還是要下載多個(gè)版本的。

    同步項(xiàng)目的時(shí)候可能會(huì)出現(xiàn)下面的錯(cuò)誤:

    你的主機(jī)中的軟件中止了一個(gè)已建立的連接。
    
    # 查看 daemon 日志關(guān)鍵內(nèi)容 .gradle/daemon/6.7.1/daemon-17316.out.log
    
    Ignoring unreachable local address on loopback interface /127.0.0.1
    ...
    Adding remote address /192.168.137.1
    ...
    Listening on [951c7aa5-4ce6-4311-8262-55d1396408a5 port:8653, addresses:[localhost/127.0.0.1]].
    ...
    Daemon server started.
    ...
    Cannot accept connection from remote address /127.0.0.1.
    

    看到 192.168.137.1 我就想起了熱點(diǎn),把熱點(diǎn)關(guān)閉果然就好了,殺 daemon 后再打開熱點(diǎn)就又不行了。總結(jié)下:

    • 有問題的 Gradle 版本為 6.4.1 ~ 6.8.3 ,新建的項(xiàng)目版本為 6.7.1 ,在這個(gè)區(qū)間內(nèi)。
    • 應(yīng)該是開啟熱點(diǎn)產(chǎn)生的虛擬網(wǎng)卡對(duì) daemon 綁定端口和通訊的邏輯產(chǎn)生了干擾,導(dǎo)致 daemon 啟動(dòng)后就關(guān)閉了。
    • daemon 進(jìn)程正常啟動(dòng)后再開啟熱點(diǎn)就不會(huì)有問題了。
    • Easy Gradle 插件的 Kill Gradle 按鈕可以殺死 daemon 進(jìn)程。

安裝插件


  • Activity Stack View

    查看設(shè)備當(dāng)前創(chuàng)建的 Activity 的堆棧狀態(tài),對(duì)應(yīng)下面的命令:

    adb shell dumpsys activity activities
    

    此插件即將下架,感興趣的可以看下 源碼

  • ADBHelper

    可以測(cè)試 URL Scheme

  • ADB Idea

    提供常用的 adb 命令菜單( 啟動(dòng)、關(guān)閉、調(diào)試等 ),快捷鍵 Ctrl + Alt + Shift + A

  • ADB Tools

    唯一有用的功能就是能修改手機(jī)的分辨率。

  • ADB Wi-Fi

    無(wú)需手動(dòng)敲命令,一鍵通過 WiFi 連接安卓手機(jī)。眾多同類插件中顏值最高,還集成了 scrcpy

    使用 scrcpy 的時(shí)候可能會(huì)出現(xiàn)卡頓,我測(cè)試了兩個(gè)手機(jī)發(fā)現(xiàn) adb wifi 的帶寬最多只能跑到 5 Mbps ,慢得離譜( USB 差不多能跑 80 Mbps ),要想流暢點(diǎn)就要降低比特率和分辨率。

    adb push D:/xxx /sdcard/
    

    按照網(wǎng)上的 方法 通過熱點(diǎn)( 電腦開熱點(diǎn)或者手機(jī)開熱點(diǎn)都行 )直連的話,帶寬可以提升到 14 Mbps ,還是遠(yuǎn)遠(yuǎn)不夠,這個(gè)速度安裝微信這種 APK 光復(fù)制文件都要好幾分鐘。

  • Android Intent Sender

    自定義 Intent 調(diào)試。

  • AndroidProGuard Pro

    一鍵生成 proguard-rules.pro 中的混淆配置,僅供參考。

  • AndroidSourceViewer

    在線查看 Android 源碼的插件。

  • Android WiFiADB

    支持手動(dòng)輸入 IP 和端口進(jìn)行連接,支持掃描局域網(wǎng)可用設(shè)備,設(shè)備列表會(huì)持久化而不丟失。

  • AndroidZer

    APK 反編譯為 Smali ( 可用 Jadx 打開 )。

  • Android Drawable Preview

    Drawable 文件直接預(yù)覽在項(xiàng)目視圖的圖標(biāo)上,無(wú)需打開文件。

    用是能用,不過有時(shí)能顯示出來有時(shí)又顯示不出來,應(yīng)該是性能上有問題。

  • Dex 2 Java

    反編譯 Dex 中的字節(jié)碼,不過年久失修用不了了。

  • Easy Gradle

    提供一個(gè)按鈕強(qiáng)行終止 Gradle 進(jìn)程。

  • Gradle Clean Snapshot Cache

    清理 Gradle 緩存的 Snapshot 依賴包。

  • Gradle Utilities

    擴(kuò)展 Gradle 工具窗口,管理本地的 Gradle 信息和緩存。

  • Gradle View

    查看 Gradle 依賴樹。

    由于依賴 Gradle Tooling API 5.6.2 ,最高只能支持到 Gradle 6.x

  • Install Apk

    在項(xiàng)目中右鍵安裝 APK 而無(wú)需手動(dòng)敲命令。

  • Jadx Android Decompiler

    集成 Jadx GUI ,一個(gè) DexJava 的反編譯器,可在項(xiàng)目文件( apk, dex, jar, class, smali, zip, aar, arsc )視圖中右鍵選擇 在 Jadx GUI 中反編譯

    Jadx 是目前我用過最好用的、最傻瓜式的 APK 反編譯器 ,不過需要設(shè)置好內(nèi)存參數(shù) -Xmx參考 ),不然隨便多開幾個(gè)就能撐爆你的內(nèi)存。

  • Material Design Icon Generator

    輔助導(dǎo)入 Material Design 圖標(biāo)到項(xiàng)目中,快捷鍵 Ctrl + Alt + D

  • scrcpy

    直接給 scrcpy 做了一個(gè)配置界面,使用更方便。當(dāng)然,還是 QtScrcpy 的體驗(yàn)更好。

    scrcpy 是一個(gè)手機(jī)投屏到電腦的工具,有了它就可以在電腦上無(wú)縫操作手機(jī),調(diào)試可以更加方便( 尤其是有多個(gè)開發(fā)設(shè)備 )。

  • Spock ADB

    Plugin Helps you to have full control of your project and device.

  • SQL Android

    看截圖應(yīng)該是個(gè)不錯(cuò)的數(shù)據(jù)庫(kù)管理工具,不過太久沒更新了,安裝后使用沒反應(yīng)甚至?xí)?IDE 卡死。新版 IDE 可以考慮自帶的 Database Inspector

  • Vector Drawable Thumbnails

    顯示安卓項(xiàng)目中所有的 Drawable 文件。

其他配置和功能


常見錯(cuò)誤


  • 找不到 SDK

    SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

    Android Studio 在同步 Gradle 前會(huì)自動(dòng)創(chuàng)建 local.properties 文件,而 IntelliJ IDEA 不會(huì)。

    為了避免每次打開新項(xiàng)目要手動(dòng)創(chuàng)建,可以添加一個(gè)環(huán)境變量 ANDROID_HOME參考 )。

  • 使用老舊 Gradle 的項(xiàng)目( 比如 )構(gòu)建失敗

    Executing tasks: [:app:assembleDebug] in project ...

    Gradle build failed with 1 error(s) in 24 ms

    找不到任何更多的錯(cuò)誤信息,而且手動(dòng)跑命令是完全沒問題的。

    # gradlew :app:assembleDebug
    
    BUILD SUCCESSFUL in 0s
    25 actionable tasks: 1 executed, 24 up-to-date
    

    測(cè)試了 Android Studio ( Arctic Fox | 2020.3.1 )IntelliJ IDEA 2020.3 都沒問題,但 IntelliJ IDEA 2021.x 全都不行。

    臨時(shí)解決方案 :構(gòu)建步驟使用 Gradle 命令代替 Gradle-aware Make 。( 參考

    Edit Configurations -> Android App -> app -> Before launch

    - Gradle-aware Make
    + Run Gradle task :app:assembleDebug
    

    如果繼續(xù)報(bào)錯(cuò),可以考慮把安裝步驟也替換了。( 參考

    ApkProvisionException: No outputs for the main artifact of variant: debug

學(xué)習(xí)資料


新手入門強(qiáng)烈建議去看一遍官方文檔,可以少走很多彎路。


轉(zhuǎn)載請(qǐng)注明出處: https://github.com/anyesu/blog/issues/39

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,582評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,540評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,801評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,223評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,442評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,976評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,800評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,996評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評(píng)論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,233評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,702評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容