Update fragment from Activity from different thread - android

I'm very new to Androind, trying to figure how fragment and activity should be work together. I have a very ugly layout. 1 activity and 1 "root" fragment. When user click on left menu fragment are replaced by fragment manager.
expandableList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) {
LeftMenuItem group = groups.get(i);
String fragmentTag = group.getFragmentTag();
if (fragmentTag.equals(Fragment1.TAG)) {
Fragment1 fragment = (Fragment1) currentFragmentManager.findFragmentByTag(Fragment1.TAG);
if (fragment == null) {
fragment = new Fragment1();
}
FragmentTransaction ft = currentFragmentManager.beginTransaction();
ft.replace(R.id.root_frame, fragment, Fragment1.TAG);
ft.commitAllowingStateLoss();
} else if (fragmentTag.equals(Fragment2.TAG)) {
I assume code above is supposed to replace current fragment with new one. Fragments are always null actually. I do not know why.
In onCreateView of RootFragment Fragment1 is created by default.
if (savedInstanceState == null) {
Log.d(TAG, "savedInstanceState is null, creating Framgent1");
Fragment1 fragment = new Fragment1();
FragmentTransaction ft = currentFragmentManager.beginTransaction();
ft.replace(R.id.root_frame, fragment, Fragment1.TAG);
ft.commitAllowingStateLoss();
}
In onCreateView of rootFragment, rootFragment replaces itself with another Fragment1, which looks very ugly for me. Is it well known Androind pattern or just bad design ?
Let's assume that'm sending httpRequest from onCreateView of MyActivity using Volley. Once I received response I need to update Fragment1 UI from callback. How can I do it ?
Should I try to find fragment using findFragmentByTag in my activity and update UI directly ? Is http volley response is in the same thread ? If no, it is ok to update UI from different thread ?
Should I use a Handler class to send message from Activity to Fragment ?

Should I try to find fragment using findFragmentByTag in my activity and update UI directly ? Is http volley response is in the same thread ? If no, it is ok to update UI from different thread ?
findFragmentByTag will not be useful since as soon as replace is
called, the previous fragment is destroyed. If you have only few
fragment you want to switch, you can use hte below solution :
How can I switch between two fragments, without recreating the fragments each time?
Answer to second part of your question 1 :
Volley response is always called on the main thread to its perfectly
ok to update UI on the callback. You should never update UI elements
on any thread other than MAIN / UI thread.
Should I use a Handler class to send message from Activity to Fragment ?
You can choose to send the message by handler but if usually android documentation suggests to send communicate between activity and attached fragments via callbacks
https://developer.android.com/training/basics/fragments/communicating.html

Related

onCreateView from Fragment not called

I am refactoring an Android App to use fragments. I have a fragment which I add to the layout with transaction.replace method but whose onCreateView method is not called. Code looks as follows:
FragmentManager fragmentManager = m_Activity.getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
m_StartNewGameFragment = new StartNewGameToggleButtonsFragment();
fragmentTransaction.replace(R.id.bottom_pane, m_StartNewGameFragment);
fragmentTransaction.commit();
String winnerText = null;
if (isComputerWinner)
winnerText = m_Activity.getResources().getText(R.string.computer_winner).toString();
else
winnerText = m_Activity.getResources().getText(R.string.player_winner).toString();
m_StartNewGameFragment.updateStats(computerWins, playerWins, winnerText);
The method updateStats of fragment is as follows:
public void updateStats(int computer_wins, int player_wins, String winnerText) {
System.out.println("Update stats " + m_ComputerWins);
m_ComputerWins.setText(Integer.toString(computer_wins));
m_PlayerWins.setText(Integer.toString(player_wins));
m_WinnerTextView.setText(winnerText);
}
When updateStats is called m_ComputerWins is null and the program crashes. m_ComputerWins is initialized inside the onCreateView method of the fragment which seems not to be called.
Can anyone please help ?
Take global variables in your fragment class for computer_wins, player_wins, winnerText and init them inside updateStats method.
then inside onViewCreated() method, set values like
m_ComputerWins.setText(Integer.toString(computer_wins));
m_PlayerWins.setText(Integer.toString(player_wins));
m_WinnerTextView.setText(winnerText);
You never know when the fragments is created (which calls onCreate()), because fragment's life cycle differs from your activity's. In your code, you are trying to update fragment from activity. But in the way you have written your code, it is almost guaranteed that your fragment is not created yet.
Search for how to pass parameters to fragment or update fragment to find out the correct way of updating fragment from activity. You can find several related question, e.g. this one.
FragmentTransaction only runs on the next event loop. If you want to immediately run a fragment transaction, use fragmentTransaction.commitNowAllowingStateLoss();.
Please note that this won't actually work correctly after process death, which is why you should use the fragment.setArguments(Bundle method to pass initial argument values to a fragment.

Android Fragments sharing info ways

i have a internal discussion about what way is better to share info between fragments contents inside a controller activity. In a first classical way, you can set arguments when you are going to replace fragments as follows:
//Just now i'm inside Fragment 1 and i'll navigate to Fragment 2
Fragment newFragment = getFragmentManager().findFragmentByTag(Fragment2.TAG);
Bundle b = new Bundle();
b.putBoolean("test1", true);
// Create new fragment and transaction
if(newFragment==null)
newFragment = Fragment2.newInstance(b);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)//.setCustomAnimations(R.anim.enter_anim, R.anim.exit_anim)
.replace(R.id.fragment_place, newFragment, Fragment2.class.getName())
.addToBackStack(newFragment.getClass().getName())
.commit();
The newInstace method does as i meant above, so, with setArguments:
public static Fragment2 newInstance(Bundle arguments){
Fragment2 f = new Fragment2();
if(arguments != null){
f.setArguments(arguments);
}
return f;
}
But Fragment1 and Fragment2 they are both inside a ControllerActivity, so i can also think about a second way to share information obtained in Fragment1 towards Fragment2, through declaring attributes in the ControllerActivity, so i could do (declaring previously an object in the activity) as follows inside any fragment:
EDIT
public class ControllerActivity extends FragmentActivity{
int value = 5;
...
And then, inside my fragment:
((SplashActivity)getActivity()).value = 10; //i can assign or recover value when i desire
My question is what inconveniences would have doing as the second way.
Writing code using 2nd way is fast. But the problem is you have to cast the general Activity to the more specific SplashActivity in which the value variable exists. If you want to use the Fragment with another Activity, or you want a Fragment to be a general purpose UI component you have to use interface for passing the data.
As mentioned in comments, bellow links provide more details about interface/callback method:
android docs
video from slidenerd
Hope this answers your question.

Fragment and Host Activity Commnication

I have this design problem. I have an activity which hosts two fragment and any given point of time only one activity is visible.
Activity A hosts Fragment B and Fragment C
Host Activity A implements FragmentCommunicator interface and implement respond(int code) method using this method communicator both Fragment B and C can talk to host Activity.
Now here is the problem.
In onClick of Host activity I check certain condition and based on that I take decision which fragment to show.
public void onClick(View v) {
switch (v.getId()) {
case R.id.some_button
if(authNotedone)
showFragmentA();
else{
EDIT: //setting some properties before showing Fragment B
showFragmentB();
}
}
}
So far it works fine. If condition is true FragmentA will be visible with login form. After successful login I would like to show fragment B again. How can I achieve this.
What I have tried?
1) After successful login Fragment A send message to Host activity using Fragmentcommunicator's respond(code) method but it was ugly design as I have to either call performClick() or call showFragmentA() in respond method if code is success.
There could be multiple such conditions in my program How I can handle these neatly?
Use a interface as a call back to the activity. Once you get the message in the activity there is no need to click the button just replace the existing fragment in the container.
Implement the interface in the activity
FragmentB newFragment = new FragmentB();
Bundle args = new Bundle();
args.putInt("key", "message");
newFragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
// replace with fragmentb. no need to perform click again.
// based on the message you decide which fragment you want to replace with
transaction.commit();
You can find an example #
http://developer.android.com/training/basics/fragments/communicating.html
Do you want to navigate between fragments? If so, why don't you implement navigation tabs, have a look at this link.

Create a fragment and send data to it

Here is what I do to create a new fragment (called AlbumsTrackFragment) and replace the existing one :
AlbumsTrackFragment albumstrackfragment = new AlbumsTrackFragment();
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.layout_ipod, albumstrackfragment, "albumsTrackfragment");
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
albumstrackfragment.DoTask(data);
The problem is that when I can't call the method DoTask() and pass the new fragment some data because the app crashes, indeed I get : Content view not yet created
How can I wait that the commit was performed to call DoTask() method of my fragment ?
Thanks
You might consider passing in data to your fragment through DoTask and then wait until OnActivityCreated() is called to perform whatever action happens in DoTask. This way you are passing in your data but not doing anything until your view is loaded and won't crash.

Android: on screen orientation change fragment.getActivity is null

I'll try to explain my problem:
all my fragments are using setRetainInstance(true)
In my activity onCreate I'm doing this:
if (savedInstanceState == null) {
fragment = onCreatePane();
fragment.setArguments(intentToFragmentArguments(getIntent()));
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
trans.add(R.id.root_container, fragment, getFragmentTag());
trans.commit();
} else {
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
if ( fragment == null ) {
fragment = getSupportFragmentManager().findFragmentByTag(getFragmentTag());
}
if (fragment == null) {
fragment = onCreatePane();
fragment.setArguments(intentToFragmentArguments(getIntent()));
}
trans.add(R.id.root_container, fragment, getFragmentTag());
trans.commit();
}
So when I create the activity and savedInstance is null I create the Fragment, I set it's arguments, I begin the transaction and add my fragment to the transaction with it's own tag (to get it back later).
The user interact with the activity and change the orientation. The activity is destroyed and recreated (as normal activity lifelycle). So now it enter the else, the fragment is null and I do a fragment = getSupportFragmentManager().findFragmentByTag(getFragmentTag()); that returns the correct Fragment holded by the fragmentManager.
The problem is that this fragment hold a reference to the old Activity that has been destoyed so if I do a fragment.getActivity it returns null. How can I update the fragment reference to the acitivy to the new re-created Activity?
UPDATE: To be more precise I'm on the SearchActivity that call the onNewIntent when it get a new Search. So the actual interaction is this-> user do a search -> search is displayed correctly -> user change orientation, result is displayed correctly (if user interact with results they are fine) -> user do a new search from the search button and this call the SearchActivity's onNewIntent that dispatch the new intent to the fragment that has the search logic. Here it crashes because the reference to the activity is null
When and where are you calling getActivity()? The activity reference does get updated automatically, but not immediately. You should be safe to access it after onActivityCreated() was called.
just remove null check from else part if ( fragment == null ) { fragment = getSupportFragmentManager( ...... let update fragment with new one created on orientation change ......
If setRetainInstance(true) -- yes, it's the solution (to find fragment by tag, because instantiate it again -- is the wrong way -- there could be background routines in progress). Otherwise, if you have setRetainInstance(false) the problem can be back.
I had the similar one. I my case -- because I used Loader (in background). Solution was simple: to destroy fragment's loaders in onDestroy() method of the fragment.
In your baseAcvivity you can override onSaveInstanceStat to resolve this problem :
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//solution of fragment.getActivity() is null
outState.remove("android:support:fragments");
}

Categories

Resources