Anas Ambri
Android Montreal Meetup - November 2015
Android & iOS dev @ Guaraná
@AnasAmbri
At Guaraná, we make Android & iOS apps
We are also hiring!
Platform maturing
Community interest increasing
Architecture can actually solve problems
Integration tests: the one your client cares about
UI is very coupled to Android framework
You can't instantiate Activity, the system does it for you
public static void main(String [] args) {
HomeActivity activity = new HomeActivity();
activity.onCreate();
activity.onStart();
activity.onResume();
assertNotNull("Activity was null", activity);
}
Testing UI in the JVM is not possible
@RunWith(AndroidJUnit4.class)
public class ExampleTest
extends ActivityInstrumentationTestCase2<HomeActivity> {
HomeActivity mActivity;
public ExampleTest() { super(HomeActivity.class); }
@Before
public void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
}
@Test
public void testPreconditions() {
assertNotNull("Activity was null", mActivity);
}
}
Congratulations, by complaining about testing support you're now one step closer to becoming a true Android dev
/u/RattlesnakeSpeedway [4]
public class HomeActivity extends Activity {
private HomeState state;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
//setup
state = new HomeState();
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) { state.buttonClicked(); }
});
}
}
public class HomeState {
public buttonClicked() {
//Decide what to do with the button click
}
}
public class HomeState {
private HomeActivity activity;
public HomeState(HomeActivity activity) { this.activity = activity; }
public buttonClicked() { this.activity.populateWithData(new String {"Item"}); }
}
public class HomeActivity extends Activity {
private HomeState state;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
//setup layout, etc.
state = new HomeState(this);
//Button click listener
}
public void populateWithData(String [] items) {
//Populate
}
}
public class HomePresenter {
private WeakReference<HomeView> view;
public HomePresenter(HomeView v) {
this.view = new WeakReference(v);
}
public buttonClicked() {
((HomeView)view).get().populateWithData(new String {"Item"});
}
}
public interface HomeView {
void populateWithData(String [] items);
}
public class HomeActivity extends Activity implements HomeView {
private HomePresenter presenter;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
//setup layout, etc.
presenter = new HomePresenter(this);
button.setOnClickListener(new View.OnClickListener() {
//Handle button click
});
}
@Override
public void populateWithData(String [] items) {
//Populate
}
}
public static void main(String [] args) {
TestHomeView view = new TestHomeView();
HomePresenter presenter = new HomePresenter(view);
presenter.buttonClicked();
assertEquals(1, view.getItems().size()); //Tests all the logic
}
public class TestHomeView implements HomeView {
private List<String> items = new ArrayList();
@Override
public void populateWithData(String[] items) {
this.items = new ArrayList(Arrays.asList(items));
}
}
Many people have been doing MVP for some time now on Android.
You can too
Follow Martin Fowler's advice
Presenter has two responsibilities:
Partial because data-binding takes care of simple synchronization
Model is no longer dumb.
(Thank god Google came up with that data binding library)
You want to keep the view as passive as possible:
This is the view's responsibility
Your app can be killed by Ninjas the OS at any time
public class HomeViewState {
private final int LOADING = 0;
private final int SHOWING_ITEMS = 1;
private int state = SHOWING_ITEMS;
public void setLoading() {
this.state = LOADING;
}
public void applyState(HomeView v) {
if(state == LOADING) {
view.showLoading();
}
//Handle other states
}
}
class HomeActivity implements HomeView {
@State HomeViewState state;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Icepick.restoreInstanceState(this, savedInstanceState);
}
@Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Icepick.saveInstanceState(this, outState);
}
}
Pretty sure there are plenty others
Should you use Activity, Fragment or ViewGroup?
It depends. This can work with any of the three.
Do you keep only one presenter at a time?
You have as many presenters as views. You can combine them, but why not KISS?
Taken from [2]
How to ensure Presenter is a POJO?
Never import anything from com.android.* in your Presenters
What about MVVM?
Yeah, what about it?
Can you combine MVVM with MVP?
Maybe not combine (MPVMM is a mouth-full), but use data-binding, yes
Can you combine this with other strategies?
Should I tell my boss about this?
Absolutely. Do you think that Google trends line is gonna go up by itself?
[1]: https://corner.squareup.com/2014/10/advocating-against-android-fragments.html
[2]: http://hannesdorfmann.com/android/mosby
[3]: https://github.com/konmik/nucleus
[4]: https://speakerdeck.com/richk/effective-android-architecture
[5]: https://www.reddit.com/r/androiddev/comments/39h62v/why_is_testing_so_broken_on_android/
[6]: https://github.com/google/iosched
[7]: http://martinfowler.com/eaaDev/SupervisingPresenter.html
[8]: http://martinfowler.com/eaaDev/PresentationModel.html
[9]: http://martinfowler.com/eaaDev/PassiveView.html
[10]: https://github.com/frankiesardo/icepick
If you like memes, and hate the state of Android testing, share these : http://verybadalloc.com/testing-memes
Would love to hear your feedback @AnasAmbri