Is there a canonical solution using Espresso to wait for a specific Activity to finish or start?
I have a SplashActivity that appears for a few seconds, then a MainActivity. I want Espresso to interact with the MainActivity, not the SplashActivity, but I can't seem to find any information about waiting for such a condition.
The closest thing I can find is a mention of idle resources but its not clear to me how I would use that here to wait for the Activity.
I guess your splash activity is performing some initialization.
If this is the case, my suggestion is to define some sort of listener pattern in order to be able to get a callback when the initialization is done. Then, you can make Espresso wait for the initialization with an IdlingResource.
NB: The following is NOT complete code, but it is meant to give you a hint in how to do so:
public class SplashIdlingResource implements IdlingResource, YourApplicationInitListener {
// volatile because can be set by a different
// thread than the test runner: the one calling back
private volatile boolean mIsInitialized;
private ResourceCallback mCallback;
public SplashIdlingResource() {
YourApplication application = // retrieve your Application object
mIsInitialized = application.isInitialized();
if (!mIsInitialized) {
application.addInitListener(this);
}
}
#Override
public String getName() {
return SplashIdlingResource.class.getName();
}
#Override
public boolean isIdleNow() {
return mIsInitialized;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
#Override
public void onApplicationInitCompleted() {
m_isInitialized = true;
if (m_callback != null) {
m_callback.onTransitionToIdle();
}
}
}
Where onApplicationInitCompleted() is the callback you defined and which must be called when the Splash Activity, and so the initialization, is done.
Finally, register this new IdlingResource with Espresso by calling Espresso.registerIdlingResource in test setup.
Related
I would like to create a CountdownTimer which will trigger events that will update the UI (trigger popup, start an animation, etc.).
I wonder how to do this clean, here are my hypothesis and why :
A separate component EventCountdownTimer. I could then benefit the use of LifecycleObserver, but I wonder how to communicate the information back to the activity (I tried extending CountdownTimer and using it in the activity but I have an error and can't get it to compile)
In the Activity itself, it's the simplest but I'm not sure it belongs there as it isn't a UI component and I can't benefit the LifecycleObserver
In the ViewModel. I thought as it's activity related and the CountdownTimer is kinda logic data, it should go in here, but that means also watching the lifecycle of the activity, and holding any Activity related field within ViewModel is bad practice.
What's the best option according to you? And why?
In a MVVM pattern you could have a LiveData observable in your ViewModel which will be observed by the UI and upon value change you update the UI accordingly. How that observable changes value, that is your business logic and all of it should be in your ViewModel or in separate components that will be used by the ViewModel to update the observable state.
This will allow you to separate the UI from the business logic being your observable the bridge of communication between both, without the ViewModel having any knowledge of whats going on in the UI. In simple words it only executes what it is told to execute and updates a variable that is being observed, what then happens in the UI is the UI responsibility and with this you have reached a clear separation of concerns.
A separate component "EventCountdownTimer"
In my opinion, this is the best implementation that you might have in your case. For communicating information back to your activity, you might consider having an interface like the following.
public interface TimerListener {
void onTimerResponse(String response);
}
Modify your EventCountdownTimer to have a constructor which takes TimerListener as a parameter and override the onTimerResponse method in your activity. Now from your EventCountdownTimer, when you are trying to communicate with your activity along with a message, for example, you might just call the function onTimerResponse(msgToDeliver).
Hence your EventCountdownTimer should look something like this.
public class EventCountdownTimer {
public static Context context;
public static TimerListener listener;
public EventCountdownTimer(Context context, TimerListener listener) {
this.context = context;
this.listener = listener;
}
public startCountdown() {
// Start the count down here
// ... Other code
// When its time to post some update to your activity
listener.onTimerResponse(msgToDeliver);
}
}
And from your activity, initialize the EventCountdownTimer like the following.
EventCountdownTimer timer = new EventCountdownTimer(this, new TimerListener() {
#Override
public void onTimerResponse(String message) {
// Do something with the message data
// Update your UI maybe
}
});
I think you have provided good reasons already for not going for other options that you have mentioned.
Google solution : see it on github
/**
* A ViewModel used for the {#link ChronoActivity3}.
*/
public class LiveDataTimerViewModel extends ViewModel {
private static final int ONE_SECOND = 1000;
private MutableLiveData<Long> mElapsedTime = new MutableLiveData<>();
private long mInitialTime;
private final Timer timer;
public LiveDataTimerViewModel() {
mInitialTime = SystemClock.elapsedRealtime();
timer = new Timer();
// Update the elapsed time every second.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final long newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000;
// setValue() cannot be called from a background thread so post to main thread.
mElapsedTime.postValue(newValue);
}
}, ONE_SECOND, ONE_SECOND);
}
public LiveData<Long> getElapsedTime() {
return mElapsedTime;
}
#Override
protected void onCleared() {
super.onCleared();
timer.cancel();
}
}
I am trying to find a way to verify that an activity has finished loading everything only without doing any changes in the application code. The method mentioned in this question and many others requires some application code change and I would like to do it via androidTest section.
There are scenarios when the activity is not fully loaded and running the following code fails:
onView(allOf(withId(R.id.user_name), withText("username1"))).perform(click());
In this example I am waiting for a ListView to load, so the data may also be loaded asynchronously (I am using espresso).
Might be too late but you should take a look at espresso idling resource to sync your background loading tasks with espresso. You wont need to change any of your application code. Here you have a deeper insight on android custom idling resources: http://dev.jimdo.com/2014/05/09/wait-for-it-a-deep-dive-into-espresso-s-idling-resources/
or this http://blog.sqisland.com/2015/04/espresso-custom-idling-resource.html
Here is what I did to make espresso wait for my list to be populated (from data comming from the network) before running the UI test.
public class ListResultsIdlingResource implements IdlingResource {
private ResourceCallback callback;
private RecyclerView mRecyclerListings;
public ListResultsIdlingResource(RecyclerView recyclerListings) {
mRecyclerListings = recyclerListings;
}
#Override
public boolean isIdleNow() {
if (mRecyclerListings != null && mRecyclerListings.getAdapter().getItemCount() > 0) {
if (callback != null) {
callback.onTransitionToIdle();
}
return true;
}
return false;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.callback = callback;
}
#Override
public String getName() {
return "Recycler idling resource";
}
You just have to check that your list has items in you isIdleNow() method before running your UI test over its elements.
And in your espresso test class in your setup method register your idling resource and pass it a reference to your ListView or Recyclerview or any view you are using as list.
#Before
public void setUp() throws Exception {
Intent intent = createIntentForActivity();
activityRule.launchActivity(intent);
mActivity = activityRule.getActivity();
mListResultsIdlingResource = new ListingResultsIdlingResource(
(RecyclerView) mActivity.findViewById(R.id.recycler_view));
registerIdlingResources(mListResultsIdlingResource);
}
Hope this is helpful for anyone looking for this.
I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger
I want to get clarity on loading of classes, destruction of objects etc in android because I noticed some weird things happening when using Singleton in My Activity. Best I will describe it using code :
My Singleton class
public class FilterCriteria {
private final String TAG=FilterCriteria.class.getSimpleName();
private static FilterCriteria filterCriteria=new FilterCriteria();
private FilterCriteria()
{
}
public static FilterCriteria getInstance()
{
return filterCriteria;
}
private int rentUpperBound,rentLowerBound;
private int bedrooms,baths;
private float distance;
private ObjectStateListener listener;
public void setFilters(float distance,int baths,int bedrooms,int rentLowerBound,int rentUpperBound) {
this.distance = distance;
this.baths=baths;
this.bedrooms=bedrooms;
this.rentLowerBound=rentLowerBound;
this.rentUpperBound=rentUpperBound;
if(listener!=null)
listener.onObjectStateChanged();
}
public void attachListener(ObjectStateListener listener) {
if (this.listener == null) {
this.listener = listener;
Log.v(TAG, "NO LISTENER PRESENT AS EXPECTED");
} else {
Log.v(TAG, "LISTENER PRESENT!!! BUT THE ACTIVITY WAS STARTED JUST NOW.");
}
}
public void destroy()
{
filterCriteria=null;
}
}
The attachListener(ObjectStateListener listener) function is called only once in the activity. So, when I open my activity the first time, I get this log from attachListener function
NO LISTENER PRESENT AS EXPECTED
Now, I close the activity and then reopen it. But now I get this log
"LISTENER PRESENT!!! BUT THE ACTIVITY WAS STARTED JUST NOW."
So, that means the object still lives on even after the activity (and the application) was closed. Is this normal???
So, I tried to destroy the singleton instance using the destroy() function in the onDestroy() function of Activity.
#Override
protected void onDestroy(){
filterCriteria.destroy();//Trying to destroy the singleton
super.onDestroy();
Log.v(TAG,"Destroying activity");
}
But I got NullPointerException on this line filterCriteria.destroy(). So, that means android has already made object null, whereas when I see in debug mode, other members of the Activity are still alive. Why is only this null?
What is happening!???
When you invoke the method attachListener() you are creating a reference to the linked object (even if it is static): this reference will be binded to the activity lifecycle.
On the other hand, filterCriteria will follow the static field Java-like lifecycle (but you can still remove this reference manually).
Some third party libraries use hooks into the activity lifecycle to work correctly - for instance, the Facebook SDK (https://developers.facebook.com/docs/android/login-with-facebook/).
I'm having some trouble figuring out how to reconcile this model cleanly with a single-activity flow+mortar setup.
For instance, if I want to use Facebook login as part of a login Flow (w/ FlowView/FlowOwner), but not otherwise in the activity, what's the smartest way to pull this off if you need hooks for that particular flow in onCreate, onResume, onPause, onDestroy, onSaveInstanceState, onActivityResult, etc?
It's not immediately obvious what the cleanest path is - create an observable for each lifecycle activity stage and subscribe the flow to it? Seems like that path quickly devolves to the same Android lifecycle if you're not careful. Is there a better way?
I love the single activity model, and I'd really like to keep everything managed by flow/mortar and not activities, if possible. Or am I thinking about this in a way that is fundamentally making it more difficult than it should be?
We haven't had a need for start and stop so far, but do have a few spots that rely on pause and resume. We use an ActivityPresenter as you suggest, but avoid any kind of universal superclass. Instead it exposes a service that interested presenters can opt in to. This kind of hookup need is why the onEnterScope(Scope) method was added. Here's the code.
First, have the activity implement this interface:
/**
* Implemented by {#link android.app.Activity} instances whose pause / resume state
* is to be shared. The activity must call {#link PauseAndResumePresenter#activityPaused()}
* and {#link PauseAndResumePresenter#activityResumed()} at the obvious times.
*/
public interface PauseAndResumeActivity {
boolean isRunning();
MortarScope getMortarScope();
}
And have it inject the presenter and make the appropriate calls:
private boolean resumed;
#Inject PauseAndResumePresenter pauseNarcPresenter;
#Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pauseNarcPresenter.takeView(this);
}
#Override public boolean isRunning() {
return resumed;
}
#Override protected void onResume() {
super.onResume();
resumed = true;
pauseNarcPresenter.activityResumed();
}
#Override protected void onPause() {
resumed = false;
super.onPause();
pauseNarcPresenter.activityPaused();
}
#Override protected void onDestroy() {
pauseNarcPresenter.dropView(this);
super.onDestroy();
}
Now interested parties can inject a registrar interface to opt-in to pause and resume calls, without subclassing anything.
/**
* Provides means to listen for {#link android.app.Activity#onPause()} and {#link
* android.app.Activity#onResume()}.
*/
public interface PauseAndResumeRegistrar {
/**
* <p>Registers a {#link PausesAndResumes} client for the duration of the given {#link
* MortarScope}. This method is debounced, redundant calls are safe.
*
* <p>Calls {#link PausesAndResumes#onResume()} immediately if the host {#link
* android.app.Activity} is currently running.
*/
void register(MortarScope scope, PausesAndResumes listener);
/** Returns {#code true} if called between resume and pause. {#code false} otherwise. */
boolean isRunning();
}
Have the client presenter implement this interface:
/**
* <p>Implemented by objects that need to know when the {#link android.app.Activity} pauses
* and resumes. Sign up for service via {#link PauseAndResumeRegistrar#register(PausesAndResumes)}.
*
* <p>Registered objects will also be subscribed to the {#link com.squareup.otto.OttoBus}
* only while the activity is running.
*/
public interface PausesAndResumes {
void onResume();
void onPause();
}
And hook things up like this. (Note that there is no need to unregister.)
private final PauseAndResumeRegistrar pauseAndResumeRegistrar;
#Inject
public Presenter(PauseAndResumeRegistrar pauseAndResumeRegistrar) {
this.pauseAndResumeRegistrar = pauseAndResumeRegistrar;
}
#Override protected void onEnterScope(MortarScope scope) {
pauseAndResumeRegistrar.register(scope, this);
}
#Override public void onResume() {
}
#Override public void onPause() {
}
Here's the presenter that the activity injects to make it all work.
/**
* Presenter to be registered by the {#link PauseAndResumeActivity}.
*/
public class PauseAndResumePresenter extends Presenter<PauseAndResumeActivity>
implements PauseAndResumeRegistrar {
private final Set<Registration> registrations = new HashSet<>();
PauseAndResumePresenter() {
}
#Override protected MortarScope extractScope(PauseAndResumeActivity view) {
return view.getMortarScope();
}
#Override public void onExitScope() {
registrations.clear();
}
#Override public void register(MortarScope scope, PausesAndResumes listener) {
Registration registration = new Registration(listener);
scope.register(registration);
boolean added = registrations.add(registration);
if (added && isRunning()) {
listener.onResume();
}
}
#Override public boolean isRunning() {
return getView() != null && getView().isRunning();
}
public void activityPaused() {
for (Registration registration : registrations) {
registration.registrant.onPause();
}
}
public void activityResumed() {
for (Registration registration : registrations) {
registration.registrant.onResume();
}
}
private class Registration implements Scoped {
final PausesAndResumes registrant;
private Registration(PausesAndResumes registrant) {
this.registrant = registrant;
}
#Override public void onEnterScope(MortarScope scope) {
}
#Override public void onExitScope() {
registrations.remove(this);
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Registration that = (Registration) o;
return registrant.equals(that.registrant);
}
#Override
public int hashCode() {
return registrant.hashCode();
}
}
}
So I've been porting a personal app over to flow and mortar to evaluate it for businesses use. I haven't encountered a scenario where I HAD to have the entire activity lifecycle yet, but as things stand with the current version of flow (v0.4) & mortar (v0.7), this is something I think you will have to creatively solve for yourself. I've recognized this as a potential problem for myself and have put some thought of how to overcome it:
I would also like to note that I haven't actually used the Facebook SDK. You will have to choose the best method for yourself.
You could post events from the activity for each Activity life cycle event. You essentially mentioned this approach using RXJava's Observables. If you really really wanted to use RXJava, you could use a PublishSubject for this, but I'd probably go with simple events from an EventBus you could subscribe to. This is probably the easiest approach.
You could also, depending on how the Facebook SDK works, possibly inject the Facebook SDK component in the activity, and from there initialize it. Then also inject the Facebook SDK component into your view to be used. Flow and Mortar's entire system is deeply integrated into dependency injection after all? This approach is also fairly simple, but depending on how the Facebook SDK works it probably isn't the best option. If you did go this route, you'd need to heed my warning at the bottom of this post.
This brings us to my last idea. Square had a similar problem when they needed access to an Activity's ActionBar in it's sub-views/presenters. They exposed access to the ActionBar in their sample app via something they called the ActionBarOwner.java. They then implement the ActionBarOwner interface and give an instance of itself in the DemoActivity.java. If you study how they implemented this and share it through injection, you could create a similar class. AcivityLifecycleOwner or something (the name needs work), and you could subscribe to callbacks on it from a presenter. If you decide to go down this route, and aren't careful you can easily end up with a memory leak. Any time you would subscribe to any of the events (I'd recommend you subscribe in the presenter), you'd need to make sure you unsubscribe in the onDestroy method as well. I've created a short untested sample of what I mean for this solution below.
No matter which approach you use, you'll probably need to make sure your onCreate and onDestroy methods actually come from your presenter, and not the exact events from the activity. If you are only using the sdk on a single view, the activity's onCreate has been called long before your view is instantiated probably, and the onDestroy for the Activity will be called after your view is destroyed. The presenter's onLoad and onDestroy should suffice I think, however I haven't tested this.
Best of luck!
Untested code example for solution #3:
All your presenters could extend this class instead of ViewPresenter and then override each method you wanted events for just like you would in an activity:
public abstract class ActivityLifecycleViewPresenter<V extends View> extends ViewPresenter<V>
implements ActivityLifecycleListener {
#Inject ActivityLifecycleOwner mActivityLifecycleOwner;
#Override protected void onLoad(Bundle savedInstanceState) {
super.onLoad(savedInstanceState);
mActivityLifecycleOwner.register(this);
}
#Override protected void onDestroy() {
super.onDestroy();
mActivityLifecycleOwner.unregister(this);
}
#Override public void onActivityResume() {
}
#Override public void onActivityPause() {
}
#Override public void onActivityStart() {
}
#Override public void onActivityStop() {
}
}
Activity Lifecycle owner that would be injected into the activity and then hooked up to the corresponding events. I purposely didn't include onCreate and onDestroy, as you presenter's wouldn't be able to get access to those events as they wouldn't be created or they would already be destroyed. You'd need to use the presenters onLoad and onDestroy methods in place of those. It's also possible that some of these other events wouldn't be called.
public class ActivityLifecycleOwner implements ActivityLifecycleListener {
private List<ActivityLifecycleListener> mRegisteredListeners
= new ArrayList<ActivityLifecycleListener>();
public void register(ActivityLifecycleListener listener) {
mRegisteredListeners.add(listener);
}
public void unregister(ActivityLifecycleListener listener) {
mRegisteredListeners.remove(listener);
}
#Override public void onActivityResume() {
for (ActivityLifecycleListener c : mRegisteredListeners) {
c.onActivityResume();
}
}
#Override public void onActivityPause() {
for (ActivityLifecycleListener c : mRegisteredListeners) {
c.onActivityPause();
}
}
#Override public void onActivityStart() {
for (ActivityLifecycleListener c : mRegisteredListeners) {
c.onActivityStart();
}
}
#Override public void onActivityStop() {
for (ActivityLifecycleListener c : mRegisteredListeners) {
c.onActivityStop();
}
}
}
Now you need to hook the lifecycle owner to the activity:
public class ActivityLifecycleExample extends MortarActivity {
#Inject ActivityLifecycleOwner mActivityLifecycleOwner;
#Override protected void onResume() {
super.onResume();
mActivityLifecycleOwner.onActivityResume();
}
#Override protected void onPause() {
super.onPause();
mActivityLifecycleOwner.onActivityPause();
}
#Override protected void onStart() {
super.onStart();
mActivityLifecycleOwner.onActivityStart();
}
#Override protected void onStop() {
super.onStart();
mActivityLifecycleOwner.onActivityStop();
}
}