Android Studio,使用技巧,问题记录和一些好用的插件

DomCedeno 8年前
   <h2><strong>使用技巧</strong></h2>    <h2><strong>根据不同 buildType 使用不同包名和资源</strong></h2>    <p>比如在 production,development,qa 不同阶段使用不同的 packageName 和 authority,在 build.gradle 中配置如下:</p>    <pre>  <code class="language-java">productFlavors {          production {              applicationId = "com.myapp.app"              resValue "string", "authority", "com.非死book.app.非死bookContentProvider5435651423234"          }          development {              applicationId = "com.myapp.development"              resValue "string", "authority", "com.非死book.app.非死bookContentProvider2134564533421"          }          qa {              applicationId = "com.myapp.qa"              resValue "string", "authority", "com.非死book.app.非死bookContentProvider29831237981287319"          }  }</code></pre>    <p>通过 resValue 方法设置的资源会在 R 文件生成对应的 id。</p>    <p>在 AndroidManifest.xml 中使用 authority 字符串</p>    <pre>  <code class="language-java"><provider      android:name="com.非死book.非死bookContentProvider"      android:authorities="@string/authority"      android:exported="true" /></code></pre>    <h2><strong>替换 AndroidManifest.xml 中的占位符</strong></h2>    <p>使用 manifestPlaceholders 可以定义相关字段替换 AndroidManifest.xml 中的占位符。</p>    <p>格式:manifestPlaceholders = [ key1:"value1", key2:"value2", ... ]</p>    <p>下面是一个示例:</p>    <pre>  <code class="language-java">android {        defaultConfig {          def authorityName = "com.linchaolong.android.app.droidplugin_stub"          manifestPlaceholders = [              authorityName:"${authorityName}",              key:"value",          ]      }  }</code></pre>    <p>说明:manifestPlaceholders 是一个 map。</p>    <p>在 AndroidManifest.xml 中通过 ${key} 引用相关字段。</p>    <h2><strong>自定义 BuildConfig 字段</strong></h2>    <p>通过 buildConfigField 可以添加自定义的字段到 BuildConfig 中。</p>    <p>格式:buildConfigField "type", "key", "value"</p>    <pre>  <code class="language-java">android {      defaultConfig{          def authority = "com.linchaolong.android.app"          buildConfigField "String", "AUTHORITY", "\"${authority }\""          buildConfigField "int", "FOO", "42"          buildConfigField "String", "FOO_STRING", "\"foo\""          buildConfigField "boolean", "LOG", "true"      }  }</code></pre>    <p>def 操作符可用于定义变量和函数。</p>    <p>在 java 代码中你可以通过 BuildConfig.key 访问对应的字段,比如: BuildConfig.AUTHORITY 访问 AUTHORITY 常量。</p>    <h2><strong>统一管理不同 module 依赖库的版本</strong></h2>    <ol>     <li> <p>新建一个 config.gradle 在工程根目录下,并添加一些全局的配置:</p> <pre>  <code class="language-java">allprojects {  repositories {   jcenter()  }    ext{   compileSdkVersion = 25   buildToolsVersion = "25.0.0"   minSdkVersion = 14   targetSdkVersion = 25     support = '25.0.0'   play_services = '9.8.0'     rxjava = '2.0.1'     // Json   gson = '2.8.0'     // HTTP   okhttp = '3.4.1'   retrofit = '2.1.0'   glide = '3.7.0'     // DEPS INJECTION   dagger = '2.6'     // VIEW INJECTION   butterknife = '8.4.0'     // DEBUG   stetho = '1.4.1'  }  }</code></pre> </li>     <li> <p>然后在 project-level 下的 build.gradle 添加如下配置应用 config.gradle :</p> <pre>  <code class="language-java">apply from: rootProject.file('config.gradle')</code></pre> </li>     <li> <p>在各 module 的 build.gradle 引用 ext 中的字段</p>      <ul>       <li> <p>在 android 块中引用 ext 中的字段</p> <pre>  <code class="language-java">android {  compileSdkVersion project.ext.compileSdkVersion  buildToolsVersion project.ext.buildToolsVersion  defaultConfig {     applicationId "linchaolong.demo"     minSdkVersion project.ext.minSdkVersion     targetSdkVersion project.ext.targetSdkVersion       def QQ_APPID = rootProject.ext.QQ_APPID     manifestPlaceholders = [QQ_APPID:"${QQ_APPID}"]  }  }</code></pre> </li>       <li> <p>在 dependencies 块下引用 ext 中的字段</p> <pre>  <code class="language-java">dependencies {  compile "com.android.support:appcompat-v7:$support"  // Debug  compile "com.非死book.stetho:stetho:$stetho"  compile "com.非死book.stetho:stetho-okhttp3:$stetho"  debugCompile "com.非死book.stetho:stetho-js-rhino:$stetho"  // Json  compile "com.google.code.gson:gson:$gson"  // Butterknife  compile "com.jakewharton:butterknife:$butterknife"  annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife"  // RxJava  compile "io.reactivex.rxjava2:rxjava:$rxjava"  compile "io.reactivex.rxjava2:rxandroid:$rxjava"  }</code></pre> <p>严重注意:引用 ext 中字段时要使用双引号,不能是单引号。格式为 ${key} 或 $key</p> </li>      </ul> </li>    </ol>    <h2><strong>建立各 Module 共享的配置</strong></h2>    <p>比如,这里想统一设置各 Module 支持的 so 库架构,可以在工程目录下新建一个 module.gradle 存放共享的配置,配置如下:</p>    <pre>  <code class="language-java">// Module 共享的配置  android {    defaultConfig {      ndk {        //设置支持的SO库架构        abiFilters 'armeabi-v7a' //, 'armeabi' , 'x86', 'x86_64', 'arm64-v8a'      }    }  }</code></pre>    <p>然后,在各 Module 下的 build.gradle 配置中应用 module.gradle,示例如下:</p>    <pre>  <code class="language-java">apply plugin: 'com.android.application'  apply from: rootProject.file('module.gradle')</code></pre>    <h2><strong>自定义 release apk 名称</strong></h2>    <p>修改 build.gradle 配置如下:</p>    <pre>  <code class="language-java">android {    buildTypes {      release {        minifyEnabled false        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        signingConfig signingConfigs.release        // 自定义 release apk 名称        applicationVariants.all { variant ->          variant.outputs.each { output ->            def releaseTime = new Date().format('yyyyMMddHHmmss')            // 修改 release apk 名称,app_1.0_20170108224145.apk            def fileName = output.outputFile.name.replace("-release", "_${defaultConfig.versionName}_"+releaseTime)            output.outputFile = new File(output.outputFile.parent, fileName)          }        }      }    }  }</code></pre>    <h2><strong>手动 build 工程</strong></h2>    <p>我们应该都或多或少遇到过这种情况,当我们第一次打开一个项目时,该项目会先下载相关的依赖项,如果很多的话,将会等很久,而且又不能做其他操作,这样太浪费时间了。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/5f2ce2f0706de3f3e09e0f8e782302d2.png"></p>    <p style="text-align:center">Image</p>    <p>下面说说如何在命令行中手动 build 工程:</p>    <p>每个 android studio 工程下应该都会有 gradlew.bat (Windows) 和 gradlew (Mac) 两个脚本文件,我们可以打开命令行,cd 到工程目录下,通过执行 gradlew.bat build 命令,手动去构建工程。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8545f221d71f3c12082b51d16927ec2c.png"></p>    <p style="text-align:center">Image</p>    <p>通过在命令行构建工程可以不影响当前工作,而且构建完成后再使用 android studio 打开该工程将会比较快。</p>    <h2><strong>手动下载和替换本地 gradle/jar</strong></h2>    <p>build 过程很慢?这个多数是由于个别依赖项下载速度过慢引起的,有时还会卡在某个点,但我们可以通过手动下载替换解决该问题。</p>    <p>这里分为两个部分,一个是 gradle 的下载替换,一个是依赖库的下载替换。</p>    <p>我们通过在命令行执行 gradlew.bat build 命令可以看到相关依赖的下载地址:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ba5d9f7143ff003e5d965cc5b13bd718.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>正常下载速度可能只有几十KB(可能更低...),但是如果我们把下载链接 copy 到迅雷下载,它是这样的:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/43acbfa9d4fbd2859c9c66ad04413b35.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>在 windows 下,gradle 存放目录就在 C:\Users\用户名\.gradle\wrapper\dists 下。下载完成后,把 *.lck 和 *.zip.part 文件删掉,替换为我们刚下载的 .zip 。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a3db2a27a0be3b53b91ee00af7cdd952.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>重新执行 gradlew.bat build 命令可以看到它将会自动解压该 *.zip 文件。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/776b8f9e375f82c4940b638d3ce7b595.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>在 windows 下,依赖库的存放目录就在 C:\Users\用户名\.gradle\caches 。jar文件的下载替换也是同理,比如我这里下载一个 common-25.3.0-alpha2.jar ,下载完成后把它放在 common-25.3.0-alpha2.pom 同级目录下,重新执行 build 命令即可。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/03a4e214c0e8f56b41472d6dd79c29b0.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>这里的目录路径可能和你的不太一样,可以使用 Everything 搜索一下文件名。</p>    <h2><strong>构建一个aar文件</strong></h2>    <p>如果你的 module 声明为 android library(即在 build.gradle 文件中使用 apply plugin:'com.android.library' ),它将在构建时输出 .aar 文件,在 module 的 build/outputs/aar 目录下。</p>    <p>你可以选择该 module ,然后 Build —> Make Module * 构建该 module。或者使用 gradlew.bat build 命令构建工程。然后就可以在输出目录下找到 .aar 文件了。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8e122b0f52e138e25c0abfc210848fb0.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <h2><strong>引用 aar 文件</strong></h2>    <p>假设把 aar 文件放在 module 下的 libs 目录。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3f6f3c344b4c38dae6380a88c75417b4.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>引用本 module 的 aar 文件 build.gradle 配置如下:</p>    <pre>  <code class="language-java">repositories{    flatDir{      dirs 'libs'    }  }  dependencies {    compile(name:'stetho_realm-release', ext:'aar')  }</code></pre>    <p>如果是引用其他 module 的 aar 文件,还要在本 module 的 build.gradle 中配置被引用 module 的 aar 目录,否则会找不到文件。</p>    <pre>  <code class="language-java">repositories {    flatDir {      dirs project(':Library').file('libs')    }  }</code></pre>    <p>多个目录使用 “,” 分隔</p>    <h2><strong>快捷键</strong></h2>    <p>由于 Android Studio 是基于 IDEA 的,所以很多快捷键是通用的。</p>    <h2><strong>全局替换</strong></h2>    <p>Edit —&gt; Find —&gt; Replace in Path</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e26d1f9d2c0ceb39464d2dca9cb490ca.png"></p>    <p style="text-align:center">Replace in Path</p>    <h2><strong>动态调试app</strong></h2>    <p>1.在源码先打断点,点击挂接 Android 进程按钮。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8ab03d01d9f55ee5a6941e08902be787.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <p>2.选择 app 进程,点OK,开始调试。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/fa7dee0117d7c0605750943423aa3a4b.png"></p>    <p style="text-align:center">Paste_Image.png</p>    <h2><strong>一些好用的插件</strong></h2>    <p>插件安装说明:</p>    <ul>     <li>在线安装<br> 打开 File ——> Settings ——> Plugins ——> Browse repositories... 搜索插件名,点 Install 下载安装,安装完成后重启生效。</li>     <li>离线安装<br> 下载插件安装包,打开 File ——> Settings ——> Plugins ——> Install plugin from disk... 选择插件安装包,安装完成后重启生效。</li>    </ul>    <h2><strong>GsonFormat</strong></h2>    <p>GsonFormat 是一个可以快速将 JSON 字符串转换为 Entity 类的插件。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1425cf52bc482b946fbe08f13afc7fd0.gif"></p>    <p><strong>GsonFormat</strong></p>    <p>使用说明: Alt + S 调出 GsonFormat,或者 Alt Insert + GsonFormat 。</p>    <p>在 Settings 中选择 filed(public) ,Enter 保存修改,可以只生成 public 字段,不生成一大堆的 getter 和 setter,代码会更简洁些。</p>    <p>插件地址: <a href="/misc/goto?guid=4959741067679564597" rel="nofollow,noindex">https://plugins.jetbrains.com/idea/plugin/7654-gsonformat</a></p>    <p>Github地址: <a href="/misc/goto?guid=4958875619111090072" rel="nofollow,noindex">https://github.com/zzz40500/GsonFormat</a></p>    <h2><strong>Android ButterKnife Zelezny</strong></h2>    <p>Android ButterKnife Zelezny 是 Android 下的注解框架 ButterKnife 的辅助插件,用于一键生成 Butterknife 视图注入代码。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/84b66a1c0caee9c008da2a266095c9a7.gif"></p>    <p><strong>Android ButterKnife Zelezny</strong></p>    <p>使用说明:把光标停在 setContentView(R.layout.activity_settings) 那行中的 activity_settings<br> 上右键选择 Generate ( alt + insert )→ Generate Butterknife Injections</p>    <p>注意:需要把光标停在 layout 的名称上才能调出 ButterKnife Zelezny 。</p>    <p>ButterKnife: <a href="/misc/goto?guid=4958961775931067718" rel="nofollow,noindex">https://github.com/JakeWharton/butterknife</a></p>    <p>插件地址: <a href="/misc/goto?guid=4959741067824580679" rel="nofollow,noindex">https://plugins.jetbrains.com/idea/plugin/7369-android-butterknife-zelezny</a></p>    <p>Github地址: <a href="/misc/goto?guid=4958875618985371190" rel="nofollow,noindex">https://github.com/avast/android-butterknife-zelezny</a></p>    <h2><strong>ECTranslation</strong></h2>    <p>Android Studio 翻译插件,可以将英文翻译为中文。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ab87c1dec949bce33e44799ee1c60f73.jpg"></p>    <p><strong>ECTranslation</strong></p>    <p>使用说明:选中要翻译的内容,选择 Edit ——> Translate 或者按下 command + I (我这里设置为 Shift + I )。</p>    <p>修改快捷键: Preferences -> Keymap -> 搜索Translate - > 右键 add Keyboard Shortcut . 输入你想要的快捷键即可。</p>    <p>插件地址: <a href="/misc/goto?guid=4959741067939458593" rel="nofollow,noindex">https://plugins.jetbrains.com/idea/plugin/8469-ectranslation</a></p>    <p>Github地址: <a href="/misc/goto?guid=4959714115159123733" rel="nofollow,noindex">https://github.com/Skykai521/ECTranslation</a></p>    <h2><strong>问题记录</strong></h2>    <h2><strong>Error:The number of method references in a .dex file cannot exceed 64K.</strong></h2>    <p>这是错误是因为应用的方法数已经超过了64K了,在 class 转换 dex 文件时报错了。一个 dex 文件的最大方法数是65536,所以这时候要启用 Multidex。</p>    <p>1.app 的 build.gradle 添加如下配置</p>    <pre>  <code class="language-java">android {      defaultConfig {          // 启用 multidex 支持.          multiDexEnabled true      }  }    dependencies {    compile 'com.android.support:multidex:1.0.1'  }</code></pre>    <p>2.在 AndroidManifest.xml 下配置 MultiDexApplication</p>    <pre>  <code class="language-java"><?xml version="1.0" encoding="utf-8"?>  <manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="com.example.android.multidex.myapplication">      <application          ...          android:name="android.support.multidex.MultiDexApplication">          ...      </application>  </manifest></code></pre>    <p>如果是自定义了 Application 则让其继承 MultiDexApplication。</p>    <p>官方文档: <a href="/misc/goto?guid=4959653310437223048" rel="nofollow,noindex">https://developer.android.com/tools/building/multidex.html</a></p>    <h2><strong>Error: "*" is not translated in "en" (English) [MissingTranslation]</strong></h2>    <p>这种错误一般出现在打包的时候,表示 strings.xml 中缺失本地化的字符串资源。</p>    <p>这个错误有两个解决方案:</p>    <ol>     <li>在 values\strings.xml 或出问题的 strings.xml 中添加如下配置: <pre>  <code class="language-java"><resources xmlns:tools="http://schemas.android.com/tools" tools:locale="en"></code></pre> </li>     <li>在 values\strings.xml 或出问题的 strings.xml 中添加如下配置: <pre>  <code class="language-java"><resources  xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation" ></code></pre> </li>    </ol>    <p>缺失的本地化字符串资源会使用默认字符串资源。</p>    <h2><strong>Could not find Library</strong></h2>    <p>日志如下:</p>    <pre>  <code class="language-java">Error:A problem occurred configuring project ':app'.  > Could not resolve all dependencies for configuration ':app:_debugCompile'.     > Could not find com.android.support:appcompat-v7:22.2.0.       Searched in the following locations:           https://jcenter.bintray.com/com/android/support/appcompat-v7/22.2.0/appcompat-v7-22.2.0.pom           https://jcenter.bintray.com/com/android/support/appcompat-v7/22.2.0/appcompat-v7-22.2.0.jar           file:/D:/AndroidDeveloper/adt-bundle-windows-x86-20130917/sdk/extras/google/m2repository/com/android/support/appcompat-v7/22.2.0/appcompat-v7-22.2.0.pom           file:/D:/AndroidDeveloper/adt-bundle-windows-x86-20130917/sdk/extras/google/m2repository/com/android/support/appcompat-v7/22.2.0/appcompat-v7-22.2.0.jar       Required by:           HelloWorld:app:unspecified</code></pre>    <p>解决办法:更新你的 Android Support Library 和 Android Support Repository.</p>    <h2><strong>修改 applicationId 后启动出现ClassNotFoundException</strong></h2>    <p>日志如下:</p>    <pre>  <code class="language-java">E/AndroidRuntime: FATAL EXCEPTION: main     Process: com.linchaolong.android.app, PID: 10690     java.lang.RuntimeException: Unable to instantiate application com.android.tools.fd.runtime.BootstrapApplication: java.lang.IllegalStateException: java.lang.ClassNotFoundException: com.linchaolong.android.app.MyApplication         at android.app.LoadedApk.makeApplication(LoadedApk.java:565)         at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4545)         at android.app.ActivityThread.access$1500(ActivityThread.java:154)         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)         at android.os.Handler.dispatchMessage(Handler.java:102)         at android.os.Looper.loop(Looper.java:135)         at android.app.ActivityThread.main(ActivityThread.java:5275)         at java.lang.reflect.Method.invoke(Native Method)         at java.lang.reflect.Method.invoke(Method.java:372)         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:909)         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:704)      Caused by: java.lang.IllegalStateException: java.lang.ClassNotFoundException: com.linchaolong.demo.App         at com.android.tools.fd.runtime.BootstrapApplication.createRealApplication(BootstrapApplication.java:220)         at com.android.tools.fd.runtime.BootstrapApplication.attachBaseContext(BootstrapApplication.java:239)         at android.app.Application.attach(Application.java:185)         at android.app.Instrumentation.newApplication(Instrumentation.java:996)         at android.app.Instrumentation.newApplication(Instrumentation.java:980)         at android.app.LoadedApk.makeApplication(LoadedApk.java:560)         at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4545)          at android.app.ActivityThread.access$1500(ActivityThread.java:154)          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)          at android.os.Handler.dispatchMessage(Handler.java:102)          at android.os.Looper.loop(Looper.java:135)          at android.app.ActivityThread.main(ActivityThread.java:5275)          at java.lang.reflect.Method.invoke(Native Method)          at java.lang.reflect.Method.invoke(Method.java:372)          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:909)          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:704)       Caused by: java.lang.ClassNotFoundException: com.linchaolong.android.app.MyApplication         at java.lang.Class.classForName(Native Method)         at java.lang.Class.forName(Class.java:306)         at java.lang.Class.forName(Class.java:270)         at com.android.tools.fd.runtime.BootstrapApplication.createRealApplication(BootstrapApplication.java:209)         at com.android.tools.fd.runtime.BootstrapApplication.attachBaseContext(BootstrapApplication.java:239)          at android.app.Application.attach(Application.java:185)          at android.app.Instrumentation.newApplication(Instrumentation.java:996)          at android.app.Instrumentation.newApplication(Instrumentation.java:980)          at android.app.LoadedApk.makeApplication(LoadedApk.java:560)          at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4545)          at android.app.ActivityThread.access$1500(ActivityThread.java:154)          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)          at android.os.Handler.dispatchMessage(Handler.java:102)          at android.os.Looper.loop(Looper.java:135)          at android.app.ActivityThread.main(ActivityThread.java:5275)          at java.lang.reflect.Method.invoke(Native Method)          at java.lang.reflect.Method.invoke(Method.java:372)          at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:909)          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:704)       Caused by: java.lang.ClassNotFoundException: Didn't find class "com.linchaolong.android.app.MyApplication" on path: DexPathList[[zip file</code></pre>    <p>原因是 Android Studio 的 Instant Run 尝试对你的代码执行热交换,导致 Application 类被移除所以出现 ClassNotFoundException。</p>    <p>停用 Instant Run, File --> Settings--> Build,Execution,Deployment -->Instant Run ---> 取消勾选 "Enable instant run"</p>    <h2><strong>相关文章</strong></h2>    <ul>     <li><a href="/misc/goto?guid=4959741068082130860" rel="nofollow,noindex">Intellij IDEA使用技巧整理</a></li>     <li><a href="/misc/goto?guid=4959741068175074453" rel="nofollow,noindex">Android Studio各种快捷功能及好用的插件</a></li>     <li><a href="/misc/goto?guid=4959741068254539157" rel="nofollow,noindex">tools 属性参考文档</a></li>    </ul>    <p> </p>    <p>来自:https://juejin.im/entry/58bfd80ea22b9d00588c3444</p>    <p> </p>