LoaderCallbacks missed after rotation - android

I have this problem every time I use loader. If I start my AsyncTaskLoader and then rotate screen (doesn't matter one or several times) onLoaderFinished() is not called. loadInBackground() finishes its work but onLoaderFinished() is not called. It looks like callbacks are missed but in activity onCreate() I have:
LoaderManager lm = getSupportLoaderManager();
if (lm.getLoader(LOADER_FLIGHTS) != null) {
lm.initLoader(LOADER_FLIGHTS, null, mFlightsLoaderCallbacks);
}
Doesn't matter callbacks are implemented by activity or field. In my case I use field:
LoaderCallbacks<SearchResult> mFlightsLoaderCallbacks = new LoaderCallbacks<SearchResult>() {
Sometimes it works pretty good but in some devices I have got this trouble again.
I searched in Android bugs tracker but found nothing useful.
Is this my mistake and how can I correct it?
EDIT:
private Handler mHandler = new Handler();
#Override
public void onLoadFinished(Loader<SearchResult> loader, SearchResult res) {
mHandler.post(new Runnable() {
#Override
public void run() {
dismissProgress(R.string.dlg_wait_flights);
}
});
mResult = res;
Helper.Log.d(TAG, "Loader.onLoadFinished.");

Don't wrap your initLoader call in an if statement. In fact, I'm not even sure how initLoader is ever called if you are expecting the loader to not be null before you even call initLoader.

Related

Notify when all onCreateViewHolder inside a RecylerView have finished

RecyclerView calls onCreateViewHolder a bunch of times and then just keeps binding the data to these views. My view creation is slightly expensive and hence I need to defer rest of the UI tasks until my RecyclerView is done creating all the views.
I tried adding a ViewTreeObserver.OnGlobalLayoutListener but this callback gets called before even the first onCreateViewHolder() call.
Any idea how do I go about it?
After some research I've found out a solution with Handler. As you I'm looking for a beautiful code and this is a bit messy for me. But works perfectly anyway.
Handler is a class that you can use in a way to post message and/or Runnable, which will be added in a queue, then executed when that queue is finished.
My plan is, given that the adapter works on the UI, (inflate ect...) the creation and initialization (all onCreateViewHolder and onBindViewHolder) are added at a moment in the handler of the main thread.
That means that if you post a message in the main thread queue (the same obligatory used by your adapter), then the message will be executed after any previous request (after your adapted has finished to initialize everything).
Exemple :
Main activity
Initialization of the handler :
private Handler mHandler;
#Override
protected void onCreate(Bundle iSavedInstanceState) {
...
mHandler = new Handler(Looper.getMainLooper());
}
Initialization of your CustomAdapter :
private void initializeAdapter(...) {
MyCustomAdapter lMyNewAdapter = new MyCustomAdapter(...)
...
lNewAdapter.SetOnFirstViewHolderCreation(new
MyCustomAdapter.OnFirstViewHolderCreation {
#Override
public void onCreation() {
mHandler.post(new Runnable() {
#Override
public void run() {
// Finally here, the code you want to execute
// At the end of any Create and Bind VH of your
// Adapter
}
});
}
});
}
MyCustomAdapter
private boolean mIsViewHolderCreationStarted;
private OnFirstViewHolderCreation mOnFirstViewHolderCreation;
public CustomItemViewAdapter onCreateViewHolder(
#NonNull ViewGroup iViewGroup, int iI) {
...
if (!mIsViewHolderCreationStarted) {
mIsViewHolderCreationStarted = true;
if (mOnFirstViewHolderCreation != null) {
// It's at this point that we want to add a new request
// in the handler. When we're sure the request of the
// adapter has begun.
mOnFirstViewHolderCreation.onCreation();
}
}
}
public void setOnFirstViewHolderCreation(OnFirstViewHolderCreation iAction) {
mOnFirstViewHolderCreation = iAction;
}
public interface OnFirstViewHolderCreation {
void onCreation();
}
Note
Be aware that this solution will execute a code at the end of the first initialization of the enteer page that it is possible to show in a case of a RecyclerView.
A onCreateViewHolder might be called in case the screen is scrolled.
Which means that this solution does not guarantee you this handler message is executed after all possible onCreateViewHolder.
It only helps you to avoid an overload on the MainThread, during the greedy work of the adapter init.
Something else, in case you're using animations with your adapter to make it appears smoothly or something else (one of the good reasons to use this way to do), don't forget to put your RecyclerView in VISIBLE and not GONE, otherwise, the initialization of the adapter never happens.

How to correctly use a Workerthread?

I've been writing android apps for some months now, and I'm at the point where I'm building an actual needed app.
As I want that to work nice and fast, I made a Workerthread to do all kinds of tasks in the background while the UI can...build up and work and stuff.
It's based on the Android Studio Drawer app blueprint.
In Main.onCreate I got my operator=new Operator(), which extends Thread.
Now, when loading a new Fragment, it sometimes calls MainActivity.operator.someMethod() (I made operator static so I can use it from anywhere), and after some time I realized, the only tasks actually running in background are those in the operators run() method and an Asynctask my login Fragment runs. Everything else the UI waits for to complete and therefore gets executed by the UI thread.
So I thought: no problem! My operator gets a handler which is built in run(), and I change those tasks:
public void run() {
Looper.prepare(); //Android crashed and said I had to call this
OpHandler = new Handler();
LoadLoginData();
[...Load up some Arrays with hardcoded stuff and compute for later use...]
}
public void LoadLoginData() {
OpHandler.post(LoadLoginDataRunnable);
}
private Runnable LoadLoginDataRunnable = new Runnable() {
#Override
public void run() {
if(sharedPreferences==null)
sharedPreferences= PreferenceManager.getDefaultSharedPreferences(context);
sessionID=sharedPreferences.getString("sessionID", null);
if(sessionID!=null) {
postenID = sharedPreferences.getString("postenID", PID_STANDARD);
postenName = sharedPreferences.getString("postenName", PID_STANDARD);
context.QuickToast(sessionID, postenName, postenID);
}
}
};
context is my MainActivity, I gave the operator a reference so I could send Toasts for Debugging.
But now, the Runnables seem to not run or complete, any Log.e or Log.d stuff doesn't arrive in the console.
After some googeling and stackoverflowing, everyone is just always explaining what the difference is between Handlers, Asynctask, and Threads. And the multitask examples always only show something like new Thread(new Runnable{run(task1)}).start times 3 with different tasks.
And so became my big question:
How to correctly, over a longer time (~lifecycle of the MainActivity), with different tasks, use a background thread?
Edit: to clarify, I would also like a direct solution to my special problem.
Edit 2: after reading nikis comment (thank you), the simple answer seems to be "use HandlerThread instead of thread". Will try that as soon as I get home.
Trying a HandlerThread now. It seems my OpHandler, initialized in run(), gets destroyed or something after run() has finished, not sure whats up here (this is btw another mystery of the kind I hoped would get answered here). I get a NullpointerException as soon as I try to use it after run() has finished.
Make your worker thread own a queue of tasks. In the run() method, just pop a task from the queue and execute it. If the queue is empty, wait for it to fill.
class Operator extends Thread
{
private Deque<Runnable> tasks;
private boolean hasToStop=false;
void run()
{
boolean stop=false;
while(!stop)
{
sychronized(this)
{
stop=hasToStop;
}
Runnable task=null;
synchronized(tasks)
{
if(!tasks.isEmpty())
task=tasks.poll();
}
if(task!=null)
task.run();
}
}
void addTask(Runnable task)
{
synchronized(tasks)
{
tasks.add(task);
}
}
public synchronized void stop()
{
hasToStop=true;
}
}

Android: CalledFromWrongThreadException thrown when broadcast intent is handled

Here is the basic life cycle of my application. It targets SDK version 8 by now, since I am still running Android 2.3.3 on my device.
The application starts, onResume() is called
The method show() is called to display cached data.
A background service gets started which downloads and stores data. It uses AsyncTask instances to accomplish its work.
One of the tasks stores downloaded data in a SQLite database.
A broadcast intent is sent in onPostExecute() when the storing task has finished.
The MapActivity receives the intent and handles it.
The method show() is called to display cached and new data.
Within the method show() the map view gets invalidated after the overlay has been added. This works fine when show() has been called from the MapActivity itself. It raises an exception, however, when the asynchonous task is the source of the method call (indirectly).
As far as I understand, I am at the UI thread when I trigger show() in both cases. Is this true?
public class CustomMapActivity extends MapChangeActivity {
private boolean showIsActive = false;
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(IntentActions.FINISHED_STORING)) {
onFinishedStoring(intent);
}
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerReceiver(mReceiver, new IntentFilter(IntentActions.FINISHED_STORING));
}
#Override
protected void onResume() {
super.onResume();
show();
}
#Override
protected void onMapZoomPan() {
loadData();
show();
}
#Override
protected void onMapPan() {
loadData();
show();
}
#Override
protected void onMapZoom() {
loadData();
show();
}
private void onFinishedStoring(Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
boolean success = extras.getBoolean(BundleKeys.STORING_STATE);
if (success) {
show();
}
}
private void loadData() {
// Downloads data in a AsyncTask
// Stores data in AsyncTask
}
private void show() {
if (showIsActive) {
return;
}
showIsActive = true;
Uri uri = UriHelper.getUri();
if (uri == null) {
showIsActive = false;
return;
}
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
List<Overlay> mapOverlays = mapView.getOverlays();
CustomItemizedOverlay overlay = ItemizedOverlayFactory.getCustomizedOverlay(this, cursor);
if (overlay != null) {
mapOverlays.clear();
mapOverlays.add(overlay);
}
}
cursor.close();
mapView.invalidate(); // throws CalledFromWrongThreadException
showIsActive = false;
}
}
Here is the stack trace ...
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRoot.checkThread(ViewRoot.java:3020)
at android.view.ViewRoot.invalidateChild(ViewRoot.java:647)
at android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:673)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:2511)
at android.view.View.invalidate(View.java:5332)
at info.metadude.trees.activities.CustomMapActivity.showTrees(CustomMapActivity.java:278)
at info.metadude.trees.activities.CustomMapActivity.onMapPan(CustomMapActivity.java:126)
at info.metadude.trees.activities.MapChangeActivity$MapViewChangeListener.onChange(MapChangeActivity.java:50)
at com.bricolsoftconsulting.mapchange.MyMapView$1.run(MyMapView.java:131)
at java.util.Timer$TimerImpl.run(Timer.java:284)
Note: I use the MapChange project in order to receive notifications on map events.
EDIT:
From what I now read in the documentation about AsyncTask (scroll down a bit), I am not sure if I use it the correct way. As previously mentioned I start AsyncTask instances from within a Service class. In contrary, the documentation states ...
AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.
... which sounds as if AsyncTask should only be used within an Activity not within a Service?!
The reason for your crash is because of the way that the MapChange library you are using is implemented. Under the hood, this library uses Timer and TimerTask implementations to delay firing the change event and reduce the number of calls your application gets to onMapChanged(). However, you can see from the docs on Timer that it runs its tasks in created threads:
Each timer has one thread on which tasks are executed sequentially. When this thread is busy running a task, runnable tasks may be subject to delays.
Since the MapChange library does nothing to ensure that callbacks are posted to your application on the main thread (a serious bug IMO, especially on Android), you have to protect the code you call as a result of this listener. You can see this in the example MyMapActivity bundled with the library, everything from that callback gets funneled through a Handler which posts the calls back to the main thread for you.
In your application, the code inside onMapPan() and subsequently showTrees() is being called on a background thread so it is not safe to manipulate the UI there. Using either a Handler or runOnUiThread() from your Activity will guarantee your code is called in the right place.
With regards to your second questions about AsyncTask, there is nothing stopping you from using it inside of any application component, not just Activity. Even though it's a "background" component, by default a Service is still running on the main thread as well, so AsyncTask is still necessary to offload long-term processing to another thread temporarily.
If it's getting called on the wrong thread, then it's likely not on the UI thread. Have you tried this:
runOnUiThread(new Runnable() {
public void run() {
mapView.invalidate();
}});

How can I run my code after Activity is made visible?

I have an Activity with 3 spinners. These spinners get their data from a web-service by a method that takes about 1 minute to be completed.
I want to load the Activity first and after it is made visible, call that web-service method and load data. I have tested the following codes separately but none of them solved my problem. In these samples application goes into a black screen and when the web-service operation completed, it is made visible.
#Override
protected void onCreate() {
//.........
final Runnable r = new Runnable()
{
public void run()
{
loadMyData();
}
};
Utilities.performOnBackgroundThread(r);
}
#Override
protected void onResume() {
new Thread() {
#Override
public void run() {
loadMyData();
}
}.start();
super.onResume();
}
#Override
protected void onStart() {
if (comesFromOnCreateMethod)
{
final Runnable r = new Runnable()
{
public void run()
{
loadMyData();
}
};
Utilities.performOnBackgroundThread(r);
}
comesFromOnCreateMethod = false;
super.onStart();
}
#Override
protected void onResume() {
if (comesFromOnCreateMethod)
{
final Runnable r = new Runnable()
{
public void run()
{
loadMyData();
}
};
Utilities.performOnBackgroundThread(r);
}
comesFromOnCreateMethod = false;
}
If you are getting a black screen, then I would assume your code is being run on the UI thread and not on the background, causing the UI to hang until the work is completed.
One of the best solutions to doing background work is an AsyncTask. Using this, you can call it in your onCreate() method, and when its done, it will post a callback to the UI thread for you in which you can display you data.
If you want this method to run everytime this Activity displays, then call it in onResume(). Otherwise, call it in onCreate().
In your onCreate, make the async tasks as the others have advised. Make sure you generate the content for the app first and then call the asyncTasks. You can control your spinners from the callback.
First of all, you might want to increase your accept rate, 39% is pretty low.
Anyway, you might want to check AsyncTask, it should do the thing. http://developer.android.com/reference/android/os/AsyncTask.html
Typically, you will want to initialize in onPreExecute, do the networking in the doInBackGround, and set the result to the UI thread on the OnPostExecute. Hope this will help.
Use AssynchTask() and you should call super.onResume() or any lifecycle method in respective life cycle method first then other specific method you want to do....

Android: Implication of using AsyncTask to make repeated Ajax Calls

I need my Android app to periodically fetch data from a server using AJAX calls, and update the UI accordingly (just a bunch of TextViews that need to be updated with setText()). Note that this involves 2 tasks:
Making an AJAX call, and updating the UI once I receive a response - I use a simple AsyncTask for this.
Doing the above repeatedly, at regular intervals.
I haven't figured out an elegant way to achieve Point 2 above. Currently, I am simply executing the task itself from OnPostExecute(). I read on this thread at SO that I need not worry about garbage collection as far as the AsyncTask objects are concerned.
But I'm still unsure as to how I set up a timer that will fire my AsyncTask after it expires. Any pointers will be appreciated. Here is my code:
public class MyActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new AjaxRequestTask().execute(MY_REST_API_URL);
}
private void updateReadings(String newReadings) {
//Update the UI
}
class AjaxRequestTask extends AsyncTask<String, Integer, String> {
#Override
protected String doInBackground(String... restApiUrl) {
//Do AJAX Request
}
#Override
protected void onPostExecute(String result) {
updateReadings(result);
/*Is there a more elegant way to achieve this than create a new AsyncTask object every 10 seconds? Also, How can I update the UI if I create a timer here? */
new AjaxRequestTask().execute(MY_REST_API_URL);
}
}
}
Thanks in advance
EDIT:
I tried posting an answer but couldn't do it since I don't have the reputation to answer within 8 hours.
Well, so I found a solution. I'm not convinced however.
protected void onPostExecute(String result) {
updateReadings(result);
// super.onPostExecute(result);
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
new AjaxRequestTask().execute(MY_REST_API_URL);
}
},
TIMER_ONE_TIME_EXECUTION_DELAY
);
}
Are there any flip sides that I should be aware of when I use this? In particular, I am seeing lots of GCs happening in the LogCat. Also, I am wondering how an AsyncTask can be candidate for GC unless the onPostExecute() completes?
How can I "stop" the updates? One way I thought of was to make the very first AsyncTask instance as a member variable of the Activity. That way, I can invoke cancel(true) on it and hope that this will "stop" the tasks.
SOLUTION:
In case anyone is looking for something similar - none of the solutions I mentioned here work satisfactorily. They all suffer from OutOfMemory issues. I did not debug into the details of the OOM, but I suspect it could either be because of the recursion, or because of having HTTP-related objects as member variables in the AsyncTask rather than as members of the Activity (basically because of NOT reusing HTTP and other objects).
I discarded this approach for a different one - making my Ajax Calls endlessly in the doInBackground() of my AsyncTask; and updating the UI in onProgressUpdate(). That way I also avoid the overhead of maintaining too many threads or Handlers for updating the UI (remember UI can be updated in onProgressUpdate() ).
This approach also eliminates the need for Timers and TimerTasks, favoring the use of Thread.sleep() instead. This thread on SO has more details and a code snippet too.
Call postDelayed() on any View to schedule a hunk of code to be run on the main application thread after a certain delay. Do this in onPostExecute() of the AsyncTask to create and execute another AsyncTask.
You could use AlarmManager, as others have cited, but I would agree with you that it feels a bit like overkill for timing that occurs purely within an activity.
That being said, if the AJAX calls should be occurring regardless of whether the activity exists, definitely consider switching to AlarmManager and an IntentService.
I think the android way to do this is using AlarmManager. Or you can user a basic java Timer as well. I'd recommend AlarmManager.
Set it up to send some intent with a custom Action, and register a broadcastreceiver for it.
If the ajax calls are only executed in the activity you can just use a timer in the activity which starts the tasks.
Otherwise use a service which uses the AlarmManager and which connects to the gui via a broadcast.
The recommended way to do a repeated task, is via AlarmManager, as alluded to by Scythe. Basically it involves setting up a broadcast listener, and having AlarmManager fire off an intent to that listener at whatever interval you choose. You then would have your broadcast listener call out to the activity to run the AsyncTask. If you need a very tight timer (less than 5s calls I'd say), then you're better off using a Timer within a Service, and using AIDL to call back to the activity.
Instead of talking directly from the broadcast intent, you could also setup an IntentService which you can poke, and use AIDL to update the activity.
This is how I achieved it finally. Note that the AsyncTask cancel(true) method is useless in my scenario because of the recursion. I used what #CommonsWare suggested - used a flag to indicate whether any more tasks should be executed.
public class MyActivity extends Activity {
/*Flag which indicates whether the execution should be halted or not.*/
private boolean mCancelFlag = false;
private AjaxRequestTask mAjaxTask;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if(mAjaxTask == null){
mAjaxTask = new AjaxRequestTask();
}
mAjaxTask.execute(MY_REST_API_URL);
}
#Override
protected void onResume() {
super.onResume();
mCancelFlag = false; /*when we resume, we want the tasks to restart. Unset cancel flag*/
/* If the main task is Finished, create a new task and execute it.*/
if(mAjaxTask == null || mAjaxTask.getStatus().equals(AsyncTask.Status.FINISHED)){
new AjaxRequestTask().execute(TLS_REST_API_URL);
}
}
#Override
protected void onPause() {
mCancelFlag = true; /*We want the execution to stop on pause. Set the cancel flag to true*/
super.onPause();
}
#Override
protected void onDestroy() {
mCancelFlag = true;/*We want the execution to stop on destroy. Set the cancel flag to true*/
super.onDestroy();
}
private void updateReadings(String result) {
//Update the UI using the new readings.
}
class AjaxRequestTask extends AsyncTask<String, Integer, String> {
private AjaxRequestTask mChainAjaxRequest;
private Timer mTimer;
private TimerTask mTimerTask;
#Override
protected String doInBackground(String... restApiUrl) {
//Do AJAX call and get the response
return ajaxResponse;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "Updating readings");
updateReadings(result);
// super.onPostExecute(result);
if(mTimer == null){
mTimer = new Timer();
}
if(!mCancelFlag){/*Check if the task has been cancelled prior to creating a new TimerTask*/
if(mTimerTask == null){
mTimerTask = new TimerTask() {
#Override
public void run() {
if(!mCancelFlag){/*One additional level of checking*/
if(mChainAjaxRequest == null){
mChainAjaxRequest = new AjaxRequestTask();
}
mChainAjaxRequest.execute(MY_REST_API_URL);
}
}
};
}
mTimer.schedule(mTimerTask,TIMER_ONE_TIME_EXECUTION_DELAY);
}
}
}
}

Categories

Resources