不只自動化而且更敏捷的android開發工具 gradle mopcon

64
A productive Android development environment 不只自動化而且更敏捷的Android開發工具 Gradle Sam Chiu

Upload: sam-chiu

Post on 09-Jan-2017

3.361 views

Category:

Mobile


4 download

TRANSCRIPT

Page 1: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

A productive Android development environment

不只自動化而且更敏捷的Android開發工具 Gradle

Sam Chiu

Page 2: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Sam Chiu 邱炫儒Sam Chiu@HTC engineering build dept.

promote DevOps in HTC

[email protected] / mindcraft4life.tumblr.com

Active Gradle Using Developers

500+

Software Components

1000+

Release Builds per Day

1000+

Page 3: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

“ ”

The challenge of Mobile App development

Page 4: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

If your App crashes for 0.01% of your users, they’re going to post a negative review.

However, the 99.99% of people for whom it works great are NOT going to post five-star ratings for the software working as expected.

-- The Loop magazine

Page 5: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Automate everythingand shrink the cycle time

可重複運行 減少人為/環境錯誤 流程透明 產生更有品質的程式碼

Coding Check-in Build Test Publish

易於分享

Page 6: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Using Gradle...

put every new code commit through a full cycle

Automated provisioning and configuration management also part of the continuous delivery

From Command line to IDE to CI

Page 7: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

消除溝通的嫌隙From Command line to IDE to CI

Convention 1

Page 8: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

一句話惹毛老闆:

“我電腦沒這個問題呀!”“It works on my machine!”

Page 9: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

“一定有鬼偷偷動過我的程式碼”

Page 10: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

使用不一致的工具

ant

proguardshrinkresources

androidgradle plugin

build.xmlADT

eclipse

gradle

debug on/off

Source Code

module A

module B

moduleC build failure

Those tools have different build processes on your source code!

Page 11: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Android Studio/IntelliJ/..

gradle

減少溝通的嫌隙From command line to IDE to continuous integration

proguardshrinkresources

android gradle plugin

debug on/off

Coding with IDE

Building Automation

Page 12: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

減少溝通的嫌隙From command line to IDE to continuous integration

Android Studio

proguardshrinkresources

android gradle plugin

gradle

debug on/off customizedconfiguration

genericconfiguration

Source Code

Source Code

Source Code

APK

ALPHA BETA RCProject

Schedule

Page 13: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

後期出現的錯誤往往影響巨大

Page 14: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

將商業版本客製盡量提前到開發時期

Page 15: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Product Flavor and Multiple APK

Convention 2

Page 16: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

<manifest ... >

<supports-screens android:smallScreens="false"

android:normalScreens="false"

android:largeScreens="true"

android:xlargeScreens="true"

android:requiresSmallestWidthDp="600"

/>

...

</manifest>

custom AndroidManifest.xml for tablet

Example on Google dev site:Design Multi-APK for tablet and phone

Page 17: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Google Play

The versionName/versionCode schema for multiple APK

versionCode (04 01 310)

phone screenApp version(3.1.0)API Level(4+)

versionCode (04 02 310)

tablet screenApp version(3.1.0)API Level(4+)

release 1upload APKs

Version Name: 3.1.0-build1

Page 18: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Edit your versionCode in AndroidManifest.xml?<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.android.testing.unittesting.BasicSample" >

<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:versioncode="0401310" android:versionName="3.1.0-build1" > <activity android:name="com.example.android.testing.unittesting.BasicSample.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>

</manifest>

error-prone manual efforts around deployment

Page 19: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

把處理邏輯都編寫至gradle設定檔中

Page 20: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

android{defaultConfig {

versionName computeVersionName()}productFlavors {

phone{ versionCode computeVersionCode(1) } tablet { versionCode computeVersionCode(2) }

}}def computeVersionCode(int flavor) { def version_code = ext.minSdkVersion * 100000 + flavor * 1000 + ext.versionMajor * 100 + ext.versionMinor*10 \

+ ext.versionIncremental return version_code}def computeVersionName(){

def buildNumber = System.getenv("BUILD_NUMBER") ?: "dev"def version_name = ext.versionMajor+"."+ext.versionMinor+”.”+ext.versionIncremental+"-"+buildNumberreturn version_name

}

custom build logic and integrate with Jenkinsbuild.gradle:

Page 21: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

為什麼要把Jenkins buildNumber 寫進version name?

混在一起做撒尿牛丸?

Page 22: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

為什麼要把Jenkins buildNumber 寫進version name?

混在一起做Dogfooding

Page 23: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

What is Dogfooding?

Page 24: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Dogfooding means a good product versioningLibrarySource

ProjectSource

alpha 1 beta1Debug APK

Release 2Release APK

beta2

Release 1

alpha 1 beta2

OTA

Issue Tracking bug report

Release 2

OTAPRELOAD

Employee Device

Page 25: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

透過Jenkins追朔程式碼

versionName 3.1.0-Build#2

Page 26: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Q:處理邏輯要寫在gradle script 還是寫在CI Server script?

Page 27: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

寫在CI Server script的處理邏輯如何管理?

期待 jenkins 2.0

pipeline as code !

what is “pipeline as code”:https://wiki.jenkins-ci.org/display/JENKINS/2.0+Pipeline+as+Code

Page 28: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

java code preprocessing

Convention 3

Page 29: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

public final static boolean MY_DEBUG = false;

@Overridepublic void onConnectionFailed(ConnectionResult result) { if (MY_DEBUG){ Log.i(TAG, "[Security sensitive Log] GoogleClient connection failed." ); Log.i(TAG, "[Security sensitive Log] UserName: "+UserName ); Log.i(TAG, "[Security sensitive Log] InternalModelName: "+InternalModelName ); }else{ Log.i(TAG, "GoogleApiClient connection failed: " + result.toString()); }

每次專案定義可能都不同大家都需要去記Flag

增加自動化的複雜度

adjust consts to switch debug on/off

What is bad直接修改程式碼

error-prone manual efforts around deployment AGAIN!

Page 30: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Use BuildConfig in gradlepublic final static boolean MY_DEBUG = false;

@Overridepublic void onConnectionFailed(ConnectionResult result) { if (BuildConfig.DEBUG){ Log.i(TAG, "[Security sensitive Log] GoogleClient connection failed." ); Log.i(TAG, "[Security sensitive Log] UserName: "+UserName ); Log.i(TAG, "[Security sensitive Log] InternalModelName: "+InternalModelName ); }else{ Log.i(TAG, "GoogleApiClient connection failed: " + result.toString()); }

編譯時期無需修改程式碼

$ gradlew assembleRelease$ gradlew assembleDebug

Page 31: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Customize BuildConfig in gradle

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

Page 32: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

從程式碼管理

到組態管理

Convention 4

Page 33: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

“By distributing your project source based on gradle ,anyone can work with it without needing to install many annoying tools/dependencies beforehand”

“Users of the build are guaranteed to use the same process and the same dependencies version that was designed to work with”

from http://gradle.org/

Page 34: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

SCM (git)

.gradle/local.properties/.idea/workspace.xml/.idea/libraries.DS_Store/build/captures

Use .gitignore

建立可重複且可靠的流程

Build script as code

Page 35: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Build Once and build anywhere

Why upload shell/batch files to version control server?

Page 36: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

gradlew.sh

gradle wrapper

#Sun Aug 09 19:57:07 CST 2015distributionBase=GRADLE_USER_HOMEdistributionPath=wrapper/distszipStoreBase=GRADLE_USER_HOMEzipStorePath=wrapper/distsdistributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip

gradle-wrapper.properties

Page 37: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Even better! Install android-sdk automaticallyapply plugin: 'android-sdk-manager'apply plugin: 'com.android.application'

android { compileSdkVersion 21 buildToolsVersion '22.0.1' defaultConfig { applicationId "com.example.android.testing.unittesting.BasicSample" minSdkVersion 17 targetSdkVersion 23 }}

Reference: https://github.com/JakeWharton/sdk-manager-plugin

Page 38: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

gradle project on git

pure Mac OS

Java

source code

build script

dependenciesconfiguration

toolchain configuration

Maven Repo

toolchain sites

google play servicefacebook sdk lib firebase sdk lib...

gradle tool

android sdk

pure Windows

Java

less administration and more scalable for the client machines

gradle 2.4 gradle 2.5

build-tool 19build-tool 20platforms 19platforms 23

pure linux

Java

pure linux

Java

pure linux

Java ...developers machines Continuous Integration servers

install tools on build time

Page 39: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Dependencies Management

Convention 5

Page 40: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

root project lib-proj widget

lib-projActivity

firebase sdk

facebook sdk

evernote sdk

dropbox sdk

Google play service libgooglesupport v4

log/util module

drive

Google+

games

analytics

Location

Ads

maps

nearby

wallet

google support appcompat-v7

google support design lib

google supportmultidex

google supportmultidex instrumentation

A large scale Android App

Page 41: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

root project lib-proj widget

lib-projActivity

firebase sdk

evernote sdk

dropbox sdk

Google play service libgooglesupport v4

log/util module

drive

Google+

games

analytics

Location

Ads

maps

nearby

wallet

google support appcompat-v7

google support design lib

google supportmultidex

google supportmultidex instrumentation

facebook sdk

The dependency nightmare

Page 42: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Before Gradle

Getting started with the Dropbox Android SDK:1. Include everything under lib/ in your project/build.2. You'll want to start off by creating an AndroidAuthSession with your consumer key and secret.3...

Download dropbox-android-sdk from dropbox website

1

2

Page 43: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

到底用了哪一版library?

┌Top Android Project│├── Project 1 - (Pure Java Modules)│ ││ ├── Module A1│ │ └──libs/android-support-v4_22.jar│ ├── Module B1│ :│ └── facebook-lib-project│ └──libs/android-support-v4-v22.0.0.jar│

BUILD FAIL

Page 44: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

After Gradle

dependencies {

compile 'com.facebook.android:facebook-android-sdk:4.1.0'

}

1. Use gradle

2.

3. That’s it.

dependencies {

compile 'com.google.android.gms:play-services-wearable:7.3.0'

}

Page 45: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

How about the depend on a tree of dependencies?

dependencies {

compile 'com.facebook.android:facebook-android-sdk:4.1.0'

}

dependencies {

compile 'com.google.android.gms:play-services-wearable:7.3.0'

}

android-support-v4:21.0.0

android-support-v4:22.0.0

Page 46: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Transitive Dependencies on Gradle(可遞移性相依)

A -> B and B -> Chence A -> C

Page 47: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

google play service aar library hierarchy on maven

Page 48: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Tips of transitive dependencies

dependencies {

compile 'com.google.android.gms:play-services-wearable:7.3.0'

}

dependencies {

compile 'com.google.android.gms:play-services-wearable:7.3.0

@aar'}

android-support-v4:22.0.0

android-support-v4:22.0.0

transitive=false

Page 49: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

As a library provider

<?xml version="1.0" encoding="UTF-8"?><project> <modelVersion>4.0.0</modelVersion> <groupId>com.google.android.gms</groupId> <artifactId>play-services-location</artifactId> <version>7.8.0</version> <packaging>aar</packaging> <dependencies> <dependency> <groupId>com.google.android.gms</groupId> <artifactId>play-services-maps</artifactId> <version>7.8.0</version> <scope>compile</scope> <type>aar</type> </dependency> …...

Maven Repository

HelloWorld Library/pom.xml

remember to provide your transitive dependencies in pom.xml

Page 50: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Handle dependency conflict

Gradle offers the following conflict resolution strategies:

Newest: The newest version of the dependency is used. This is Gradle's default strategy, and is often an appropriate choice as long as versions are backwards-compatible.

Fail: A version conflict results in a build failure. This strategy requires all version conflicts to be resolved explicitly in the build script.

Page 51: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Use ‘Fail’ resolution strategy

configurations.all { resolutionStrategy { // fail eagerly on version conflict (includes transitive dependencies) // e.g. multiple different versions of the same dependency (group and name are equal) failOnVersionConflict() }}

build.gradle:

Page 52: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Handle dependency conflict

dependencies { compile('org.hibernate:hibernate:3.1') { //in case of versions conflict '3.1' version of hibernate wins: force = true //disabling all transitive dependencies of this dependency transitive = false }}

build.gradle:

Page 53: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Versioning Control dependencies { compile 'com.facebook.android:facebook-android-sdk:4.1.0'}

dependencies { compile 'com.facebook.android:facebook-android-sdk:4.1.+'}

dependencies { compile 'com.facebook.android:facebook-android-sdk:4.1.0-SNAPSHOT'}

建置系統始終應該指定專案所需外部類別庫的確切版本 :

開發時期指定專案所需外部函式庫的最新版本 :

開發時期指定專案所需外部函式庫的 SNAPSHOT版本:

Page 54: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

about testing

Convention 6

Page 55: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Building Effective Testing planUnit test● mokito● Robolectric

Integration test (acceptance testing)

● Instrumentation● emulation

○ Genymotion (local with virtualBox)○ Manymo (online)

● online device testing○ appthwack (AWS Cloud)

Page 56: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Repeatable

- 可在任何環境下進行測試(沒有網路、db、Android Context的狀況下仍能進行)

Purpose

- 讓單元測試運行速度加快,可加速回饋,更可了解自己的修改是否破壞現有的任何功能。

How

- 單元測試依存於測試替身,不應該存取資料庫、檔案系統、與外部系統互動。- 簡單來說,單元測試不應該與系統元件之間產生互動。

A unit test should be “Repeatable”

Page 57: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Building Effective Unit TestsUnit test● mokito● Robolectric

put every new code commit through a full cycle!

Integration test (acceptance testing)

● Instrumentation● emulation

○ Genymotion (local with virtualBox)○ Manymo (online)

● online device testing○ appthwack (AWS Cloud)

Page 58: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

測試策略的設計主要是個「識別和評估專案風險之優先順序及

決定採用哪些行動來緩解風險」的歷程

Test Case Strategy

--Continuous Delivery:reliable software rleases though build, test and deployment automation

Page 59: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

測試策略的設計主要是個「識別和評估專案風險之優先順序及

決定採用哪些行動來緩解風險」的歷程

Test Case Strategy

--Continuous Delivery:reliable software rleases though build, test and deployment automation

未慮勝 先慮敗

Page 60: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Example: com.facebook.login.widget.LoginButtonTest.java

@Testpublic void testLoginButtonWithReadPermissions() throws Exception { LoginManager loginManager = mock(LoginManager.class); Activity activity = Robolectric.buildActivity(MainActivity.class).create().get();

LoginButton loginButton = (LoginButton) activity.findViewById(R.id.login_button); ArrayList<String> permissions = new ArrayList<>(); permissions.add("user_location"); loginButton.setReadPermissions(permissions); loginButton.setDefaultAudience(DefaultAudience.EVERYONE); loginButton.setLoginManager(loginManager); loginButton.performClick();

verify(loginManager).logInWithReadPermissions(activity, permissions); verify(loginManager, never()).logInWithPublishPermissions(isA(Activity.class), anyCollection()); // Verify default audience is channeled verify(loginManager).setDefaultAudience(DefaultAudience.EVERYONE);}

Reference: https://github.com/facebook/facebook-android-sdk

Page 61: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

com.facebook.login.widget.LoginButton.java

private class LoginClickListener implements OnClickListener {@Overridepublic void onClick(View v) {…

if (LoginAuthorizationType.PUBLISH.equals(properties.authorizationType)) {loginManager.logInWithPublishPermissions(LoginButton.this.getActivity(),properties.permissions);

} else {

loginManager.logInWithReadPermissions(LoginButton.this.getActivity(),properties.permissions);//[sam] demo unit test//loginManager.logInWithPublishPermissions(LoginButton.this.getActivity(),properties.permissions);

} }

Page 62: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Command-line Optionenable continuous build with the -t or –continuous command-line option which will automatically re-execute builds when changes are detected to its inputs.ex:

reference:http://gradle.org/feature-spotlight-continuous-build/

speed up your build-edit-build feedback loop

$ gradlew test -t

Page 63: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

From handcrafted to mindcrafted

Q&A

Page 64: 不只自動化而且更敏捷的Android開發工具 gradle mopcon

Reference:● Android plugin for gradle:https://developer.android.com/tools/building/plugin-for-gradle.html: ● Android tools project site, tips:http://tools.android.com/tech-docs/new-build-system/tips● Gradle dependency management:https://docs.gradle.org/current/userguide/dependency_management.html● Google dev site, multiple apk:https://developer.android.com/google/play/publishing/multiple-apks.html● Sample project on github:https://github.com/iamsamchiu/AndroidSampleForGradleUsage

The Android robot is modified from work created by Google/StockLogos and used according to the Creative Commons 3.0 Attribution License.