Restore fragment view on pressing the back button - android

I have an activity where I place fragment A. Then on clicking the fragment A, I replace the fragment A with fragment B. Now when I press the back button it shows fragment A. But the whole view is recreated. I am calling services in Fragment A so those services get called again. I want Fragment A to be restored without creating the view again. If service call is in process on Fragment A and I go to fragment B and come back, then it should resume the service call.
Please suggest me how to do this.

Option 1:
Set your services up in a retained fragment.
This blog provides a good overview on this topic. It talks about retained fragments in relation to AsycTasks, but the principle is the same.
Option 2:
In your Fragment lifecycle methods, put a check in to ensure the services are only started once.
Consider the following psudo-code:
Fragment A {
onCreate() {
boolean servicesStarted = false;
...
}
onResume() {
if (servicesStarted == false) {
startTheServices();
servicesStarted = true;
}
}

Related

Managing Fragments on Android Foldable

I am creating a new Android Project and soon android foldable devices will be launched. I have an Activity which has fragment called first fragment.
First Fragment has a button called first button which open second fragment which has a button called second and on click of second, third fragment opens.
Suppose user is in third fragment and user decides to unfold his device, will the user go back to fragment one or will he stay in fragment three. As far as I have understood from the Developer Summit, the activity will be destroyed and recreated when user unfolds his device so technically user goes backs to first fragment leading to poor user experience.
So my question is should I consider even using fragments?, If yes how to manage state so that user goes to the same fragment he was when he folds or unfolds his device.
Following is my code if I am changing fragments
private fun displayView(fragment: Fragment?, title: String) {
if (fragment != null) {
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
supportFragmentManager.beginTransaction()
.replace(R.id.framelayout_activity_main, fragment, title).commit()
}
}
In onCreate(), you only want to execute a FragmentTransaction if this activity is being
newly created, instead of being recreated from a configuration change. Or, more accurately,
you only want to execute a FragmentTransaction if you do not already have fragments in the state that you want them.
So, a typical approach is to see if you already have a fragment in your container:
override fun onCreate(state: Bundle) {
super.onCreate(state)
if (supportFragmentManager.findFragmentById(R.id.framelayout_activity_main) == null) {
// do something to show your fragment
}
// other good stuff goes here
}
On the first onCreate() invocation, findFragmentById() will return null, so you execute your code to display your first fragment. On a subsequent onCreate() invocation after a configuration change, Android will have already set up your fragment(s) for you by the time onCreate() is called. So, in that case, findFragmentById() will return something other than null, so you know that you already have a fragment in your container and do not need to do anything more.

Fragment does not call lifecycle methods

I've a fragment A. I add() it with tag like this:
fragmentTransaction.addToBackStack(special_tag);
Then I simply add() fragment B on top of fragment A. After that, I decide to remove fragment B and go back to fragment A using:
activity.fragmentManager.popBackStackImmediate(special_tag, 0)
When I reach the fragment A, it seems that fragment doesn't re-run it's lifecycle methods: onAttach(), onResume(), onCreate() ect.
Can someone explain this behavior and maybe suggest an alternative?
(I need to "refresh" the data when I come back to fragment A second time)
What is causing this result?
Is there a clean solution/work-around?
Update
Fragment B is GuidedStepFragment and does not have a .replace() function. I found that it has finishGuidedStepFragments(), but it behaves the same (it does not call fragment life cycle functions)
Situation (again):
Fragment A (Simple fragment) -> .add(Fragment B) (GuidedStepFragment) -> popBackStackImmediate() or finishGuidedStepFragments()
I add Fragment B like this:
GuidedStepFragment.add(activity.fragmentManager, fragmentB.createInstance())
Using fragmentTransaction.add(Fragment) doesn't remove Fragment A. What is actually happening is that Fragment A is still running behind Fragment B. Since Fragment A never stopped running, it's lifecycle has no need to retrigger.
Consider using fragmentTransaction.replace(Fragment) and replace the fragment in the container (fragment A) with fragment B. If you pop that transaction from the back stack, then Fragment A will reattach and follow your expected lifecycle.
Update
Since you seem to be using GuidedStepFragments from the leanback library, this is a little tricky. GuidedStepFragment actually performs replace(...) under the hood, but you're adding fragment B to a different container so the original behavior I mentioned doesn't apply.
I'm not super familiar with leanback (since it's usually only used for android tv), but I do know that you can at least do the following. If you keep track of your backstack size, when all of the GuidedStepFragments have been popped, you will have returned to your original fragment. For example, let's assume your backstack starts at zero:
activity.fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (activity.fragmentManager.getBackStackEntryCount() == 0){
// handle your updates
}
}
});
// the next line of code will add an entry to the backstack
GuidedStepFragment.add(activity.fragmentManager, fragmentB.createInstance());
// eventually when back is pressed and the guided fragment is removed, the backstack listener should trigger

Android Restoring order of fragments in the stack

I got 2 activities, A and B. Each activity is a container for fragments which are replaced with a FragmentTransaction.
I got an issue on some devices that when a user opens Activity B while he was in Activity A, the first activity is probably destroyed, which means that when a user clicks the back button, it makes the first activity recreated while in a normal device, it would just resume.
My main issue is that the user loses its fragment stack he had in the first activity. When the user opened the 2nd activity, he was already 3 fragments "deep" the first activity. How can I restore the stack and return the user to the point he's been before the first activity was destroyed?
This should be handled by the Android OS automatically. You can turn developer option "don't keep activities" on to always mimic this behavior (destroying your activity) when your activity goes to the background. After that you can start debugging. Some things to check:
In onCreate of the activity, are you calling the super onCreate with
the savedInstanceState?
If you put a breakpoint at the start of onCreate, when you "come
back" to the activity, is there a saved instance state?
Where are you creating the fragments? Are you re-creating them
manually (you shouldn't)?
Are your fragments hardcoded in the layout or replaced in the layout
(replacing a container view)?
* EDIT *
From your reply I derive that this is the problem, you say: "In the end of the onCreate I am replacing the fragment with a fragment transaction and thus load the first fragment of the app" => you should not do that when the savedInstanceState is not null. Otherwise you're destroying what is already there from the saved state.
Check here: https://developer.android.com/training/basics/fragments/fragment-ui.html
Notice the return when savedInstanceState is not null.
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}

How to restore fragment back stack with in an activity (After application is killed in background)

In an Android app-
Say I am in an Activity - MyActivity which holds one Fragment at a time.
First I loaded Fragment A to it (With no tags I added it to back stack of the FragmentManager)
Then at some point I loaded Fragment B (Again with no tags I added it to back stack of the FragmentManager)
Then at some point i loaded Fragment C (Again with no tags I added it to back stack of the FragmentManager)
I am using popBackStack() to enable back button behavior so whenever I press back from Fragment C the flow is like:
Fragment C -> Fragment B -> Fragment A -> Close MyActivity..
Everything is perfect :-)
But if I am in Fragment C and the app gets killed in background (I used "do not keep activity flag" from Settings)
and come back online Fragment C is loaded in MyActivity
but the FragmentManager's back stack contains only Fragment C..
The Back button is messing it up
Fragment C -> Close MyActivity..
Why is it so?
How to properly restore FragmentManager's back stack within an Activity?
Try using alwaysRetainTaskState on your root activity. Android automatically clears the Activity backstack because it assumes that it has been a long time since you used the app and that the user wants to start again from the start.
<activity android:alwaysRetainTaskState="true"/>
This setting will prevent that behaviour and it may follow that the behaviour is inherited by the Fragment Manager.
While developing your app, I recommend you to test restore/saved states of the activities, fragments with ADB:
Open app
Navigate between activities
Press home
ADB -> Kill (stop) app
Press the application stack (menu button from the device) and resume the application
This way you can debug the saved/restore states.
If you don't have a complex application, I suggest you to handle the saved/restore state in the activity:
private Fragment1 mFragment;
#Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
// ...
if (savedState == null) {
mFragment = new Fragment1();
getFragmentManger().beginTransacation().add(mFragment, TAG).addToBackStack(TAG).commit();
}
else {
mFragment = getFragmentMananager().findFragmentByTag(TAG);
}
}
If you have several Fragments or a ViewPager or nested fragments, then things can get really complicated. I suggest you to restart the whole application:
#Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
if (savedState != null) {
Intent intent = new Intent(ActivityMain.this, ActivityMain.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
return;
}
}
If you want to handle each saved/restore state, please read this post: http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
try this method
public void setRetainInstance (boolean retain)
Control whether a fragment instance is retained across Activity re-creation (such as from a configuration change). This can only be used with fragments not in the back stack. If set, the fragment lifecycle will be slightly different when an activity is recreated:
from developer website
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance%28boolean%29
Fragment C should never be loaded after your application dies. Do you have an Init Fragment in your application ? Ideally when you are implementing a pop of fragments there should be an Init screen. If the application dies or is killed for memory reasons you application should start from Fragment A (Init Fragment). Not from Fragment C.
If your problem demands this solution, then you should have to save each fragment persistently when a new fragment come on top. Which ideally means you are persisting your backstack in a preference or a database to achieve this.

Android: ActivityGroup -- How to "pop" a view

I have implemented a ActivityGroup in my TabView.
I can successfully add a view to the ActivityGroup from an activity in a tab.
How do I pop this new view off the stack to go back to the original view??
In the ActivityGroup I handle the BackButton with a Finsih() but the entire app goes away and the home screen is displayed. Note that the app is not killed, it's still running.
(Written in .NET)
public override void OnBackPressed ()
{
int length = mIdList.Count;
if (length > 1)
{
Activity current = LocalActivityManager.GetActivity (mIdList [length - 1]);
current.Finish();
}
base.OnBackPressed ();
}
(I'm coming from an iOS background)
First, I agree with aneal that you should try Fragments rather than activity groups. If you do, the following text from the Android on-line training class appears to address the issue:
Keep in mind that when you perform fragment transactions, such as replace or remove one, it's often appropriate to allow the user to navigate backward and "undo" the change. To allow the user to navigate backward through the fragment transactions, you must call addToBackStack() before you commit the FragmentTransaction.
When you add the view, call addToBackStack() before committing your "add fragment" transaction.

Categories

Resources