I just saw that onActivityCreated() is going to be deprecated in future. I try to implement LifecycleOwner and LifecycleObserver pattern but I'm not quite sure about what I'm doing here.
I'm using NavigationComponent, which meens :
I have a MainActivity
I have a MainFragment, instanciated as the home fragment
I have multiple fragments that can be accessed from this home fragment
For some reasons I need to know when activity is created from all of these fragments (MainFragment and sub fragments)
From what I've seen until now, I need to :
In the MainActivity, getLifecycle().addObserver(new MainFragment()). And do this for all sub fragments (which is verbose for nothing)
In fragments, implements LifecycleObserver and
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
private void onCreateEvent() {
Timber.i("%s MainActivity created", TAG);
}
This seems to work well, but I have some questions :
The syntax addObserver(new MainFragment() disturbs me. It looks like we are creating a new fragment instance, while the fragment is normally instantiated with the navigation defined in the navGraph.
As I said before, if I have my MainFragment with 10 sub fragments, I'll have to declare 11 observers ? Weird
Do I have to clear these observers at some point in the activity lifecycle ?
What is the proper way to implement it ?
EDIT 1:
To answer the question why I need to know when the activity is created :
I need this because I need to access my MainActivity viewmodel (new ViewModelProvider(requireActivity()).get(ViewModel.class). To call requireActivity() or getActivity() I need to know when the activity is created (was easy with onActivityCreated()).
Databinding is implemented with my MainActivity and this viewmodel. The layout of this activity is hosting a loader to show when network requests are performed.
I can perform requests from the MainFragment and from the sub fragments. When I perform a request from one of these fragments I need to enable this loader view, and when I got datas back I need to hide this loader.
And yes, all these fragments are in the graph
You have never needed to wait for onActivityCreated() to call requireActivity() or getActivity() - those are both available as soon as the Fragment is attached to the FragmentManager and hence can be used in onAttach(), onCreate(), onCreateView(), onViewCreated() all before onActivityCreated() is called.
This is one of the reasons why onActivityCreated() was deprecated - it actually has nothing to do with the activity becoming available to the Fragment, nor does it have anything to do with the activity finishing its onCreate() (it, in fact, can be called multiple times - every time the Fragment's view is created, not just once after the first time the Activity finishes onCreate()).
As per the deprecation notice:
use onViewCreated(View, Bundle) for code touching the Fragment's view and onCreate(Bundle) for other initialization.
Those are the recommended replacements, depending on whether the code you had in onActivityCreated() was accessing the Fragment's views or not.
Once you realize that requireActivity() can be called in onAttach(), etc., the rest of the deprecation notice makes more sense:
To get a callback specifically when a Fragment activity's Activity.onCreate(Bundle) is called, register a LifecycleObserver on the Activity's Lifecycle in onAttach(Context), removing it when it receives the Lifecycle.State.CREATED callback.
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
// Register a LifecycleObserver on the Activity's Lifecycle in onAttach()
requireActivity().getLifecycle().addObserver(this);
}
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
private void onCreateEvent() {
// Remove the LifecycleObserver once you get a callback to ON_CREATE
requireActivity().getLifecycle().removeObserver(this);
// Then do your logic that specifically needs to wait for the Activity
// to be created
Timber.i("%s MainActivity created", TAG);
}
But, as mentioned above, this is not what you should be doing if you are trying to access a ViewModel at the activity level.
Related
I'm like an intermediate in android programming. I decided to take a deep dive into the Activity lifecycle methods and I realised something, like why do methods Toasts.show() and many other methods get called again when the activity is resumed. If the methods are in the onCreate so why then if you like go to another activity and return it will still give you a Toast message. Let me give an example.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
{Your Initialization go here }
//Toas message
Toast.makeText(code).show();
}
}
So imagine you leaving this activity for another one and then coming back... why does it still show the Toast message. Because since the lifecycle is
OnCreate
onResume
onStart
onPause
onstop
onDestroy
And when you come back to your MainActivity it calls the
onResume
onStart.
So if onCreate is not called...So how does the Toast message show.
Please someone should help me answer this I've searched all day but couldn't find answers.
accoording to lifecycle of activity https://developer.android.com/guide/components/activities/activity-lifecycle, if there is no memory engouh the app process will kill, so when you back to your activity on created will call another time.
the image below for clarification:
When your activity goes in the background, and memory is required for other apps. It is possible that the system frees up space even though your activity is in the background with ONPAUSED state. and now if you navigate to your activity, since the saved instance is removed due to memory requirements. instead of ONRESUME, ONSTART is called. you can read more about it here: https://developer.android.com/guide/components/activities/activity-lifecycle
and see this image for better understanding: https://developer.android.com/guide/components/images/activity_lifecycle.png
This uncertainty is one of the reasons why we now mostly use view models for a sizeable app.
I have two fragments.
There is some code in the onCreate() of fragment1.
When I'm in fragment2 and press back, it goes to fragment1 and start from onCreateView(). But I need the code that is in the onCreate() to run.
In Activity:
getSupportFragmentManager().beginTransaction().
replace(R.id.host_fragment, new Fragment1()).commit();
In fragment1 I have button that navigate to fragment2:
getParentFragmentManager().beginTransaction().addToBackStack("").
replace(R.id.host_fragment,Fragment2).commit();
And I don't have any code for onBackPressed() in activity.
The activity that has fragments, It's not MainActivity.java
Thank you.
I think, you need not do that, it is already okay. When you put fragment1 into backstack and then press back, onCreateView() of fragment1 is called only if that previously instantiated fragment1 instance is in memory. If not in memory, all the fragment lifecycle callbacks are called accordingly, including onCreate().
So, if you make your non graphical initializations in onCreate(), they are already initialized, when you press back; or at least be initialized if it is created as new. All these will be managed by fragment manager.
Is there any reason that you put your code inside OnCreate()?
if no then put your code inside OnCreateView() because OnCreateView() is called right after OnCreate(). so if you want to call that code on fragment creation it's guaranteed that your code is called.
also if you look at fragment lifecycle you can see that if a fragment is returned from backstack, its OnCreateView() method will be called. so when you come back from your second fragment, your code will run.
Simple workflow is move your code which need to execute every time from onCreate() method to onResume().
onResume() calls every time when user focus on the screen. Or from background to foreground.
Thank you all for the answers.
First I thought my problem was just because of onCreate(), but after I put my code inside the onCreateView(), my problem was still remains.
So, I decided to restart the fragment when Back Pressed. So I came up with this solution:
In the activity:
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
getSupportFragmentManager().beginTransaction().
replace(R.id.host_fragment, new Fragment1()).commit();
} else {
super.onBackPressed();
}
}
Is that efficient?
I am creating an app which uses a fragment.
Say you have an Activity and a Fragment.
Activity calls the Fragment and if I press one of the positive/negative buttons on it, I want to send an order from the Fragment to the Activity to call up the camera.
So it will once return to the Activity and then it calls up the camera.
Do you have any suggestions?
Create a method in the activity like
void openCamera(){
//write open camera code here
}
Just call the open camera (YourActivityClassname)getActivity().openCamera();
For example, your activity name is MyActivity the code is :
((MyActivity)getActivity()).openCamera();
call from Fragment
There are possible solutions that I can think of. First one is using an interface and second one which I can think of is using viewmodel. Once you press positive or negative you can update your viewmodel from fragment and can listen it inside Activity or fragment which ever you are using. But I think for this interface will be perfect as you want to listen it into activity and could have used viewmodel approach if you were doing fragment to fragment transaction.
I have a TextView in my MainActivity and a Button which is in a Fragment attached to the MainActivity. When I click the Button I want the TextView to say, "Button Clicked".
Is this possible?
I know two Fragments attached to the same Activity can easily communicate with each other and send data to each other. But can an object in the Fragment send data to an object in an Activity
Is it better programming to make the TextView its own Fragment and attach it to the Activity? Then I can simply have the two fragments send data to each other.
Sorry if this isn't a proper type of question for StackOverflow. I am new to Fragments and have not been able to find a clear explanation on this issue.
Thanks in advance!
The currently accepted answer (to use a static method in the Activity) is both odd and arguably "wrong".
The use of the static method is odd because there's just no need for it to be static.
It's wrong because the Fragment must have knowledge of the particular Activity in which it is hosted. This is "tight coupling", and also makes it so that the fragment is not re-usable.
There are two common solutions to this issue:
Create an interface containing the methods in the Activity that can be called by the fragment. Implement that interface in the Activity (all Activities that use the fragment), and then in the Fragment, use getActivity() to get the Activity, and cast it to the interface. In this pattern, one also typically checks (using 'instanceof') whether the Activity implements the interface, and throws a RuntimeException if it does not.
Use an Event Bus (e.g. Square's Otto, GreenRobot's EventBus) to communicate between the Fragment and it's parent Activity. I feel
that this is the cleanest solution, and completely abstracts the
Fragment from it's Activity.
You can create a static method inside your Activity which will have the TextView inside it. And when you need updatation just call it from fragment.
something like:
In Activity:
public static void updateText(){
//your textview
text.setText("Button Clicked");
}
Just call it when you will click on the Button from fragment.
something like:
From Fragment:
//Inside Button click listener
MainActivity.updateText();
Not tested, but hope this approach will work.
Have you tried the getActivity() method to retrieve a reference to the parent activity and use a method to set the data, something like:
// public final Activity getActivity ()
MyActivity activity = (MyActivity) getActivity();
activity.setText("...");
I may be wrong but I would try that.
I have set up 1 Main Activity (that extends Activity class) and 2 Fragments (that extends the fragment class).
I've tried setting up an Interface, which is implemented by the 2 fragments. And each fragment implements the particular function from the interface like this:
public stopMusic()
{
mediaplay.release(); // here, the mediaplay object belongs to only the respective fragment
}
Now, I know doesn't work, because the MediaPlayer object is not common to both fragments, i.e it's not being shared among them.
I'm looking to release the mediaplayer object streaming a file in Fragment1.java, if I hit a button from another fragment, like Fragment2java. Likewise, to release the mediaplayer object streaming a file in Fragment2.java, if I hit a button from Fragment1.java.
How can I make this happen? An example code would really help.
Let's say fragment A is the controlling fragment and fragment B is the media player. All communication should be done via the parent Activity. So in fragment B you create 'public interface(s)' which the parent activity implements, then the parent Activity should call the method(s) in fragment B.
Also depending on what you are really doing with the media player or whatever, does that belong in the background as opposed to fragment B?
Note: fragments should be loosely coupled and never communicate from fragment to fragment, always communicate via the parent Activity.
Hope this helps.