SlideShare a Scribd company logo
在2017年當個
潮潮der~
Android工程師
必須要知道的事
Johnny Lee 2017/1/25
2016 Android Top 10 Libraries
Ref: http://chuansong.me/n/1493021151427
2016 Android Top 10 Libraries
Ref: http://chuansong.me/n/1493021151427
2016 Android Top 10 Libraries
Ref: http://chuansong.me/n/1493021151427
為什麼需要這些東西?
“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
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
Outline
● Architectural patterns
○ MVP
○ MVP-Clean
● Architectural tools
○ MVP-Dagger
○ MVP-RxJava
To-do App
● Task list activity
● Task detail activity
● Add task activity
● Statistics activity
To-do App
● Task list activity
● Task detail activity
● Add task activity
● Statistics activity
To-do App
● Task list activity
● Task detail activity
● Add task activity
● Statistics activity
To-do App
● Task list activity
● Task detail activity
● Add task activity
● Statistics activity
MVP
Model-View-Presenter
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/
Activity
Without MVP -
Activity is a god object
With MVP
Presenter
Model
Activity
View
Constract
View and Presenter’s creation & binding
public class TaskDetailActivity extends AppCompatActivity {
@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);
}
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);
}
}
View
Presenter
View and Presenter’s creation & binding
public class TaskDetailActivity extends AppCompatActivity {
@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);
}
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);
}
}
public class TaskDetailFragment extends Fragment
implements TaskDetailContract.View {
@Override
public void setPresenter(@NonNull
TaskDetailContract.Presenter presenter)
{
mPresenter = checkNotNull(presenter);
}
}
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
private String mTaskId;
public TaskDetailPresenter(String taskId,
TasksRepository tasksRepository,
TaskDetailContract.View taskDetailView) {
mTaskDetailView.setPresenter(this);
}
}
Contract
public interface TaskDetailContract {
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showMissingTask();
void hideTitle();
void showTitle(String title);
// ...
}
interface Presenter extends BasePresenter {
void editTask();
void deleteTask();
void completeTask();
void activateTask();
}
}
public interface BaseView<T> {
void setPresenter(T
presenter);
}
View implementation
public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
@Override
public void onResume() {
mPresenter.start();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.editTask();
}
});
return root;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_delete:
mPresenter.deleteTask();
return true;
}
return false;
}
// ...
}
Presenter operation
Presenter operation
Presenter operation
Presenter implementation
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
public TaskDetailPresenter(@Nullable String taskId,
@NonNull TasksRepository tasksRepository,
@NonNull TaskDetailContract.View taskDetailView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mTaskDetailView.setPresenter(this);
}
@Override
public void start() { openTask(); }
private void openTask() {
mTaskDetailView.setLoadingIndicator(true);
mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
@Override
public void onTaskLoaded(Task task) {
mTaskDetailView.setLoadingIndicator(false);
showTask(task);
}
@Override
public void onDataNotAvailable() {
mTaskDetailView.showMissingTask();
}
});
}
// ...
}
View operation
View operation
View operation
View operation
Model operation
public class TasksRepository implements TasksDataSource {
private static TasksRepository INSTANCE = null;
private final TasksDataSource mTasksRemoteDataSource;
private final TasksDataSource mTasksLocalDataSource;
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource,
tasksLocalDataSource);
}
return INSTANCE;
}
// ...
}
Model implementation
public class TasksRepository implements TasksDataSource {
private static TasksRepository INSTANCE = null;
private final TasksDataSource mTasksRemoteDataSource;
private final TasksDataSource mTasksLocalDataSource;
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource,
tasksLocalDataSource);
}
return INSTANCE;
}
// ...
}
Model implementation
public interface TasksDataSource {
interface LoadTasksCallback {
void onTasksLoaded(List<Task> tasks);
void onDataNotAvailable();
}
interface GetTaskCallback {
void onTaskLoaded(Task task);
void onDataNotAvailable();
}
void getTasks(@NonNull LoadTasksCallback callback);
void getTask(@NonNull String taskId,
@NonNull GetTaskCallback callback);
void saveTask(@NonNull Task task);
void completeTask(@NonNull Task task);
void completeTask(@NonNull String taskId);
void activateTask(@NonNull Task task);
void activateTask(@NonNull String taskId);
void clearCompletedTasks();
void refreshTasks();
void deleteAllTasks();
void deleteTask(@NonNull String taskId);
}
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.
Clean
What is Clean Architecture
Ref: https://en.wikipedia.org/wiki/Robert_Cecil_Martin
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/
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/
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
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
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
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();
}
});
}
}
RxJava
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
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);
}
}
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();
}
}
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
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
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();
}
}
}
private Observable<List<Task>> getAndCacheLocalTasks() {
return mTasksLocalDataSource.getTasks()
.flatMap(new Func1<List<Task>, Observable<List<Task>>>() {
@Override
public Observable<List<Task>> call(List<Task> tasks) {
return Observable.from(tasks)
.doOnNext(task ->
mCachedTasks.put(task.getId(), task))
.toList();
}
});
}
private Observable<List<Task>> getAndSaveRemoteTasks() {
return mTasksRemoteDataSource
.getTasks()
.flatMap(new Func1<List<Task>, Observable<List<Task>>>() {
@Override
public Observable<List<Task>> call(List<Task> tasks) {
return Observable.from(tasks).doOnNext(task -> {
mTasksLocalDataSource.saveTask(task);
mCachedTasks.put(task.getId(), task);
}).toList();
}
})
.doOnCompleted(() -> mCacheIsDirty = false);
}
Dagger
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/
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
Dagger configuration
public class TaskDetailActivity extends AppCompatActivity {
@Inject TaskDetailPresenter mTaskDetailPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
DaggerTaskDetailComponent.builder()
.taskDetailPresenterModule(
new TaskDetailPresenterModule(taskDetailFragment, taskId))
.tasksRepositoryComponent(((ToDoApplication) getApplication())
.getTasksRepositoryComponent()).build()
.inject(this);
}
}
@FragmentScoped
@Component(dependencies = TasksRepositoryComponent.class,
modules = TaskDetailPresenterModule.class)
public interface TaskDetailComponent {
void inject(TaskDetailActivity taskDetailActivity);
}
Dagger configuration
public class TaskDetailActivity extends AppCompatActivity {
@Inject TaskDetailPresenter mTaskDetailPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
DaggerTaskDetailComponent.builder()
.taskDetailPresenterModule(
new TaskDetailPresenterModule(taskDetailFragment, taskId))
.tasksRepositoryComponent(((ToDoApplication) getApplication())
.getTasksRepositoryComponent()).build()
.inject(this);
}
}
@Module
public class TaskDetailPresenterModule {
private final TaskDetailContract.View mView;
private final String mTaskId;
public TaskDetailPresenterModule(
TaskDetailContract.View view, String taskId) {
mView = view;
mTaskId = taskId;
}
@Provides
TaskDetailContract.View provideTaskDetailContractView() {
return mView;
}
@Provides
String provideTaskId() {
return mTaskId;
}
}
Dagger configuration
public class TaskDetailActivity extends AppCompatActivity {
@Inject TaskDetailPresenter mTaskDetailPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
DaggerTaskDetailComponent.builder()
.taskDetailPresenterModule(
new TaskDetailPresenterModule(taskDetailFragment, taskId))
.tasksRepositoryComponent(((ToDoApplication) getApplication())
.getTasksRepositoryComponent()).build()
.inject(this);
}
} @Singleton
@Component(modules = {TasksRepositoryModule.class,
ApplicationModule.class})
public interface TasksRepositoryComponent {
TasksRepository getTasksRepository();
}
@Module
public class TasksRepositoryModule {
@Singleton @Provides @Local
TasksDataSource provideTasksLocalDataSource(Context context) {
return new TasksLocalDataSource(context);
}
@Singleton @Provides @Remote
TasksDataSource provideTasksRemoteDataSource() {
return new FakeTasksRemoteDataSource();
}
}
@Module
public final class ApplicationModule {
private final Context mContext;
ApplicationModule(Context context) {
mContext = context;
}
@Provides
Context provideContext() {
return mContext;
}
}
Presenter implementation
final class TaskDetailPresenter implements TaskDetailContract.Presenter
{
private TasksRepository mTasksRepository;
private TaskDetailContract.View mTaskDetailView;
@Nullable String mTaskId;
@Inject
TaskDetailPresenter(@Nullable String taskId,
TasksRepository tasksRepository,
TaskDetailContract.View taskDetailView) {
mTasksRepository = tasksRepository;
mTaskDetailView = taskDetailView;
mTaskId = taskId;
}
@Inject
void setupListeners() {
mTaskDetailView.setPresenter(this);
}
}
Constructor
injection
Method
injection
Reference
● Android Architecture Blueprints
● Architecting Android…The clean
way?
● The Clean Architecture
● Introduction to Model View
Presenter on Android

More Related Content

What's hot

JS and patterns
JS and patternsJS and patterns
JS and patterns
David Rodenas
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
Ali Parmaksiz
 
Springを用いた社内ライブラリ開発
Springを用いた社内ライブラリ開発Springを用いた社内ライブラリ開発
Springを用いた社内ライブラリ開発
Recruit Lifestyle Co., Ltd.
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
Fabio Collini
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
Fabio Collini
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
Fabio Collini
 
Don't Make Android Bad... Again
Don't Make Android Bad... AgainDon't Make Android Bad... Again
Don't Make Android Bad... Again
Pedro Vicente
 
The real beginner's guide to android testing
The real beginner's guide to android testingThe real beginner's guide to android testing
The real beginner's guide to android testing
Eric (Trung Dung) Nguyen
 
Android development
Android developmentAndroid development
Android development
Gregoire BARRET
 
Android TDD
Android TDDAndroid TDD
Android TDD
Godfrey Nolan
 
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
First Tuesday Bergen
 
Dependency Injection for Android
Dependency Injection for AndroidDependency Injection for Android
Dependency Injection for Android
First Tuesday Bergen
 
Test or Go Fishing - a guide on how to write better Swift for iOS
Test or Go Fishing - a guide on how to write better Swift for iOSTest or Go Fishing - a guide on how to write better Swift for iOS
Test or Go Fishing - a guide on how to write better Swift for iOS
Paul Ardeleanu
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroid
GDG Korea
 
Under the Hood: Using Spring in Grails
Under the Hood: Using Spring in GrailsUnder the Hood: Using Spring in Grails
Under the Hood: Using Spring in Grails
GR8Conf
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
David Rodenas
 
Under the Hood: Using Spring in Grails
Under the Hood: Using Spring in GrailsUnder the Hood: Using Spring in Grails
Under the Hood: Using Spring in Grails
Burt Beckwith
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for Programmers
David Rodenas
 
Test or Go Fishing - A guide on how to write better Swift for iOS
Test or Go Fishing - A guide on how to write better Swift for iOSTest or Go Fishing - A guide on how to write better Swift for iOS
Test or Go Fishing - A guide on how to write better Swift for iOS
Paul Ardeleanu
 
Rxjs ppt
Rxjs pptRxjs ppt

What's hot (20)

JS and patterns
JS and patternsJS and patterns
JS and patterns
 
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo A GWT Application with MVP Pattern Deploying to CloudFoundry using  Spring Roo
A GWT Application with MVP Pattern Deploying to CloudFoundry using Spring Roo
 
Springを用いた社内ライブラリ開発
Springを用いた社内ライブラリ開発Springを用いた社内ライブラリ開発
Springを用いた社内ライブラリ開発
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
 
Don't Make Android Bad... Again
Don't Make Android Bad... AgainDon't Make Android Bad... Again
Don't Make Android Bad... Again
 
The real beginner's guide to android testing
The real beginner's guide to android testingThe real beginner's guide to android testing
The real beginner's guide to android testing
 
Android development
Android developmentAndroid development
Android development
 
Android TDD
Android TDDAndroid TDD
Android TDD
 
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
Dependency Injection for Android @ Ciklum speakers corner Kiev 29. May 2014
 
Dependency Injection for Android
Dependency Injection for AndroidDependency Injection for Android
Dependency Injection for Android
 
Test or Go Fishing - a guide on how to write better Swift for iOS
Test or Go Fishing - a guide on how to write better Swift for iOSTest or Go Fishing - a guide on how to write better Swift for iOS
Test or Go Fishing - a guide on how to write better Swift for iOS
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroid
 
Under the Hood: Using Spring in Grails
Under the Hood: Using Spring in GrailsUnder the Hood: Using Spring in Grails
Under the Hood: Using Spring in Grails
 
TDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving TestingTDD CrashCourse Part4: Improving Testing
TDD CrashCourse Part4: Improving Testing
 
Under the Hood: Using Spring in Grails
Under the Hood: Using Spring in GrailsUnder the Hood: Using Spring in Grails
Under the Hood: Using Spring in Grails
 
ReactJS for Programmers
ReactJS for ProgrammersReactJS for Programmers
ReactJS for Programmers
 
Test or Go Fishing - A guide on how to write better Swift for iOS
Test or Go Fishing - A guide on how to write better Swift for iOSTest or Go Fishing - A guide on how to write better Swift for iOS
Test or Go Fishing - A guide on how to write better Swift for iOS
 
Rxjs ppt
Rxjs pptRxjs ppt
Rxjs ppt
 

Viewers also liked

大型App面臨的挑戰
大型App面臨的挑戰大型App面臨的挑戰
大型App面臨的挑戰
Chih-Chung Lee
 
為什麼Method數超過65535會build fail?
為什麼Method數超過65535會build fail?為什麼Method數超過65535會build fail?
為什麼Method數超過65535會build fail?
Chih-Chung Lee
 
例外處理設計
例外處理設計例外處理設計
例外處理設計
Chih-Chung Lee
 
Rxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJavaRxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJava
Kros Huang
 
Model View Presenter for Android
Model View Presenter for AndroidModel View Presenter for Android
Model View Presenter for Android
shinnosuke kugimiya
 
Cracking the Facebook Coding Interview
Cracking the Facebook Coding InterviewCracking the Facebook Coding Interview
Cracking the Facebook Coding Interview
Gayle McDowell
 
HTC RE Camera 開發分享
HTC RE Camera 開發分享HTC RE Camera 開發分享
HTC RE Camera 開發分享
Chih-Chung Lee
 
How to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & PushHow to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & Push
Mu Chun Wang
 
產品設計的0到1,與1到1億
產品設計的0到1,與1到1億產品設計的0到1,與1到1億
產品設計的0到1,與1到1億
Ivan Wei
 
用戶體驗設計,從需求到產品落地
用戶體驗設計,從需求到產品落地用戶體驗設計,從需求到產品落地
用戶體驗設計,從需求到產品落地
Ivan Wei
 

Viewers also liked (10)

大型App面臨的挑戰
大型App面臨的挑戰大型App面臨的挑戰
大型App面臨的挑戰
 
為什麼Method數超過65535會build fail?
為什麼Method數超過65535會build fail?為什麼Method數超過65535會build fail?
為什麼Method數超過65535會build fail?
 
例外處理設計
例外處理設計例外處理設計
例外處理設計
 
Rxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJavaRxjava 介紹與 Android 中的 RxJava
Rxjava 介紹與 Android 中的 RxJava
 
Model View Presenter for Android
Model View Presenter for AndroidModel View Presenter for Android
Model View Presenter for Android
 
Cracking the Facebook Coding Interview
Cracking the Facebook Coding InterviewCracking the Facebook Coding Interview
Cracking the Facebook Coding Interview
 
HTC RE Camera 開發分享
HTC RE Camera 開發分享HTC RE Camera 開發分享
HTC RE Camera 開發分享
 
How to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & PushHow to build a scalable SNS via Polling & Push
How to build a scalable SNS via Polling & Push
 
產品設計的0到1,與1到1億
產品設計的0到1,與1到1億產品設計的0到1,與1到1億
產品設計的0到1,與1到1億
 
用戶體驗設計,從需求到產品落地
用戶體驗設計,從需求到產品落地用戶體驗設計,從需求到產品落地
用戶體驗設計,從需求到產品落地
 

Similar to Android architecture blueprints overview

Architecture components - IT Talk
Architecture components - IT TalkArchitecture components - IT Talk
Architecture components - IT Talk
Constantine Mars
 
Architecture Components
Architecture Components Architecture Components
Architecture Components
DataArt
 
To-Do App With Flutter: Step By Step Guide
To-Do App With Flutter: Step By Step GuideTo-Do App With Flutter: Step By Step Guide
To-Do App With Flutter: Step By Step Guide
Biztech Consulting & Solutions
 
Android best practices
Android best practicesAndroid best practices
Android best practices
Jose Manuel Ortega Candel
 
Modern Android app library stack
Modern Android app library stackModern Android app library stack
Modern Android app library stack
Tomáš Kypta
 
Building user interface with react
Building user interface with reactBuilding user interface with react
Building user interface with react
Amit Thakkar
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
Jose Manuel Pereira Garcia
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
Ernesto Hernández Rodríguez
 
Android dev toolbox
Android dev toolboxAndroid dev toolbox
Android dev toolbox
Shem Magnezi
 
Appcelerator droidcon15 TLV
Appcelerator droidcon15 TLVAppcelerator droidcon15 TLV
Appcelerator droidcon15 TLV
YishaiBrown
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
C.T.Co
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
Alexey Buzdin
 
Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!
Stacy Devino
 
Office 365 Groups and Tasks API - Getting Started
Office 365 Groups and Tasks API - Getting StartedOffice 365 Groups and Tasks API - Getting Started
Office 365 Groups and Tasks API - Getting Started
Dragan Panjkov
 
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
Daniel Fisher
 
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with EclipseEclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
Heiko Behrens
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
Alexander Varwijk
 
Android classes in mumbai
Android classes in mumbaiAndroid classes in mumbai
Android classes in mumbai
Vibrant Technologies & Computers
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
javatwo2011
 
Clean Architecture @ Taxibeat
Clean Architecture @ TaxibeatClean Architecture @ Taxibeat
Clean Architecture @ Taxibeat
Michael Bakogiannis
 

Similar to Android architecture blueprints overview (20)

Architecture components - IT Talk
Architecture components - IT TalkArchitecture components - IT Talk
Architecture components - IT Talk
 
Architecture Components
Architecture Components Architecture Components
Architecture Components
 
To-Do App With Flutter: Step By Step Guide
To-Do App With Flutter: Step By Step GuideTo-Do App With Flutter: Step By Step Guide
To-Do App With Flutter: Step By Step Guide
 
Android best practices
Android best practicesAndroid best practices
Android best practices
 
Modern Android app library stack
Modern Android app library stackModern Android app library stack
Modern Android app library stack
 
Building user interface with react
Building user interface with reactBuilding user interface with react
Building user interface with react
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 
Arquitecturas de microservicios - Medianet Software
Arquitecturas de microservicios   -  Medianet SoftwareArquitecturas de microservicios   -  Medianet Software
Arquitecturas de microservicios - Medianet Software
 
Android dev toolbox
Android dev toolboxAndroid dev toolbox
Android dev toolbox
 
Appcelerator droidcon15 TLV
Appcelerator droidcon15 TLVAppcelerator droidcon15 TLV
Appcelerator droidcon15 TLV
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!Async task, threads, pools, and executors oh my!
Async task, threads, pools, and executors oh my!
 
Office 365 Groups and Tasks API - Getting Started
Office 365 Groups and Tasks API - Getting StartedOffice 365 Groups and Tasks API - Getting Started
Office 365 Groups and Tasks API - Getting Started
 
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
 
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with EclipseEclipseCon2011 Cross-Platform Mobile Development with Eclipse
EclipseCon2011 Cross-Platform Mobile Development with Eclipse
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
 
Android classes in mumbai
Android classes in mumbaiAndroid classes in mumbai
Android classes in mumbai
 
比XML更好用的Java Annotation
比XML更好用的Java Annotation比XML更好用的Java Annotation
比XML更好用的Java Annotation
 
Clean Architecture @ Taxibeat
Clean Architecture @ TaxibeatClean Architecture @ Taxibeat
Clean Architecture @ Taxibeat
 

Recently uploaded

Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...
OnePlan Solutions
 
How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?
ToXSL Technologies
 
Modelling Up - DDDEurope 2024 - Amsterdam
Modelling Up - DDDEurope 2024 - AmsterdamModelling Up - DDDEurope 2024 - Amsterdam
Modelling Up - DDDEurope 2024 - Amsterdam
Alberto Brandolini
 
Photoshop Tutorial for Beginners (2024 Edition)
Photoshop Tutorial for Beginners (2024 Edition)Photoshop Tutorial for Beginners (2024 Edition)
Photoshop Tutorial for Beginners (2024 Edition)
alowpalsadig
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
Grant Fritchey
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
Patrick Weigel
 
14 th Edition of International conference on computer vision
14 th Edition of International conference on computer vision14 th Edition of International conference on computer vision
14 th Edition of International conference on computer vision
ShulagnaSarkar2
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
gapen1
 
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom KittEnhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Peter Caitens
 
一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理
一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理
一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理
kgyxske
 
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
safelyiotech
 
Kubernetes at Scale: Going Multi-Cluster with Istio
Kubernetes at Scale:  Going Multi-Cluster  with IstioKubernetes at Scale:  Going Multi-Cluster  with Istio
Kubernetes at Scale: Going Multi-Cluster with Istio
Severalnines
 
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
kalichargn70th171
 
The Comprehensive Guide to Validating Audio-Visual Performances.pdf
The Comprehensive Guide to Validating Audio-Visual Performances.pdfThe Comprehensive Guide to Validating Audio-Visual Performances.pdf
The Comprehensive Guide to Validating Audio-Visual Performances.pdf
kalichargn70th171
 
TMU毕业证书精仿办理
TMU毕业证书精仿办理TMU毕业证书精仿办理
TMU毕业证书精仿办理
aeeva
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
XfilesPro
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
dakas1
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
Drona Infotech
 
Liberarsi dai framework con i Web Component.pptx
Liberarsi dai framework con i Web Component.pptxLiberarsi dai framework con i Web Component.pptx
Liberarsi dai framework con i Web Component.pptx
Massimo Artizzu
 

Recently uploaded (20)

Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...Transforming Product Development using OnePlan To Boost Efficiency and Innova...
Transforming Product Development using OnePlan To Boost Efficiency and Innova...
 
How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?How Can Hiring A Mobile App Development Company Help Your Business Grow?
How Can Hiring A Mobile App Development Company Help Your Business Grow?
 
Modelling Up - DDDEurope 2024 - Amsterdam
Modelling Up - DDDEurope 2024 - AmsterdamModelling Up - DDDEurope 2024 - Amsterdam
Modelling Up - DDDEurope 2024 - Amsterdam
 
Photoshop Tutorial for Beginners (2024 Edition)
Photoshop Tutorial for Beginners (2024 Edition)Photoshop Tutorial for Beginners (2024 Edition)
Photoshop Tutorial for Beginners (2024 Edition)
 
Using Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query PerformanceUsing Query Store in Azure PostgreSQL to Understand Query Performance
Using Query Store in Azure PostgreSQL to Understand Query Performance
 
WWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders AustinWWDC 2024 Keynote Review: For CocoaCoders Austin
WWDC 2024 Keynote Review: For CocoaCoders Austin
 
14 th Edition of International conference on computer vision
14 th Edition of International conference on computer vision14 th Edition of International conference on computer vision
14 th Edition of International conference on computer vision
 
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
如何办理(hull学位证书)英国赫尔大学毕业证硕士文凭原版一模一样
 
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom KittEnhanced Screen Flows UI/UX using SLDS with Tom Kitt
Enhanced Screen Flows UI/UX using SLDS with Tom Kitt
 
一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理
一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理
一比一原版(sdsu毕业证书)圣地亚哥州立大学毕业证如何办理
 
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
Safelyio Toolbox Talk Softwate & App (How To Digitize Safety Meetings)
 
Kubernetes at Scale: Going Multi-Cluster with Istio
Kubernetes at Scale:  Going Multi-Cluster  with IstioKubernetes at Scale:  Going Multi-Cluster  with Istio
Kubernetes at Scale: Going Multi-Cluster with Istio
 
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf8 Best Automated Android App Testing Tool and Framework in 2024.pdf
8 Best Automated Android App Testing Tool and Framework in 2024.pdf
 
The Comprehensive Guide to Validating Audio-Visual Performances.pdf
The Comprehensive Guide to Validating Audio-Visual Performances.pdfThe Comprehensive Guide to Validating Audio-Visual Performances.pdf
The Comprehensive Guide to Validating Audio-Visual Performances.pdf
 
TMU毕业证书精仿办理
TMU毕业证书精仿办理TMU毕业证书精仿办理
TMU毕业证书精仿办理
 
Assure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyesAssure Contact Center Experiences for Your Customers With ThousandEyes
Assure Contact Center Experiences for Your Customers With ThousandEyes
 
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
Everything You Need to Know About X-Sign: The eSign Functionality of XfilesPr...
 
一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理一比一原版(USF毕业证)旧金山大学毕业证如何办理
一比一原版(USF毕业证)旧金山大学毕业证如何办理
 
Mobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona InfotechMobile App Development Company In Noida | Drona Infotech
Mobile App Development Company In Noida | Drona Infotech
 
Liberarsi dai framework con i Web Component.pptx
Liberarsi dai framework con i Web Component.pptxLiberarsi dai framework con i Web Component.pptx
Liberarsi dai framework con i Web Component.pptx
 

Android architecture blueprints overview

  • 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
  • 7. Outline ● Architectural patterns ○ MVP ○ MVP-Clean ● Architectural tools ○ MVP-Dagger ○ MVP-RxJava
  • 8. To-do App ● Task list activity ● Task detail activity ● Add task activity ● Statistics activity
  • 9. To-do App ● Task list activity ● Task detail activity ● Add task activity ● Statistics activity
  • 10. To-do App ● Task list activity ● Task detail activity ● Add task activity ● Statistics activity
  • 11. To-do App ● Task list activity ● Task detail activity ● Add task activity ● Statistics activity
  • 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/
  • 16. View and Presenter’s creation & binding public class TaskDetailActivity extends AppCompatActivity { @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); } new TaskDetailPresenter( taskId, Injection.provideTasksRepository(getApplicationContext()), taskDetailFragment); } } View Presenter
  • 17. View and Presenter’s creation & binding public class TaskDetailActivity extends AppCompatActivity { @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); } new TaskDetailPresenter( taskId, Injection.provideTasksRepository(getApplicationContext()), taskDetailFragment); } } public class TaskDetailFragment extends Fragment implements TaskDetailContract.View { @Override public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } } public class TaskDetailPresenter implements TaskDetailContract.Presenter { private final TasksRepository mTasksRepository; private final TaskDetailContract.View mTaskDetailView; private String mTaskId; public TaskDetailPresenter(String taskId, TasksRepository tasksRepository, TaskDetailContract.View taskDetailView) { mTaskDetailView.setPresenter(this); } }
  • 18. Contract public interface TaskDetailContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); void showMissingTask(); void hideTitle(); void showTitle(String title); // ... } interface Presenter extends BasePresenter { void editTask(); void deleteTask(); void completeTask(); void activateTask(); } } public interface BaseView<T> { void setPresenter(T presenter); }
  • 19. View implementation public class TaskDetailFragment extends Fragment implements TaskDetailContract.View { @Override public void onResume() { mPresenter.start(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mPresenter.editTask(); } }); return root; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_delete: mPresenter.deleteTask(); return true; } return false; } // ... } Presenter operation Presenter operation Presenter operation
  • 20. Presenter implementation public class TaskDetailPresenter implements TaskDetailContract.Presenter { private final TasksRepository mTasksRepository; private final TaskDetailContract.View mTaskDetailView; public TaskDetailPresenter(@Nullable String taskId, @NonNull TasksRepository tasksRepository, @NonNull TaskDetailContract.View taskDetailView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!"); mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!"); mTaskDetailView.setPresenter(this); } @Override public void start() { openTask(); } private void openTask() { mTaskDetailView.setLoadingIndicator(true); mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() { @Override public void onTaskLoaded(Task task) { mTaskDetailView.setLoadingIndicator(false); showTask(task); } @Override public void onDataNotAvailable() { mTaskDetailView.showMissingTask(); } }); } // ... } View operation View operation View operation View operation Model operation
  • 21. public class TasksRepository implements TasksDataSource { private static TasksRepository INSTANCE = null; private final TasksDataSource mTasksRemoteDataSource; private final TasksDataSource mTasksLocalDataSource; private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource, @NonNull TasksDataSource tasksLocalDataSource) { mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource); mTasksLocalDataSource = checkNotNull(tasksLocalDataSource); } public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource, TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } return INSTANCE; } // ... } Model implementation
  • 22. public class TasksRepository implements TasksDataSource { private static TasksRepository INSTANCE = null; private final TasksDataSource mTasksRemoteDataSource; private final TasksDataSource mTasksLocalDataSource; private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource, @NonNull TasksDataSource tasksLocalDataSource) { mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource); mTasksLocalDataSource = checkNotNull(tasksLocalDataSource); } public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource, TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } return INSTANCE; } // ... } Model implementation public interface TasksDataSource { interface LoadTasksCallback { void onTasksLoaded(List<Task> tasks); void onDataNotAvailable(); } interface GetTaskCallback { void onTaskLoaded(Task task); void onDataNotAvailable(); } void getTasks(@NonNull LoadTasksCallback callback); void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback); void saveTask(@NonNull Task task); void completeTask(@NonNull Task task); void completeTask(@NonNull String taskId); void activateTask(@NonNull Task task); void activateTask(@NonNull String taskId); void clearCompletedTasks(); void refreshTasks(); void deleteAllTasks(); void deleteTask(@NonNull String taskId); }
  • 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.
  • 24. Clean
  • 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
  • 38. 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(); } } } private Observable<List<Task>> getAndCacheLocalTasks() { return mTasksLocalDataSource.getTasks() .flatMap(new Func1<List<Task>, Observable<List<Task>>>() { @Override public Observable<List<Task>> call(List<Task> tasks) { return Observable.from(tasks) .doOnNext(task -> mCachedTasks.put(task.getId(), task)) .toList(); } }); } private Observable<List<Task>> getAndSaveRemoteTasks() { return mTasksRemoteDataSource .getTasks() .flatMap(new Func1<List<Task>, Observable<List<Task>>>() { @Override public Observable<List<Task>> call(List<Task> tasks) { return Observable.from(tasks).doOnNext(task -> { mTasksLocalDataSource.saveTask(task); mCachedTasks.put(task.getId(), task); }).toList(); } }) .doOnCompleted(() -> mCacheIsDirty = false); }
  • 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
  • 42. Dagger configuration public class TaskDetailActivity extends AppCompatActivity { @Inject TaskDetailPresenter mTaskDetailPresenter; @Override protected void onCreate(Bundle savedInstanceState) { // ... DaggerTaskDetailComponent.builder() .taskDetailPresenterModule( new TaskDetailPresenterModule(taskDetailFragment, taskId)) .tasksRepositoryComponent(((ToDoApplication) getApplication()) .getTasksRepositoryComponent()).build() .inject(this); } } @FragmentScoped @Component(dependencies = TasksRepositoryComponent.class, modules = TaskDetailPresenterModule.class) public interface TaskDetailComponent { void inject(TaskDetailActivity taskDetailActivity); }
  • 43. Dagger configuration public class TaskDetailActivity extends AppCompatActivity { @Inject TaskDetailPresenter mTaskDetailPresenter; @Override protected void onCreate(Bundle savedInstanceState) { // ... DaggerTaskDetailComponent.builder() .taskDetailPresenterModule( new TaskDetailPresenterModule(taskDetailFragment, taskId)) .tasksRepositoryComponent(((ToDoApplication) getApplication()) .getTasksRepositoryComponent()).build() .inject(this); } } @Module public class TaskDetailPresenterModule { private final TaskDetailContract.View mView; private final String mTaskId; public TaskDetailPresenterModule( TaskDetailContract.View view, String taskId) { mView = view; mTaskId = taskId; } @Provides TaskDetailContract.View provideTaskDetailContractView() { return mView; } @Provides String provideTaskId() { return mTaskId; } }
  • 44. Dagger configuration public class TaskDetailActivity extends AppCompatActivity { @Inject TaskDetailPresenter mTaskDetailPresenter; @Override protected void onCreate(Bundle savedInstanceState) { // ... DaggerTaskDetailComponent.builder() .taskDetailPresenterModule( new TaskDetailPresenterModule(taskDetailFragment, taskId)) .tasksRepositoryComponent(((ToDoApplication) getApplication()) .getTasksRepositoryComponent()).build() .inject(this); } } @Singleton @Component(modules = {TasksRepositoryModule.class, ApplicationModule.class}) public interface TasksRepositoryComponent { TasksRepository getTasksRepository(); } @Module public class TasksRepositoryModule { @Singleton @Provides @Local TasksDataSource provideTasksLocalDataSource(Context context) { return new TasksLocalDataSource(context); } @Singleton @Provides @Remote TasksDataSource provideTasksRemoteDataSource() { return new FakeTasksRemoteDataSource(); } } @Module public final class ApplicationModule { private final Context mContext; ApplicationModule(Context context) { mContext = context; } @Provides Context provideContext() { return mContext; } }
  • 45. Presenter implementation final class TaskDetailPresenter implements TaskDetailContract.Presenter { private TasksRepository mTasksRepository; private TaskDetailContract.View mTaskDetailView; @Nullable String mTaskId; @Inject TaskDetailPresenter(@Nullable String taskId, TasksRepository tasksRepository, TaskDetailContract.View taskDetailView) { mTasksRepository = tasksRepository; mTaskDetailView = taskDetailView; mTaskId = taskId; } @Inject void setupListeners() { mTaskDetailView.setPresenter(this); } } Constructor injection Method injection
  • 46. Reference ● Android Architecture Blueprints ● Architecting Android…The clean way? ● The Clean Architecture ● Introduction to Model View Presenter on Android