Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Development, the Right Way

Android Development, the Right Way

Current tools, essential libraries, and some best practices. This talk was for Google Developer Group Davao DevFest 2014.

Jayson Basañes

November 15, 2014
Tweet

More Decks by Jayson Basañes

Other Decks in Programming

Transcript

  1. Download Lifebit for iOS and Android jayson@basanes.net ANDROID STUDIO (0.8+)

    • Gradle-based build system • Improved Interface Designer • Better code completion and refactoring • Code analysis • Faster in every aspect
  2. Download Lifebit for iOS and Android jayson@basanes.net buildscript { repositories

    { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.12.+' } } apply plugin: 'com.android.application' dependencies { compile 'com.netflix.rxjava:rxjava-core:0.19.1' compile 'com.netflix.rxjava:rxjava-android:0.19.1' compile 'com.squareup.retrofit:retrofit:1.6.1' compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0' compile 'com.squareup.okhttp:okhttp:2.0.0' } android { compileSdkVersion 19 buildToolsVersion "20.0.0" defaultConfig { minSdkVersion 15 targetSdkVersion 19 versionCode 1 versionName "1.0" } buildTypes { debug { runProguard false } release { signingConfig signingConfigs.release runProguard true proguardFiles 'proguard-rules.pro' } } }
  3. Download Lifebit for iOS and Android jayson@basanes.net Build Variants productFlavors

    { demo { applicationId "com.buildsystemexample.app.demo" versionName "1.0-demo" } full { applicationId "com.buildsystemexample.app.full" versionName "1.0-full" } }
  4. Download Lifebit for iOS and Android jayson@basanes.net GENYMOTION • A

    lot faster than the Android Emulator • GPS, Battery, Accelerometer • Simpler interface
  5. Download Lifebit for iOS and Android jayson@basanes.net VERSION CONTROL (GIT)

    • SourceTree + Bitbucket • History • Collaboration • Multiple build versions • Blame games
  6. Download Lifebit for iOS and Android jayson@basanes.net CRASHLYTICS • Crash

    reporting • Logging • Analytics • Distribution
  7. Download Lifebit for iOS and Android jayson@basanes.net RETROFIT • REST

    client for Android and Java • Stupidly easy to use. • Uses Gson for JSON parsing square.github.io/retrofit
  8. Download Lifebit for iOS and Android jayson@basanes.net RETROFIT public interface

    LifebitService { @GET("/users/{user}/bits") List<Bit> getUserBits(@Path("user") String user); } RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.lifebit.com") .build(); ! LifebitService service = restAdapter.create(LifebitService.class); List<Bit> bits = service.getUserBits(“shiki"); Profit!
  9. Download Lifebit for iOS and Android jayson@basanes.net OKHTTP • Efficient

    HTTP Client. Alternative to Apache's HTTPClient and java.net.HttpUrlConnection. • Can be used with Retrofit. • From their site: Perseveres when the network is troublesome square.github.io/okhttp
  10. Download Lifebit for iOS and Android jayson@basanes.net OKHTTP OkHttpClient client

    = new OkHttpClient(); ! String run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); ! Response response = client.newCall(request).execute(); return response.body().string(); }
  11. Download Lifebit for iOS and Android jayson@basanes.net PICASSO • Image

    downloading and caching library. • Automatic memory and disk caching. • Mostly one-liners to load a remote image into an ImageView. • Supports: resizing, cropping, placeholders. • Can use OkHttp square.github.io/picasso
  12. Download Lifebit for iOS and Android jayson@basanes.net PICASSO Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView); Picasso.with(context)

    .load(url) .resize(50, 50) .centerCrop() .into(imageView) Picasso.with(context) .load(url) .placeholder(R.drawable.user_placeholder) .error(R.drawable.user_placeholder_error) .into(imageView);
  13. Download Lifebit for iOS and Android jayson@basanes.net EVENTBUS OR OTTO

    • publish-subscribe-style communication between components github.com/greenrobot/EventBus or square.github.io/otto
  14. Download Lifebit for iOS and Android jayson@basanes.net EVENTBUS public class

    MessageEvent { /* Additional fields if needed */ } EventBus.getDefault().register(this); ! public void onEvent(MessageEvent event) { /* Do something */ }; MessageEvent event = new MessageEvent(); EventBus.getDefault().post(event);
  15. Download Lifebit for iOS and Android jayson@basanes.net BUTTER KNIFE •

    View injection • No more findViewById boilerplate jakewharton.github.io/butterknife
  16. Download Lifebit for iOS and Android jayson@basanes.net BUTTER KNIFE class

    ExampleActivity extends Activity { TextView title; TextView subtitle; TextView footer; ! @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); title = (TextView) findViewById(R.id.title); subtitle = (TextView) findViewById(R.id.subtitle); footer = (TextView) findViewById(R.id.footer); } } The old way
  17. Download Lifebit for iOS and Android jayson@basanes.net BUTTER KNIFE class

    ExampleActivity extends Activity { @InjectView(R.id.title) TextView title; @InjectView(R.id.subtitle) TextView subtitle; @InjectView(R.id.footer) TextView footer; ! @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.inject(this); } } Using injection
  18. Download Lifebit for iOS and Android jayson@basanes.net BUTTER KNIFE Events

    @OnClick(R.id.submit) public void sayHi(Button button) { button.setText("Hello!"); }
  19. Download Lifebit for iOS and Android jayson@basanes.net ROBOLECTRIC • Unit

    test framework • Runs outside of the emulator robolectric.org
  20. Download Lifebit for iOS and Android jayson@basanes.net ROBOLECTRIC @RunWith(RobolectricTestRunner.class) public

    class MyActivityTest { ! @Test public void clickingButton_shouldChangeResultsViewText() throws Exception { Activity activity = Robolectric.buildActivity(MyActivity.class).create().get(); ! Button pressMeButton = (Button) activity.findViewById(R.id.press_me_button); TextView results = (TextView) activity.findViewById(R.id.results_text_view); ! pressMeButton.performClick(); String resultsText = results.getText().toString(); assertThat(resultsText, equalTo("Testing Android Rocks!")); } }
  21. Download Lifebit for iOS and Android jayson@basanes.net Beware of the

    65K DEX methods limit Unable to execute dex: method ID not in [0, 0xffff]: 65536 Conversion to Dalvik format failed: Unable to execute dex: method ID not in [0, 0xffff]: 65536 • Choose third-party libraries wisely • Use Proguard if you can’t avoid it
  22. Download Lifebit for iOS and Android jayson@basanes.net Use Proguard for

    release builds buildTypes { debug { runProguard false } release { signingConfig signingConfigs.release runProguard true proguardFiles 'proguard-rules.pro' } } • smaller, optimized, obfuscated builds
  23. Download Lifebit for iOS and Android jayson@basanes.net # Obfuscation parameters:

    #-dontobfuscate -useuniqueclassmembernames -keepattributes SourceFile,LineNumberTable -allowaccessmodification # Keep Jackson stuff -keep class org.codehaus.** { *; } -keep class com.fasterxml.jackson.annotation.** { *; } # Keep these for GSON and Jackson -keepattributes Signature -keepattributes *Annotation* -keepattributes EnclosingMethod # Keep Retrofit -keep class retrofit.** { *; } -keepclasseswithmembers class * { @retrofit.** *; } -keepclassmembers class * { @retrofit.** *; } # Keep Picasso -keep class com.squareup.picasso.** { *; } -keepclasseswithmembers class * { @com.squareup.picasso.** *; } -keepclassmembers class * { @com.squareup.picasso.** *; }
  24. Download Lifebit for iOS and Android jayson@basanes.net Prefer Maven dependencies

    instead of jar files dependencies { compile 'com.squareup.okio:okio:1.0.+' compile 'com.squareup.okhttp:okhttp:2.0.+' ! compile 'com.squareup.retrofit:retrofit:1.7.0' } • ez
  25. Download Lifebit for iOS and Android jayson@basanes.net Not Invented Here

    • yet-another-image-loading library • Unless you can make something better in the little time that you have.
  26. Download Lifebit for iOS and Android jayson@basanes.net Load heavy objects

    on demand Not class MyActivity extends Activity { private Service service; @Override public void onCreate(Bundle savedInstanceState) { ... service = restAdapter.create(LifebitService.class); } @OnClick(R.id.submit) public void onButtonClick(Button button) { List<Bit> bits = service.getBits(); // Do something with bits } }
  27. Download Lifebit for iOS and Android jayson@basanes.net Load heavy objects

    on demand Not Not class MyActivity extends Activity { private Service service; ! Service getService() { if (service == null) { service = restAdapter.create(LifebitService.class); } return service; } ! @OnClick(R.id.submit) public void onButtonClick(Button button) { List<Bit> bits = getService().getBits(); // Do something with bits } }
  28. Download Lifebit for iOS and Android jayson@basanes.net Know when to

    use background threads • API calls • Loading files • Anything that’s gonna take more than a second
  29. Download Lifebit for iOS and Android jayson@basanes.net Know when to

    use background threads private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } } new DownloadFilesTask().execute(url1, url2, url3);
  30. Download Lifebit for iOS and Android jayson@basanes.net View Holder Pattern

    public class MyAdapter extends BaseAdapter { @Override public View getView(int position, View view, ViewGroup parent) { ViewHolder holder; if (view != null) { holder = (ViewHolder) view.getTag(); } else { view = inflater.inflate(R.layout.whatever, parent, false); holder = new ViewHolder(view); view.setTag(holder); } holder.name.setText("John Doe"); // etc... return view; } static class ViewHolder { @InjectView(R.id.title) TextView name; @InjectView(R.id.job_title) TextView jobTitle; public ViewHolder(View view) { ButterKnife.inject(this, view); } } }
  31. Download Lifebit for iOS and Android jayson@basanes.net Please do not

    ignore exceptions! try { List<Bit> bits = service.getBits(); ... } catch (APIException e) { // Something terrible has happened // but I am too lazy to handle this exception. // So f*ck you users! ! // Logging is not considered handling an exception! Log.d(getClass().getName(), e.getMessage()); }
  32. Download Lifebit for iOS and Android jayson@basanes.net Please do not

    ignore exceptions! try { List<Bit> bits = service.getBits(); ... } catch (APIException e) { // If you are sure that the message is safe for the end user: showDialog(e.getMessage()); } Inform the user
  33. Download Lifebit for iOS and Android jayson@basanes.net Please do not

    ignore exceptions! Throw it up the call chain private void loadBits() throws APIException { List<Bit> bits = service.getBits(); ... }
  34. Download Lifebit for iOS and Android jayson@basanes.net Please do not

    ignore exceptions! Crash that motherf*cker try { List<Bit> bits = service.getBits(); ... } catch (APIException e) { // I am pretty sure that an exception will never happen, // but just in case: throw new RuntimeException(e); }
  35. Download Lifebit for iOS and Android jayson@basanes.net Please do not

    ignore exceptions! Log to Crashlytics try { List<Bit> bits = service.getBits(); ... } catch (APIException e) { Crashlytics.logException(e); ! showDialog(e.getMessage()); }
  36. Download Lifebit for iOS and Android jayson@basanes.net Enforced Coding Style

    • Everyone can read everyone’s shit • Unity through uniformity • Automate!
  37. Download Lifebit for iOS and Android jayson@basanes.net Automated Tests •

    Robolectric, Robotium, whatever floats your boat • Pays off in the long run • Good luck convincing your boss ^_^x
  38. Download Lifebit for iOS and Android jayson@basanes.net Find a Mentor

    • a.k.a. “Gamitan” • Pay it forward
  39. Download Lifebit for iOS and Android jayson@basanes.net REFERENCES • androidweekly.net:

    Free weekly newsletter for the latest Android dev news, tutorials, articles, etc. • android-arsenal.com: List of free and paid Android libraries • github.com/futurice/android-best-practices: Android dev best practices