Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

3,842 views

Published on

探討 build script as code 的觀念以及如何善用 Gradle 工具幫助 Android App 的開發。

Published in: Mobile
  • Hey guys! Who wants to chat with me? More photos with me here 👉 http://www.bit.ly/katekoxx
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

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

  1. 1. A productive Android development environment 不只自動化而且更敏捷的Android開發工具 Gradle Sam Chiu
  2. 2. Sam Chiu 邱炫儒 Sam Chiu@HTC engineering build dept. promote DevOps in HTC iamsamchiu@gmail.com / mindcraft4life.tumblr.com Active Gradle Using Developers 500+ Software Components 1000+ Release Builds per Day 1000+
  3. 3. “ ” The challenge of Mobile App development
  4. 4. 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
  5. 5. Automate everything and shrink the cycle time 可重複運行 減少人為/環境錯誤 流程透明 產生更有品質的程式碼 Coding Check-in Build Test Publish 易於分享
  6. 6. 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
  7. 7. 消除溝通的嫌隙 From Command line to IDE to CI Convention 1
  8. 8. 一句話惹毛老闆: “我電腦沒這個問題呀!” “It works on my machine!”
  9. 9. “一定有鬼偷偷動過我的程式碼”
  10. 10. 使用不一致的工具 ant proguard shrink resources android gradle plugin build.xmlADT eclipse gradle debug on/off Source Code module A module B module C build failure Those tools have different build processes on your source code!
  11. 11. Android Studio/IntelliJ/.. gradle 減少溝通的嫌隙 From command line to IDE to continuous integration proguard shrink resources android gradle plugin debug on/off Coding with IDE Building Automation
  12. 12. 減少溝通的嫌隙 From command line to IDE to continuous integration Android Studio proguard shrink resources android gradle plugin gradle debug on/off customized configuration generic configuration Source Code Source Code Source Code APK ALPHA BETA RC Project Schedule
  13. 13. 後期出現的錯誤往往影響巨大
  14. 14. 將商業版本客製盡量提前到開發時期
  15. 15. Product Flavor and Multiple APK Convention 2
  16. 16. <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
  17. 17. Google Play The versionName/versionCode schema for multiple APK versionCode (04 01 310) phone screen App version(3.1.0)API Level(4+) versionCode (04 02 310) tablet screen App version(3.1.0)API Level(4+) release 1 upload APKs Version Name: 3.1.0-build1
  18. 18. 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
  19. 19. 把處理邏輯都編寫至gradle設定檔中
  20. 20. 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+"-"+buildNumber return version_name } custom build logic and integrate with Jenkins build.gradle:
  21. 21. 為什麼要把Jenkins buildNumber 寫進 version name? 混在一起做撒尿牛丸?
  22. 22. 為什麼要把Jenkins buildNumber 寫進 version name? 混在一起做Dogfooding
  23. 23. What is Dogfooding?
  24. 24. Dogfooding means a good product versioning Library Source Project Source alpha 1 beta1Debug APK Release 2Release APK beta2 Release 1 alpha 1 beta2 OTA Issue Tracking bug report Release 2 OTAPRELOAD Employee Device
  25. 25. 透過Jenkins追朔程式碼 versionName 3.1.0-Build#2
  26. 26. Q: 處理邏輯要寫在gradle script 還是 寫在CI Server script?
  27. 27. 寫在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
  28. 28. java code preprocessing Convention 3
  29. 29. public final static boolean MY_DEBUG = false; @Override public 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!
  30. 30. Use BuildConfig in gradle public final static boolean MY_DEBUG = false; @Override public 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
  31. 31. 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' } }
  32. 32. 從 程式碼管理 到 組態管理 Convention 4
  33. 33. “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/
  34. 34. SCM (git) .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures Use .gitignore 建立可重複且可靠的流程 Build script as code
  35. 35. Build Once and build anywhere Why upload shell/batch files to version control server?
  36. 36. gradlew.sh gradle wrapper #Sun Aug 09 19:57:07 CST 2015 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 gradle-wrapper.properties
  37. 37. Even better! Install android-sdk automatically apply 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
  38. 38. gradle project on git pure Mac OS Java source code build script dependencies configuration toolchain configuration Maven Repo toolchain sites google play service facebook 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 19 build-tool 20 platforms 19 platforms 23 pure linux Java pure linux Java pure linux Java ... developers machines Continuous Integration servers install tools on build time
  39. 39. Dependencies Management Convention 5
  40. 40. root project lib-proj widget lib-proj Activity firebase sdk facebook sdk evernote sdk dropbox sdk Google play service libgoogle support v4 log/util module drive Google+ games analytics Location Ads maps nearby wallet google support appcompat-v7 google support design lib google support multidex google support multidex instrumentation A large scale Android App
  41. 41. root project lib-proj widget lib-proj Activity firebase sdk evernote sdk dropbox sdk Google play service libgoogle support v4 log/util module drive Google+ games analytics Location Ads maps nearby wallet google support appcompat-v7 google support design lib google support multidex google support multidex instrumentation facebook sdk The dependency nightmare
  42. 42. 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
  43. 43. 到底用了哪一版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
  44. 44. 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' }
  45. 45. 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
  46. 46. Transitive Dependencies on Gradle(可遞移性相依) A -> B and B -> C hence A -> C
  47. 47. google play service aar library hierarchy on maven
  48. 48. 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
  49. 49. 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
  50. 50. 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.
  51. 51. 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:
  52. 52. 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:
  53. 53. 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版本:
  54. 54. about testing Convention 6
  55. 55. Building Effective Testing plan Unit test ● mokito ● Robolectric Integration test (acceptance testing) ● Instrumentation ● emulation ○ Genymotion (local with virtualBox) ○ Manymo (online) ● online device testing ○ appthwack (AWS Cloud)
  56. 56. Repeatable - 可在任何環境下進行測試(沒有網路、db、Android Context的狀況下仍能進行) Purpose - 讓單元測試運行速度加快,可加速回饋,更可了解自己的修改是否破壞現有的任 何功能。 How - 單元測試依存於測試替身,不應該存取資料庫、檔案系統、與外部系統互動。 - 簡單來說,單元測試不應該與系統元件之間產生互動。 A unit test should be “Repeatable”
  57. 57. Building Effective Unit Tests Unit 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)
  58. 58. 測試策略的設計主要是個「識別和評估專案風險之優先順序及 決定採用哪些行動來緩解風險」的歷程 Test Case Strategy --Continuous Delivery:reliable software rleases though build, test and deployment automation
  59. 59. 測試策略的設計主要是個「識別和評估專案風險之優先順序及 決定採用哪些行動來緩解風險」的歷程 Test Case Strategy --Continuous Delivery:reliable software rleases though build, test and deployment automation 未慮勝 先慮敗
  60. 60. Example: com.facebook.login.widget.LoginButtonTest.java @Test public 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
  61. 61. com.facebook.login.widget.LoginButton.java private class LoginClickListener implements OnClickListener { @Override public 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); } }
  62. 62. Command-line Option enable 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
  63. 63. From handcrafted to mindcrafted Q&A
  64. 64. 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.

×