Android Studio 自动更新失败解决办法(2015-11-21,1.4.1升级1.5.0版本有效)

昨天在G+中看到Android Studio又有更新了就心血来潮想去更新体验一下,可是无论我怎么点Check Update却一直提示

Connection failed. Please check your network connection and try again

,开始以为是由于G*W在捣乱,但是打开VPN后还是无法更新,然后开始Google了一下找到了下面的解决办法。

android-studio-update-error.png

Mac OSX

首先打开Finder在左边选择Application目录,在右边找到Android Studio.app,然后右击选择“显示包内容”如下图:

android_studio_in_finder

然后找到 Contents/bin/ 目录下的 .vmoptions 后缀的文件

mac-studio-vmoptions.png

Linux

找到 bin/ 目录下的 studio.vmoptions (32位系统) 或者 studio64.vmoptions (64位系统)文件

linux-studio-vmoptions.png

Windows

找到 bin/ 目录下的 studio.vmoptions (32位系统) 或者 studio64.exe.vmoptions (64位系统)文件

windows-studio-vmoptions.png

接着用自己趁手的编辑器打开 idea.vmoptions 或者 studio.vmoptions/studio64.vmoptions 或者 studio.exe.vmoptions/studio64.exe.vmoptions 文件添加如下内容:

-Djava.net.preferIPv4Stack=true  
-Didea.updates.url=http://dl.google.com/android/studio/patches/updates.xml  
-Didea.patches.url=http://dl.google.com/android/studio/patches/

保存后,重新打开Android Studio点击Check Update就会弹出更新信息提示了

android-studio-update-info

如果仍然无效,将url里的修改httphttps,然后重启点击Check Update试试~~~

剽窃地址 Android Studio 自动更新失败解决办法

Android Studio(JAVA) 编译警告:使用了未经检查或不安全的操作

在编译Android Studio(JAVA)程序时发生了如下的警告:

使用了未经检查或不安全的操作
要了解详细信息,请使用 "-Xlint:unchecked" 重新编译。

  • 如何设置Android Stuido开启 "-Xlint:unchecked"

修改build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.a.b.c"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
}

增加

tasks.withType(JavaCompile) {
   options.compilerArgs << "-Xlint:unchecked"
}

修改后的如下:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.a.b.c"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled false
        }
    }

    tasks.withType(JavaCompile) {
       options.compilerArgs << "-Xlint:unchecked"
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
}
  • 警告信息的处理例子

包含-Xlint:unchecked警告的代码

final LinkedHashMap<WeakReference<Object>,WeakReference<Object>> aWeakArray = new LinkedHashMap<>();
for (Iterator iter = aWeakArray.entrySet().iterator(); iter.hasNext(); ) {
	LinkedHashMap.Entry element = (LinkedHashMap.Entry)iter.next();
	WeakReference<Object> aWeakObj = (WeakReference<Object>)element.getKey();
	WeakReference<Object> aWeakTag = (WeakReference<Object>)element.getValue();
}

消除警告后的代码如下:

final LinkedHashMap<WeakReference<Object>,WeakReference<Object>> aWeakArray = new LinkedHashMap<>();
for (Iterator<LinkedHashMap.Entry<WeakReference<Object>,WeakReference<Object>> > iter = aWeakArray.entrySet().iterator(); iter.hasNext(); ) {
	LinkedHashMap.Entry<WeakReference<Object>,WeakReference<Object>> element = iter.next();
	WeakReference<Object> aWeakObj = element.getKey();
	WeakReference<Object> aWeakTag = element.getValue();
}

同样的方法适用于"-Xlint:deprecation"。

Android Studio 1.4主工程中使用gradle:1.3.0生成包含NDK的APK没有包含非".so"后缀的文件

Android Studio 1.4主工程中使用gradle:1.3.0,编译NDK,其中生成两个文件,一个是.so的动态链接库,另一个是可执行程序,没有扩展名,在最后生成的APK中没有包含非".so"后缀的文件。这个问题纠结了好久,最后追踪到了Android Studio对应的Gradle代码文件中。发现在

Android Studio\gradle\m2repository\com\android\tools\build\builder\1.3.0\builder-1.3.0-sources.jar!\com\android\builder\internal\packaging\Packager.java

文件中有一个变量

private static final Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",Pattern.CASE_INSENSITIVE);

这个变量决定了最后的打包时候的过滤条件。

具体的代码如下图所示:ndk_build_not_include
最后的解决方案,就是把编译之后的文件重新命名成为“.so”。

这边的build.gradle的配置如下:

(其他配置参考Android Studio 1.2 开发JNI工程

task ndkBuild(type: Exec) {
	def Properties localProps = new Properties()
	localProps.load(new FileInputStream("local.properties"))
	def ndk_dir=localProps['ndk.dir']

	def ndk_build_cmd = "$ndk_dir/ndk-build"
	if (Os.isFamily(Os.FAMILY_WINDOWS)) {
		ndk_build_cmd = "$ndk_dir/ndk-build.cmd"
	}
	if(ndkBuild_Debug_Enabled) {
		commandLine ndk_build_cmd,
				'-j', Runtime.runtime.availableProcessors(),
				"NDK_PROJECT_PATH=$rootDir/app/src/main",
				"NDK_OUT=$buildDir/native/obj",
				"NDK_DEBUG=1"
	} else{
		commandLine ndk_build_cmd,
				'-j', Runtime.runtime.availableProcessors(),
				"NDK_PROJECT_PATH=$rootDir/app/src/main",
				"NDK_OUT=$buildDir/native/obj"
	}
	doLast {
		/* com.android.build.gradle.tasks.PackageApplication->doFullTaskAction->getBuilder().packageApk
		*Android Studio\gradle\m2repository\com\android\tools\build\builder\1.3.0\builder-1.3.0-sources.jar!\com\android\builder\internal\packaging\Packager.java
		*中的过滤变量     private static final Pattern PATTERN_NATIVELIB_EXT = Pattern.compile("^.+\\.so$",Pattern.CASE_INSENSITIVE);
		* 不能包含名字不是SO的其他文件
		*/
		File file = new File("$rootDir/app/src/main/libs");
		File[] files = file.listFiles(new FilenameFilter() {
			/**
			 * 测试指定文件是否应该包含在某一文件列表中。
			 *
			 * @param dir
			 *            被找到的文件所在的目录。
			 * @param name
			 *            文件的名称。
			 * @return 当且仅当该名称应该包含在文件列表中时返回 true;否则返回
			 *         false。返回true时,该文件将被允许添加到文件列表中,否则不能添加到文件列表中。
			 */
			public boolean accept(File dir, String name) {
				File f = new File(dir, name);
				if (f.isDirectory()) {
					File rf = new File(f.getPath(), "Standalone");
					if(rf.exists()){
						return true;
					}
					return false;
				}
				else {
					return false;// 否则返回false。
				}
			}
		});
		for(File f : files) {
			File src_file = new File(f.getAbsolutePath(), "Standalone");
			File dst_file = new File(f.getAbsolutePath(), "Standalone.so");
			dst_file.delete();
			src_file.renameTo(dst_file)
		}
	}
}

Windows下面Android Studio提示“Can't use Subversion command line client: svn”

Windows下面Android Studio提示

Errors found while svn working copies detection. Fix it.
Can't use Subversion command line client: svn
Probably the path to Subversion executable is wrong. Fix it. (show balloon)
Can't use Subversion command line client: svn
Probably the path to Subversion executable is wrong. Fix it. (show balloon)

如果安装TortoiseSVN的时候没有选中Command Line Client的话,可能会导致上面的问题。

解决方法就是重新安装Tortoise SVN,在安装的时候选上“command line client tools”即可。
TortoiseSVNCommandLineInstall

Android模拟器, push文件到system下文件夹权限,空间,SO文件没有自动安装的问题

  • 只读文件系统

需要把APK Push到模拟器下面的 /system/app 目录下面,报告

$ adb push app-debug.apk /system/app
failed to copy 'app-debug.apk' to '/system/app/app-debug.apk': Read-only file system

解决方法

$ adb remount
  • 内存不足
$ adb push app-debug.apk /system/app
failed to copy 'app-debug.apk' to '/system/app/app-debug.apk': Out of memory

原因众说纷纭,基本上大家都没怎么深究,有些镜像没有这个问题,有些就有问题。
解决方法:
不要使用Eclipse或者Android Studio 或者 AVD Manager的图形界面去启动模拟器,而是使用下面的命令:

$Android_SDK_HOME/tools/emulator -partition-size 256 -avd "模拟器的名字"
  • 包含SO的APK启动崩溃,日志中显示无法找到SO文件

原因,Android 设计问题,如果system/app下面的APK包含SO文件,不会自动安装,需要手工PUSH 到 "/system/lib"目录下面。

  • Android 5.0之后,最好推送到/system/priv-app目录

5.0之后的Android,最好推送到/system/priv-app目录。

  • Android 5.0之后,推送到系统目录后,没有自动安装应用

原因,Android 5.0之后,没有实时监视/system/priv-app目录的变化,只有在系统启动的时候才会扫描一下(重启系统很慢,我们可以按照如下操作节约时间),因此需要手工通知一下(有时候需要修改一下权限才可以)。

$ chown 644 /system/priv-app/my-app.apk

$ adb shell am restart

ERROR: Non-debuggable application installed on the target device. Please re-install the debuggable version!

注意,本文的描述,必须完全满足下面的条件,并且要确定已经在编译ndk的时候,已经使用了 NDK_DEBUG=1。并且打包APK的时候已经包含gdbserver,gdb.setup。

最近在调试NDK的时候,发现一个比较棘手的问题,一直报告错误“ERROR: Non-debuggable application installed on the target device. Please re-install the debuggable version! ”如下面所示:

$ ndk-gdb
WARNING: The shell running this script isn't bash.  Although we try to avoid bashism in scripts, things can happen.
/path_ndk/ndk-gdb: 214: /path_ndk/ndk-gdb: Bad substitution
ERROR: Non-debuggable application installed on the target device.
       Please re-install the debuggable version!

要求ndk-gdb输出详细的执行过程如下:

$ ndk-gdb.py --force --verbose
Android NDK installation path: /Android/android-ndk-r10e
ADB version found: Android Debug Bridge version 1.0.31
Using ADB flags: 
Using auto-detected project path: .
Found package name: com.xxxx.xxxx
ABIs targetted by application: Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 14 in ./AndroidManifest.xml    
Device API Level: 18
Device CPU ABIs: armeabi-v7a armeabi
Compatible device ABI: 
Using gdb setup init: Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 14 in ./AndroidManifest.xml    
Using toolchain prefix: Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 14 in ./AndroidManifest.xml    
Using app out directory: Android NDK: WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 14 in ./AndroidManifest.xml    
Found debuggable flag: true
ERROR: Could not find gdbserver binary under ./libs/
       This usually means you modified your AndroidManifest.xml to set
       the android:debuggable flag to 'true' but did not rebuild the
       native binaries. Please call 'ndk-build' to do so,
       *then* re-install to the device!

可以观察到几个奇怪的地方,比如,报错的地方提示"ERROR: Could not find gdbserver binary under ./libs/" ,正常情况下,应该是"./libs/armeabi-v7a","./libs/armeabi"之类的东西,并且“Compatible device ABI: ”部分,是不应该输出为空的情况的。这说明,没有正确的读取到APK中的关于CPU相关的数据。另外,注意这句话“WARNING: APP_PLATFORM android-19 is larger than android:minSdkVersion 14 in ./AndroidManifest.xml

庆幸的是,Google提供了Python版本的ndk-gdb 因此,我们在Ubuntu 15.04下面使用Spyder来跟踪调试,观察到底哪里出了问题。
设置如下:
Configure_Spyder
调整需要跟踪调试的目录到工程中正常执行ndk-gdb所在的目录:
Configure_Path
跟踪之后发现问题如下图所示:
ndk_gdb_py_bug

也就是说,当AndroidManifest.xml中设置的版本号“<uses-sdk android:minSdkVersion="14" />跟在 Application.mk 中设置的版本号“APP_PLATFORM := android-19”,当两者不一致的时候,会导致返回的APP_ABIS不是正常情况下的"[armeabi,armeabi-v7a]"这种形式的返回,而是返回了错误信息的详情,而这个仅仅是个警告而已,也就是说是Google 的一个BUG.

了解了原因,就比较好解决问题了,只要两者修改成为一致就可以了

Android Studio 1.3 配置编译NDK参考文档

Experimental Plugin User Guide

Introduction

The new experimental plugin is based on Gradle’s new component model mechanism, while allows significant reduction in configuration time. It also includes NDK integration for building JNI applications. This user guides provides details on how to use it and highlights the difference between the new plugin and the original plugin.

WARNING: Note that this is plugin is at the experimental stage. The Gradle API for the new component model is not final, which means it’ll only work with a specific version of Gradle until the APIs are final.

Additionally, the DSL is likely change significantly, as APIs to create the DSL are finalized.

This is a very early preview of the plugin for feedback on performance and NDK integration.

Requirements

  • Gradle 2.5 only
  • Android NDK r10e (if you are using NDK)
  • SDK with Build Tools at least version 19.0.0 and we aim to minimize the amount of changes needed for the migration process in the future. Some features may require a more recent version.

Migrating from Traditional Android Gradle Plugin

A typical Android Studio project may have a directory structure as follows. File that needs to be change is highlighted in red:
There are some significant changes in the DSL between the new plugin and the traditional one.

.

├── app/

│   ├── app.iml

│   ├── build.gradle

│   └── src/

├── build.gradle

├── gradle/

│   └── wrapper/

│       ├── gradle-wrapper.jar

│       └── gradle-wrapper.properties

├── gradle.properties

├── gradlew*

├── gradlew.bat

├── local.properties

├── MyApplication.iml

└── settings.gradle

 

./gradle/wrapper/gradle-wrapper.properties

  • The new plugin supports only gradle-2.5.

#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip

./build.gradle

  • Classpath for the plugin is com.android.tools.build:gradle-experimental instead of com.android.tools.build:gradle.

  • The current version is 0.2.0.

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

      repositories {

            jcenter()

      }

      dependencies {

            classpath 'com.android.tools.build:gradle-experimental:0.2.0'

          // NOTE: Do not place your application dependencies here

        // in the individual module build.gradle files

      }

}

allprojects {

      repositories {

            jcenter()

      }

}

 

./app/build.gradle

There are significant changes to the DSL of the plugin.  We understand that many of the changes are frustrating and seem unnecessary, and our goal is to remove some of these current changes to minimize the migration process from the traditional plugin in the future.

DSL Changes:

  • Plugin name is com.android.model.application instead of com.android.application. Or use apply plugin: 'com.android.model.library' if you want to create an Android aar library.

  • Configuration is wrapped with the model { } block

  • Most properties require the = operator

  • Adding elements to a Collection should be done using the += operator.

Current DSL Limitations that will hopefully go away:

  • buildTypes, productFlavors and signingConfigs must be place outside of the  android { } block.

  • Nested options within the android { } block must be configured using the with keyword.

  • Properties are only set with their direct types only, with no way to accept other types and adapting them. For instance:

    • Properties of type File accepts only File instead of File and String objects.

    • minSdkVersion cannot directly receive either an integer or string (for codename).

  • Creating a buildType or productFlavor requires calling the create method.  Modifying an existing one such as the release and debug buildType can be done using the just the name.

  • The DSL for modifying variants and their tasks is very, very limited right now.

apply plugin: 'com.android.model.application'

model {

      android {

           compileSdkVersion = 22

           buildToolsVersion = "22.0.1"

           defaultConfig.with {

                applicationId =  "com.example.user.myapplication"

                minSdkVersion.apiLevel = 15

                targetSdkVersion.apiLevel = 22

                versionCode = 1

                versionName = "1.0"

                buildConfigFields.with {

                     create() {

                     type = "int"

                     name = "VALUE"

                     value = "1"

                     }

                }

           }

      }

      android.buildTypes {

           release {

                minifyEnabled = false

                proguardFiles += file('proguard-rules.pro')

           }

      }

      android.productFlavors {

           create("flavor1") {

                applicationId = ‘com.app’

           }

      }

      // Configures source set directory.

      android.sources {

           main {

                java {

                     source {

                          srcDir 'src'

                     }

                }

           }

      }

}

dependencies {

      compile fileTree(dir: 'libs', include: ['*.jar'])

      compile 'com.android.support:appcompat-v7:22.2.0'

}

 

Ndk Integration

The experimental plugin comes with NDK integration for creating native applications.  To use the NDK integration:

  • Use the SDK Manager inside Studio to download the NDK.

  • Set ndk.dir in local.properties or the ANDROID_NDK_HOME environment variable to the directory containing the NDK.

  • Add an android.ndk block to the model in build.gradle.

Known Limitations

  • There’s no support for NDK-only modules. The only supported project types are hybrid app projects and hybrid Library Projects.
  • Consumed Library project don’t impact compilation of jni code in the consuming project (ie the AAR so files are simply packaged in the APK)
  • No support for creating and depending on static libraries
  • No support for using a NDK modules like cpu_features
  • No support for integrating external build systems.

The build.gradle of a simple NDK application may look like this:

apply plugin: 'com.android.model.application'

model { 
  android { 
    compileSdkVersion = 22 
    buildToolsVersion = "22.0.1"
  }
  android.ndk {
    moduleName = "native"
  }
}

*Note that the moduleName is required.  It determines the name of the resulting native library.

By default, it will look in src/main/jni for C/C++ file.  Configure android.sources to change the source directory.

model { 
  android { 
    compileSdkVersion = 22 
    buildToolsVersion = "22.0.1"
  }
  android.ndk {
    moduleName = "native"
  }
  android.sources {
    main {
      jni {
        source {
          srcDir 'src'
        }
      }
    }
  }
}

Various build options can be set within the android.ndk { } block.  For example,

model {
  android {
    compileSdkVersion = 22
    buildToolsVersion = "22.0.1"
  }
  android.ndk {
    // All configurations that can be changed in android.ndk.
    moduleName = "native"
    toolchain = "clang"
    toolchainVersion = "3.5"
    // Note that CFlags has a capital C, which is inconsistent with
    // the naming convention of other properties.  This is a
    // technical limitation that will be resolved
    CFlags += "-DCUSTOM_DEFINE"
    cppFlags += "-DCUSTOM_DEFINE"
    ldFlags += "-L/custom/lib/path"
    ldLibs += "log"
    stl = "stlport_static"
  }
  android.buildTypes {
    release {
      ndk.with {
        debuggable = true
      }
    }
  }

   android.productFlavors {
       create("arm") {
           ndk.with {
               // You can customize the NDK configurations for each
               // productFlavors and buildTypes.
               abiFilters += "armeabi-v7a"
           }
       }
       create("fat") {
           // If ndk.abiFilters is not configured, the application
           // compile and package all suppported ABI.
       }
   }
   // You can modify the NDK configuration for each variant.
   components.android {
       binaries.afterEach { binary ->binary.mergedNdkConfig.cppFlags.add("-DVARIANT=\"" + binary.name + "\"")}
   }
}

Samples

Additional samples can be found at https://github.com/googlesamples/android-ndk.
对于不能正常访问GitHub的,可以点击这里下载最近保存的版本。

Android Studio 1.3.2 NDK编译报错 'com.android.build.gradle.managed.ProductFlavor_Impl'

Android Studio 1.3.2已经支持NDK的编译,调试。但是已经跟老的项目通过配置 Application.mk,Android.mk来编译NDK的形式完全不同了。参考编译配置文档,采用了Gradle 2.5的配置方式(详细配置参考 Android Studio 1.3 配置编译NDK参考文档,Google官方链接),结果在编译报错,显示如下信息:

Error:Unable to load class 'com.android.build.gradle.managed.ProductFlavor_Impl'.
Possible causes for this unexpected error include:

  • You are using JDK version 'java version "1.7.0_80"'. Some versions of JDK 1.7 (e.g. 1.7.0_10) may cause class loading errors in Gradle.
    Please update to a newer version (e.g. 1.7.0_67).
    Open JDK Settings
  • Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)
    Re-download dependencies and sync project (requires network)
  • The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.
    Stop Gradle build processes (requires restart)
  • Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.

In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.

查找了好长时间原因,最后发现,是由于build.gradle中修改有问题。
先看看有问题的build.gradle,注意defaultConfig.with部分的差别

apply plugin: 'com.android.model.application'

model {
	android {
		compileSdkVersion = 23
		buildToolsVersion = "23.0.0"

		defaultConfig.with {
			applicationId = "com.helloworld"
			minSdkVersion = 14
			targetSdkVersion = 22
			}
		}
	android.buildTypes {
		release {
			minifyEnabled = false
			proguardFiles += file('proguard-rules.txt')
			}
		}
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
}

修改后的为(注意defaultConfig.with部分的差别

apply plugin: 'com.android.model.application'

model {
	android {
		compileSdkVersion = 23
		buildToolsVersion = "23.0.0"

		defaultConfig.with {
			applicationId = "com.helloworld"
			minSdkVersion.apiLevel = 14
			targetSdkVersion.apiLevel = 22
			}
		}
	android.buildTypes {
		release {
			minifyEnabled = false
			proguardFiles += file('proguard-rules.txt')
			}
		}
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.2.0'
}

一个疏忽,浪费时间很多啊!

在Android Studio中进行单元测试和UI测试

本篇教程翻译自Google I/O 2015中关于测试的codelab,掌握科学上网的同学请点击这里阅读:Unit and UI Testing in Android Studio。能力有限,如有翻译错误,请批评指正。如需转载,请注明出处。
Github下载测试源码

目录


1.概述

在这个codelab中,你将学习如何在Android Studio中配置工程用于测试,在开发机器上编写并运行单元测试,以及如何在手机上做功能UI测试。

你会学到什么

  • 更新包含JUnit和Android Testing Support Library的Gradle构建文件
  • 编写运行在本机Java虚拟机上的单元测试
  • 编写运行在手机或者虚拟机上的Espresso测试

你需要什么


2.创建新的Android Studio工程

如果是第一次启动Android Studio,从欢迎页选择“Start a new Android Studio project”。如果已经打开了一个工程,选择File>New>New Project...

Create new project”向导会指导整个过程,在第一页输入如下内容:

Setting Value
Application Name TestingExample
Company demain testing.example.com

这样会保证你的代码同codelab讲解的内容具有一致的命名。其他的选项都设置为默认,一路点击Next直到工程创建完毕。

点击Run按钮检查app是否运行正常,要么从模拟器列表中选择一个启动,要么确认开启了debug模式的设备通过USB同电脑正确连接。

app目前没有做任何事情,但是屏幕上应该显示“Hello world!”和app的名字。

580359-839b775d39f912f6

经常被问及的问题


3.配置支持单元测试的工程

在写测试之前,让我们做下简单的检查,确保工程配置正确。

首先,确认在Build Variants窗口内的Test Artifact中选择了"Unit Tests"。

580359-ab4402443ad7dc5f

然后,在工程的src文件夹内创建testtest/java文件夹。需要注意的是,你不能在Android视图下进行这些操作,要么在系统的文件管理器内创建,要么在工程窗口左上方点击下拉菜单选择Project视图。最终的工程结构应该是这样的:

580359-9e098817f6fcca44

(在codelab的剩余部分,你可以返回继续使用Android工程视图)

最后,打开工程的build.gradle(Module:app)文件,添加JUnit4依赖,点击Gradle sync按钮。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.1.1'
    testCompile 'junit:junit:4.12'
}

当你同步Gradle配置时,可能需要联网下载JUnit依赖。


4.创建第一个单元测试

现在,万事俱备,让我们开始写第一个测试吧。首先,创建一个非常简单的被测类:Calculator类。

580359-20cce1345b5076de

然后,向类中添加一些基本的算术运算方法,比如加法和减法。将下列代码复制到编辑器中。不用担心实际的实现,暂时让所有的方法返回0。

package com.example.testing.testingexample;

public class Calculator {

    public double sum(double a, double b){
        return 0;
    }

    public double substract(double a, double b){
        return 0;
    }

    public double divide(double a, double b){
        return 0;
    }

    public double multiply(double a, double b){
        return 0;
    }
}

Android Studio提供了一个快速创建测试类的方法。只需在编辑器内右键点击Calculator类的声明,选择Go to > Test,然后"Create a new test…"

580359-729c021ff61b0dc7

在打开的对话窗口中,选择JUnit4和"setUp/@Before",同时为所有的计算器运算生成测试方法。

580359-19f96a03d2fa811a

这样,就会在正确的文件夹内(app/src/test/java/com/example/testing/testingexample)生成测试类框架,在框架内填入测试方法即可。下面是一个示例:

package com.example.testing.testingexample;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class CalculatorTest {

    private Calculator mCalculator;

    @Before
    public void setUp() throws Exception {
        mCalculator = new Calculator();
    }

    @Test
    public void testSum() throws Exception {
        //expected: 6, sum of 1 and 5
        assertEquals(6d, mCalculator.sum(1d, 5d), 0);
    }

    @Test
    public void testSubstract() throws Exception {
        assertEquals(1d, mCalculator.substract(5d, 4d), 0);
    }

    @Test
    public void testDivide() throws Exception {
        assertEquals(4d, mCalculator.divide(20d, 5d), 0);
    }

    @Test
    public void testMultiply() throws Exception {
        assertEquals(10d, mCalculator.multiply(2d, 5d), 0);
    }
}

请将代码复制到编辑器或者使用JUnit框架提供的断言来编写自己的测试。


5.运行单元测试

终于到运行测试的时候了!右键点击CalculatorTest类,选择Run > CalculatorTest。也可以通过命令行运行测试,在工程目录内输入:

./gradlew test

无论如何运行测试,都应该看到输出显示4个测试都失败了。这是预期的结果,因为我们还没有实现运算操作。

580359-00a07e968baebccc

让我们修改Calculator类中的sum(double a, double b)方法返回一个正确的结果,重新运行测试。你应该看到4个测试中的3个失败了。

public double sum(double a, double b){
    return a + b;
}

作为练习,你可以实现剩余的方法使所有的测试通过。

可能你已经注意到了Android Studio从来没有让你连接设备或者启动模拟器来运行测试。那是因为,位于src/tests目录下的测试是运行在本地电脑Java虚拟机上的单元测试。编写测试,实现功能使测试通过,然后再添加更多的测试...这种工作方式使快速迭代成为可能,我们称之为测试驱动开发
值得注意的是,当在本地运行测试时,Gradle为你在环境变量中提供了包含Android框架的android.jar包。但是它们功能不完整(所以,打个比方,你不能单纯调用Activity的方法并指望它们生效)。推荐使用Mockito等mocking框架来mock你需要使用的任何Android方法。对于运行在设备上,并充分利用Android框架的测试,请继续阅读本篇教程的下个部分。


6.配置支持Instrumentation测试的工程

虽然在Android框架内支持运行instrumentation测试,但是目前开发重心主要集中在刚刚发布的作为Android Testing Support Library一部分的新的AndroidJUnitRunner。测试库包含Espresso,用于运行功能UI测试的框架。让我们通过编辑build.gradle的相关部分来把它们添加进我们的工程。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.example.testing.testingexample"
        minSdkVersion 15
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        //ADD THIS LINE:
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    //ADD THESE LINES:
    packagingOptions {
        exclude 'LICENSE.txt'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.0.0' //← MAKE SURE IT’S 22.0.0
    testCompile 'junit:junit:4.12'

    //ADD THESE LINES:
    androidTestCompile 'com.android.support.test:runner:0.2'
    androidTestCompile 'com.android.support.test:rules:0.2'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1'
}

重要:由于一些依赖版本冲突,你需要确认com.android.support:appcompat-v7库的版本号是22.0.0,像上面的代码片段一样。
另外,Android Studio可能会提醒你Build Tools 22.0.1没有安装。你应该接受修复建议,Studio会为你安装Build Tools或者在build.gradle中把这行修改成已经安装在你电脑的版本。

上面的工作完成后,在Build Variants窗口内切换成Android Instrumentation Tests,你的工程应该自动同步。如果没有,点击Gradle sync按钮。


7.为app添加简单的交互

580359-40a6436d81203a3d

在使用Espresso进行UI测试前,让我们为app添加一些Views和简单的交互。我们使用一个用户可以输入名字的EditText,欢迎用户的Button和用于输出的TextView。打开res/layout/activity_main.xml,粘贴如下代码:
activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:text="@string/hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <EditText
        android:hint="Enter your name here"
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Say hello!"
        android:layout_below="@+id/editText"
        android:onClick="sayHello"/>
</RelativeLayout>

还需要在MainActivity.java中添加onClick Handler

public void sayHello(View v){
    TextView textView = (TextView) findViewById(R.id.textView);
    EditText editText = (EditText) findViewById(R.id.editText);
    textView.setText("Hello, " + editText.getText().toString() + "!");
}

现在可以运行app并确认一切工作正常。在点击Run按钮之前,确认你的Run Configuration没有设置为运行测试。如需更改,点击下拉选项,选择app


8.创建并运行Espresso测试

580359-182d42c3cc27596a

在工程的整体视图上,找到以(androidTest)后缀结尾的包名并创建一个新的Java类。可以将它命名为MainActivityInstrumentationTest,将如下代码粘贴过去。

package com.example.testing.testingexample;

import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.action.ViewActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {

    private static final String STRING_TO_BE_TYPED = "Peter";

    @Rule
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
        MainActivity.class);

    @Test
    public void sayHello(){
        onView(withId(R.id.editText)).perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); //line 1

        onView(withText("Say hello!")).perform(click()); //line 2

        String expectedText = "Hello, " + STRING_TO_BE_TYPED + "!";
        onView(withId(R.id.textView)).check(matches(withText(expectedText))); //line 3
    }

}

测试类通过AndroidJUnitRunner运行,并执行sayHello()方法。下面将逐行解释都做了什么:

  • 1.首先,找到ID为editText的view,输入Peter,然后关闭键盘;
  • 2.接下来,点击Say hello!的View,我们没有在布局的XML中为这个Button设置id,因此,通过搜索它上面的文字来找到它;
  • 3.最后,将TextView上的文本同预期结果对比,如果一致则测试通过;

你也可以右键点击域名运行测试,选择Run>MainActivityInstrume...(第二个带Android图标的)

580359-86da68654bd41cb1

这样就会在模拟器或者连接的设备上运行测试,你可以在手机屏幕上看到被执行的动作(比如在EditText上打字)。最后会在Android Studio输出通过和失败的测试结果。

Github下载测试源码


9.祝贺

我们希望你能喜欢本篇教程,并且开始着手测试你的应用程序。接着你可以学习如下内容:

出处 在Android Studio中进行单元测试和UI测试