Android Activity with FragmentTabHost - going back from details calls "onCreate" in MainActivity - android

I am facing some strange behaviour on a Samsung Alpha (4.4.4). I have built an App with a MainActivity that has a FragmentTabHost with 4 different Fragments (Tabs). On each of these tabs you can search for different things, and when tap on one of them a new activity is being called and the details are being shown.
Now, when running the App on my Samsung S3 (4.2) and the emulator (4.4, Samsung S5) everything works fine, that means, when I click on the details and go back to my MainActivity I can still see my list entries (and all the other fragments are presistent in their state). Now, when I run my app on my gilfriends phone, Samsung Alpha 4.4.4, every time I turn back from the details activity onCreate is being called in my **MainActivity** and that of course means that all my fragments are being created again (so they are empty). How is it possible that the phones behave different and what can I do to to save my data ? I know that I can save my list entries and the restore them (and then I would have to restore all my fragments every time somebody clicks on the details), but I think, since it is working on my phone, maybe it is a flag or something I have to set.
This is how I have been restoring my lists after navigating back to my activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
registerForContextMenu(getView());
if (mPersonList == null)
mPersonList = new ArrayList<Person>();
mListView = (SwipeListView)getActivity().findViewById(R.id.liste_personen);
if (mPersonAdapter != null)
mPersonAdapter = new ArrayAdapterPerson(getActivity(), R.layout.zelle_person, mPersonList,mPersonAdapter.getGefilterteListe());
else
mPersonAdapter = new ArrayAdapterPerson(getActivity(), R.layout.zelle_person, mPersonList,mPersonList);
mListView.setAdapter(mPersonAdapter);
}
But since the activity, and so the fragment, is being newly created mPersonList is always null

Related

Android activity loses data when switching between apps

I'm writing my first Android app. Essentially it shows and controls the score of a straight pool match. I've read articles about the lifecycle of an activity but I still have a problem I don't understand:
When I switch back to my app with a running game (so a score different from the initial 0:0 is shown) the activity sometimes loses its state and shows 0:0 instead of the score when I left the app. I overloaded the methods onSaveInstanceState and onRestoreInstanceState. The former get's called when I press the home button of my device. The latter never gets called. I've read that the method only gets called when onCreate is called. The onCreate method doesn't get called although the app needs longer than usual to reload after switching to some other apps in between. So I think the activity does get rebuild but obviously not by onCreate and the saved score is not loaded by onRestoreInstanceState.
Can you explain me what's happening and how to achieve the desired behaviour? Thank you very much!
edit: I was asked to post my onCreate() and onSaveInstanceState() methods. I tried to shorten them in a useful way. Please tell me If there is anything unclear or missing.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final GameSetting gameSetting = getGameSettings();
final GameData gameData = new GameData(gameSetting.getLeague());
game = new GameLogic(gameData);
scoreViews.put(PlayerId.PLAYER_A, (TextView) findViewById(R.id.playerAScore));
}
#Override
protected void onSaveInstanceState(#NonNull final Bundle outState) {
GameDataInstanceSupplier.saveInstance(game.getGameData(), outState);
// inside the method the data gets saved like
// outState.putInt(STATE_SCORE_A, data.getScore(PlayerId.PLAYER_A));
super.onSaveInstanceState(outState);
}
#Override
protected void onRestoreInstanceState(#NonNull final Bundle inState) {
super.onRestoreInstanceState(inState);
GameDataInstanceSupplier.restoreInstance(game.getGameData(), inState);
// inside the method the data gets loaded like
// data.setScore(PlayerId.PLAYER_A, inState.getInt(STATE_SCORE_A));
}
I put a "debug output" inside the onCreate() method and it did not appear in the debug log. The same was true for onRestoreInstanceState(). A message was printed inside onSaveInstanceState() when I pushed the home button.
I think your app is not the problem, you see, I came to find this page because I have the same problem switching aplications in my android phone, it's specially noticiable when usig 2 factor authentication and have to switch to text message, mail or other app to get the sent code, when switch back to the code input screen on the browser or app (teams for example) it is no longer there anymore and it has return to the begining, the login screen for example. This is very annoying because I have disable every thing that has to do with energy savings but still have the issue. I discovered that if I open first the message app and then do the authentication while switching very quickly just to see the code in 1 or 2 seconds and then return sometimes it works. It could be memory issue, or may be an unfulfilled politic on the energy savings. I think is probably the last because other explicitly stated properties like setting the screen brightness automatic adjust off and set to maximun works for a while and then returns to a default. It may even be a planned obsolescence. My phone is a Huawei P smart 2019 (POT-LX3) with android 10. I'll may be try a factory reset. Any suggestions are welcome.

confused about android example code

I'm looking over some code on the android developer's site and have a quick question about the example show here - http://developer.android.com/guide/components/fragments.html
In particular, I'm looking at this piece of code -
public static class DetailsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// If the screen is now in landscape mode, we can show the
// dialog in-line with the list so we don't need this activity.
finish();
return;
}
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
}
}
What is the point of the second if statement -
if (savedInstanceState == null) {
I can't find any situation where this if statement wouldn't be true. I've tested this code by adding an else statement and setting a breakpoint in it. I could not get to that breakpoint no matter I tried. So why even bother with an if statement? Why not leave it out all together?
There are situations in which your Activity is stopped by the Android operating system. In those cases, you get a chance to save the state of your Activity by a call to [onSaveInstanceState](http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle). If after this, your Activity is started again, it'll be passed the Bundle you created so that you can restore the state properly.
You have to look at the complete example code. With this part it makes sense.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
If you start your Activity the first time the Bundle savedInstanceState will be null and the body of the if statement will be executed. If onSaveInstanceState is called, because you navigated away from the Activity, the Bundle isn't null anymore and the if body will be not executed.
If your app was paused/killed, etc and you saved state by onSaveInstanceState then
savedInstanceState will contain the state of your app that you saved. Otherwise it will be null.
Apparently this was added to the example for future expansion on this code. While it has absolutely no functionality as it stands right now, if this activity were to launch another activity and get killed while the new activity had focus, this code would rebuild the activity when the user hits the back button, rather than rebuilding from scratch.

onClick launches Activity incorrectly (in 2.3.6)

I'm working on an interface that provides a set of multiple Button objects, each of which has attached the same OnClickListener. When said Buttons are clicked, they should launch an Activity, as specified in onClick.
Here is my code for reference:
public class Calcs extends SherlockFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
CalcLoader buttonListener = new CalcLoader(getActivity());
LinearLayout buttons = (LinearLayout) v.findViewById(R.id.calculatorlist); // v is the inflated View
for (int i = 0; i < buttons.getChildCount(); i++) {
View b = buttons.getChildAt(i);
if (b instanceof Button) {
((Button) b).setOnClickListener(buttonListener);
}
}
// Test Code: Location 1
Intent i = new Intent(getActivity(), MyCalcActivity.class);
getActivity().startActivity(i);
// ...
}
private class CalcLoader implements OnClickListener {
private Activity mOwner;
public CalcLoader(Activity owner) {
mOwner = owner;
// Test Code: Location 2
Intent i = new Intent(mOwner, MyCalcActivity.class);
mOwner.startActivity(i);
}
public void onClick(View v) {
if (v instanceof Button) {
// Actual Code: Location 3
Intent i = new Intent(mOwner, MyCalcActivity.class);
mOwner.startActivity(i);
}
}
}
}
Despite this, however, I'm getting some odd behavior. In the above code, I've placed some startActivity tests, labelled locations 1 and 2. In both cases, the Activity launches correctly, and all is well!
However, at location 3, where the working code should execute, I get some strange behavior from the launched Activity:
At first, the Activity is launched just fine. It displays a single text field and it is focused, with the soft keyboard coming up. This is correct.
Now, when I click the back button, the keyboard closes. This is correct.
Click back again, and the field loses focus. This should NOT happen. Instead, the Activity SHOULD close and return to the previous one.
Click back again, and the entire app closes (instead of returning to the previous Activity). Obviously, this should NOT happen.
To reiterate, when the Activity is started from location 1 or 2, everything functions correctly; the back stack is correct and returns to the initial Activity properly.
What is going wrong here? Why, when I start my Activity from onClick, does it fail, while it works from any other location?
Update: Saving the Intent in the constructor and reusing it in the onClick method produces the same glitched result, as does starting the Activity from the UI thread.
Second update: Making the text field unfocusable had no effect on the glitch; the back button still closed the app. Additionally, running in the 2.3.3 emulator had the same result. Oddly, though, after the second back button press (the text field losing focus), if you wait ~3 seconds, the Activity closes and returns to the main one.
Third update: No key events (onKeyDown or onBackPressed) are fired for the back button that takes focus from the text field. Additionally, if you interact with the Activity after the text field loses focus, it shows the animation of loading a new Activity of the same type, but the glitch is present here as well.
This appears to be an OS-level issue, found in Android 2.2 (API 8), 2.3.1 (API 9), and 2.3.3 (API 10). Eclair (API 7), and APIs 11+ do not have this issue. At this point, I believe I'm looking for some kind of workaround...
Turns out my issue was not caused by something I detailed in my original post, so I apologize for that.
The issue was caused by a SurfaceView item on a tab unrelated to the tab I was testing on. After rebuilding the tabs and layouts from the ground up (and building each time), I discovered the lag-back-glitch was only caused when a SurfaceView was present in a non-focused tab.
I finally found that I was not the only one with this issue.
To solve:
Created an onPause method in the main Activity. In here, I destroy the SurfaceView using container.removeView(..);.
Created an onResume method in the main Activity. In here, I inflate the SurfaceView from a new XML file containing ONLY the SurfaceView item, and add it to the original container.
Lastly, implemented a android.view.SurfaceHolder.Callback in the SurfaceView to erase the contents of the surface before it is removed.
It stumps me that this happens only on APIs 8-10, but I'm glad it's solved now. Kudos to everyone that offered their assistance!

Duplicate ListFragment entries when changing orientation

So, I'm developing an app with fragments, and everything is fine, except that when I change orientation the entries on my ListFragment duplicate like this:
And when I go back to portrait mode, the items are still duplicated; until I kill the process and restart.
To populate the list I'm using an AsyncTaskLoader, but it only runs once, when the application starts for the first time; then my adapter gets filled with the info for the rows.
I'm not using the <fragment> tag in my main.xml, the fragment is added by code in the 'parent' activity like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (savedInstanceState == null) {
TodoListFragment fragment = new TodoListFragment();
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.fragment_list_container, fragment,
LIST_FRAGMENT_TAG);
trans.commit();
}
}
The replace was and add, but changed trying to test if it made any difference, it didn't.
I also tried using the setRetainInstance(true) method, to no avail.
I'm stumped, any help would be greatly appreciated.
You say the AsyncTaskLoader only runs when the app starts... On orientation change the app is restarting, so maybe it's running a second time?

Android Fragment not visible

I have added a background service to my application which creates a notificaion when a new item is added to my application. When pressing the notification the user is taken into the application and the intent passes an object which allows the application to select the newly added item.
The application is for both mobile phones and tablets. When running on phones the item is shown in a separate activity, when on a tablet a dual fragment layout is used and the item is shown on the right fragment.
In the main activity onCreate I check the intent and check if a item has been passed through and display it if it has. This is working fine on the phone but on a tablet the right fragment is not visible and hence the item can not be shown.
This is what I call at the end of onCreate (I had tried it in onStart and onResume)
Bundle data = queryIntent.getExtras();
if (data!=null){
Deal deal = data.getParcelable("notificationDeal");
if (deal!=null){
onDealSelected(deal);
}
}
The method onDealSeletced does the following
public void onDealSelected(Deal deal) {
if (!mDualFragments){
Intent showDealDetails = new Intent(getApplicationContext(), DealDetailsActivity.class);
showDealDetails.putExtra("Deal", deal);
showDealDetails.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(showDealDetails);
Log.d("OnDealSelected", "1");
}
else{ // must be tablet
if (dealDetailsFragment == null)
dealDetailsFragment = (DealDetailsFragment) getFragmentManager().findFragmentByTag("dealDetailsFragment");
if (!dealDetailsFragment.isVisible()){
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.right_fragment_container, dealDetailsFragment);
transaction.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
transaction.commit();
getFragmentManager().executePendingTransactions(); // ensure it is done before we call update deal!
Log.d("OnDealSelected", "2");
}
if (dealDetailsFragment.isVisible()) {
dealDetailsFragment.updateDeal(deal);
Log.d("OnDealSelected", "3");
}
}
}
On a smartphone mDualFragments is false and hence it shows the deal in a new activity and works as expected.
When on a tablet it goes into the else, however it never gets into the final if as the fragment is not visible.
When running the application on a tablet it goes into the second if but after it the fragment is still not visible.
The same method is used at other points in the application (when a deal is not passed through in the intent) and has been working as expected.
You can use setArguments(Bundle bundle) to pass data to the fragment before it is attached (before the commit action). This way when the Fragment initializes itself it can call getArguments and parse the bundle. This way you don't have to worry about the fragment being visible yet, it can create its views when ready. There is a full example in the Fragment Docs
Try the transaction.add() method and hide the previous fragment. I suppose your fragment will be visible now.
transaction.add(R.id.right_fragment_container, dealDetailsFragment);

Categories

Resources