I have over 10 fragments that execute the same kind of task which is :
Retrieving the Data from the server using Retrofit
Starting an Async Task to update the Database (Using ORMLite)
Once the Database is updated, retrieving the new data from the Database
Notify Dataset has changed in the adapter
I'm wondering if it's useless to put the update database code inside an AsyncTask within my fragment once I retrieve the data from the server?
I have trouble understanding what run on the UI thread and what doesn't and should be started as his own thread through an AsyncTask
Here my code:
private void getLocalIncidentTemplate() {
mIncidentTemplate.clear();
mIncidentTemplate.addAll(GenericDAO.getInstance(EntityGroup.class).queryForAll());
Collections.sort(mIncidentTemplate);
Log.e(TAG, "Incident Template count:" + mIncidentTemplate.size());
mListAdapter.notifyDataSetChanged();
spinner.setVisibility(View.GONE);
}
private void getRemoteIncidentTemplate() {
Call<EntityIncident> call = meepServices.getIncidentTemplate();
call.enqueue(new Callback<EntityIncident>() {
#Override
public void onResponse(Call<EntityIncident> call, Response<EntityIncident> response) {
if (response.isSuccessful()) {
new updateIncidentTemplateTask().execute(response.body());
}
}
#Override
public void onFailure(Call<EntityIncident> call, Throwable t) {
t.getStackTrace();
Log.e(TAG, t.toString());
Utils.showToastMessage(getActivity(), "Error retrieving Incidents", true);
}
});
}
private class updateIncidentTemplateTask extends AsyncTask<EntityCategories, Void, Boolean> {
#Override
protected Boolean doInBackground(EntityCategories... params) {
updateIncidents(params[0]);
return true;
}
protected void onPostExecute(Boolean b) {
getLocalIncidentTemplate();
spinner.setVisibility(View.GONE);
}
}
Here is the Database Update Using ORMlite:
private void updateIncident(EntityCategories categories) {
try {
categories.setId("MobilePlan");
//Update base categories
GenericDAO.getInstance(EntityCategories.class).addOrUpdate(categories);
for (EntityCategories.EntityCategory currentCategory : new ArrayList<>(categories.getCategories())) {
if (currentCategory.getmPlans() != null) {
for (EntityPlan myPlan : new ArrayList<>(currentCategory.getmPlans())) {
EntityPlan oldPlan = GenericDAO.getInstance(EntityPlan.class).queryById(String.valueOf(myPlan.getmId()));
myPlan.setCategories(currentCategory);
if (oldPlan != null) {
if (!myPlan.getmDateModification().equals(oldPlan.getmDateModification())) {
GenericDAO.getInstance(EntityPlan.class).addOrUpdate(myPlan);
}
} else {
GenericDAO.getInstance(EntityPlan.class).addOrUpdate(myPlan);
}
}
} else {
continue;
}
GenericDAO.getInstance(EntityLabel.class).addOrUpdate(currentCategory.getmLabel());
currentCategory.setCategories(categories);
GenericDAO.getInstance(EntityCategories.EntityCategory.class).addOrUpdate(currentCategory);
}
} catch (Exception e) {
e.printStackTrace();
}
Log.d(TAG, "DATA updated");
}
For your particular case, you should use the AsyncTask to retrieve data from the backend and place it in the database.
Remember that AsyncTask has three main methods:
onPreExecute() that runs on the UI thread. Useful when you need to prep something that requires UI thread (touching views and whatnot)
doInBackGround() this runs on background thread
onPostExecute() runs also on the UI thread.
In onPostExecute() you could notify your adapter of the new data.
If I were you, I'd use loaders to get notified and retrieve the data off the database. So that the complete chain would be some:
AsyncTask pulls data from the backend and stores it in the database
Your loader will get notified that something changed inside the database and will pull the data from it and call onLoadFinished() method inside your activity/fragment
onLoadFinished() passes the data to the view adapter.
I haven't gone into detail as to how to implement this. I just presented the overall architecture.
I have trouble understanding what run on the UI thread and what doesn't and should be started as his own thread
The short answer:
Everything that might block the UI thread (in other words, might take time) should run on a worker thread (threadpool or dedicated)
DB actions and network requests are classic examples for actions that should always run asynchronously.
In your case I would just use an ORM to wrap all the interaction with the DB, such as ORMlite or any other you find more suitable, in that case you will not have to concern yourself with the inner workings and just provide callbacks for when your calls have finished (successfully or not)
Related
I need a safe practise to load data from the Database.
I need to load the data from the Sql db and put it into a list or recylerview.
So what i tryied, i created a class which call the method load more everytime when user reaches the end of the list. And than it load the data limited.
Like 'Select * From a Where 1 Limit listItemCount, 20'.
I run into problems with this because, the thread which was loading, started 2 times with the same count value. Than i have changed it to synch the global run method from the loading thread. And accessing the count from the background worker of the list. This helped me a lot, and the data was loaded correctly, but i got still troubles with this. Because i need to insert the data into listview from the background thread, because of the synchronisation and calling notify in main thread after. If i am not doing it like this, i will get still problem.
Thread A: Started work.
Thread B: Waiting for finishing thread A (synchronized)
Thread A: runOnUiThread() trying to fill data.
Thread B: Allowed to run the code, getting count from listView.
Thread B: Got the same count as A, because A not finished the insert
statement.
Thread A: Added data.
Thread B: Added same data.
After this i added a atomicboolean, that if the thread is running he is just not executed. With this it was working perfect, but sometimes you have to scroll up a bit and down to trigger the load more.
And i think this solution is a bit dirty for me. Anyone now how to load data in pieces depending on list size of the listview, in a background thread without this issues?
This method is getting called when reaching the end of list:
private ExecutorService executorService;
private void loadMore()
{
if(!isAdded())
return;
if(executorService == null)
executorService = Executors.newSingleThreadExecutor();
executorService.execute(new AsyncLoadMoreHashTags(getContext(), this, esaphTagAdapterVertical));
}
Background Thread:
public class AsyncLoadMoreHashTags extends MyDataLoaderClass
{
private static final Object obLock = new Object();
public AsyncLoadMoreHashTags(Context context, DataBaseLoadWaiter dataBaseLoadWaiter, SpecialRecylerView spRecylerView)
{
super(context, dataBaseLoadWaiter, spRecylerView);
}
#Override
public void run()
{
super.run();
synchronized (AsyncLoadMoreHashTags.obLock)
{
List<Object> objectList = null;
try
{
int startFrom = super.getStartFrom()[0];
SQLHashtags sqlHashtags = new SQLHashtags(super.getSoftReferenceContext().get());
if(startFrom <= 0)
{
objectList = sqlHashtags.getMostUsedHashtags();
}
else
{
objectList = sqlHashtags.getAllHashtagLimited(startFrom);
}
sqlHashtags.close();
}
catch (Exception ec)
{
Log.i(getClass().getName(), "AsyncLoadMoreHashTags run() failed: " + ec);
}
finally
{
publish(objectList);
}
}
}
}
What i need is to, just start one thread a time.
Anyone a idea?
First of all use You should have to use PagedList library provided by google, for implementing paging facility, and if you want to use background thread for database operations you can use Anko commons to do this. Here is an example,
internal val pagedList by lazy {
runBlocking { // Coroutine Context
withContext(Dispatchers.IO) {
// Read from database, and it will be initialised in background
}
}
}
and if you want something to be dispatched on UI but performed in Background Thread, then go with this, doAsync operation and then return data in uiThread by using a custom interface.
doAsync {
// LONG OPERATION
Thread.sleep(2000)
uiThread {
callback.onActionsDone(dataList)
}
}
Let me know if you want more help on this.
Fixed it with like this, working fine.
public class AsyncLoadMoreHashTags extends OwnDataLoader
{
private static AtomicBoolean obLock = new AtomicBoolean(false);
public AsyncLoadMoreHashTags(Context context, DataBaseLoadWaiter dataBaseLoadWaiter, MomentsRecylerView momentsRecylerView)
{
super(context, dataBaseLoadWaiter, momentsRecylerView);
}
#Override
public void run()
{
super.run();
if(!AsyncLoadMoreHashTags.obLock.compareAndSet(false, true)) //when any thread loading data, its return and skips.
return;
List<Object> objectList = null;
try
{
int startFrom = super.getStartFrom()[0];
SQLHashtags sqlHashtags = new SQLHashtags(super.getSoftReferenceContext().get());
if(startFrom <= 0)
{
objectList = sqlHashtags.getMostUsedHashtags();
}
else
{
objectList = sqlHashtags.getAllHashtagLimited(startFrom);
}
sqlHashtags.close();
System.out.println("TEST: " + startFrom + " LOAD: " + objectList.size());
}
catch (Exception ec)
{
Log.i(getClass().getName(), "AsyncLoadMoreHashTags run() failed: " + ec);
}
finally
{
publish(objectList, AsyncLoadMoreHashTags.obLock); //Passing the lock object, when data has been added, obLock.set(false); is called.
}
}
}
I already asked this as a part of question multi threadaing with room database.
I am accessing room database from multiple threads in android however using same instance of databse in all threads.
new Thread(new Runnable(){
db.getInstance().taskdao().insertAll(list)
}).start();
new Thread(new Runnable(){
List<Task> data = db.getInstance().taskDao.loadAll();
}).start();
However data returned is null and I guess loadAll() is getting exeucted before insertAll() completes. Note that in actual code I am using Rxjava and hence I make sure that I read data after loadAll() is completed. Why this happening since sqlite enables lock to make sure serialize access.
If you try to read immediately after wrinting try to set a callback.
You must wait till function of inserting completes.
Suppose it takes 10ms to write into db and 5ms to start the thread and go to next thread to start (the one that reads). The reading thread starts reading at 6th ms while writing is not yet completed and will return unexpected result.
Better to use an AsyncTask to handle callback.
Simple example:
class Task extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
db.getInstance().taskdao().insertAll(list);
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
CallBack c = (CallBack) context; // your activity that implements context
c.onComplete();
}
}
and for interface and overriding:
interface CallBack {
void onComplete();
}
and implement it in your activity:
#Override
public void onComplete() {
//Now read it
}
I have a method that loads data from Firebase into ArrayList. After this,I use that ArrayList to construct RecyclerView. I've decided to load data on another thread. Below is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_just);
citiesRecyclerView =
(RecyclerView)findViewById(R.id.recyclerView);
handler = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1)
{
cityAdapter = new
CityAdapter(MainActivity.this,cities) ;
citiesRecyclerView.setAdapter(cityAdapter);
}
}
};
t = new Thread(new Runnable() {
#Override
public void run() {
//method that loads data into List.If this method was
//successfully done,then I send message 1 to Handler
loadDataFromFirebase();
}
});
t.start();
//other operations below
}
Hope,that everything understandable. Code works fine. And my problem is that I need to use loadDataFromFirebase method in thread again. I wanted to call t.start() again in order to call loadDataFromFirebase method,but there was error that thread already started. I checked that by writing this code:
if(t.getState()== Thread.State.NEW)
t.start();
else
someMethod();
else statement worked above.
And my questions are:
1) Does loadDataFromFirebase method work really on another thread by this way?
2) How to call loadDataFromFirebase method again in another thread, if something happened? Do I need to create another variable for Thread again?
It's not a good idea to handle all low-level thread work by your own.
Accroding to Android you could:
Use AsyncTask (but notice that they have many drawbacks such as context leak in some cases etc),
I could suggest you to get into RxJava - it's a painless way to use async work in your app.
To 'download' data from Firebase you could probably use FCM (push notifications) to load data on demand.
And what about your question:
"It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution."(c) http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#start()
If you are using firebase SDK you can use realtime database feature, so do not need to query it each time.
You should just subscribe one time and get updates. For example:
firebaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
YourDataObject value = dataSnapshot.getValue(YourDataObject.class);
Log.d(TAG, "Value is: " + value);
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
You can read docs here.
I'm trying to use RxJava through RxAndroid and its others librarys as RxLifecycle to update an Activity after fetching some data from a SyncAdapter. I know how to execute a network call throug a Service when it is call from the Activity. For example:
Observable<String> fetchFromGoogle = Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
String data = fetchData("http://www.google.com");
subscriber.onNext(data); // Emit the contents of the URL
subscriber.onCompleted(); // Nothing more to emit
}catch(Exception e){
subscriber.onError(e); // In case there are network errors
}
}
});
fetchFromGoogle
.subscribeOn(Schedulers.newThread()) // Create a new Thread
.observeOn(AndroidSchedulers.mainThread()) // Use the UI thread
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
view.setText(view.getText() + "\n" + s); // Change a View
}
});
The problem is that when using a SyncAdapter the SyncManager is the responsable to start the Service to fetch the data. Therefore I don't know how to create an Observable that suscribes on a thread that hasn't been started and that observes on the UI.
I was thinking in creating a Singleton Subject that my Activity can suscribe on, and in the SyncAdapter create an Observable that emitis an event when it is done (after suscribing the Singleton Subject).
What could be a better/right solution?
In my Android project i use Azure Mobile Services SDK and the way i make queries to the local sqlite database is like the following:
(example taken from http://azure.microsoft.com/en-us/documentation/articles/mobile-services-android-get-started-data/).
The problem is that i get the following errors:
1)com.microsoft.windowsazure.mobileservices.table.sync.localstore.MobileServiceLocalStoreException: java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed
2) A SQLiteConnection object for database 'LocalDatabase' was leaked! Please fix your application to end transactions in progress properly and to close the database when it is no longer needed
3) java.util.concurrent.ExecutionException: com.microsoft.windowsazure.mobileservices.table.sync.localstore.MobileServiceLocalStoreException: java.lang.IllegalStateException: attempt to re-open an already-closed object
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
final MobileServiceList<ToDoItem> result = mToDoTable.where().field("complete").eq(false).execute().get();
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.clear();
for (ToDoItem item : result) {
mAdapter.add(item);
}
}
});
} catch (Exception exception) {
createAndShowDialog(exception, "Error");
}
return null;
}
}.execute();
In this implementation there is no Cursor or SQLiteOpenHelper object to close. What could i do?
Thank you!
I think this is because you are getting the result in one thread but trying to use that same result in the UI thread. One possible method is to use Broadcast or Eventbus to send the data back to the UI once the operation completes:
Here is sort of what I would do:
Futures.addCallback(mToDoTable.where().field("complete").eq(false).execute(), new FutureCallback() {
#Override
public void onSuccess(Object result) {
//Do something with result
//I would use event bus or broadcast here to notify the UI
}
#Override
public void onFailure(Throwable t) {
}
});
If this doesn't work, I would use the debugger and put a break point on that execute().get() line and see what is happening internally.
Here is another possible way to do it:
mToDoTable.where().field("complete").eq(false).execute(new TableQueryCallback<Object>() {
#Override
public void onCompleted(List result, int count, Exception exception, ServiceFilterResponse response) {
}
});