Upload
tomas-kypta
View
105
Download
8
Embed Size (px)
Citation preview
Modern Android app library stackTomáš 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 2public class SimpleGameProvider {
private ApiProvider mApiProvider; private StorageProvider mStorageProvider;
@Inject public SimpleProvider(ApiProvider apiProvider, StorageProvider storageProvider) { mApiProvider = apiProvider; mStorageProvider = storageProvider; }
public void doSomething() { // … }}
Dagger 2@Modulepublic 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 2public 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
Retrofitpublic 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);}
RetrofitRestAdapter 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 flowObservable .from(new String[]{"Hello", "Droidcon!"})
creation
RxJava data flowObservable .from(new String[]{"Hello", "Droidcon!"}) .map(new Func1<String, String>() { @Override public String call(String s) { return s.toUpperCase(Locale.getDefault()); } })
creation
RxJava data flowObservable .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 flowObservable .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
creationtransformationsubscription
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
RxAndroidRxBinding
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?