2. 2016 Android Top 10 Libraries
Ref: http://chuansong.me/n/1493021151427
3. 2016 Android Top 10 Libraries
Ref: http://chuansong.me/n/1493021151427
4. 2016 Android Top 10 Libraries
Ref: http://chuansong.me/n/1493021151427
5. 為什麼需要這些東西?
“The Android framework offers a lot of flexibility
when it comes to defining how to organize and
architect an Android app. This freedom, whilst
very valuable, can also result in apps with large
classes, inconsistent naming and architectures
(or lack of) that can make testing, maintaining
and extending difficult.”
Ref: https://github.com/googlesamples/android-architecture
6. Android
Architecture
Blueprints
“A collection of samples to discuss and showcase different
architectural tools and patterns for Android app”
Ref: https://github.com/googlesamples/android-architecture
13. What is MVP
● View is a layer that displays data and reacts
to user actions
○ Activity
○ Fragment
○ View
● Model is a data access layer
○ Database API
○ Remote server API
● Presenter provides View with data from
Model
○ Presenter also handles background tasks
Ref: http://konmik.com/post/introduction_to_model_view_presenter_on_android/
23. Model implementation
public class TasksRepository implements TasksDataSource {
public void getTasks(@NonNull final LoadTasksCallback callback) {
if (mCachedTasks != null && !mCacheIsDirty) {
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
return;
}
if (mCacheIsDirty) {
getTasksFromRemoteDataSource(callback);
} else {
mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
refreshCache(tasks);
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
}
@Override
public void onDataNotAvailable() {
getTasksFromRemoteDataSource(callback);
}
});
}
}
}
Respond immediately with cache if available and not dirty
If the cache is dirty we need to fetch new data from the
network.
Query the local storage if available. If not, query the
network.
25. What is Clean Architecture
Ref: https://en.wikipedia.org/wiki/Robert_Cecil_Martin
26. What is Clean Architecture
Dependency Rule: source code dependencies can only point inwards and nothing in an
inner circle can know anything at all about something in an outer circle.
Ref: http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
27. What is Clean Architecture
● Entities
○ These are the business objects of the application.
● Use Cases
○ These use cases orchestrate the flow of data to and from the
entities. Are also called Interactors.
● Interface Adapters (Presenter)
○ This set of adapters convert data from the format most
convenient for the use cases and entities. Presenters and
Controllers belong here.
● Frameworks and Drivers (UI)
○ This is where all the details go: UI, tools, frameworks, etc.
Ref: http://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/
28. What is Clean Architecture
● Independent of Frameworks
● Testable
○ Business rules (Use cases) can be tested without external
element
● Independent of UI
○ The UI can change easily, without changing the rest of the
system
● Independent of Database
○ Business rules (Use cases) are not bound to the database
● Independent of any external agency
○ Business rules (Use cases) don’t know anything at all about
the outside world
Ref: https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html
29. Presenter implementation
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TaskDetailContract.View mTaskDetailView;
private final UseCaseHandler mUseCaseHandler;
private final GetTask mGetTask;
private final CompleteTask mCompleteTask;
private final ActivateTask mActivateTask;
private final DeleteTask mDeleteTask;
public TaskDetailPresenter(@NonNull UseCaseHandler useCaseHandler,
@Nullable String taskId,
@NonNull TaskDetailContract.View taskDetailView,
@NonNull GetTask getTask,
@NonNull CompleteTask completeTask,
@NonNull ActivateTask activateTask,
@NonNull DeleteTask deleteTask) {
mUseCaseHandler = checkNotNull(useCaseHandler, "useCaseHandler cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mGetTask = checkNotNull(getTask, "getTask cannot be null!");
mCompleteTask = checkNotNull(completeTask, "completeTask cannot be null!");
mActivateTask = checkNotNull(activateTask, "activateTask cannot be null!");
mDeleteTask = checkNotNull(deleteTask, "deleteTask cannot be null!");
mTaskDetailView.setPresenter(this);
}
}
Use case
Background executor
30. Presenter implementation
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private void openTask() {
mTaskDetailView.setLoadingIndicator(true);
mUseCaseHandler.execute(mGetTask, new GetTask.RequestValues(mTaskId),
new UseCase.UseCaseCallback<GetTask.ResponseValue>() {
@Override
public void onSuccess(GetTask.ResponseValue response) {
Task task = response.getTask();
mTaskDetailView.setLoadingIndicator(false);
showTask(task);
}
@Override
public void onError() {
mTaskDetailView.showMissingTask();
}
});
}
}
View operation
View operation
View operation
View operation
Model operation
31. Use case implementation
public class GetTask extends UseCase<GetTask.RequestValues, GetTask.ResponseValue> {
private final TasksRepository mTasksRepository;
public GetTask(@NonNull TasksRepository tasksRepository) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
}
@Override
protected void executeUseCase(final RequestValues values) {
mTasksRepository.getTask(values.getTaskId(), new TasksDataSource.GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
if (task != null) {
ResponseValue responseValue = new ResponseValue(task);
getUseCaseCallback().onSuccess(responseValue);
} else {
getUseCaseCallback().onError();
}
}
@Override
public void onDataNotAvailable() {
getUseCaseCallback().onError();
}
});
}
}
33. What is RxJava
“RxJava is a Java VM implementation of Reactive Extensions: a
library for composing asynchronous and event-based programs
by using observable sequences.”
“It extends the observer pattern to support sequences of
data/events and adds operators that allow you to compose
sequences together declaratively while abstracting away
concerns about things like low-level threading, synchronization,
thread-safety and concurrent data structures.”
Ref: https://github.com/ReactiveX/RxJava
34. View implementation
public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
@Override
public void onResume() {
super.onResume();
mPresenter.subscribe();
}
@Override
public void onPause() {
super.onPause();
mPresenter.unsubscribe();
}
@Override
public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}
}
35. Presenter implementation
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
@NonNull private final TasksRepository mTasksRepository;
@NonNull private final TaskDetailContract.View mTaskDetailView;
@NonNull private final BaseSchedulerProvider mSchedulerProvider;
@NonNull private CompositeSubscription mSubscriptions;
public TaskDetailPresenter(@Nullable String taskId,
@NonNull TasksRepository tasksRepository,
@NonNull TaskDetailContract.View taskDetailView,
@NonNull BaseSchedulerProvider schedulerProvider) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mSchedulerProvider = checkNotNull(schedulerProvider, "schedulerProvider cannot be null");
mSubscriptions = new CompositeSubscription();
mTaskDetailView.setPresenter(this);
}
@Override
public void subscribe() {
openTask();
}
@Override
public void unsubscribe() {
mSubscriptions.clear();
}
}
36. Presenter implementation
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
private final BaseSchedulerProvider mSchedulerProvider;
private CompositeSubscription mSubscriptions;
private void openTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTaskDetailView.setLoadingIndicator(true);
mSubscriptions.add(mTasksRepository
.getTask(mTaskId)
.subscribeOn(mSchedulerProvider.computation())
.observeOn(mSchedulerProvider.ui())
.subscribe(
// onNext
this::showTask,
// onError
throwable -> {
},
// onCompleted
() -> mTaskDetailView.setLoadingIndicator(false)));
}
}
View operation
View operation
View operation
View operation
Model operation
37. Model implementation
public class TasksRepository implements TasksDataSource {
private final TasksDataSource mTasksRemoteDataSource;
private final TasksDataSource mTasksLocalDataSource;
@VisibleForTesting Map<String, Task> mCachedTasks;
@VisibleForTesting boolean mCacheIsDirty = false;
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
@Override
public Observable<List<Task>> getTasks() {
if (mCachedTasks != null && !mCacheIsDirty) {
return Observable.from(mCachedTasks.values()).toList();
} else if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
Observable<List<Task>> remoteTasks = getAndSaveRemoteTasks();
if (mCacheIsDirty) {
return remoteTasks;
} else {
Observable<List<Task>> localTasks = getAndCacheLocalTasks();
return Observable.concat(localTasks, remoteTasks)
.filter(tasks -> !tasks.isEmpty())
.first();
}
}
}
Observer pattern
Compose sequences
40. What is Dagger
“Dagger is a fully static, compile-time dependency injection
framework for both Java and Android. It is an adaptation of an
earlier version created by Square and now maintained by
Google.”
“Dagger is a replacement for FactoryFactory classes that
implements the dependency injection design pattern without the
burden of writing the boilerplate. It allows you to focus on the
interesting classes. Declare dependencies, specify how to satisfy
them, and ship your app.”
Ref: https://google.github.io/dagger/
41. View and Presenter’s creation & binding
public class TaskDetailActivity extends AppCompatActivity {
@Inject TaskDetailPresenter mTaskDetailPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
TaskDetailFragment taskDetailFragment =
(TaskDetailFragment) getSupportFragmentManager()
.findFragmentById(R.id.contentFrame);
if (taskDetailFragment == null) {
taskDetailFragment = TaskDetailFragment.newInstance(taskId);
ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
taskDetailFragment, R.id.contentFrame);
}
DaggerTaskDetailComponent.builder()
.taskDetailPresenterModule(
new TaskDetailPresenterModule(taskDetailFragment,
taskId))
.tasksRepositoryComponent(((ToDoApplication) getApplication())
.getTasksRepositoryComponent()).build()
.inject(this);
}
}
Field injection
Inject presenter
Provide view