I'm writing UI tests with Espresso. App cooperates tightly with server, so in many cases, I need to wait for either value to be calculated, or data is got and displayed, etc. Espresso suggests using IdlingResource for this.
My IdlingResource classes look like this (simple and clear example).
public class IRViewVisible implements IdlingResource {
private View view;
private ResourceCallback callback;
public IRViewVisible(View view) {
this.view = view;
}
#Override
public String getName() {
return IRViewVisible.class.getName();
}
#Override
public boolean isIdleNow() {
if(view.getVisibility() == View.VISIBLE && callback != null) {
callback.onTransitionToIdle();
return true;
}
return false;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
this.callback = resourceCallback;
}
}
Please correct me if I'm wrong anywhere (as sometimes it seems to me that my IdlingResources do not work properly).
I register the idling resource in setUp() like this:
IRViewVisible ir = new IRViewVisible(View v);
Espresso.registerIdlingResources(ir).
Unregister it on tearDown().
I found this article (there is a section called "Register a component tied to an Activity instance") — I do not use his schema, but I checked hashcode of view that was set to IdlingResource after registering (in each method), and it's not the same view — all hashes are different.
Another question: One Test class (it's results) can't have any effect on another Test class, can it?
I'm guessing your problem stems from getName() returning the same name for all instances of IRViewVisible. This means you can only have one registered instance of it at a time - any subsequent registrations will fail (silently!).
You mention that you clear the IdlingResources at the end of each test, but if you are register multiple instances of it at once, you need to make sure each instance has a unique name. it's not clear from your question if you're registering multiple instances of IRViewVisible in a single test.
As to your final question: Yes, it is possible. Android doesn't completely shut down the Application between test runs - just the Activities. Common things which can cause problems:
Failing to clear persistent state (saved data).
Failing to clear global state - e.g. static variables/singletons
Not waiting for background threads to finish running.
As an aside, it's worth noting that you only call onTransitionToIdle() inside isIdleNow(). This works (thanks #Be_Negative for the heads up!) but it could slow down your tests a lot, since Espresso will only poll isIdleNow() every few seconds. If you call onTransitionToIdle() as soon as the view becomes visible, it should speed things up considerably.
I needed something similar to your IRViewVisible myself, here's my effort.
So the isIdleNow() method will never return true if you don't set a callback to the idlingResource?
I reckon it's better to refactor it like this:
#Override
public boolean isIdleNow() {
boolean idle = view.getVisibility() == View.VISIBLE;
if(idle && callback != null) {
callback.onTransitionToIdle();
}
return idle;
}
Well, first of all you shouldn't need to use Espresso IdlingResource to test server calls. If you use AsyncTasks in your server calls, Espresso will be able to know when to be idle and when not. If this is not enough: try to refactor your code in this way:
IRViewVisible idlingResource = new IRViewVisible(yourView);
IdlingPolicies.setMasterPolicyTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(waitingTime * 2, TimeUnit.MILLISECONDS);
// Now we wait
Espresso.registerIdlingResources(idlingResource);
// Stop and verify
// Clean up
Espresso.unregisterIdlingResources(idlingResource);
Hope to be helpful.
Related
I have a MVVM architecture in my Android app. In an activity, I invoke a method to try to create something from service/repository and return it. I am using RxJava.
Here is the flow:
I click something in view, it invokes method in the Activity.
Method in Activity invokes method in ViewModel.
Method in ViewModel invokes method in Interactor(/use-case).
Interactor has access to service and tries to create something from that service.
Here is the code for this:
Activity:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password);
}
ViewModel:
public class AddWalletViewModel extends BaseViewModel {
private AddWalletInteractor addWalletInteractor;
private final MutableLiveData<Wallet> newWallet = new MutableLiveData<Wallet>();
private final MutableLiveData<ErrorCarrier> newWalletError = new MutableLiveData<ErrorCarrier>();
public LiveData<Wallet> newWallet() {
return newWallet;
}
public AddWalletViewModel(AddWalletInteractor addWalletInteractor) {
this.addWalletInteractor = addWalletInteractor;
}
public Single<Wallet> createWallet(String password){
return addWalletInteractor.addWallet(password)
.subscribe(wallet -> newWallet.postValue(wallet), this::addErrorToLiveData);
}
private void addErrorToLiveData(Throwable throwable){
newWalletError.postValue(new ErrorCarrier());
}
}
Interactor:
public class AddWalletInteractor {
private final KeyStoreServiceInterface keyStoreServiceInterface;
public AddWalletInteractor(KeyStoreServiceInterface keyStoreServiceInterface) {
this.keyStoreServiceInterface = keyStoreServiceInterface;
}
public Single<Wallet> addWallet(String password){
return keyStoreServiceInterface.
createWalletAndReturnWallet(password);
}
}
Service:
#Override
public Single<Wallet[]> getAllWallets() {
return Single.fromCallable(()-> {
Accounts accounts = keyStore.getAccounts();
int amount = (int) accounts.size();
Wallet[] wallets = new Wallet[amount];
for (int i = 0; i<amount; i++){
org.ethereum.geth.Account gethAccount = accounts.get(i);
wallets[i] = new Wallet(gethAccount.getAddress().getHex().toLowerCase());
}
return wallets;
}).subscribeOn(Schedulers.io());
}
Problem is I can not manage to get this to work by tweaking the code. Right now it forces me to cast to (Single) in the return of the createWallet() method in the viewmodel. When running the app, it crashes in that method with:
java.lang.ClassCastException:
io.reactivex.internal.observers.ConsumerSingleObserver cannot be cast
to io.reactivex.Single
at addwallet.AddWalletViewModel.createWallet(AddWalletViewModel.java:31)
Please keep in mind I am new to RxJava, I am still trying to figure it out. Any suggestions here?
The cast performed in the createWallet method will always fail.
Solution 1
The simplest way to fix the crash is to change the return type of that method to io.reactivex.disposables.Disposable, assuming you're using RxJava 2. If you're using RxJava 1, then have it return rx.Subscription. The code you presented that calls the createWallet method doesn't seem to use the returned value so it shouldn't make a difference.
Solution 2
If you really do need the return type to be Single and you want to keep the same behavior, then an alternate solution would be to change the createWallet method as follows:
public Single<Wallet> createWallet(String password) {
return addWalletInteractor.addWallet(password)
.doOnSuccess(wallet -> newWallet.postValue(wallet))
.doOnError(this::addErrorToLiveData);
}
The method now returns a new Single that does whatever the Single returned from addWallet does and additionally invokes the appropriate lambda function when a value is successfully emitted or an error occurs. You would also need to modify the call site for the method as follows:
#Override
public void onCreateWalletClick(String password) {
addWalletViewModel.createWallet(password).subscribe();
}
That subscribe call is needed to have the Single start emitting values. It takes no parameters because you already do all of the interesting work in the createWallet method itself. Both snippets were written with RxJava 2 in mind, but I believe they will also work in RxJava 1 as is.
If you haven't already done so, you should check out the official Rx website as it provides a ton of information on how reactive streams work and how to use them.
Since you're new to RxJava and the documentation is so vast, here's a brief overview of the subscription concept and how it applies to your situation.
RxJava and other stream-based libraries like it have two main components: producers and consumers. Producers supply values and consumers do something with those supplied values.
Single is a kind of producer that only produces one value before terminating. In your case, it produces a reference to the newly created wallet. In order to do something with that reference, it needs to be consumed. That's what the subscribe method on the Single class does. When the Single returned by the addWallet method produces a value, the lambda passed to the subscribe method is invoked and the wallet parameter in that lambda is set to the produced value.
The return type of the subscribe method is NOT itself a Single. When a consumer and a producer are coupled together by the subscribe method, it forms a connection which is represented by the Disposable class. An instance of that class has methods to cancel the connection before the producer is done producing values or to check if the connection has been cancelled. It is this connection object that is returned by the subscribe method.
Note that until this connection is made via one of the subscribe overloads, the producer will not start producing items. I.e., a Single that is never subscribed to will never do anything. It's analogous to a Runnable whose run method is never called.
The Android espresso is very useful for test case. But when I use IdlingResource there is some trouble.
I have a flag in my Activity, and I will set it to true when every initial complete.
So my IdlingResource is this:
/**
* 等待所有初始化工作完成
*/
private class WaitPingSuccessIdlingResource implements IdlingResource {
private ChoiceServerActivity choiceServerActivity;
private ResourceCallback mResourceCallback;
public WaitPingSuccessIdlingResource(ChoiceServerActivity choiceServerActivity) {
this.choiceServerActivity = choiceServerActivity;
}
#Override
public String getName() {
return String.valueOf(hashCode());
}
#Override
public boolean isIdleNow() {
if (mResourceCallback != null && choiceServerActivity.isAllDataInited()) {
mResourceCallback.onTransitionToIdle();
}
boolean rst = choiceServerActivity.isAllDataInited();
Log.i("tonghu","WaitPingSuccessIdlingResource, isIdleNow(L94): rst " + rst);
return rst;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
this.mResourceCallback = callback;
}
}
And I register like this:
Espresso.registerIdlingResources(new WaitPingSuccessIdlingResource(activity));
Log.i("tonghu", "ChoiceServerActivityTest, testPingSuccess(L42): 2222");
In normally, the second log will print only when isIdleNow() return true.
But now my log is:
I/tonghu (23470): WaitPingSuccessIdlingResource, isIdleNow(L94): rst false
I/tonghu (23470): ChoiceServerActivityTest, testPingSuccess(L42): 2222
Why the second log can print when my IdlingResource wasn't idle.
My English is poor, any problem, please let me know! Thx!
EDITED:
I have already solve this problem:
I see there is a comment on class IdlingResource:
In such cases, test authors can register the custom resource and
{#link Espresso} will wait for the resource to become idle prior
to executing a view operation.
So after register Idling resource, just give any a view action:
Espresso.registerIdlingResources(new WaitPingSuccessIdlingResource(activity));
Espresso.onView(ViewMatchers.withId(R.id.list_view)).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));
Same problem here, found that registering idlingResources won't cause Espresso to wait, but besides Espresso.onView, you still can use Espresso.onIdle() to wait for registered idlingResources to turn idle.
Finally, I found the official document, quote from here:
Register idling resources before you need them.
The synchronization benefits associated with idling resources only take effect following Espresso's first invocation of that resource's
isIdleNow() method.
The following list shows several examples of this property:
If you register an idling resource in a method annotated with
#Before, the idling resource takes effect in the first line of each
test.
If you register an idling resource inside a test, the idling resource takes effect during the next Espresso-based action. This
behavior still occurs even if the next action is in the same test as
the statement that registers the idling resource.
I want to customize the process of obtaining the authentication token from AccountManager.
AccountManager has getAuthToken() and getAuthTokenByFeatures() methods, but I want to implement a customized flow, which includes switching between activities, etc...
I wanted to implement it the following way:
public AccountManagerFuture<Bundle> getAuthTokenForActiveAccount() {
GetAuthTokenForActiveAccountFuture future =
new GetAuthTokenForActiveAccountFuture(MyActivity.this);
future.start();
return future;
}
Using the following nested class in my activity:
private static class GetAuthTokenForActiveAccountFuture extends Thread implements
AccountManagerFuture<Bundle> {
private final Activity mActivity;
public GetAuthTokenForActiveAccountFuture(Activity activity) {
mActivity = activity;
// TODO: write this method
}
#Override
public void run() {
// TODO: write this method
}
#Override
public boolean cancel(boolean b) {
// TODO: write this method
return false;
}
#Override
public boolean isCancelled() {
// TODO: write this method
return false;
}
#Override
public boolean isDone() {
// TODO: write this method
return false;
}
#Override
public Bundle getResult() throws
OperationCanceledException, IOException, AuthenticatorException {
return internalGetResult(null, null);
}
#Override
public Bundle getResult(long timeout, TimeUnit timeUnit) throws
OperationCanceledException, IOException, AuthenticatorException {
return internalGetResult(timeout, timeUnit);
}
private Bundle internalGetResult(Long timeout, TimeUnit timeUnit) throws
OperationCanceledException, IOException, AuthenticatorException {
// TODO: write this method
return null;
}
}
My idea was that I could create my own AccountManagerFuture object and "unblock" its getResult() method only after all the required steps were done (some of them include activity switching).
I got two issues here:
I need Activity context for switching to other activities when necessary, but the Activity I pass into constructor should be destroyed when I switch to other activity, but it won't because my Thread holds a reference to it... So I create a memory leak here. It seems that making the inner class non-static won't resolve this issue - the reference returned from getAuthTokenForActiveAccount() will still prevent from the outer Activity to be garbage collected. Is there any way I could achieve what I try to do without leaking the context?
Thread is eligible for garbage collection once its run() method returns, right? But in my case I want this thread to stick around because it also functions as AccountManagerFuture - it should be kept in memory until all references to it are gone. My question is this: is it enough to keep a (strong) reference to Thread for preventing it from being garbage collected? If not, how could I force this Thread to stick around until all references are gone?
At first. Making your Future non-static would make it having an implicit reference to its outer class - the Activity.
You should used some form of indirect communication between your future and your Activities..You should probably move it into Service anyway - did you think about any configuration change? Where do you hold the reference for your Future?
I would advice you to either move your flow into fragments - then you wouldn't have to switch Activities - and place your future into a retained Fragment (to make it survive orientation change) or move it into a background service and communicate with your activities (or any sort of UI) through broadcastreceivers or event bus.
Thread won't be garbage collected as long as you keep some reference to it. No matter if its finished or not. I think that you are confusing this with the fact that a running Thread won't be garbage collected even without keeping references to it. (I guess tha JVM does so, but I have to admit I'm not sure about this)
issue 1 solution:
use private WeakReference mContextHolder. when you need context - call mContextHolder.get() and check on null;
issue 2 solution:
Use Service which will host your threads.
I'm writing an application which run a background Service which communicate with a remote server.
when the server sends me a new message, i need to update an object which is represent in the UI and then to update the UI View to represent the new state of the object (for example if the object's background propery is true - set the background of the View to green and if false set the background of the view to red).
I'm using a list view to show all an ArrayList of all those objects throw an ArrayAdapter.
I have an Application object (named app) for static reference and i have there a CurrentActivity property which store the current activity running (or null if the UI is closed).
i'm using this code to update the UI:
in my Service:
onNewMessage(boolean backgruond)
{
if (app.getCurrentActivity != null)
app.getCurrentActivity.onNewMessage(background);
}
in my Activity:
onNewMessage(boolean background)
{
object.setBackground(bacground);
Log.d("Background", String.valueof(background));
runOnUiThread(new Runnable() {
#Override
public void run()
{
arrayAdapter.notifyDataSetChanged();
}
});
}
and although the Log returns the right background state, the view isn't refreshing with the notifyDataSetChanged();
i've tried to send message to Activity throw BroadcastRecevier but it much more complicated because i have lots of messages coming from the server and i will have to register many receivers.
And besides - i don't understand why would the recevier work and this mechanism wont..
example of working method which updates the ListView:
ListViewActivity - inheritance from BaseActivity:
#Override
public void onUnFriend(FacebookUser facebookUser, boolean isYouRemovedClient)
{
super.onUnFriend(facebookUser, isYouRemovedClient);
updateView();
}
BaseActivity (the super class which extends Activity):
public void onUnFriend(FacebookUser facebookUser, boolean isYouRemovedClient)
{
facebookUser.setApplicationFriend(false);
app.getApplicationFriends().remove(facebookUser);
app.getDatabaseManager().deleteApplicationFriend(facebookUser.getId());
if (isYouRemovedClient)
app.showToast(facebookUser.getName() + " has removed from your friends", true);
else
app.showToast(facebookUser.getName() + " has removed you from friends", true);
}
this one works and does change the background color in the ListView.
not working example
ListViewActivity:
#Override
public void onFriendRequestAccepted(FacebookUser facebookUser, boolean showDialog) {
super.onFriendRequestAccepted(facebookUser, showDialog);
updateView();
}
BaseActivity:
public void onFriendRequestAccepted(FacebookUser facebookUser, boolean showDialog)
{
facebookUser.setApplicationFriend(true);
app.getApplicationFriends().add(facebookUser);
app.getDatabaseManager().addApplicationFriend(facebookUser);
if (showDialog)
app.showNewEventActivity(facebookUser, EventDialogManager.EVENT_FRIEND_ACCEPTED);
}
no update is made... i can't really understand why..
i have there a CurrentActivity property which store the current activity running (or null if the UI is closed)
I do not recommend this practice. It relies upon you consistently and reliably updating that Application data member, and it increases the coupling between your service and your UI.
and although the Log returns the right background state, the view isn't refreshing with the notifyDataSetChanged();
It would appear that you did not change the data in the adapter. Certainly, there is no evidence in the code that you have here that you updated the data in the adapter.
BTW, neither of the code snippets you have shown here are likely to compile (first is not valid Java, second has a typo).
i have lots of messages coming from the server and i will have to register many receivers
No, you will have to register one receiver, and in onReceive(), use an if statement (or perhaps a switch, if you prefer) to distinguish one message from another.
In addition to what CommonsWare said, I assume that object in the first line of your onNewMessage is the view. setBackround accepts an int parameter, not a boolean.
Use 0xFF00FF00 for green and 0xFFFF0000 for red.
By the way, it's a very bad practice to keep static references of Context objects and it's derived classes (Application and Activity both derive from Context, and keeping a static reference of them may lead to serious memory leaks. Read more here.)
Use a BroadcastReceiver instead. They are much more simple comparing to how you described them - you only need one.
With the release of Gingerbread, I have been experimenting with some of the new API's, one of them being StrictMode.
I noticed that one of the warnings is for getSharedPreferences().
This is the warning:
StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
and it's being given for a getSharedPreferences() call being made on the UI thread.
Should SharedPreferences access and changes really be made off the UI thread?
I'm glad you're already playing with it!
Some things to note: (in lazy bullet form)
if this is the worst of your problems, your app's probably in a good spot. :) Writes are generally slower than reads, though, so be sure you're using SharedPreferenced$Editor.apply() instead of commit(). apply() is new in GB and async (but always safe, careful of lifecycle transitions). You can use reflection to conditionally call apply() on GB+ and commit() on Froyo or below. I'll be doing a blogpost with sample code of how to do this.
Regarding loading, though...
once loaded, SharedPreferences are singletons and cached process-wide. so you want to get it loaded as early as possible so you have it in memory before you need it. (assuming it's small, as it should be if you're using SharedPreferences, a simple XML file...) You don't want to fault it in the future time some user clicks a button.
but whenever you call context.getSharedPreferences(...), the backing XML file is stat'd to see if it's changed, so you'll want to avoid those stats during UI events anyway. A stat should normally be fast (and often cached), but yaffs doesn't have much in the way of concurrency (and a lot of Android devices run on yaffs... Droid, Nexus One, etc.) so if you avoid disk, you avoid getting stuck behind other in-flight or pending disk operations.
so you'll probably want to load the SharedPreferences during your onCreate() and re-use the same instance, avoiding the stat.
but if you don't need your preferences anyway during onCreate(), that loading time is stalling your app's start-up unnecessarily, so it's generally better to have something like a FutureTask<SharedPreferences> subclass that kicks off a new thread to .set() the FutureTask subclasses's value. Then just lookup your FutureTask<SharedPreferences>'s member whenever you need it and .get() it. I plan to make this free behind the scenes in Honeycomb, transparently. I'll try to release some sample code which
shows best practices in this area.
Check the Android Developers blog for upcoming posts on StrictMode-related subjects in the coming week(s).
Accessing the shared preferences can take quite some time because they are read from flash storage. Do you read a lot? Maybe you could use a different format then, e.g. a SQLite database.
But don't fix everything you find using StrictMode. Or to quote the documentation:
But don't feel compelled to fix everything that StrictMode finds. In particular, many cases of disk access are often necessary during the normal activity lifecycle. Use StrictMode to find things you did by accident. Network requests on the UI thread are almost always a problem, though.
One subtlety about Brad's answer: even if you load the SharedPreferences in onCreate(), you should probably still read values on the background thread because getString() etc. block until reading the shared file preference in finishes (on a background thread):
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
edit() also blocks in the same way, although apply() appears to be safe on the foreground thread.
(BTW sorry to put this down here. I would have put this as a comment to Brad's answer, but I just joined and don't have enough reputation to do so.)
I know this is an old question but I want to share my approach. I had long reading times and used a combination of shared preferences and the global application class:
ApplicationClass:
public class ApplicationClass extends Application {
private LocalPreference.Filter filter;
public LocalPreference.Filter getFilter() {
return filter;
}
public void setFilter(LocalPreference.Filter filter) {
this.filter = filter;
}
}
LocalPreference:
public class LocalPreference {
public static void saveLocalPreferences(Activity activity, int maxDistance, int minAge,
int maxAge, boolean showMale, boolean showFemale) {
Filter filter = new Filter();
filter.setMaxDistance(maxDistance);
filter.setMinAge(minAge);
filter.setMaxAge(maxAge);
filter.setShowMale(showMale);
filter.setShowFemale(showFemale);
BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
babysitApplication.setFilter(filter);
SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
securePreferences.edit().putInt(Preference.FILER_MAX_DISTANCE.toString(), maxDistance).apply();
securePreferences.edit().putInt(Preference.FILER_MIN_AGE.toString(), minAge).apply();
securePreferences.edit().putInt(Preference.FILER_MAX_AGE.toString(), maxAge).apply();
securePreferences.edit().putBoolean(Preference.FILER_SHOW_MALE.toString(), showMale).apply();
securePreferences.edit().putBoolean(Preference.FILER_SHOW_FEMALE.toString(), showFemale).apply();
}
public static Filter getLocalPreferences(Activity activity) {
BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
Filter applicationFilter = babysitApplication.getFilter();
if (applicationFilter != null) {
return applicationFilter;
} else {
Filter filter = new Filter();
SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
filter.setMaxDistance(securePreferences.getInt(Preference.FILER_MAX_DISTANCE.toString(), 20));
filter.setMinAge(securePreferences.getInt(Preference.FILER_MIN_AGE.toString(), 15));
filter.setMaxAge(securePreferences.getInt(Preference.FILER_MAX_AGE.toString(), 50));
filter.setShowMale(securePreferences.getBoolean(Preference.FILER_SHOW_MALE.toString(), true));
filter.setShowFemale(securePreferences.getBoolean(Preference.FILER_SHOW_FEMALE.toString(), true));
babysitApplication.setFilter(filter);
return filter;
}
}
public static class Filter {
private int maxDistance;
private int minAge;
private int maxAge;
private boolean showMale;
private boolean showFemale;
public int getMaxDistance() {
return maxDistance;
}
public void setMaxDistance(int maxDistance) {
this.maxDistance = maxDistance;
}
public int getMinAge() {
return minAge;
}
public void setMinAge(int minAge) {
this.minAge = minAge;
}
public int getMaxAge() {
return maxAge;
}
public void setMaxAge(int maxAge) {
this.maxAge = maxAge;
}
public boolean isShowMale() {
return showMale;
}
public void setShowMale(boolean showMale) {
this.showMale = showMale;
}
public boolean isShowFemale() {
return showFemale;
}
public void setShowFemale(boolean showFemale) {
this.showFemale = showFemale;
}
}
}
MainActivity (activity that get called first in your application):
LocalPreference.getLocalPreferences(this);
Steps explained:
The main activity calls getLocalPreferences(this) -> this will read your preferences, set the filter object in your application class and returns it.
When you call the getLocalPreferences() function again somewhere else in the application it first checks if it's not available in the application class which is a lot faster.
NOTE: ALWAYS check if an application wide variable is different from NULL, reason -> http://www.developerphil.com/dont-store-data-in-the-application-object/
The application object will not stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.
If I didn't check on null I would allow a nullpointer to be thrown when calling for example getMaxDistance() on the filter object (if the application object was swiped from the memory by Android)
SharedPreferences class does some reads & writes within XML files on disk, so just like any other IO operation it could be blocking. The amount of data currently stored in SharedPreferences affects the time and resource consumed by the API calls. For minimal amounts of data it's a matter of a few milliseconds (sometimes even less than a millisecond) to get/put data. But from the point of view of an expert it could be important to improve the performance by doing the API calls in background. For an asynchronous SharedPreferences I suggest checking out the Datum library.
i do not see any reason to read them from a background thread. but to write it i would. at startup time the shared preference file is loaded into memory so its fast to access, but to write things can take a bit of time so we can use apply the write async. that should be the difference between commit and apply methods of shared prefs.