Modern Android app library stack
Tomáš Kypta
#MobCon
Getting into Android
Q: “How do I get the data from the server?”
A: “Use AsyncTask!”
The old school approach™
public class MainActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener {
public void onClick(View view) {
new DownloadTask().execute(inputString);
}
});
}
private class DownloadTask extends AsyncTask<String, Void, String> {
@Override protected String doInBackground(String... params) {
// download some data
}
@Override protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.text);
txt.setText(result);
}
}
}
Old school Android apps
• business logic in activities
• with all the bad stuff such as networking
• and memory leaks
• and crashes
Modern Android apps
• use cleaner architectures
• MVP, MVVM, MVI
• use libraries heavily
• use tests
Libraries
• save time and work
• simplify API
• back-port new APIs to older Android version
Ideal Android Library
• “perform one task and perform it well”
• easy to use
• open-source
• easily available
• through a remote Maven repository
Ideal Android Library
• doesn’t eat too much resources
• doesn’t require too many permissions
• behave nicely when crashing
“I wan’t my app to work on Android from version 4.1.”
98% of Android devices!
Support libraries
• available through Android SDK
• backport newer Android APIs
• helper classes
• debugging, testing, utilities
Support libraries
• AsyncTaskLoader
• com.android.support:support-core-utils:25.3.0
• ViewPager
• com.android.support:support-core-ui:25.3.0
• support fragments
• com.android.support:support-fragment:25.3.0
Support libraries
• AppCompatActivity, ActionBar
• com.android.support:appcompat-v7:25.3.0
• RecyclerView
• com.android.support:recyclerview-v7:25.3.0
• CardView
• com.android.support:cardview-v7:25.3.0
Support libraries
• com.android.support:support-annotations:25.3.0
• useful annotations
• StringRes, IntDef, Nullable, UiThread, WorkerThread, CallSuper,
VisibleForTesting, …
• com.android.support:design:25.3.0
• Material design
Support libraries
• Having more than 64k methods?
• And supporting Android prior 5.0?
• com.android.support:multidex:1.0.1
“This dependency injection thing sounds useful.”
Dagger 2
• dependency injection framework for Android and Java
• avoids reflection
• uses compile-time generated code
Dagger 2
public class SimpleGameProvider {
private ApiProvider mApiProvider;
private StorageProvider mStorageProvider;
@Inject
public SimpleProvider(ApiProvider apiProvider, StorageProvider storageProvider) {
mApiProvider = apiProvider;
mStorageProvider = storageProvider;
}
public void doSomething() {
// …
}
}
Dagger 2
@Module
public class AppModule {
private Context mApplicationContext;
public AppModule(Context applicationContext) {
mApplicationContext = applicationContext;
}
@Singleton @Provides
protected OtherProvider provideTheOther(Context context) {
return new OtherProvider(context);
}
}
Dagger 2
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity activity);
}
Dagger 2
public class MainActivity extends AppCompatActivity {
@Inject SimpleProvider mSimpleProvider;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((MyApplication) getApplication()).getAppComponent().inject(this);
// and now we can use mSimpleProvider
}
}
“My server has this REST API…”
Retrofit
• simple REST client for Android and Java
• annotation-based API
• type-safe
• Rx compatible
Retrofit
public interface GitHubApi {
@GET("/users/{username}")
User getUser(@Path("username") String username);
@GET("/users/{username}/repos")
List<Repo> getUserRepos(@Path("username") String username);
@POST("/orgs/{org}/repos")
RepoCreationResponse createRepoInOrganization(
@Path("org") String organization,
@Body RepoCreationRequest request);
}
Retrofit
RestAdapter adapter = new RestAdapter.Builder()
.setEndpoint(GITHUB_API_URL)
.setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
request.addHeader("Accept", "application/vnd.github.v3+json");
}
})
.setLogLevel(RestAdapter.LogLevel.BASIC)
.build();
GitHubApi gitHubApi = adapter.create(GitHubApi.class);
“And my server has this fancy new features…”
OkHttp
• an efficient HTTP client for Android and Java
• requests can be easily customized
• support for HTTP/2
• connection pooling
• transparent GZIP
• response caching
OkHttp
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = client.newCall(request).execute();
return response.body().string();
OkHttp
• Works out of the box with the latest Retrofit!
“The server returns 400. What’s wrong?”
Stetho
• A debug bridge
• hooks into Chrome Developer Tools
Stetho
• network inspection
• database inspection
• view hierarchy
• dumpapp system allowing custom plugins
• command-line interface for communication with the plugins
Stetho
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Stetho.initializeWithDefaults(this);
}
}
Stetho
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addNetworkInterceptor(new StethoInterceptor())
.build();
“Why I’m getting this OutOfMemoryError?”
LeakCanary
• memory leak detection library
• notifies about memory leaks during app development
LeakCanary
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
“How to display this remote product image?”
Image loaders
• tons of libs
• Universal Image Loader
• Picasso
• Glide
Picasso
Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.into(vImageView);
“How can I notify that class?“
“There has to be some way without refactoring the whole
thing!”
Event bus
• for communication between decoupled parts of an app
• EventBus
EventBus
• events
• subscribers
• register and unregister
• post events
public static class MessageEvent { /* fields if needed */ }
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* handle event */};
EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);
EventBus.getDefault().post(new MessageEvent());
“How do I get the data from the server?”
“And I have to combine couple of sources.”
RxJava
• general Java library
• reactive programming
• push concept
• composable data flow
RxJava
• useful for simple async processing
• async composition
• offers simple chaining of operations on data
• eliminates callback hell
RxJava
• works well with Retrofit
• can completely replace event bus libraries
• hard to learn
• RxJava 1 vs. RxJava 2
• they will coexist for some time
RxJava data flow
Observable
.from(new String[]{"Hello", "Droidcon!"}) creation
RxJava data flow
Observable
.from(new String[]{"Hello", "Droidcon!"})
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s.toUpperCase(Locale.getDefault());
}
})
creation
RxJava data flow
Observable
.from(new String[]{"Hello", "Droidcon!"})
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s.toUpperCase(Locale.getDefault());
}
})
.reduce(new Func2<String, String, String>() {
@Override
public String call(String s, String s2) {
return s + ' ' + s2;
}
})
creation
transformation
RxJava data flow
Observable
.from(new String[]{"Hello", "Droidcon!"})
.map(new Func1<String, String>() {
@Override
public String call(String s) {
return s.toUpperCase(Locale.getDefault());
}
})
.reduce(new Func2<String, String, String>() {
@Override
public String call(String s, String s2) {
return s + ' ' + s2;
}
})
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Timber.i(s);
}
});
creation
transformation
subscription
RxJava data flow with Java 8
creation
transformation
subscription
Observable
.from(new String[]{"Hello", "Droidcon!"})
.map(s -> s.toUpperCase(Locale.getDefault()))
.reduce((s,s2) -> s + ' ' + s2)
.subscribe(s -> Timber.i(s));
Other Rx libraries
RxAndroid
RxBinding
RxLifecycle
RxNavi
SQLBrite
RxRelay
“This new feature is great! I bet users will love it!”
Analytics & crash reporting
• Google Analytics
• Crashlytics
• Firebase
“I don’t like this Java language.”
Kotlin
• not a library
• a JVM programming language
• “Swift for Android devs"
Q: “So all I have to do is to Google for a library to do the thing?”
A: “think wisely before adding a new library.”
Final thoughts
• many potential problems
• transitive dependencies
• permissions
• app size
• slow app start
• threads
• logs
Questions?

Modern Android app library stack

  • 1.
    Modern Android applibrary stack Tomáš Kypta #MobCon
  • 2.
  • 3.
    Q: “How doI get the data from the server?”
  • 4.
    A: “Use AsyncTask!” Theold school approach™
  • 5.
    public class MainActivityextends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener { public void onClick(View view) { new DownloadTask().execute(inputString); } }); } private class DownloadTask extends AsyncTask<String, Void, String> { @Override protected String doInBackground(String... params) { // download some data } @Override protected void onPostExecute(String result) { TextView txt = (TextView) findViewById(R.id.text); txt.setText(result); } } }
  • 6.
    Old school Androidapps • business logic in activities • with all the bad stuff such as networking • and memory leaks • and crashes
  • 7.
    Modern Android apps •use cleaner architectures • MVP, MVVM, MVI • use libraries heavily • use tests
  • 8.
    Libraries • save timeand work • simplify API • back-port new APIs to older Android version
  • 9.
    Ideal Android Library •“perform one task and perform it well” • easy to use • open-source • easily available • through a remote Maven repository
  • 10.
    Ideal Android Library •doesn’t eat too much resources • doesn’t require too many permissions • behave nicely when crashing
  • 11.
    “I wan’t myapp to work on Android from version 4.1.” 98% of Android devices!
  • 12.
    Support libraries • availablethrough Android SDK • backport newer Android APIs • helper classes • debugging, testing, utilities
  • 13.
    Support libraries • AsyncTaskLoader •com.android.support:support-core-utils:25.3.0 • ViewPager • com.android.support:support-core-ui:25.3.0 • support fragments • com.android.support:support-fragment:25.3.0
  • 14.
    Support libraries • AppCompatActivity,ActionBar • com.android.support:appcompat-v7:25.3.0 • RecyclerView • com.android.support:recyclerview-v7:25.3.0 • CardView • com.android.support:cardview-v7:25.3.0
  • 15.
    Support libraries • com.android.support:support-annotations:25.3.0 •useful annotations • StringRes, IntDef, Nullable, UiThread, WorkerThread, CallSuper, VisibleForTesting, … • com.android.support:design:25.3.0 • Material design
  • 16.
    Support libraries • Havingmore than 64k methods? • And supporting Android prior 5.0? • com.android.support:multidex:1.0.1
  • 17.
    “This dependency injectionthing sounds useful.”
  • 18.
    Dagger 2 • dependencyinjection framework for Android and Java • avoids reflection • uses compile-time generated code
  • 19.
    Dagger 2 public classSimpleGameProvider { private ApiProvider mApiProvider; private StorageProvider mStorageProvider; @Inject public SimpleProvider(ApiProvider apiProvider, StorageProvider storageProvider) { mApiProvider = apiProvider; mStorageProvider = storageProvider; } public void doSomething() { // … } }
  • 20.
    Dagger 2 @Module public classAppModule { private Context mApplicationContext; public AppModule(Context applicationContext) { mApplicationContext = applicationContext; } @Singleton @Provides protected OtherProvider provideTheOther(Context context) { return new OtherProvider(context); } }
  • 21.
    Dagger 2 @Singleton @Component(modules ={AppModule.class}) public interface AppComponent { void inject(MainActivity activity); }
  • 22.
    Dagger 2 public classMainActivity extends AppCompatActivity { @Inject SimpleProvider mSimpleProvider; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()).getAppComponent().inject(this); // and now we can use mSimpleProvider } }
  • 23.
    “My server hasthis REST API…”
  • 24.
    Retrofit • simple RESTclient for Android and Java • annotation-based API • type-safe • Rx compatible
  • 25.
    Retrofit public interface GitHubApi{ @GET("/users/{username}") User getUser(@Path("username") String username); @GET("/users/{username}/repos") List<Repo> getUserRepos(@Path("username") String username); @POST("/orgs/{org}/repos") RepoCreationResponse createRepoInOrganization( @Path("org") String organization, @Body RepoCreationRequest request); }
  • 26.
    Retrofit RestAdapter adapter =new RestAdapter.Builder() .setEndpoint(GITHUB_API_URL) .setRequestInterceptor(new RequestInterceptor() { @Override public void intercept(RequestFacade request) { request.addHeader("Accept", "application/vnd.github.v3+json"); } }) .setLogLevel(RestAdapter.LogLevel.BASIC) .build(); GitHubApi gitHubApi = adapter.create(GitHubApi.class);
  • 27.
    “And my serverhas this fancy new features…”
  • 28.
    OkHttp • an efficientHTTP client for Android and Java • requests can be easily customized • support for HTTP/2 • connection pooling • transparent GZIP • response caching
  • 29.
    OkHttp OkHttpClient client =new OkHttpClient(); Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute(); return response.body().string();
  • 30.
    OkHttp • Works outof the box with the latest Retrofit!
  • 31.
    “The server returns400. What’s wrong?”
  • 32.
    Stetho • A debugbridge • hooks into Chrome Developer Tools
  • 33.
    Stetho • network inspection •database inspection • view hierarchy • dumpapp system allowing custom plugins • command-line interface for communication with the plugins
  • 35.
    Stetho public class MyApplicationextends Application { @Override public void onCreate() { super.onCreate(); Stetho.initializeWithDefaults(this); } }
  • 37.
    Stetho OkHttpClient okHttpClient =new OkHttpClient.Builder() .addNetworkInterceptor(new StethoInterceptor()) .build();
  • 38.
    “Why I’m gettingthis OutOfMemoryError?”
  • 39.
    LeakCanary • memory leakdetection library • notifies about memory leaks during app development
  • 40.
    LeakCanary dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile'com.squareup.leakcanary:leakcanary-android-no-op:1.5' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' }
  • 41.
    “How to displaythis remote product image?”
  • 42.
    Image loaders • tonsof libs • Universal Image Loader • Picasso • Glide
  • 43.
  • 44.
    “How can Inotify that class?“ “There has to be some way without refactoring the whole thing!”
  • 45.
    Event bus • forcommunication between decoupled parts of an app • EventBus
  • 46.
    EventBus • events • subscribers •register and unregister • post events public static class MessageEvent { /* fields if needed */ } @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* handle event */}; EventBus.getDefault().register(this); EventBus.getDefault().unregister(this); EventBus.getDefault().post(new MessageEvent());
  • 47.
    “How do Iget the data from the server?” “And I have to combine couple of sources.”
  • 48.
    RxJava • general Javalibrary • reactive programming • push concept • composable data flow
  • 49.
    RxJava • useful forsimple async processing • async composition • offers simple chaining of operations on data • eliminates callback hell
  • 50.
    RxJava • works wellwith Retrofit • can completely replace event bus libraries • hard to learn • RxJava 1 vs. RxJava 2 • they will coexist for some time
  • 51.
    RxJava data flow Observable .from(newString[]{"Hello", "Droidcon!"}) creation
  • 52.
    RxJava data flow Observable .from(newString[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) creation
  • 53.
    RxJava data flow Observable .from(newString[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) .reduce(new Func2<String, String, String>() { @Override public String call(String s, String s2) { return s + ' ' + s2; } }) creation transformation
  • 54.
    RxJava data flow Observable .from(newString[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } }) .reduce(new Func2<String, String, String>() { @Override public String call(String s, String s2) { return s + ' ' + s2; } }) .subscribe(new Action1<String>() { @Override public void call(String s) { Timber.i(s); } }); creation transformation subscription
  • 55.
    RxJava data flowwith Java 8 creation transformation subscription Observable .from(new String[]{"Hello", "Droidcon!"}) .map(s -> s.toUpperCase(Locale.getDefault())) .reduce((s,s2) -> s + ' ' + s2) .subscribe(s -> Timber.i(s));
  • 56.
  • 57.
    “This new featureis great! I bet users will love it!”
  • 58.
    Analytics & crashreporting • Google Analytics • Crashlytics • Firebase
  • 59.
    “I don’t likethis Java language.”
  • 60.
    Kotlin • not alibrary • a JVM programming language • “Swift for Android devs"
  • 61.
    Q: “So allI have to do is to Google for a library to do the thing?”
  • 62.
    A: “think wiselybefore adding a new library.”
  • 63.
    Final thoughts • manypotential problems • transitive dependencies • permissions • app size • slow app start • threads • logs
  • 64.