Any thoughts on why the fragment is not getting removed? This is an interface method being called from a 'AsyncTask' fragment with retaininstance(true). In the test of remove mTaskFragment is not equal to null so the log line prints.
#Override
public void onPostExecute(boolean success, String result, String message) {
Log.d(TAG, "onPostExecute()");
//Remove the Fragment if its still there
FragmentManager fm = getSupportFragmentManager();
mTaskFragment = (MyAsyncTaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
if (mTaskFragment != null) {
fm.beginTransaction().remove(mTaskFragment).commit();
}
mTaskFragment = null;
//Test of remove
mTaskFragment = (MyAsyncTaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT);
if (mTaskFragment != null) {
Log.d(TAG, "mTaskFragment is not null");
}
Related
I'm facing the problem about removing Fragment in SDK < 24.
removeFragment()
FragmentTransaction frgTrans = fragmentMng.beginTransaction();
MyFragment myFrg = (MyFragment) fragmentMng.findFragmentByTag(TAG_MY_FRAGMENT);
frgTrans.remove(myFrg).commit();
getFragment()
MyFragment myFrg = (MyFragment) fragmentMng.findFragmentByTag(TAG_MY_FRAGMENT);
if (myFrg == null ) {
// Do sth
}
// But I checked that myFrg is NOT NULL ???
Furthermore, this problem only happened in SDK < 24 ( Android 5,6 ).
What's difference things between Android SDK < 24 and 24 above ?
I also try to call commitNow() for execute synchronously but it's same problem.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
frgTrans.commit();
fragmentMng.popBackStack();
} else {
frgTrans.commitNow();
}
Anyone here has same problem ?
Update:
I also check Fragment hashcode I get before add and before remove. It's same so I can affirm that it's existing...
05-12 11:34:38.705 3916-3916/myapp.test E/FragmentControllerTest: hashcode before remove: 136290746
05-12 11:34:39.856 3916-3916/myapp.test E/FragmentControllerTest: hashcode before add: 136290746
Update code
Calling follow:
GotoActivity 1: AddMyFragment()
GotoActivity 2: (destroy Activity1 ) removeMyFragment()
BackToActivity1: AddMyFragment() (onResume)
Code:
private void addMyFragment() {
MyFragment myFrg = (MyFragment) mActivity.getSupportFragmentManager().findFragmentByTag(TAG_MY_FRAGMENT);
if (myFrg == null) {
try {
myFrg = new MyFragment();
FragmentTransaction frgTrans = mActivity.getSupportFragmentManager().beginTransaction();
frgTrans.add(R.id.my_fragment, myFrg, TAG_MY_FRAGMENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
frgTrans.commit();
} else {
frgTrans.commitNow();
}
} catch (Exception e) {}
} else {
Log.e(TAG, "hash code after remove: " + myFrg.hashCode());
}
}
private void removeMyFragment() {
MyFragment myFrg = (MyFragment) mActivity.getSupportFragmentManager().findFragmentByTag(TAG_MY_FRAGMENT);
if (myFrg != null) {
Log.e(TAG, "hash code after add: " + myFrg.hashCode());
try {
FragmentTransaction frgTrans = mActivity.getSupportFragmentManager().beginTransaction();
frgTrans.remove(myFrg);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
frgTrans.commit();
mActivity.getSupportFragmentManager().popBackStack();
} else {
frgTrans.commitNow();
}
} catch (Exception e) {}
}
}
I have two fragments in my activity. When I switch fragment use the code:
private void switchCourseFragment() {
if (mCourseFragment == null || getActivity().isFinishing()) return;
FragmentTransaction mTransaction = mFragmentManager.beginTransaction();
if (mFragmentLogin != null && mFragmentLogin.isAdded()) {
mTransaction.remove(mFragmentLogin);
}
if (!mCourseFragment.isAdded()) {
mTransaction.add(R.id.learncenter_contain, mCourseFragment).commitAllowingStateLoss();
}
}
But this is not what I expected. The mFragmentLogin is not been removed, but the mCourseFragment is been added.
You have to commit for it to take effect.
private void switchCourseFragment() {
if (mCourseFragment == null || getActivity().isFinishing()) return;
if (mFragmentLogin != null && mFragmentLogin.isAdded()) {
mFragmentManager.beginTransaction().remove(mFragmentLogin).commit();
}
if (!mCourseFragment.isAdded()) {
mFragmentManager.beginTransaction().add(R.id.learncenter_contain, mCourseFragment).commitAllowingStateLoss();
}
}
but why don't you use replace instead?
private void switchCourseFragment() {
if (mCourseFragment == null || getActivity().isFinishing()) return;
if (!mCourseFragment.isAdded()) {
mFragmentManager.beginTransaction().replace(R.id.learncenter_contain, mCourseFragment).commitAllowingStateLoss();
}
}
This assume that both your fragments are in the same container.
Is private copy ofBundle is passed to Fragment and the parent Activity?
I try to save key,value pair in onSaveInstanceState of Fragment and try to retrieve it in the onCreate of Activity. It is absent there.
But there also seems to be a connection between the two.
When I pass null to super.onCreate of Activity the Bundle passed to Fragment's onCreate is also null.
Bundle wich is sent to onCreate for Activity and Fragment are completely different. If you're sending null to super.onCreate -> activity will recreate all the fragments from the scratch. So your Fragment will receive null -> also. Cause this is NEW fragment
This is a part of code of FragmentActivity:
/**
* Perform initialization of all fragments and loaders.
*/
#SuppressWarnings("deprecation")
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
mFragments.attachHost(null /*parent*/);
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
mFragments.restoreLoaderNonConfig(nc.loaders);
}
if (savedInstanceState != null) {
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
mFragments.restoreAllState(p, nc != null ? nc.fragments : null);
// Check if there are any pending onActivityResult calls to descendent Fragments.
if (savedInstanceState.containsKey(NEXT_CANDIDATE_REQUEST_INDEX_TAG)) {
mNextCandidateRequestIndex =
savedInstanceState.getInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG);
int[] requestCodes = savedInstanceState.getIntArray(ALLOCATED_REQUEST_INDICIES_TAG);
String[] fragmentWhos = savedInstanceState.getStringArray(REQUEST_FRAGMENT_WHO_TAG);
if (requestCodes == null || fragmentWhos == null ||
requestCodes.length != fragmentWhos.length) {
Log.w(TAG, "Invalid requestCode mapping in savedInstanceState.");
} else {
mPendingFragmentActivityResults = new SparseArrayCompat<>(requestCodes.length);
for (int i = 0; i < requestCodes.length; i++) {
mPendingFragmentActivityResults.put(requestCodes[i], fragmentWhos[i]);
}
}
}
}
if (mPendingFragmentActivityResults == null) {
mPendingFragmentActivityResults = new SparseArrayCompat<>();
mNextCandidateRequestIndex = 0;
}
mFragments.dispatchCreate();
}
I'm having an issue restarting the fragment in my Activity. When the user completes a round, I need the fragment that is implemented by the activity to completely restart. The fragment has an if / else statement that checks to see if a round has already been played, and if so, it needs to reload the fragment with alternative values. I've been looking all over and can't seem to find much of anything on this, so any help is much appreciated!
public class BoardActivity extends Activity implements BoardFragment.OnFragmentInteractionListener {
public void gameTimer() {
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
TextView tv = (TextView) findViewById(R.id.RoundTimer);
tv.setText(String.valueOf(minutes) + ":" + String.valueOf(seconds) + ":" + String.valueOf(milliseconds));
milliseconds -= 1;
if (milliseconds == 0) {
tv.setText(String.valueOf(minutes) + ":" + String.valueOf(seconds) + ":" + String.valueOf(milliseconds));
milliseconds = 1000;
seconds = seconds - 1;
}
if (seconds == 0) {
timerout = "Out of Time!";
tv.setText(String.valueOf(timerout));
}
if (timerout == "Out of Time!") {
Model.gameCounter = Model.gameCounter + 1;
nextRound();
}
}
});
}
}, 0, 1);
}
private void nextRound(){
//redirect to next round
if (Model.gameCounter <= 1 && Model.gameCounter < 4) {
Log.d(TAG,"new round");
Fragment frg = null;
frg = getFragmentManager().findFragmentByTag("BoardFragment");
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(frg);
ft.attach(frg);
ft.commit();
} else {
//eventually add score screen
Log.d(TAG,"same round");
}
}
The documentation on attach and detach does say that the view hierarchy get destroyed and rebuilt. However, the state of the views is maintained, and so it should be expected to seem as if nothing has happened (which it sounds like what you are seeing). The docs say it is the same thing that happens when the fragment goes on the back stack.
Thus I think you have two options for "restarting" the fragment.
Option 1: Create a new fragment each time.
private void nextRound(){
//redirect to next round
if (Model.gameCounter <= 1 && Model.gameCounter < 4) {
Log.d(TAG,"new round");
Fragment frg = null;
frg = getFragmentManager().findFragmentByTag("BoardFragment");
Fragment newfrg = new BoardFragment();
final FragmentTransaction ft = getFragmentManager().beginTransaction();
if (frg == null) {
ft.add(R.id.board_container, newfrg, "BoardFragment");
} else {
ft.replace(R.id.board_container, newfrg, "BoardFragment");
}
ft.commit();
} else {
//eventually add score screen
Log.d(TAG,"same round");
}
}
Option 2: Write a method in BoardFragment to reset the board for the next round, and call that when you are restarting.
private void nextRound(){
//redirect to next round
if (Model.gameCounter <= 1 && Model.gameCounter < 4) {
Log.d(TAG,"new round");
BoardFragment frg = (BoardFragment)
getFragmentManager().findFragmentByTag("BoardFragment");
frg.restart();
} else {
//eventually add score screen
Log.d(TAG,"same round");
}
}
I'm trying to use the Facebook 3.0 SDK's login by following this guide from the facebook developer's:
https://developers.facebook.com/docs/tutorials/androidsdk/3.0/scrumptious/authenticate/
my problem is that when the user clicks the login button, my activity closes and Facebook's process dies.
this is the logcat from Android Studio:
08-20 12:17:40.124 353-353/system_process I/ActivityManager: START u0 {act=SSO_WITH_FALLBACK cmp=com.my.app/com.facebook.LoginActivity (has extras)} from pid 30362
08-20 12:17:40.434 353-370/system_process I/ActivityManager: Displayed com.my.app/com.facebook.LoginActivity: +268ms
08-20 12:17:44.094 353-546/system_process I/ActivityManager: Start proc android.process.acore for content provider com.android.providers.contacts/.ContactsProvider2: pid=30500 uid=10014 gids={50014, 3003, 1015, 1028}
08-20 12:17:44.134 30500-30500/android.process.acore E/Trace: error opening trace file: No such file or directory (2)
08-20 12:17:45.494 353-12974/system_process I/ActivityManager: Process com.facebook.katana:dash (pid 30233) has died.
Any suggestion on my problem?
EDIT:
Here's the code of the main activity
public class MainActivity extends FragmentActivity {
public static final boolean D = SystemConstants.ACTIVE_DEBUG;
public static final String TAG = "MainActivity";
private static final int SPLASH = 0;
private static final int SELECTION = 1;
private static final int FRAGMENT_COUNT = SELECTION +1;
private Fragment[] fragments = new Fragment[FRAGMENT_COUNT];
private boolean isResumed = false;
private UiLifecycleHelper uiHelper;
private Session.StatusCallback callback =
new Session.StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
onSessionStateChange(session, state, exception);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
uiHelper = new UiLifecycleHelper(this, callback);
uiHelper.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getSupportFragmentManager();
fragments[SPLASH] = fm.findFragmentById(R.id.splashFragment);
fragments[SELECTION] = fm.findFragmentById(R.id.selectionFragment);
FragmentTransaction transaction = fm.beginTransaction();
for (Fragment fragment : fragments) {
transaction.hide(fragment);
}
transaction.commit();
}
/**
* Configure files destinations.
*/
private void configureEnvironment() {
File sd = Environment.getExternalStorageDirectory();
if (sd.canWrite()) {
File destination = new File(sd, SettingConstants.BASE_DIR);
if (!destination.mkdir() && !destination.isDirectory()) {
Log.e(TAG, "Unable to create Base Directory.");
Tracking.sendException(new IllegalStateException("Unable to create Base Directory."));
}
File audio = new File(sd, SettingConstants.AUDIO_DIR);
if (!audio.mkdir() && !audio.isDirectory()) {
Log.e(TAG, "Unable to create Audio Directory.");
Tracking.sendException(new IllegalStateException("Unable to create Audio Directory."));
}
File avatar = new File(sd, SettingConstants.AVATAR_DIR);
if (!avatar.mkdir() && !avatar.isDirectory()) {
Log.e(TAG, "Unable to create Avatar Directory.");
Tracking.sendException(new IllegalStateException("Unable to create Avatar Directory."));
}
File image = new File(sd, SettingConstants.IMAGE_DIR);
if (!image.mkdir() && !image.isDirectory()) {
Log.e(TAG, "Unable to create Image Directory.");
Tracking.sendException(new IllegalStateException("Unable to create Image Directory."));
}
File video = new File(sd, SettingConstants.VIDEO_DIR);
if (!video.mkdir() && !video.isDirectory()) {
Log.e(TAG, "Unable to create Video Directory.");
Tracking.sendException(new IllegalStateException("Unable to create Video Directory."));
}
}
}
/**
* Shows a fragment
* #param fragmentIndex
* #param addToBackStack
*/
private void showFragment(int fragmentIndex, boolean addToBackStack) {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
for (int i = 0; i < fragments.length; i++) {
if (i == fragmentIndex) {
transaction.show(fragments[i]);
} else {
transaction.hide(fragments[i]);
}
}
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.commit();
}
/**
* called due to session state changes. The method shows the relevant fragment based on the person's authenticated state.
* #param session Facebook Session
* #param state Facebook login state
* #param exception Eventual exception
*/
private void onSessionStateChange(Session session, SessionState state, Exception exception) {
// Only make changes if the activity is visible
if (isResumed) {
FragmentManager manager = getSupportFragmentManager();
// Get the number of entries in the back stack
int backStackSize = manager.getBackStackEntryCount();
// Clear the back stack
for (int i = 0; i < backStackSize; i++) {
manager.popBackStack();
}
if (state.isOpened()) {
// If the session state is open:
// Show the authenticated fragment
showFragment(SELECTION, false);
} else if (state.isClosed()) {
// If the session state is closed:
// Show the login fragment
showFragment(SPLASH, false);
}
}
}
/**
* case where fragments are newly instantiated and the authenticated versus nonauthenticated UI needs to be properly set.
*/
#Override
protected void onResumeFragments() {
super.onResumeFragments();
Session session = Session.getActiveSession();
if (session != null && session.isOpened()) {
// if the session is already open,
// try to show the selection fragment
showFragment(SELECTION, false);
} else {
// otherwise present the splash screen
// and ask the person to login.
showFragment(SPLASH, false);
}
}
#Override
protected void onStart() {
super.onStart();
Tracking.startActivityTracking(this);
}
#Override
protected void onStop() {
super.onStop();
Tracking.stopActivityTracking(this);
}
#Override
public void onResume() {
super.onResume();
uiHelper.onResume();
isResumed = true;
}
#Override
public void onPause() {
super.onPause();
uiHelper.onPause();
isResumed = false;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
uiHelper.onActivityResult(requestCode, resultCode, data);
}
#Override
public void onDestroy() {
super.onDestroy();
uiHelper.onDestroy();
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
uiHelper.onSaveInstanceState(outState);
}
}
EDIT 2:
Tried on the emulator gives the same error, given that the facebook APK is not installed it shows a webview, asks for login and then closes the activity.
I've added method tracing logging, the last call inside MainActivity is onDestroy...
Just found the solution to this problem.
Make sure that you don't use
android:noHistory="true"
in the manifest, relative to the MainActivity (In the given sample).