Slide 1

Slide 1 text

গ͠޾ͤʹͳΔٕज़ DroidKaigi 2017 3/9 18:00~ Kameron

Slide 2

Slide 2 text

ॕʂॳొஃ

Slide 3

Slide 3 text

ࣗݾ঺հ

Slide 4

Slide 4 text

ϓϩϑΟʔϧ • Χϝυϯ • Kotlin޷͖Ͱ͢ • ΫϥεϝιουͰΞϯυϩΠυ ͷΞϓϦ։ൃ΍ͬͯ·͢ • ϘʔυήʔϜ͕޷͖Ͱ͢

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

ొஃͷ͖͔͚ͬ • 2.3࣌୅ɺEclipse࣌୅ͷݹ͍৘ใ͕ώοτ͢Δ • ೖ໳ॻ͕ຊ౰ʹೖ໳ॻ • ಈ͘ίʔυ͸ॻ͍ͯ͋Δ͕ɺϓϩδΣΫτͰ
 ࢖͏ͨΊʹඞཁͳ͜ͱ͸͋·Γॻ͍͍ͯͳ͍ • ࣍ͷεςοϓͷΩʔϫʔυͱͯ͠
 ৄࡉʹऔΓѻ͍·ͤΜ • ొஃऀͷࢀՃඅແྉɻ • Ԡื͚ͩͰ΋׬ച࣌΋ߪೖՄೳ • μϝ΋ͱͰԠืͯ͠Έ·ͨ͠

Slide 7

Slide 7 text

͜ͷൃදͰ޾ͤʹͳΓͦ͏ͳਓ • ॳ৺ऀ͔Βதڃऀʹεςοϓ͠Α͏ͱ͍ͯ͠Δํ • Android։ൃΛ΋ͬͱָʹ͢Δ • Android ։ൃͰͭ·͖ͮʹ͘͘͢Δ • ΞΧ΢ϯτఀࢭ͞Εͳ͍Α͏ʹ͢Δ

Slide 8

Slide 8 text

ΞδΣϯμ • ٯҾ͖ܗࣜͰ঺հ͍͖ͯ͠·͢ • Android Studio • Build.gradle • σόοά • OS,୺຤ • ProGuard • Google Play Console

Slide 9

Slide 9 text

Android Studioฤ

Slide 10

Slide 10 text

Android Studioฤ (ver2.3) • Jetbrains੡IDE • ҰੲEclipseͱ͍͏΋ͷ͋Γ͚Γ • ADTͱ͍͏ϓϥάΠϯ͕͕͋ͬͨɺ
 αϙʔτ͕੾Ε͍ͯΔͷͰɺ
 ඞͣAndroid StudioΛ࢖͓͏

Slide 11

Slide 11 text

ଞͷIDEͱγϣʔτΧοτ͕
 ҧ͏ͷͰɺΠϥΠϥ͖ͯͨ͠ • Android Studio ࠷ڧίϚϯυ
 Find Action • Mac: ⌘ ⇧ A • Windows: Ctrl Shift A
 • ίϚϯυΛݕࡧͰ͖ɺγϣʔτΧοτ·ͰΘ͔Δ • ઃఆͰKeymapΛมߋ͢Δͱ޷ΈͷઃఆʹͰ͖Δ

Slide 12

Slide 12 text

ಛఆϑΝΠϧʹ
 ΋ͬͱ؆୯ʹͱͼ͍ͨ • Android Studio ࠷ڧίϚϯυʹ͙࣍
 Search Everywhere • Mac: ⇧ ⇧ • Windows:Shift Shift • ϑΝΠϧ໊Λ؆୯ʹݕࡧͰ͖Δ

Slide 13

Slide 13 text

ϑΥʔϚοτΛἧ͑Δͷ͕
 ΊΜͲ͍ • Save ActionsͰอଘͰࣗಈͰϑΥʔϚοτΛ ἧ͑Δ

Slide 14

Slide 14 text

ΞϯΠετʔϧɺΠϯετʔϧ ͕ΊΜͲ͍͘͞ • Plugin ADB IDEA
 $USM4IJGU"
 ϙοϓΞοϓ͕ͰΔ

Slide 15

Slide 15 text

ςετͰ௨ͬͯͳ͍ͱ͜ΖΛ஌ Γ͍ͨ public class CalcUtil {
 public static int mod(int x, int y) {
 if (y == 0) {
 throw new IllegalArgumentException("y != 0");
 }
 return x % y;
 }
 }

Slide 16

Slide 16 text

public class CalcUtilTest {
 
 @Test
 public void 余りのテスト() { 
 Assert.assertThat(3,Is.is(CalcUtil.mod(11, 4)));
 }
 } ςετͰ௨ͬͯͳ͍ͱ͜ΖΛ஌ Γ͍ͨ Z͕ͷέʔε͕ൈ͚͍ͯΔ

Slide 17

Slide 17 text

ςετͰ௨ͬͯͳ͍ͱ͜ΖΛ஌ Γ͍ͨ 3VO`5FTUJOʜ`XJUI$PWFSBHF Λ࣮ߦ

Slide 18

Slide 18 text

Build.gradle ฤ

Slide 19

Slide 19 text

Build.gradleฤ • ΞϓϦͷجຊ৘ใΛઃఆ • ੲ͸͢΂ͯAndroidManifestͰ؅ཧ͍͕ͯͨ͠ɺ
 Ұ෦ͷઃఆ͕Build.gradleͰ؅ཧ͢ΔΑ͏ʹͳͬͨ • AndroidManifest: ύʔϛογϣϯ΍ϖʔδɺ
 ಈ࡞ͷઃఆ • Build.gradle: όʔδϣϯɺϥΠϒϥϦɺ؀ڥͷઃఆ

Slide 20

Slide 20 text

։ൃ൛ͱϦϦʔε൛ͬͯڞଘͰ ͖Δʁ • Ϗϧυ όϦΞϯτͱϓϩμΫτϑϨʔόʔΛ ࢖͏ͱͰ͖·͢ʂ • ύοέʔδ໊͕ಉ͔ͩ͡ΒڞଘͰ͖·ͤΜ • σόοΫͱϦϦʔε൛ͷύοέʔδ໊͕มΘ ΔΑ͏ʹSuffixΛ͚ͭͯ͋͛·͠ΐ͏

Slide 21

Slide 21 text

όʔδϣϯίʔυͱόʔδϣϯ ໊؅ཧ͕ΊΜͲ͍͘͞ buildTypes {
 release {}
 debug {
 applicationIdSuffix ".debug"
 }
 }
 productFlavors {
 demo {
 applicationIdSuffix ".demo"
 }
 full {
 applicationIdSuffix ".full"
 }
 } ϓϩμΫτϑϨʔόʔ ϏϧυόϦΞϯτ BQQCVJMEHSBEMF \QLH^\'MBWPS^\#VJME5ZQF^ \QLH^EFNPEFCVH

Slide 22

Slide 22 text

• Ϗϧυ όϦΞϯτͱϓϩμΫτϑϨʔόʔͰ
 ಉ໊ͳΒϏϧυόϦΞϯτͷ΄͏͕༏ઌ • BuildConfigϑΝΠϧ͕Ͱ͖Δ ։ൃͱ੡඼൛Ͱఆ਺Λ෼͚͍ͨ

Slide 23

Slide 23 text

buildTypes {
 release {
 buildConfigField "String", "ENDPOINT", "\"https://hoge.com\" "
 }
 debug {
 applicationIdSuffix ".debug"
 buildConfigField "String", "ENDPOINT", "\"https://localhost\" "
 }
 }
 
 productFlavors {
 demo {
 buildConfigField "boolean", "DEMO", "true"
 applicationIdSuffix ".demo"
 }
 full {
 buildConfigField "boolean", "DEMO", "false"
 applicationIdSuffix ".full"
 }
 } ։ൃͱ੡඼൛Ͱఆ਺Λ෼͚͍ͨ

Slide 24

Slide 24 text

buildTypes {
 release {
 buildConfigField "String", "ENDPOINT", "\"https://hoge.com\" "
 }
 debug {
 applicationIdSuffix ".debug"
 buildConfigField "String", "ENDPOINT", "\"https://localhost\" "
 }
 }
 
 productFlavors {
 demo {
 buildConfigField "boolean", "DEMO", "true"
 applicationIdSuffix ".demo"
 }
 full {
 buildConfigField "boolean", "DEMO", "false"
 applicationIdSuffix ".full"
 }
 } ։ൃͱ੡඼൛Ͱఆ਺Λ෼͚͍ͨ

Slide 25

Slide 25 text

։ൃ൛ͱϦϦʔε൛Ͳ͔ͬͪͳ ͷ͔Θ͔Γ΍͍ͨ͘͢͠ • ϦιʔεϑΝΠϧɺιʔείʔυ΋Ϗϧυ όϦΞϯτ
 ͱϓϩμΫτϑϨʔόʔʹΘ͚ΒΕ·͢ɻ
 ʢ༏ઌॱҐ͸্͔Βॱ൪ʣ • src/demoDebug/ʢϏϧυόϦΞϯτ ϑϨʔόʔʣ • src/debug/ʢϏϧυόϦΞϯτʣ • src/demo/ʢϑϨʔόʔʣ • src/main/ʢϝΠϯ ʣ

Slide 26

Slide 26 text

։ൃ൛ͱϦϦʔε൛Λ Θ͔Γ΍͍ͨ͘͢͠ • ྫ͑͹ຊ൪ͱ։ൃΞϓϦ໊Λม͑ͯΈΔ • app/src/main/res/values/strings.xmlΛ
 app/src/debug/res/values/strings.xmlʹ
 ίϐʔɺฤू͢Δ • ಉ͡৔ॴʹ࡞੒͢Δͱ্ॻ͖͞ΕΔ Debug

Slide 27

Slide 27 text

ϏϧυόϦΞϯτͱϓϩμΫτϑ Ϩʔόʔ࢖͍෼͚͕Θ͔Βͳ͍ • ϏϧυόϦΞϯτ: ؀ڥͷઃఆ • ຊ൪αʔόʔͱ։ൃαʔόʔͷΞυϨε • ຊ൪༻ॺ໊ɺ։ൃ༻ॺ໊ • ϓϩμΫτϑϨʔόʔɿΞϓϦͷৼΔ෣͍ • σόοΫϞʔυ: ON/OFF • ແྉ൛/՝ۚ൛: ON/OFF

Slide 28

Slide 28 text

όʔδϣϯίʔυͱόʔδϣϯ ໊؅ཧ͕ΊΜͲ͍͘͞ def major = 1
 def minor = 0
 def patch = 0
 def build = 0
 
 android {
 defaultConfig {
 versionCode major * 10000 + minor * 1000 + patch * 100 + build
 versionName "${major}.${minor}.${patch}"
 }
 } WFSTJPO/BNFͱWFSTJPO$PEF
 Λ࿈ಈ͢ΔΑ͏ʹ͢Δ ։ൃऀ͸WFSTJPO/BNFΛ্͍͛ͨ (PPHMF1MBZ͸ɺWFSTJPO$PEF্͕͕ͬͯΕ͹Α͍

Slide 29

Slide 29 text

ॺ໊ͷ؅ཧͲ͏ͨ͠Βྑ͍ͷʁ • ॺ໊Λbuild.gradleʹϕλॻ͖ • Private RepositoryͳͲͳΒ༗ޮ • CI͔Β΋ѻ͍΍͍͢ • ϑΝΠϧʹॻ͖ग़͢ • gitͰແࢹઃఆ͠ɺ伴͸ผ؅ཧ

Slide 30

Slide 30 text

ॺ໊Λϕλॻ͖ signingConfigs {
 release {
 storeFile file(“./filepath”)
 keyAlias “test”
 storePassword “test”
 keyPassword “test”
 }
 }

Slide 31

Slide 31 text

ॺ໊৘ใΛϑΝΠϧʹॻ͖ग़͢ • keystore.properties Λ࡞੒ storePassword=myStorePassword
 keyPassword=mykeyPassword
 keyAlias=myKeyAlias
 storeFile=myStoreFileLocation

Slide 32

Slide 32 text

ॺ໊৘ใΛϑΝΠϧʹॻ͖ग़͢ • build.gradleͰಡΉ͜Ή signingConfigs {
 debug {
 def keystorePropertiesFile = rootProject.file("keystore.properties")
 def keystoreProperties = new Properties()
 keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 
 keyAlias keystoreProperties['keyAlias']
 keyPassword keystoreProperties['keyPassword']
 storeFile file(keystoreProperties['storeFile'])
 storePassword keystoreProperties['storePassword']
 }
 }

Slide 33

Slide 33 text

σόοάฤ

Slide 34

Slide 34 text

σόοάฤ • ఻Ոͷๅ౛ϓϦϯτσόοά • ϝϞϦϦʔΫ΍௨৴पΓ͕σόοάͮ͠Β͍

Slide 35

Slide 35 text

੡඼൛ͷͱ͖͸LogΛग़ͨ͘͠ ͳ͍ • JakeWharton/timberΛ࢖͏ • Debug͚ͩϩάΛग़͢͜ͱ͕؆୯ • ϩάΛԿͰग़͔͢ม͑Δͷ΋؆୯
 if (BuildConfig.DEBUG) {
 Timber.plant(Timber.DebugTree())
 }
 Timber.d(“test”,”test”)

Slide 36

Slide 36 text

௨৴෦෼ͷσόοΫ͕ͣ͠Β͍ • facebook/stethoΛೖΕͯɺChomeͰσόοά • ιʔείʔυʹೖΕΔඞཁ͋Γ • mitmproxyΛೖΕͯɺωοτϫʔΫΛ؂ࢹ • ιʔείʔυʹೖΕΔඞཁͳ͠ • pythonίʔυͰendpointΛม͑ͨΓͰ͖·͢

Slide 37

Slide 37 text

TUFUIP

Slide 38

Slide 38 text

NJUNQSPYZ

Slide 39

Slide 39 text

ϝϞϦϦʔΫͷσόοά͕͠Μ Ͳ͍ • square/leakcanaryΛ࢖͏ͱ؆୯ • Applicationʹॻ͚ͩ͘Ͱɺ
 ϦʔΫ࣌ʹ௨஌Λग़ͯ͘͠ΕΔ

Slide 40

Slide 40 text

Logͷ͍ͤͰUnit TestͰΤϥʔ ͕ͰΔ • Log͚ͩͳΒɺClassLoaderͷ࢓૊ΈΛ࢖ͬͯLogΛ্ॻ͖͢Δ
 app/src/test/java/android/util/Log.javaʹҎԼΛஔ͘ public class Log {
 public static int v(String tag, String msg) { return 0; }
 public static int v(String tag, String msg, Throwable tr) { return 0; }
 public static int d(String tag, String msg) { return 0; }
 public static int d(String tag, String msg, Throwable tr) { return 0; }
 public static int i(String tag, String msg) { return 0; }
 public static int i(String tag, String msg, Throwable tr) { return 0; }
 public static int w(String tag, String msg) { return 0; }
 public static int w(String tag, String msg, Throwable tr) { return 0; }
 public static int w(String tag, Throwable tr) { return 0; }
 public static int e(String tag, String msg) { return 0; }
 public static int e(String tag, String msg, Throwable tr) { return 0; }
 }

Slide 41

Slide 41 text

ProGuradฤ

Slide 42

Slide 42 text

ProGuard • ηΩϡϦςΟతʹ࠷௿ݶϦϦʔε࣌ʹඞͣઃ ఆ͢Δ • ΞϓϦαΠζͷ࡟ݮ • ؾٳΊ೉ಡԽ public class MainActivity extends c {
 private TextView m;
 public MainActivity() {}
 protected void onCreate(Bundle var1) {
 super.onCreate(var1);
 this.setContentView(2130968603);
 this.m = (TextView)this.findViewById(2131427415);
 this.m.setText("hello world");
 }
 }

Slide 43

Slide 43 text

64kϝιου͔͠Ͱ͖ͳ͍ͷʁ • 64kϝιου͸ϏϧυͰ͖·ͤΜ • ProGuardΛ͔͚Δ͜ͱͰϝιου਺ͷ࡟ݮ • ͦΕͰ΋ͩΊͳ৔߹͸ɺMultidexΛ༻͍Δ ͜ͱͰ64kͷนΛ௒͑ΒΕ·͢

Slide 44

Slide 44 text

ProGuardΛ͔͚ΔͱΤϥʔʹ ͳΔɻมͳ׼͕Ͱ͖ͯͨɻ • ProGuard͕ͯ͠Δ͜ͱΛཧղ͢Δ • ະ࢖༻ͷϝιου࡟আ • Ϋϥε໊ɺϝιουΛa,bͳͲͷϦωʔϜ • ϦϑϨΫγϣϯΛ࢖༻͍ͯ͠Δͱ͜ΖͰ
 ΤϥʔʹͳΔ • ͦͷ෦෼͸KeepΛ࢖༻ͯ͠ɺϦωʔϜͤ͞ ͳ͍ɺ࡟আͤ͞ͳ͍

Slide 45

Slide 45 text

ϦϦʔεͷͱ͖͚ͩProGuard ͔͚ΔͱυΩυΩ͢Δ • ։ൃதʹ΋ProGuardΛ͔͚Δͱ҆৺ • Ϗϧυ͕஗͘ͳΔͷ͕ܽ఺ • ։ൃ࣌͸ɺίʔυ࡟ݮ͸͢Δ͕ɺϦωʔϜ͸͠ͳ͍΄͏͕ Α͍ buildTypes {
 debug {
 minifyEnabled true
 useProguard false
 proguardFiles getDefaultProguardFile('proguard- android.txt'),
 'proguard-rules.pro'
 }
 }

Slide 46

Slide 46 text

ΫϥογϡϨϙʔτ͕೉ಡԽ͞ Ε͍ͯͯݟͮΒ͍ • ೉ಡԽ࣌ʹੜ੒͞ΕΔmapping.txtΛ
 Google Play Console ʹΞοϓϩʔυ͠Α͏

Slide 47

Slide 47 text

OS,୺຤ฤ

Slide 48

Slide 48 text

OS, ୺຤ฤ • ػछґଘɺόʔδϣϯґଘɺύʔϛογϣϯɺ λεΫΩϥʔͳͲͰ࠶ݱͮ͠Β͍ͳͲ༷ʑͳ ໰୊͕ɻ

Slide 49

Slide 49 text

ػछґଘ͕ܹ͗͢͠ • ؤுΔɻ • ϋʔυ΢ΣΞʹؔ͢Δͱ͜Ζ͸ؾ߹ͱࠜੑ • ݕূηϯλʔͳͲʹߦ͚Δ৔߹͸ར༻͢Δ • ͳΔ΂͘ɺSupport Library͕͋ΔػೳΛ࢖͏

Slide 50

Slide 50 text

ϥϯλΠϜύʔϛγϣϯͷରԠ • Android 6.0͔Βಋೖ͞ΕͨϥϯλΠϜύʔϛ γϣϯ • ΞϓϦͷΠϯετʔϧ࣌ʹ͢΂ͯΛڐՄ͢Δ ͷͰ͸ͳ͘ɺ࢖༻࣌ʹڐՄΛٻΊΔ • ಠ࣮ࣗ૷͸ΊΜͲ͏ͳͷͰɺ
 hotchemi/PermissionsDispatcherΛ࢖͏

Slide 51

Slide 51 text

Google Play Consoleฤ

Slide 52

Slide 52 text

Google Play Consoleฤ • Google Play͸ɺGoogle ͕ӡӦ͢ΔετΞ • Android͸Φʔϓϯͳࢢ৔ͳͷͰɺ
 ԿॲͰΞϓϦΛ഑෍ͯ͠΋ྑ͍ • Amazon StoreɺauεϚύεɺϒϩά • Google Play Console ͸Playͷ؅ཧը໘

Slide 53

Slide 53 text

Android৹͕ࠪͳ͍͔Βɺ΍Γ ͍ͨ์୊ͳΜͰ͠ΐ͏ʁ • ͍͍͑ɻݫ͘͠ͳ͖ͬͯ·ͨ͠ɻ • ඞͣσΟϕϩούʔϙϦγʔΛ֬ೝɻ࣌ʑม ߋ͞ΕΔͷͰ஫ҙ͕ඞཁɻ • ݸਓ৘ใ΍ػີ৘ใ • ऩӹԽͱ޿ࠂ • ൒೥લͷϙϦγʔมߋ͸େ͖ΊͩͬͨͷͰɺ ඞͣ֬ೝ

Slide 54

Slide 54 text

σΟϕϩούʔϙϦγͷྫ • มߋͷྫ • ݸਓ৘ใ΍ػີ৘ใ Ϣʔβʔͷݸਓ৘ใ΍ػີ৘ใ ʢݸਓࣝผ৘ใɺࡒ຿৘ใɺࢧ෷͍৘ใɺೝূ৘ใɺ ి࿩ா΍࿈བྷઌͷσʔλɺϚΠΫ΍Χϝϥͷηϯαʔ σʔλɺ୺຤ͷػີ৘ใͳͲʣΛѻ͏ΞϓϦ͸ɺҎԼ ͷཁ݅Λຬͨ͢ඞཁ͕͋Γ·͢ɻ • ஫ҙ͢Δ఺ • ऩӹԽͱ޿ࠂɹ޿ࠂ͸ɺ޿ࠂΛ഑৴͢ΔΞϓϦ಺ʹͷ ΈදࣔͰ͖·͢ɻΞϓϦ಺ʹ഑৴͞ΕΔ޿ࠂ͸ɺΞϓ ϦͷҰ෦ͱΈͳ͞Ε·͢

Slide 55

Slide 55 text

͍ͭެ։͞ΕΔͷ͔஌Γ͍ͨ • ެ։ϘλϯΛԡ࣮ͯ͠ࡍʹ൓ө͞ΕΔͷ͸
 ̎ʙ̏࣌ؒޙɻ௨஌ͷઃఆΛ͍ΕΔ͔ • Google Play Console ΞϓϦΛ͍ΕΑ͏

Slide 56

Slide 56 text

ͱ͸͍͑΋ͬͱެ։͞ΕΔ·Ͱ ૣ͘ͳΒͳ͍ͷʁ • ܦݧଇͰ͕͢ɺBetaެ։͍ͯ͠Δ৔߹͸ɺެ ։ϘλϯΛԡͯ͠10෼ఔ౓Ͱ൓ө͞ΕΔ͜ͱ ͕ଟ͍Ͱ͢

Slide 57

Slide 57 text

·ͱΊ • Android Studio…ૉఢͰ͢ • Build.gradleपΓΛௐ΂Δͱ։ൃָ͕ʹͳΔ • σόοάπʔϧ΋๛෋ʹͳ͖ͬͯͨ • Google Play Console͸࠷ۙػೳ͕૿͑ͨͷͰɺ֬ೝ • ୺຤ґଘ͸͕Μ͹Δ • ProGuard͸ఆظతʹ͔͚࣮ͯߦ͢Δͱ҆৺

Slide 58

Slide 58 text

ࢀߟจݙ • εϥΠυʹϦϯΫΛઃఆ͠·ͨ͠ • ͦͷଞ • DroidKaigi/conference-app-2017 • JakeWharton/u2020 • Android Developers

Slide 59

Slide 59 text

͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ