Slide 1

Slide 1 text

Make it compatible Keishin Yokomaku (KeithYokoma) @ Drivemode, Inc. Shibuya Apk #2

Slide 2

Slide 2 text

Profile • Keishin Yokomaku at Drivemode, Inc. • Social: @KeithYokoma • Publication: Android Training • Product: • Like: Bicycle, Photography, Tumblr • Nickname: Qiita Meister

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Compatibility

Slide 5

Slide 5 text

Compatibility • between Android versions • between phone models

Slide 6

Slide 6 text

Make it compatible with Android versions

Slide 7

Slide 7 text

Android versions 1. SDK versions 2. Compatibility mode 3. Annotations and branching by if-statement 4. Build compatibility classes 5. Branching object with Dagger

Slide 8

Slide 8 text

**SdkVersion • minSdkVersion • Application will support this version at least. • targetSdkVersion • Application is fine on this version. • maxSdkVersion • Not recommended to specify • If specified and system is updated to above the version, the application will automatically uninstalled.

Slide 9

Slide 9 text

targetSdkVersion • Compatibility mode • If targetSdkVersion is set ’10’ • Application behaves as API Level 10 even if it is running on 22.

Slide 10

Slide 10 text

Call requires… • You likely to see this message…

Slide 11

Slide 11 text

‘if’ public void doSomething() { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { // for Lollipop } else { // for Kitkat and below } }

Slide 12

Slide 12 text

Call requires… • Still you see this warning…

Slide 13

Slide 13 text

Which is preferred? • 2 Annotations • @SuppressLint(“NewApi”) • @TargetApi(Build.VERSION_CODES.LOLLIPOP)

Slide 14

Slide 14 text

Which is preferred? • @SuppressLint(“NewApi”) • Suppress all warnings of “NewApi” • Even if someone call newer API later on, no lint warnings appear • @TargetApi(Build.VERSION_CODES.LOLLIPOP) • Recommended • Suppress warning up to specified API Level • If someone call newer API later on, new lint warning appears

Slide 15

Slide 15 text

Branching, branching, branching… public class Something { public void foo() { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { // foo on Lollipop } else { // foo on pre-Lollipop } } public void bar() { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) { // bar on Lollipop } else { // bar on pre-Lollipop } } // … }

Slide 16

Slide 16 text

Looks Not Good To Me

Slide 17

Slide 17 text

Learn from Android

Slide 18

Slide 18 text

AOSP style delegate pattern • Declare each classes corresponding to support version • Enclose those classes in the service class • Delegate all methods to enclosed object • Basic architecture on Support Library

Slide 19

Slide 19 text

AOSP style delegate pattern public class SomethingCompat { private final SomethingImpl mImpl; // initialize in ctor public void doSomething() { mImpl.doSomething(); } private interface SomethingImpl { void doSomething(); } private static class SomethingICS implements SomethingImpl { @Override public void doSomething() {} } private static class SomethingJB implements SomethingImpl { @Override public void doSomething() {} } }

Slide 20

Slide 20 text

AOSP style delegate pattern public class SomethingCompat { private final SomethingImpl mImpl; // initialize in ctor public SomethingCompat() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { mImpl = new SomethingJB(); } else if (Build.Version.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { mImpl = new SomethingICS(); } else { mImpl = new SomethingDefault(); } } }

Slide 21

Slide 21 text

AOSP style delegate pattern • Pros • Clean code • More testable, more flexible • Cons • Hard to test for each enclosed classes • Sometimes they don’t put any codes for compatibility public void doSomething() { // TODO compatibility implementation } public Object returnSomething() { return null; }

Slide 22

Slide 22 text

Service object depends on delegate object • What if using dependency injection? • Inject object in constructor ➡ Configure injected object outside of service object ➡ Easily configure test condition

Slide 23

Slide 23 text

Android versions • Inject different objects by version with Dagger • Declare common interface • Implement it enclosing API level specific processes • Configure modules which implementation to be injected SomethingImpl something SomethingICS implements ISomethingImpl SomethingJB implements ISomethingImpl SomethingL implements ISomethingImpl Module \

Slide 24

Slide 24 text

Package • Package structure ❖ com.example.model • SomethingService (or SomethingProvider, SomethingCompat) ❖ impl • SomethingImpl • SomethingICS • SomethingJB • SomethingL

Slide 25

Slide 25 text

Make it compatible with Android models

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

ΞοϋΠ

Slide 28

Slide 28 text

Model and manufacturers • S*msung • Galaxy • L* • G • Optimus • S*ny Ericsson • Xperia • F*jitsu • Arrows

Slide 29

Slide 29 text

˒ˑˑˑˑ “Doesn’t work on my device! It keeps crashing!!”

Slide 30

Slide 30 text

No way!

Slide 31

Slide 31 text

How to deal with it 1. No clue to predict problems specific to some device 2. Detect problems as early as possible 3. Use workaround utility and enclose dirty works in it

Slide 32

Slide 32 text

Test! test! test! • Remote Testing Services • PerfectoMobile • Remote TestKit • Samsung Test Lab • Cloud Test Lab • Smartphone Test Farm

Slide 33

Slide 33 text

Make it quicker to notice errors • Crash reports • Crashlytics • BugSense • ACRA(Application Crash Reports for Android) • Integrate reports to • Email • Chat(Slack, HipChat, ChatWork…)

Slide 34

Slide 34 text

Encapsulate compatibility code • Good to go with DI • like version compatibility architecture • AndroidDeviceCompatibility • https://github.com/mixi-inc/Android-Device-Compatibility • History from API 7

Slide 35

Slide 35 text

Still it doesn’t work?

Slide 36

Slide 36 text

Problems • Framework has bugs in • Java layer • Various type of bugs and result will be… ➡ Some of ‘RuntimeException’ or some of ‘Error’ causes crash • native code layer • Bad lib** implementation • Bad memory management • Result ➡ Process will be killed and no crash dialog

Slide 37

Slide 37 text

Problems • Some of ‘RuntimeException’ • Unexpectedly access `null` (NullPointerException) • Something is wrong (RuntimeException, IllegalStateException…) • Some of ‘Error’ • Class path conflict (VerifyError) • Unknown method definition on interface (AbstractMethodError)

Slide 38

Slide 38 text

˒ˑˑˑˑ “Doesn’t work for me. No crash though…”

Slide 39

Slide 39 text

–Johnny Appleseed “͜͜ʹҾ༻Λೖྗ͍ͯͩ͘͠͞ɻ” ʢPhoto by SAKURAKO - Do not get mad !/ MJ/TR (´ŋТŋ)ʣ

Slide 40

Slide 40 text

Big unknowns a lot, a lot, a lot… • (›°□°ʣ›︵ ᵲᴸᵲ • Collect logs from area that seems to have some bug • Collect everything you need to debug… • View properties, object state, etc… • stackoverflow.com

Slide 41

Slide 41 text

Be quick, be hacker • No solid solution. Just you need to be quick to get info. • If you get the device causing issue, let’s hack it!

Slide 42

Slide 42 text

Make it compatible Keishin Yokomaku (KeithYokoma) @ Drivemode, Inc. Shibuya Apk #2