android development, the right way

55
ANDROID DEVELOPMENT The Right Way

Upload: jayson-basanes

Post on 13-Jul-2015

442 views

Category:

Software


2 download

TRANSCRIPT

ANDROID DEVELOPMENTThe Right Way

JAYSON BASAÑESCTO of Lifebit

github.com/shiki

http://lifebit.com

Available on both iOS and Android

LIFEBIT

DAVAO CITY CLUBS

for iOS and Android

Download Lifebit for iOS and [email protected]

WUT

• TOOLS

• ESSENTIAL LIBRARIES

• BEST PRACTICES

Download Lifebit for iOS and [email protected]

TOOLS

Download Lifebit for iOS and [email protected]

ANDROID STUDIO (0.8+)

• Gradle-based build system

• Improved Interface Designer

• Better code completion and refactoring

• Code analysis

• Faster in every aspect

Download Lifebit for iOS and [email protected]

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' } } }

Download Lifebit for iOS and [email protected]

Build Variants productFlavors { demo { applicationId "com.buildsystemexample.app.demo" versionName "1.0-demo" } full { applicationId "com.buildsystemexample.app.full" versionName "1.0-full" } }

Download Lifebit for iOS and [email protected]

GENYMOTION• A lot faster than the Android

Emulator

• GPS, Battery, Accelerometer

• Simpler interface

Download Lifebit for iOS and [email protected]

VERSION CONTROL (GIT)

• SourceTree + Bitbucket

• History

• Collaboration

• Multiple build versions

• Blame games

Download Lifebit for iOS and [email protected]

CRASHLYTICS

• Crash reporting

• Logging

• Analytics

• Distribution

Download Lifebit for iOS and [email protected]

ESSENTIAL LIBRARIES

Download Lifebit for iOS and [email protected]

RETROFIT

• REST client for Android and Java

• Stupidly easy to use.

• Uses Gson for JSON parsing

square.github.io/retrofit

Download Lifebit for iOS and [email protected]

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!

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

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(); }

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

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);

Download Lifebit for iOS and [email protected]

EVENTBUS OR OTTO

• publish-subscribe-style communication between components

github.com/greenrobot/EventBus or square.github.io/otto

Download Lifebit for iOS and [email protected]

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);

Download Lifebit for iOS and [email protected]

BUTTER KNIFE

• View injection

• No more findViewById boilerplate

jakewharton.github.io/butterknife

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

BUTTER KNIFEEvents

@OnClick(R.id.submit) public void sayHi(Button button) { button.setText("Hello!"); }

Download Lifebit for iOS and [email protected]

ROBOLECTRIC

• Unit test framework

• Runs outside of the emulator

robolectric.org

Download Lifebit for iOS and [email protected]

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!")); } }

Download Lifebit for iOS and [email protected]

BEST PRACTICES

Download Lifebit for iOS and [email protected]

Aim for the latest Android version

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

Use Proguard for release builds

buildTypes { debug { runProguard false } release { signingConfig signingConfigs.release runProguard true proguardFiles 'proguard-rules.pro' } }

• smaller, optimized, obfuscated builds

Download Lifebit for iOS and [email protected]

# 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.** *; }

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

Not Invented Here• yet-another-image-loading library

• Unless you can make something better in the little time that you have.

Download Lifebit for iOS and [email protected]

Load heavy objects on demandNot

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 } }

Download Lifebit for iOS and [email protected]

Load heavy objects on demandNot 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 } }

Download Lifebit for iOS and [email protected]

Know when to use background threads• API calls

• Loading files

• Anything that’s gonna take more than a second

Download Lifebit for iOS and [email protected]

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);

Download Lifebit for iOS and [email protected]

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); } } }

Download Lifebit for iOS and [email protected]

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()); }

Download Lifebit for iOS and [email protected]

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

Please do not ignore exceptions!Throw it up the call chain

private void loadBits() throws APIException { List<Bit> bits = service.getBits(); ... }

Download Lifebit for iOS and [email protected]

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); }

Download Lifebit for iOS and [email protected]

Please do not ignore exceptions!Log to Crashlytics

try { List<Bit> bits = service.getBits(); ... } catch (APIException e) { Crashlytics.logException(e); ! showDialog(e.getMessage()); }

Download Lifebit for iOS and [email protected]

Enforced Coding Style• Everyone can read everyone’s shit

• Unity through uniformity

• Automate!

Download Lifebit for iOS and [email protected]

Automated Tests• Robolectric, Robotium, whatever floats your boat

• Pays off in the long run

• Good luck convincing your boss ^_^x

Download Lifebit for iOS and [email protected]

Find a Mentor• a.k.a. “Gamitan”

• Pay it forward

Download Lifebit for iOS and [email protected]

lastly

Download Lifebit for iOS and [email protected]

Don’t believe everything that I just said.

Download Lifebit for iOS and [email protected]

Download Lifebit for iOS and [email protected]

Download Lifebit for iOS and [email protected]

Download Lifebit for iOS and [email protected]

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

Download Lifebit for iOS and [email protected]

GLHF!Slides will be up at speakerdeck.com/shiki