AsyncTaskLoader basic example. (Android) - android

I am using a Loader in my application and based on the result I get from the query I perform on COntacts using this Loader I perform some calculations and store them back in a Sqlite DB. I want this operation to be Asynchronous, however I am confused between using an Async task, as I have lot of different data types to return or should I use a simple handler or an AsyncTaskLoader, I want it to be simple as I am new to Loaders. I tried to search around for examples of AsyncTaskLoader but it seems rocket science, a basic and simple functional example of any of the three in the context of my scenario would be a lot helpful.

If you wish to use AsyncTaskLoader, here's a nice sample for you.
EDIT: I've decided to make a simpler solution (based on this repo):
public abstract class AsyncTaskLoaderEx<T> extends AsyncTaskLoader<T> {
private static final AtomicInteger sCurrentUniqueId = new AtomicInteger(0);
private T mData;
public boolean hasResult = false;
public static int getNewUniqueLoaderId() {
return sCurrentUniqueId.getAndIncrement();
}
public AsyncTaskLoaderEx(final Context context) {
super(context);
onContentChanged();
}
#Override
protected void onStartLoading() {
if (takeContentChanged())
forceLoad();
//this part should be removed from support library 27.1.0 :
//else if (hasResult)
// deliverResult(mData);
}
#Override
public void deliverResult(final T data) {
mData = data;
hasResult = true;
super.deliverResult(data);
}
#Override
protected void onReset() {
super.onReset();
onStopLoading();
if (hasResult) {
onReleaseResources(mData);
mData = null;
hasResult = false;
}
}
protected void onReleaseResources(T data) {
//nothing to do.
}
public T getResult() {
return mData;
}
}
Usage:
in your activity:
getSupportLoaderManager().initLoader(TASK_ID, TASK_BUNDLE, new LoaderManager.LoaderCallbacks<Bitmap>() {
#Override
public Loader<Bitmap> onCreateLoader(final int id, final Bundle args) {
return new ImageLoadingTask(MainActivity.this);
}
#Override
public void onLoadFinished(final Loader<Bitmap> loader, final Bitmap result) {
if (result == null)
return;
//TODO use result
}
#Override
public void onLoaderReset(final Loader<Bitmap> loader) {
}
});
inner static class , or a normal class:
private static class ImageLoadingTask extends AsyncTaskLoaderEx<Bitmap> {
public ImageLoadingTask (Context context) {
super(context);
}
#Override
public Bitmap loadInBackground() {
//TODO load and return bitmap
}
}
Update: starting from support library 27.1.0, things changed a bit (link here) :
In version 27.1.0, onStartLoading() is called every time the Activity
is started. Since you call deliverResult() in onStartLoading(), you
trigger onLoadFinished(). This is Working as Intended.
You should remove your call to deliverResult() from onStartLoading()
as it is not needed (Loaders already deliver results computed in
loadInBackground() without any additional work needed on your part).
I've updated the code above for this change.
EDIT:
Updated, kotlin version can be found here.

Since Honeycomb and the v4 Compatibility Library it is possible to use AsyncTaskLoader. From what I understand, the AsyncTaskLoader can survive through config changes like screen flips. But using AsyncTask you can mess up with configuration changes.
Key information: AsyncTaskLoader is subclass of Loader. This class performs the same function as the AsyncTask, but a bit better, it can also be useful in handling configuration changes (screen orientation).
A very good example and explanation is given here.
http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html
Google has a pretty good example directly in the API Docs.
Android Design Patterns provides some more detail and the reasoning behind Loaders.
This tutorial will definetly help You. http://www.javacodegeeks.com/2013/08/android-custom-loader-to-load-data-directly-from-sqlite-database.html

Here's step by step tutorial to implement AsyncTaskLoader. or check out this same article on Medium
Implement LoaderManager.LoaderCallbacks<String> on MainActivity and create a static int to uniquely identify your loader and create a String key to pass string url to your loader
public class MainActivity extends AppCompatActivity
implements LoaderManager.LoaderCallbacks<String>{
public static final int OPERATION_SEARCH_LOADER = 22;
public static final String OPERATION_QUERY_URL_EXTRA = "query";
//...}
Override onCreateLoader,onLoadFinishedand onLoaderReset functions inside MainActivity
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
//Here we will initiate AsyncTaskLoader
return null;
}
#Override
public void onLoadFinished(Loader<String> loader, String operationResult) {
//Think of this as AsyncTask onPostExecute method, the result from onCreateLoader will be available in operationResult variable and here you can update UI with the data fetched.
Log.d("MAINACTIVITY","result : "+ operationResult);
}
#Override
public void onLoaderReset(Loader<String> loader) {
//Don't bother about it, Android Studio will override it for you
}
inside onCreateLoader() return a new AsyncTaskLoader<String> as an anonymous inner class with this as the constructor's parameter and override loadInBackground & onStartLoading inside anonymous
inner class
#Override
public Loader<String> onCreateLoader(int id, final Bundle args) {
return new AsyncTaskLoader<String>(this) {
#Override
public String loadInBackground() {
//Think of this as AsyncTask doInBackground() method, here you will actually initiate Network call
return null;
}
#Override
protected void onStartLoading() {
//Think of this as AsyncTask onPreExecute() method,start your progress bar,and at the end call forceLoad();
forceLoad();
}
};
}
Inside loadInBackground make a network call using HTTPUrlConnection or OKHttp or anything that you use.
#Override
public String loadInBackground() {
String url = args.getString(OPERATION_QUERY_URL_EXTRA);//This is a url in string form
if (url!=null&&"".equals(url)) {
return null;//if url is null, return
}
String operationResult="";
try {
operationResult = NetworkUtils.getResponseFromHttpUrl(url);//This just create a HTTPUrlConnection and return result in strings
} catch (IOException e) {
e.printStackTrace();
}
return operationResult;
}
Inside onCreate initialize the loader with OPERATION_SEARCH_LOADER as the ID, null for the bundle, and this for the context
getSupportLoaderManager().initLoader(OPERATION_SEARCH_LOADER, null, this);
Now call this method, whenever and wherever you want to trigger the loader
private void makeOperationSearchQuery(String url) {
// Create a bundle called queryBundle
Bundle queryBundle = new Bundle();
// Use putString with OPERATION_QUERY_URL_EXTRA as the key and the String value of the URL as the value
queryBundle.putString(OPERATION_QUERY_URL_EXTRA,url);
// Call getSupportLoaderManager and store it in a LoaderManager variable
LoaderManager loaderManager = getSupportLoaderManager();
// Get our Loader by calling getLoader and passing the ID we specified
Loader<String> loader = loaderManager.getLoader(OPERATION_SEARCH_LOADER);
// If the Loader was null, initialize it. Else, restart it.
if(loader==null){
loaderManager.initLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
}else{
loaderManager.restartLoader(OPERATION_SEARCH_LOADER, queryBundle, this);
}
}
Walla, you are done, just to remind you NetworkUtils.getResponseFromHttpUrl(url); is my custom function which take string convert it into URL which in turn used to create HTTPUrlConnection

I like this brief example AsyncTask and AsyncTaskLoader.
class FooLoader extends AsyncTaskLoader {
public FooLoader(Context context, Bundle args) {
super(context);
// do some initializations here
}
public String loadInBackground() {
String result = "";
// ...
// do long running tasks here
// ...
return result;
}
}
class FooLoaderClient implements LoaderManager.LoaderCallbacks {
Activity context;
// to be used for support library:
// FragmentActivity context2;
public Loader onCreateLoader(int id, Bundle args) {
// init loader depending on id
return new FooLoader(context, args);
}
public void onLoadFinished(Loader loader, String data) {
// ...
// update UI here
//
}
public void onLoaderReset(Loader loader) {
// ...
}
public void useLoader() {
Bundle args = new Bundle();
// ...
// fill in args
// ...
Loader loader =
context.getLoaderManager().initLoader(0, args, this);
// with support library:
// Loader loader =
// context2.getSupportLoaderManager().initLoader(0, args, this);
// call forceLoad() to start processing
loader.forceLoad();
}
}

Simplifying hard, maybe
private void loadContent() {
getLoaderManager().initLoader(1000, new Bundle(),
new LoaderManager.LoaderCallbacks<List<String>>() {
#Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new AsyncTaskLoader<List<String>>(MainActivity.this.getApplicationContext()) {
#Override
public List<String> loadInBackground() {
Log.i("B", "Load background data ");
ArrayList<String> data = new ArrayList<>();
for (int i = 0; i < 5000; i++) {
data.add("Data." + i + " " + System.currentTimeMillis());
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data;
}
};
}
#Override
public void onLoadFinished(Loader<List<String>> loader, List<String> data) {
Log.i("B", "Here are your data loaded" + data);
if (!loader.isAbandoned()) {
mAdapter.setData(data); // Read also about RecyclerView
}
}
#Override
public void onLoaderReset(Loader<List<String>> loader) {
Log.i("B", "Loader reset");
}
}).forceLoad();
}
#Override
protected void onDestroy() {
// Abandon the loader so that it should not attempt to modify already dead GUI component
getLoaderManager().getLoader(1000).abandon();
super.onDestroy();
}
Make this part of your Activity. The sample simulates delay, but makes new entries easy to recognize because they will have the different time stamp suffix. Of course you also need RecyclerView to display the data, the answer to this question seems very good.
The loader in this example is the inner class that keeps the reference to the parent activity. It must be external static class without such reference in production.

I prefer using Bolts-Android. it is very easy.
https://github.com/BoltsFramework/Bolts-Android
Task.callInBackground(new Callable<Void>() {
public Void call() {
// Do a bunch of stuff.
}
}).continueWith(...);

Related

Result of query is setting application variable late

I have one Fragment. For the OnClickListener() of all the views that are in the fragment I made another class UtilClickListener. There I am making db call on spinner onItemSelected using room persistence database. The database call first inserts data to the table and then updates an application variable in my application.
So I am trying to access the updated application variable on the spinner onItemSelected() just after the database call. But the variable is not updating at once, later when I click on other views of the fragment then I get the updated value.
Fragment code:
public class Calculator extends Fragment {
#Nullable
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Spinner ageSpinner = rootView.findViewById(R.id.spinner_how_old);
ageSpinner.setOnItemSelectedListener(new UtilOnClickListener(this));
CRSCalculatorAdapter ageListAdapter = new CRSCalculatorAdapter(rootView.getContext(),
android.R.layout.simple_spinner_item,Arrays.asList(rootView.getContext().getResources().getStringArray(R.array.age_group)) );
ageSpinner.setAdapter(ageListAdapter);
}
}
UtilOnClickListener class code:
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
switch (parentSpinnerId[1]) {
case "spinner_how_old}":
mGlobals.setAge(parent.getSelectedItem().toString());
CRSDatabaseInitializer.populateAsync(CRSDatabase.getDatabase(view.getContext()), crsCalculator.getContext(), Constants.QUERY_TYPE_ALL);
mListener.onChangeActionBarTitle(Integer.valueOf(mGlobals.getFinalScore())); // Here I am updating score in the action bar which is updating late on the next view click
break;
}
"CRSDatabaseInitializer" is calling an Asynctask for the database call.
Here is the database initializer code:
public class CRSDatabaseInitializer {
public static void populateAsync(#NonNull final CRSDatabase db, Context context, String typeOfQuery) {
PopulateDbAsync task = new PopulateDbAsync(db, typeOfQuery);
}
private static class PopulateDbAsync extends AsyncTask<Void, Void, Void> {
private final CRSDatabase mDb;
private final String mQueryType;
PopulateDbAsync(CRSDatabase db, String queryType) {
mDb = db;
mQueryType = queryType;
}
#Override
protected Void doInBackground(final Void... params) {
int scoreOfAge = Integer.valueOf(getScoreOnAge(mDb));
mGlobals.setFinalScore(scoreOfAge); // this is the application variable I need to update.
return null;
}
public static int getScoreOnAge(CRSDatabase db) {
int userScore = 0;
if (mGlobals.getAge() != null) {
userScore = Integer.valueOf(db.ageScoreDao().getScore(mGlobals.getAge(), mGlobals.getMarriedOrNot()));
}
return userScore;
}
}
Adding more codes from CRSDatabaseInitializer where I am inserting my data into the room database:
private static void insertNocCode(CRSDatabase db) {
NocCode nocData = new NocCode();
List<String[]> str = insertData(db, "nocCodes.csv");
for (int i = 0; i < str.size(); i++) {
nocData.setmNocCode(str.get(i)[0]);
nocData.setmTitle(str.get(i)[1]);
nocData.setmSkilltype(str.get(i)[2]);
addCRSData(db, nocData);
}
}
insertData(Database db, String filename); is the method where I am reading a csv file and inserting all the columns in the csv file.
public static List<String[]> insertData(CRSDatabase db, String fileName) {
String[] str = new String[5];
ArrayList<String[]> stringArray = new ArrayList<>();
try {
InputStreamReader is = new InputStreamReader(mContext.getAssets()
.open(fileName));
BufferedReader reader = new BufferedReader(is);
String line = "";
while ((line = reader.readLine()) != null) {
str = line.split(",");
stringArray.add(str);
}
} catch (IOException ex) {
ex.printStackTrace();
} finally
{
}
return stringArray;
}
And the insert method definition:
private static NocCode addCRSData(final CRSDatabase db, NocCode nocCode) {
db.nocCodeDao().insert(nocCode);
return nocCode;
}
So here is the update of this problem that I was going through. I solved the issue using handler. When I am making the database call, I am letting the DB to update the variable first , then I am running one handler to get the updated value later in the fragment.
Here is the code I updated in the UtilOnClickListener class.
private static class MyHandler extends Handler {}
private final MyHandler mHandler = new MyHandler();
public static class UtilRunner implements Runnable {
private final WeakReference<Calculator> mActivity;
public UtilRunner(Calculator activity) {
mActivity = new WeakReference<Calculator>(activity);
}
#Override
public void run() {
Calculator activity = mActivity.get();
if (activity.getContext() instanceof MainActivity) {
OnActionBarListener mListener = (OnActionBarListener) activity.getContext();
Globals mGlobals = (Globals) activity.getActivity().getApplicationContext();
mListener.onChangeActionBarTitle(mGlobals.getFinalScore());
}
}
}
And I am running the handler from OnClick of the views:
mHandler.postDelayed(mRunnable, 200);
There are various ways to handle this. In your case I am not able to understand why read operation is getting executed before inserted data is committed even though you are inserting and reading from the same thread. You can have a look on this discussion: stackOverFlow, what I learned from this discussion is that it's always better to take control in your hand because database internal implementation might change from time to time. Let's see the soution:
Wrap the read query inside a transaction either by using annotation #Transaction in Dao class or by wrapping the code for insertion in db.beginTransaction and db.endTransaction.devGuide. This ensures that read can't happen while database is being written.
What I find best for this is using Rx-Java See Introdution. You can do the insertion and then get notified when it completes and perform the read operation. Since insert operation will not return anything, wrap it inside Completable.fromAction. Completable observable has operator obs1.andThen(obs2), as clear from the name first obs1 is completed then only obs2 is executed. Note that your db.ageScoreDao().getScore(..) method should return an observable, hence wrap the return value in an Observable;
Completable.fromAction(new Action(){
#Override
public void run() throws Exception {
db.nocCodeDao().insert(nocCode);
}
}).andThen(return db.ageScoreDao().getScore(..)
.subscribeOn(Scheduler.io()) //do database query on io thread
.observeOn(AndroidScheduler.mainThread())
.subscribeWith(new DisposableObserver<Object>(){
#Override
public void onNext(Object o) {
//Here you update the variable
}
#Override
public void onError(Throwable e) {..}
#Override
public void onComplete() {..}
});

What happens when you modify class attributes in AsyncTaskLoader

When you try to modify/read a class attribute from the loadInBackground() method. What happens? Does android make a deep copy before passing in the variable?
Do modifications inside loadInBackground() actually change the class attribute values on the exterior context?
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Void>
{
public List<String> some_array = new ArrayList();
public String some_string = "Hello world";
...
#Override
public Loader<Void> onCreateLoader(int id, Bundle args)
{
return new AsyncTaskLoader<Void>(MainActivity.this)
{
#Override
protected void onStartLoading()
{
forceLoad();
}
#Override
public Void loadInBackground()
{
some_array.add("some element");
some_string = "good bye";
}
};
}
#Override
public void onLoadFinished(Loader<Void> loader, Void data)
{
// what are the values of some_array and some_string now?
}
#Override
public void onLoaderReset(Loader<Void> loader)
{
}
}
Okay so I manually tested it and found that in Java, arrays are passed as pointers to the background thread.
Therefore, modifications in the background thread do alter the class variables, but this should be avoided as it is asynchronous and can quickly become unpredictable and chaotic.
My current approach is to store the changes in a temporary, overwritable array and then merge them back in the onLoadFinished method, when we are all back in the same thread.

How to run SQLite query asynchronously on background thread?

I have a big database which takes time to find needed information. So I decided to use RxJava to make this process asynchronous.
#Override
public void afterTextChanged(final Editable s) {
final String query = s.toString();
Observable.create(new Observable.OnSubscribe<Cursor>() {
#Override
public void call(Subscriber<? super Cursor> subscriber) {
subscriber.onNext(database.search(query));
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Cursor>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(Cursor cursor) {
scAdapter.swapCursor(cursor);
}
});
}
But query is running on main thread: EditText where I entering text is freezing.
My question is how to run SQLite query asynchronously on background thread?
Probably this https://developer.android.com/reference/android/app/LoaderManager.html
will suite for you.
Besides, here is short implementation for you, but this is not RxJava.
Firstly, you need to implement LoaderManager.LoaderCallbacks<Cursor>, and usually this interface is implemented by Activity (or Fragment).
In onCreateLoader, a CursorLoader should be created and returned. Here is just an example with MyCursorLoader as descendant of CursorLoader, where you can perform connection to database and queries.
In onLoadFinished you have to treat cursor with results of query.
Please, consider the link to android.com, mentioned above.
public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
// start loading data using LoaderManager of Activity
// third argument only has sense in this case
getLoaderManager().initLoader(0, null, this);
}
private static final String ACTIVITY_NAME = "main_activity";
private void treatCursorRow(Cursor cursor){
// treat record from cursor
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// this callback is called by LoaderManager in order to obtain CursorLoader
// here a new one loader is created
// created loader will be processed by LoaderManager
return new MyCursorLoader(this, ACTIVITY_NAME);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// this callback is called when loader finishes load cursor
// you don't need to destroy loader - just tread the data
if(data != null)
while(data.moveToNext())
treatCursorRow(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
// here you can do something
// relevant to cancelling of loading data
// in example, when you have an event that cancels current
// loading and restarts new one
}
class MyCursorLoader extends CursorLoader {
private static final String DATABASE_NAME = "my_database";
private static final int DATABASE_VERSION = 1;
private String name_param;
public MyCursorLoader(Context context, String activity_name) {
super(context);
name_param = activity_name;
}
#Override
public Cursor loadInBackground() {
// assuming, that we have implemented SQLiteOpenHelper
// to treat sqlite-database
MyDatabaseHelper dbh = new MyDatabaseHelper(
MainActivity.this,
DATABASE_NAME,
null,
DATABASE_VERSION
);
return dbh.getWritableDatabase().rawQuery(
"SELECT * FROM some_table WHERE name=?",
new String[]{ name_param }
);
}
}
}
Another way, is in using of ContentProvider https://developer.android.com/guide/topics/providers/content-providers.html .
In this way you can separate data layer and business logic. Your data access will be abstracted to uris.
Using ContentProvider, you define your queries within it and load data using Uri:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return getContentResolver().query(
YourContentProvider.SOME_URI,
null,
null,
null,
null
);
}
This is convenient way if you have more than one or two customers of your data (Activities or Fragments) - you will use just predefined uris rather repeating sql queries or creating many CursorLoaders descendands.
Moreover, ContentProvider may be used from outside your app if you want.

Thread Synchronization with IntentService

I'm trying to create an app that makes HTTP requests through an intentservice. I need the app to wait for the service to finish its run (aka, have the request be returned with some data) before it continues its operations, as its operations involve manipulation of the data I hope to receive from the HTTP requests. I've tried numerous means of doing so - Semaphore, CountDownLatch, but it seems that for all of them, I need some method of passing in the waiting/counting object into the intentservice so that it can tell the main thread where that object is waiting that it is done processing. How do I go about doing that? Basically, I want a synchronous, blocking call to an http server to work conveniently with an Intent Service, since an intent service makes multi threading easy.
Again to reiterate just to make sure i'm not misusing terminology: What I mean by Synchronous and blocking/what I want: I make a call to the http server by sending an intent to my intentservice that makes the request. My UI thread, or thread from which this intent was sent, now waits until the request has been processed and a result has been returned before continuing to run.
If you think that I am going about this process (making http calls in a blocking, synchronous way) all wrong, what is another way you might choose to go about it? Thanks!
I am sorry, but I think your architecture is not right or I may understand it wrong. IntentService is built to do thing serial way on separate thread. Now you say you want it to be synchronous and blocking. You cannot block UI thread!
In order to create notification system from your IntentService to Activity/Fragment/etc. you have few choices: singleton, broadcast message (receiver, resultReceiver), others?
Based on assumption that service and other parts of the application are working in same process. Best option would be to create manager to do this job. Something like this can be built to start service as well as listen for completion event:
public class MyNetworkManager {
static MyNetworkManager sInstance;
Context mContext;
LinkedList<OnCompletionListener> mListeners;
private MyNetworkManager(Context context) {
mContext = context;
mListeners = new LinkedList<>();
}
public static MyNetworkManager getInstance(Context context) {
if (sInstance == null) {
synchronized (MyNetworkManager.class) {
if (sInstance == null) {
sInstance = new MyNetworkManager(context.getApplicationContext());
}
}
}
return sInstance;
}
// add listener to listen for completion event
public void addListener(OnCompletionListener listener) {
synchronized (mListeners) {
mListeners.add(listener);
}
}
// remove listener to stop listening for completion event
public void removeListener(OnCompletionListener listener) {
synchronized (mListeners) {
mListeners.remove(listener);
}
}
// call from UI to start service operation
public void startNetworkOperation() {
Intent service = new Intent();
mContext.startService(service);
}
// call from service to notify UI (still on different thread, can use Handler to make call on main thread)
public void notifyCompletion() {
synchronized (mListeners) {
for (OnCompletionListener listener : mListeners) {
listener.onCompleted(this);
}
}
}
public static interface OnCompletionListener {
void onCompleted(MyNetworkManager manager);
}
}
Use this pattern
public interface SynchronizationListener {
//void onStart(int id); not requered
//void onProgress(int id, long updateTime); not requered
void onFinish(Object data); // replace Object with your data type
}
In your service add end call this
private void startSynchronization() {
SynchronizationManager.getInstance().startSynchronizing();
}
Your Singleton Manager
public class SynchronizationManager {
private static SynchronizationManager instance;
private Object synRoot = new Object();
private boolean synchronizing = false;
private List<SynchronizationListener> synchronizationListeners;
public SynchronizationManager() {
synchronizationListeners = new ArrayList<SynchronizationListener>();
}
static {
instance = new SynchronizationManager();
}
public static SynchronizationManager getInstance() {
return instance;
}
public boolean isSynchronizing() {
synchronized (synRoot) {
return synchronizing;
}
}
public void startSynchronizing() {
synchronized (synRoot) {
if (synchronizing) {
return;
}
synchronizing = true;
}
Object data; // <-- replace Object with your data type
if (ConnectivityReceiver.hasGoodEnoughNetworkConnection()) { // check connection
data = sync();
}
synchronized (synRoot) {
synchronizing = false;
}
onSynchronizationFinish(data); // use listener for send data tu Observer Activity
}
public void stopSynchronizing() {
synchronized (synRoot) {
synchronizing = false;
}
}
public synchronized void registerSynchronizationListener(
SynchronizationListener listener) {
if (!synchronizationListeners.contains(listener)) {
synchronizationListeners.add(listener);
}
}
public synchronized void unregisterSynchronizationListener(
SynchronizationListener listener) {
if (synchronizationListeners.contains(listener)) {
synchronizationListeners.remove(listener);
}
}
public void onSynchronizationStart(int id) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onStart(id);
}
}
protected void onSynchronizationProgress(int id, long updateTime) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onProgress(id, updateTime);
}
}
protected void onSynchronizationFinish(Object data) {
for (SynchronizationListener listener : synchronizationListeners) {
listener.onFinish(data);
}
}
protected int sync) {
// code for load your data your HttpRequest
}
}
In your activity
private SynchronizationListener synchronizationListener = new SynchronizationListener() {
/*public void onStart(int id) {
}
public void onProgress(int id, long updateTime) {
}*/
public void onFinish(Object data) {
//elaborate data
}
};
#Override
protected void onResume() {
super.onResume();
SynchronizationManager.getInstance().registerSynchronizationListener(
synchronizationListener);
}
#Override
protected void onPause() {
super.onPause();
SynchronizationManager.getInstance().unregisterSynchronizationListener(
synchronizationListener);
}
See this code for example UnivrApp
A ContentProvider would be a better choice than an IntentService in my thinking. You can trigger each network call with a query and then return a MatrixCursor with details about the results of your background work. Android already has lots of good plumbing around running queries in background tasks and waiting for the results before triggering ui updates.
in ContentProvider query() method :
MatrixCursor cursor = new MatrixCursor(new String[]{"_id","uri", "status", "last_modified", "result"});
String lastModified=null;
int id =1;
// do your work here
// ..
// report your work here
cursor.addRow(new Object[]{id++, uri.toString(), HttpStatus.SC_OK, lastModified, "" });
// set uri for data observers to register
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
What you try to do is just communication between IntentService and Activity/Fragment.
You can try send broadcast at the end of onHandleIntent and catch it in registered receiver or use ResultReceiver - read more how to implement here.
Edit:
Try this:
Handle all background operations at once in onHandleIntent
On every step send new data using ResultReceiver
// operation 1
Bundle b1 = new Bundle();
b1.putParcelable("data", data1);
resultReceiver.send(0, b1);
// operation 2
Bundle b2 = new Bundle();
b2.putParcelable("data", data2);
resultReceiver.send(1, b2);
Handle it in ResultReceiver
public void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 0) { // handle step 1 }
else if (resultCode == 1) { // handle step 2 }
}

Testing a CursorLoader with Robolectric & Mockito

Given I'm developing a simple ListFragment (in this case, it reads a list of Artists from the MediaStore, but will also read data from a different source later) like this:
#EFragment
public class ArtistsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = ArtistsFragment.class.getName();
private SimpleCursorAdapter mAdapter;
Uri uri = MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI;
CursorLoader mCursorLoader;
#AfterViews
void setupView() {
mAdapter = new SimpleCursorAdapter(getActivity(),
android.R.layout.simple_list_item_1, null,
new String[]{MediaStore.Audio.Artists.ARTIST}, // lists path of files
new int[]{android.R.id.text1}, 0);
setListAdapter(mAdapter);
getLoaderManager().initLoader(0, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (mCursorLoader == null) {
mCursorLoader = new CursorLoader(getActivity(), uri, new String[]{MediaStore.Audio.Artists._ID, MediaStore.Audio.Artists.ARTIST},
null, null, MediaStore.Audio.Artists.ARTIST + " ASC");
} else {
System.out.println("mCursorLoader.count: " + mCursorLoader.loadInBackground().getCount());
}
return mCursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
setListShown(true);
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
I want to use Robolectric + Mockito + awaitility to proof the Fragment behaves properly on various conditions (e.g. empty list or invalid data etc). My test class looks like this:
#RunWith(RobolectricTestRunner.class)
public class ArtistsFragmentTest {
#Test
public void shouldNotBeNull() {
final ArtistsFragment myFragment = ArtistsFragment_.builder().build();
assertNotNull(myFragment);
// Create a mock cursor.
final Cursor mc = getSampleArtistCursor();
when(mc.getCount()).thenReturn(1);
when(mc.getInt(0)).thenReturn(1);
when(mc.getString(1)).thenReturn("Sample Title");
myFragment.mCursorLoader = mock(CursorLoader.class);
when(myFragment.mCursorLoader.loadInBackground()).thenReturn(mc);
startFragment(myFragment);
assertNotNull(myFragment.getListView());
await().atMost(5, TimeUnit.SECONDS).until(new Callable<Integer>() {
#Override
public Integer call() throws Exception {
return myFragment.getListAdapter().getCount();
}
}, equalTo(1));
System.out.println(myFragment.getListAdapter().getCount());
}
private Cursor getSampleArtistCursor() {
return new CursorWrapper(mock(MockCursor.class));
}
}
Then when running this test in IntelliJ or maven the test will fail, the adapter will always return a count of zero.
The System.out.println statement in onCreateLoader however returns 1. Do I need to take special care for Mockito in background threads? (the loadInBackground method runs on a worker thread).
I've just gotten Loader tests working in my code. In my case I found it more direct to test the loader itself rather than try to route it through the Fragment code.
I wound up using a slightly modified version of the code from this post:
https://groups.google.com/forum/#!msg/robolectric/xY5MF399jA8/V5PnUfh1D-wJ
First, I had to implement some shadow classes because Robolectric2 doesn't include shadow code for the AsyncTaskLoader or Loader classes. If you've never added a shadow class know that it's important you put these in the correct package. Both of these shadows should live in android.support.v4.content.
ShadowLoader
#Implements(Loader.class)
public class ShadowLoader<D> {
// //////////////////////
// Constants
// //////////////////////
// Fields
protected boolean reset;
// //////////////////////
// Constructors
// //////////////////////
// Getter & Setter
// //////////////////////
// Methods from SuperClass/Interfaces
#Implementation
public void reset() {
reset = true;
}
#Implementation
public boolean isReset() {
return reset;
}
// //////////////////////
// Methods
// //////////////////////
// Inner and Anonymous Classes
}
ShadowAsyncTaskLoader
#Implements(AsyncTaskLoader.class)
public class ShadowAsyncTaskLoader<D> extends ShadowLoader {
#RealObject
private AsyncTaskLoader<D> asyncTaskLoader;
#Implementation
void executePendingTask() {
new AsyncTask<Void, Void, D>() {
#Override
protected D doInBackground(Void... params) {
return (D) asyncTaskLoader.loadInBackground();
}
#Override
protected void onPostExecute(D data) {
updateLastLoadCompleteTimeField();
asyncTaskLoader.deliverResult(data);
}
#Override
protected void onCancelled(D data) {
updateLastLoadCompleteTimeField();
asyncTaskLoader.onCanceled(data);
executePendingTask();
}
}.execute((Void)null);
}
public void setReset(boolean reset) {
this.reset = reset;
}
private void updateLastLoadCompleteTimeField() {
try {
Field mLastLoadCompleteTimeField = AsyncTaskLoader.class.getDeclaredField("mLastLoadCompleteTime");
if(!mLastLoadCompleteTimeField.isAccessible()) {
mLastLoadCompleteTimeField.setAccessible(true);
}
mLastLoadCompleteTimeField.set(asyncTaskLoader, SystemClock.uptimeMillis());
} catch(NoSuchFieldException e) {
e.printStackTrace();
} catch(IllegalAccessException e) {
e.printStackTrace();
}
}
}
Then, depending on your configuration you can add an annotation to use the custom classes
#Config( shadows = { ShadowAsyncTaskLoader.class, ShadowLoader.class})
At this point calling loader.onStartLoading() caused the loader to run as expected without having to hack any wait commands into my test cases.
Hope this helps. I haven't tried using the LoaderManager with this method of testing, so I can't verify that it works through that call.
Note: the reason I added ShadowLoader is because I was finding isReset() was returning true when I didn't expect it to.
The solution is to use:
Robolectric.flushForegroundThreadScheduler();
(Robolectric 3.0)
This will run all tasks immediately, including the CursorLoader.

Categories

Resources