Upload
sam-chiu
View
3.365
Download
4
Embed Size (px)
Citation preview
A productive Android development environment
不只自動化而且更敏捷的Android開發工具 Gradle
Sam Chiu
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+
“ ”
The challenge of Mobile App development
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
Automate everythingand shrink the cycle time
可重複運行 減少人為/環境錯誤 流程透明 產生更有品質的程式碼
Coding Check-in Build Test Publish
易於分享
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
消除溝通的嫌隙From Command line to IDE to CI
Convention 1
一句話惹毛老闆:
“我電腦沒這個問題呀!”“It works on my machine!”
“一定有鬼偷偷動過我的程式碼”
使用不一致的工具
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!
Android Studio/IntelliJ/..
gradle
減少溝通的嫌隙From command line to IDE to continuous integration
proguardshrinkresources
android gradle plugin
debug on/off
Coding with IDE
Building Automation
減少溝通的嫌隙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
後期出現的錯誤往往影響巨大
將商業版本客製盡量提前到開發時期
Product Flavor and Multiple APK
Convention 2
<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
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
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
把處理邏輯都編寫至gradle設定檔中
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:
為什麼要把Jenkins buildNumber 寫進version name?
混在一起做撒尿牛丸?
為什麼要把Jenkins buildNumber 寫進version name?
混在一起做Dogfooding
What is Dogfooding?
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
透過Jenkins追朔程式碼
versionName 3.1.0-Build#2
Q:處理邏輯要寫在gradle script 還是寫在CI Server script?
寫在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
java code preprocessing
Convention 3
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!
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
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' }}
從程式碼管理
到組態管理
Convention 4
“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/
SCM (git)
.gradle/local.properties/.idea/workspace.xml/.idea/libraries.DS_Store/build/captures
Use .gitignore
建立可重複且可靠的流程
Build script as code
Build Once and build anywhere
Why upload shell/batch files to version control server?
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
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
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
Dependencies Management
Convention 5
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
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
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
到底用了哪一版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
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'
}
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
Transitive Dependencies on Gradle(可遞移性相依)
A -> B and B -> Chence A -> C
google play service aar library hierarchy on maven
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
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
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.
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:
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:
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版本:
about testing
Convention 6
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)
Repeatable
- 可在任何環境下進行測試(沒有網路、db、Android Context的狀況下仍能進行)
Purpose
- 讓單元測試運行速度加快,可加速回饋,更可了解自己的修改是否破壞現有的任何功能。
How
- 單元測試依存於測試替身,不應該存取資料庫、檔案系統、與外部系統互動。- 簡單來說,單元測試不應該與系統元件之間產生互動。
A unit test should be “Repeatable”
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)
測試策略的設計主要是個「識別和評估專案風險之優先順序及
決定採用哪些行動來緩解風險」的歷程
Test Case Strategy
--Continuous Delivery:reliable software rleases though build, test and deployment automation
測試策略的設計主要是個「識別和評估專案風險之優先順序及
決定採用哪些行動來緩解風險」的歷程
Test Case Strategy
--Continuous Delivery:reliable software rleases though build, test and deployment automation
未慮勝 先慮敗
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
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);
} }
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
From handcrafted to mindcrafted
Q&A
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.