Notifying activity of processed information in Fragment - android

Whats the recommaned approach to notifying the hosting activity of a fragment that performs some background processing, that its done. Assuming that the fragments are running some threads that are performing work outside of the main looper.
A simple callback won't do since:
The Activity could be detached due to screen rotation which would lead into a NullPointerException.
Polling from within the activity is just dumb
Only calling the activity once if attached and let the activity check after each onCreate call (i.e. due to screen rotation).
What I currently do but it seems wrong: Whenever the Fragment gets attached, it will check if the work is done and notify the activity via callback. If the fragment finishes the work it will also callback the activity (if attached).
Why I think is is wrong? Because I have some really ugly methods that check if the fragment is attached and also if the work is done in order to maybe call the callback. This becomes very stupid when an exception is raised during performing some work in the fragment and the activity is detached. If android decides to call onSaveInstance in the same moment I will have to put the Exception into the Bundle and deliver it later when the Activity and fragment is recreated from the saved state. Additionally I can run into a situation where a activity will received the same callback twice (once from checking the fragment and the second time when the fragments gets attached; this could happen when the application got saved and restored)
This generates so much code that, in my optinion, could be much more clear if activites won't get detached. That is why I hope I'm doing something wrong and hope someone will provide me with a better solution.

I would say you should use a service for your background processing but if you've chosen a fragment for a specific reason you can stick with that. The way you should notify the activity from either a fragment or a service that might have a different lifecycle from the activity would be through a BroadcastReceiver. With this method the activity can register and unregister a BroadcastReceiver during its own lifecycle callback. From the fragment or service you just send the broadcast and forget about it. If an activity is listening for the broadcast it will receive it, if not nothing will happen.

Related

I have a trouble in Fragment life cycle and need a resolution to come out of it

I have done ample research on this, and there is not one clear solution on the problem.
In the life-cycle, particularly in the Fragment life-cycle, following Exception comes any moment after onPause().
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
My logic says, that to continue with the current fragment, after it reaches this state, I have to restart the activity and again point back to the intended fragment using Intent.
I want to be clear on what is happening and what should be real solution to deal with it.
I need to know the pros and cons of this mechanism; its importance in Fragment or Activity life-cycle.
Also, if I am changing the Windows Feature in onCreate to not to go to sleep, unless if the user has manually pressed the home button, will still the activity will go to this state?
This exception happens when you're trying to add/remove/replace/interact in any other way with a Fragment inside the Activity when it's paused.
Which means Activity will not be able to restore it's state (restore the state of a Fragment which has been changed) if it will be destroyed right away.
Best solution here, is to check that Activity is NOT paused during the interaction with a Fragment.
Another option is to use commitAllowingStateLoss() to interact with Fragment transaction, with a risk of losing it's state.
See:
https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss()
In a perfect world you should analyze each crash carefully and add checks to verify that you interact with fragments only when Activity is up and running.
A better explanation is presented in a new Android developer reference and guide documents for using JetPack Life Cycle Listener.
https://developer.android.com/topic/libraries/architecture/lifecycle#kotlin
The library makes the components Activity Life Cycle aware. That means you do not require an abstract baseActivity class which overrides every life cycle callback, and record that state in a boolean variable. LifeCycle listener will do it for you.
All you have to do is stop introducing a new fragment or stop any Loader that updates the UI when its response returns. The right time to do this is before onStop or onSavedInstance state is called, and your components will be made aware of it.
It clearly states that after the onSavedInstancState or onStop is called the UI becomes immutable till the onStart of the Activity is called again. Sometimes you have to call restart the same activity using NEW TASK and CLEAR TASK flags using intent, when this state occurs and there is no chance that otherwise onStart is going to be called.
Happy Coding :-)

Setting data on Android retained Fragment before onCreate() has been called

I have an Activity that creates a retained Fragment in order to persist a long running network operation in the event that the Activity goes through any configuration changes during the long running operation. So basically I'm trying to use the retained Fragment like a Singleton that lives until Activity's onDestroy() is called.
My goal is to have Activity's onCreate() trigger the long running network operation because I only want it to run when the activity is created not every time it starts again (otherwise I'd put it in onStart()).
To do this I first create a retained fragment in Activity's onCreate, then use FragmentManager to add the retained fragment then I kick off the network call in Activity's onCreate method and pass the networking object to the retained Fragment to hold onto.
This works, however I'm concerned because if I log what's going on I can see that the Activity first sets data on the retained Fragment and then the retained Fragment's onCreate() method is called. This looks wrong and seems out of order, however it works.
Is it bad practice to utilize the retained Fragment instance before the fragment has run its onCreate() method?
EDIT
After reading the responses and thinking a bit more about this, I'm realizing that initiating the network call from Activity onCreate() although convenient, is risky to do. As noted in the responses there's a chance that the long running operation returns very quickly and attempts to manipulate the Activity's view which may not have been initialized yet. So for my specific case I am resorting to initiating the long running operation from Activity's onStart() method and then using the retainedFragment to cache the response. This way even if onStart() is called multiple times and attempts to kick off the long running operation again, the result from the first attempt will be cached and can be returned.
I will admit that's unusual, but I can't think of a reason off the top of my head why it would be bad. The advantage of a retained Fragment is the reference to the Fragment is not destroyed so the references it holds on to are also retained. onCreate() and onDestroy() are called once throughout the lifetime of the Fragment (when the Fragment is added and removed respectively).
The danger might be that the asynchronous operation finishes before onCreate() is called. Likewise, the operation could finish after onDestroy() is called and when you expected the Fragment to be running. There are some methods like Fragment#setArguments() that can not be called during some parts of the Fragment's lifecycle. If you were to call these methods when you expected the Fragment to be running, then you will get an Exception thrown. So you end up having to put in a bunch of checks like if(isAttached()) { /* do this */ }. Putting the operation in onCreate() will ensure that it at least started before the operation finished.
But if you are not actually relying on any of the Fragment's functionality, then it should be fine. The lifecycles are only meant to tell you what's going on with it.
As long as you are not relying on Activity/Fragment views being available while Fragment's onCreate, you will be fine:
Note that this can be called while the fragment's activity is still in the process of being created. As such, you can not rely on things like the activity's content view hierarchy being initialized at this point.
(https://developer.android.com/reference/android/app/Fragment.html#onCreate(android.os.Bundle)
Still, you need to secure possible edge case that long-running operation may be finished before Activity and Fragment are fully created (if you depend on their views, this might be an issue).
Therefore, think about what you will do when long-running operation has finished and the results need to be presented somewhere.

When is it safe to update the UI?

I feel like this should have been answered by the documentation in the Activity class, but I'm still not positive -- when is it actually safe to update the UI of an Activity (or a Fragment)? Only when the activity is resumed, or any point between being started and stopped?
For example, the Activity docs state:
The visible lifetime of an activity happens between a call to onStart() until a corresponding call to onStop(). During this time the user can see the activity on-screen, though it may not be in the foreground and interacting with the user. Between these two methods you can maintain resources that are needed to show the activity to the user. For example, you can register a BroadcastReceiver in onStart() to monitor for changes that impact your UI, and unregister it in onStop() when the user no longer sees what you are displaying. The onStart() and onStop() methods can be called multiple times, as the activity becomes visible and hidden to the user.
From that reading, I would assume that even if a foreground dialog is displaying, I can safely update UI elements behind it.
EDIT
To clarify: the reason I ask is having been bitten by errors when firing off an AsyncTask, and attempting to update the UI in the onPostExecute method. Even though it runs on the UI thread, the user has navigated away from that view and I would receive an exception. I'm starting a new project now and trying to establish some guidelines around better AsyncTask idioms.
I guess this comes down to what you mean by "safe" to update the UI. For elements on the Activity screen, they can be updated whenever, even if your Activity is not in the foreground (but make sure to update from the UI Thread).
However, the issue that will trip you up is saving state: onSaveInstanceState.
As you may know, the Activity that is in the background may be destroyed by the OS to free up memory. It will then be re-created when you come back to it. During this process, the onSaveInstanceState method will be called. If the OS does destroy the Activity, any changes you made to the UI State after the call to onSaveInstanceState will not be persisted.
For Fragments, you will actually get an IllegalStateException if you try to commit a FragmentTransaction after onSaveInstanceState. More info on that.
To summarize, you can update the UI of your activity at any point and try to gracefully handle the Fragment issues, but you may lose these updates when the Activity is restored.
So, you could say that it is only truly safe to update the Activity while it is in the foreground, or rather, before the call to onSaveInstanceState.
Edit in regards to Async Task onPostExectue
This is likely related to the issue I am referring to above with Fragment State Loss. From the blog post I linked to:
Avoid performing transactions inside asynchronous callback methods. This includes commonly used methods such as AsyncTask#onPostExecute() and LoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called. For example, consider the following sequence of events:
An activity executes an AsyncTask.
The user presses the "Home" key, causing the activity's onSaveInstanceState() and onStop() methods to be called.
The AsyncTask completes and onPostExecute() is called, unaware that the Activity has since been stopped.
A FragmentTransaction is committed inside the onPostExecute() method, causing an exception to be thrown.

ANR while trying to load Activity after FragmentDialogDismiss

I have an activity that sends a BC and waits 5 seconds for the response using AlarmManager.
Once i get the broadcast i:
1. remove the FragmentDialog using mDialog.dismiss();
2. start a new activity using getActivity().startActivity(myIntent);
I'm inside a Fragment (using TabFragment from the support library and MyTabActivity which i created).
The thing is i constantly getting balck screen and ANR, if i remove the line startActivity()
I'm not getting the ANR, the fragment does gets dismissed, but my activity doesn't show, i get the black screen and if i press it, ANR.
What i thought about is that the fragment manager started out working on the activity and then i started a new one that fucks up android, since i'm basing myself on fragment i really don't know how to get out of this mess, thought of using handler in the activity to post my activity start in 300 millis, this is a very very ugly solution.
Any nice ideas ?
the problem seems to be that i unbding a service twice in onStop of the activity due to the fact that once i call unbind ServiceDisconnected is NOT called and changes the state of my activity to unbonded (state i follow).
since onStop is never eding the original activity gets the UIthrehad and make the whole app stuck...

Updating Activity UI from Different Activity Method?

I have a tab widget where one of the tabs is a chat-type feature. I want to update the chat data at an interval (variable depending on whether the chat tab is active or not).
The best approach seemed to be using an AsyncTask in my main TabActivity class, as that would avoid any issues of the chat activity being destroyed while in the background, while an AsyncTask was running. I wanted to ensure that the Activity isn't destroyed and recreated, thus causing my AsyncTask to be unable to modify the actual active Activity's data.
However, now that my AsyncTask is in the TabActivity activity, I don't have a direct way to call my Chat's ListAdapter notifyDataSetChanged() from my onPostExecute() method anymore. Is there a way to get a reference to a given Tab's current Activity from the TabHost/TabActivity?
Or, alternatively, can I assume my chat activity will never be destroyed as a child activity of the TabActivity activity (well, never destroyed while the TabActivity is active at least), and then just put the AsyncTask in the Chat Activity?
Really the best way to do this is to have a thread that runs and periodically gets the chat data. Then from your UI you can poll the thread to get new messages. This way you can have the thread running no matter what happens to the Activity.
If you're using the Intent loading mechanism for your tabhost, then you should be safe in assuming the task won't get randomly killed any more than the TabHost itself (only paused), and you can safely modify views in it (as safely as you could from the TabHost, at least).
But beware: Any activity can be killed at any time (example: if a user clicks on a link in the tab that opens a new activity while your task is still running), including the tabhost itself, which can lead to ugly crashes when the task returns and tries to update the UI of the now-dead Activity. So make sure to either cancel your AsyncTasks on destroy, have the asynctasks check that your activity is still active before returning results (see WeakAsyncTask in the Android source tree), or use an Activity-independent (semi)persistent pollable background-thread solution like CaseyB suggests (cwac-bus is a nice premade solution in that vein).
Alternatively you could just have the AsyncTask's UI-updating code catch all exceptions (I've seen a few apps that do this) and fail silently, but that smells funny to me.
Lately I've used a modified version of the WeakAsyncTask in most places that checks that the activity is non-finished on returning, personally.

Categories

Resources