I have a tabhost on my application and I'm using an Activity group which handles 3 activities inside.
Example:
ActivityGroup Handles
A -> B -> C
When i start this activities i'm using the flag Intent.FLAG_ACTIVITY_CLEAR_TOP.
My problem is when the user goes from A->B->C and press back button, my B activity shows up, but it does not resume or reload or refresh. It has the same state as before.
For example if the user goes again to C, C is refreshed, but when from C goes back.... B is not.
On B I have implementend methods such as onResume, onStart, onReestart and debugging it the main thread never goes in there...
And i need to refresh B because C can make changes that change the content displayed on B.
I have googleled this for 3 days and I couldn't found a solution..
I had this problem too.
I was using ActivityGroup code based on this blog post.
When I pressed the back button the pervious View would load fine, but the activity associated with it would not fire the onResume().
I was using an extended activity with on overridden and public onResume().
I found this blog post, so tried casting the view as my extended activity and called onResume().
Bingo.
Edit.... here's some more detail...
public class YFIMenuListActivity extends ListActivity {
....
#Override
public void onResume() {
super.onResume();
}
....
}
onResume() is normally protected, but I override it and make it public so that my ActivityGroup can call it.
I only have extended list activities in this activity group (I was just playing around). If you have different activities, each will have to override onResume() and I guess you'd have to look at the type of context you got back from v.getContext() before casting and calling it.
My ActivityGroup looks something like this:
public class BrowseGroup extends ActivityGroup {
.....
#Override
protected void onResume() {
super.onResume();
// call current activity's onResume()
View v = history.get(history.size()-1);
YFIMenuListActivity currentActivity = (YFIMenuListActivity)v.getContext();
currentActivity.onResume();
}
....
}
I've managed to implement an expanded version of cousin_itt's approach.
In both of my activities being used within the activity group I changed onResume from :
protected void onResume()
to
public void onResume()
I then wrote the following onResume function in my ActivityGroup to manually fire off onResumes:
#Override
protected void onResume() {
super.onResume();
View v = history.get(history.size()-1);
MainPeopleView currentActivity = null;
try {
currentActivity = (MainPeopleView)v.getContext();
currentActivity.onResume();
}
catch ( ClassCastException e ) {
Log.e(TAG, e.toString());
}
ProfileView otherActivity = null;
try {
otherActivity = (ProfileView)v.getContext();
otherActivity.onResume();
}
catch ( ClassCastException e ) {
Log.e(TAG, e.toString());
}
}
I have to say, this feels like the worst android hack I've ever written. I'm never using activitygroup again.
((ReportActivity)getLocalActivityManager().getActivity("ReportActivity")).onResume();
ReportActivity is that name you want to back Activity
ps: v.getContext();
only return the ActivityGroup ,it can't invoke child Activity onResume
I have found that onFocusChanged(boolean hasFocus) is great for situations like ActivityGroup. This will fire, even if onResume() does not. I use it for a few of my apps that have TabHosts and ActivityGroups. Here you can force the refresh and insure that it always gets fired when your Activity regains the focus.
I hope you have write your refresh data code in this method onResume().
Related
I have two activities (A and B). Activity A calls activity B. Activity B has Back (Up) button like this:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
Now when this UP button is pressed, onCreate of activity A is again called. In the activity A, there is a classId variable (which I got from an Intent) which I want to retain. For this I have following code in my onCreate of activity A:
if (savedInstanceState == null)
{
Intent intent = getIntent();
classId = intent.getIntExtra("CLASS_ID", 0);
}
else
{
classId = savedInstanceState.getInt("CLASS_ID");
}
I have also overriden the onSavedInstanceState method:
#Override
protected void onSaveInstanceState(Bundle savedInstanceState)
{
savedInstanceState.putInt("CLASS_ID", classId);
super.onSaveInstanceState(savedInstanceState);
}
I am following this SO answer:
onCreate being called on Activity A in up navigation
The problem I am facing is that when I come again to activity A by passing back button in activity B, onCreate gets called and I found savedInstanceState to be NULL.
Edit:
Is there any other way to save my classId variable so that when I return again to activity A, I can use that?
Edit 2
If I set launch mode of my activity A to SingleTop in the manifest file, my issue gets resolved. Is it the right approach?
You should not suppose that onSaveInstanceState called each time you go to next activity.See the docs
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle).
You may further consult with official docs here
Try this
public class SingletonHolder {
//your id here as what data type you want
private static SingletonHolder instance;
public static SingletonHolder getInstance() {
if (instance == null) {
instance = new SingletonHolder();
}
return instance;
}
//set getter setter here
}
If not successfull feel free to comment
I changed the launchMode of the activity A to singleTop in the mainfest file like this:
android:launchMode="singleTop"
I followed this question on SO: Setting launchMode="singleTask" vs setting activity launchMode="singleTop"
By using this approach, activity A is not destroyed and when I just finish the activity B or click UP navigation in activity B, the existing instance of activity A is launched again.
During the normal course of development, I noticed that a particular activity appears to have stopped responding the second time it is called.
i.e. menu->(intent)->activity->(back button)->menu->(intent)
There is nothing relevant in logcat.
I don't even know where to start debugging this nor what code to show so here are the onClick and onResume fragments:
if (!dictionary.getClassName().equals("")) {
this.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Intent i;
i = new Intent(mContext, NVGlobeNavigatorVC.class);
i.putExtra("PAGE_TITLE", title);
i.putExtra("TITLE", _dictionary._title);
mContext.startActivity(i);
});
} else {
findViewById(R.id.greaterthan).setVisibility(View.GONE);
}
and in the Activity being launched:
#Override
protected void onResume() {
super.onResume();
...
Nothing unusual in manifest either:
<activity
android:name=".NVViews.NVGlobeNavigatorVC"
android:theme="#style/WindowTitleBackground"
android:label="GlobeNavigator"/>
For clarity, I put breakpoints on mContext.startActivity(i) and super.onResume(). I click the view with the onClickListener and both breakpoints are hit as expected. I then press the back button which returns me to the menu. onPause() is called as expected.
I touch the view to launch the activity again, and the breakpoint on startActivity is hit but not in onResume() in the target activity. The screen goes black and the only way I can get going again is to restart the app. If I pause the debugger, it pauses in dalvik.system.NativeStart() which implies that the activity is never relaunched.
I don't think it's relevant, but I'm using Intellij IDEA and have deleted all of the output directories, invalidated the caches and done a full rebuild.
Target API is 8. I've tested on 2.3 and 4.0.4.
Any ideas? I'm stumped.
[EDIT]
In onPause, I save some stuff to prefs. The purpose of onResume() is to get them back again:
#Override
protected void onPause() {
super.onPause();
SCPrefs.setGlobeViewViewPoint(globeView.getViewPoint());
SCPrefs.setGlobeViewZoom(globeView.getZoom());
SCPrefs.setGlobeViewScale(globeView.getScale());
}
This code:
i = new Intent(mContext, NVGlobeNavigatorVC.class);
creates a new intent. The intent is of class NVGlobeNavigatorVC.class.
If you call it once, you create a new activity, lets call it "iTheFirst". If you back out of the activity, it executes "on pause". When you run the above code again, you create another new activity of the same class, but a different acitivity. Hence it won't resume your other activity, but make a new one that we could call "iTheSecond". It looks just like iTheFirst but is unique.
If you want to resume the above, after backing out of it, you need to keep a reference to it in your menu. Then in your onClick, look to see if that activity exists, and if not, make a new one and start it, and if it does exist, just resume it.
Here is a sample activity that remembers and restarts an activity:
Intent savedCueCardActivity = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState, R.layout.landing);
}
public void goToNextScreen(View v) {
if (savedCueCardActivity == null) {
savedCueCardActivity = new Intent().setClass(this, CueCardActivity.class);
startActivity(savedCueCardActivity);
// lastActivity.finish();
} else {
savedCueCardActivity.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(savedCueCardActivity);
}
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
I found the problem, and it's a bit esoteric.
I have a large static data structure which is loaded in a class static initialiser. There was a bug in that initialiser causing an infinite loop the second time it was called if the data structure was still loaded.
Because that class is referenced in my Activity, the class loader is loading it before onCreate() or onResume() is called.
The loop gave the appearance that the Activity loader had hung.
I would like to know, why OnCreate() is called only once at the start of an activity?
Can we call OnCreate() more than once in the same activity?
If yes, than how can we call it? can anyone give an example?
Thanks a lot!!!
Why would you want to called it again? unless the activity is reconstructed, which is called by system. You cannot call OnCreate manually , it is the same reason why you won't call setContentView() twice. as docs:
onCreate(Bundle) is where you initialize your activity. Most
importantly, here you will usually call setContentView(int) with a
layout resource defining your UI, and using findViewById(int) to
retrieve the widgets in that UI that you need to interact with
programmatically.
Once you finish init your widgets Why would you?
UPDATE
I take some words back, you CAN do this manually but I still don't understand why would this be called. Have you tried Fragments ?
Samplecode:
public class MainActivity extends Activity implements OnClickListener {
private Button btPost;
private Bundle state;
private int counter = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
state = savedInstanceState;
btPost = (Button) findViewById(R.id.btPost);
btPost.setOnClickListener(this);
Toast.makeText(getBaseContext(), " " + counter, Toast.LENGTH_LONG)
.show();
}
#Override
public void onClick(View v) {
counter++;
this.onCreate(state);
}
}
onCreate() method performs basic application startup logic that should happen only once for the entire life of the activity .
Once the onCreate() finishes execution, the system calls the onStart() and onResume() methods in quick succession.
The initialization process consumes lot of resources and to avoid this the activity once created is never completely destroyed but remains non visible to user in background so that once it is bring back to front , reinitialization doesn't happen .
Where you want to call onCreate manually.
Then just do this.
finish();
Intent intent = new Intent(Main.this, Main.class);
startActivity(intent);
finish() calls the current stuff.
And if you are doing somethong getExtra in this activity then do this,
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("key",your_variable);
super.onSaveInstanceState(outState);
}
And add this to your onCreate()
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
if(savedInstanceState != null)
{
your_variable= savedInstanceState.getString("key");
}
}
Why would you want to call onCreate more than once? You will be re-creating the activity. If this is what you need for whatever reason then finish the activity and use an intent to create a new instance of that activity. Otherwise, you have two instances of the activity at the same time. Hope that helps but if that doesn't make sense then add more information as to what you want so we have context
OnCreate is basically use to create your activity (UI). If you have already created your activity then you need not create it again as you have already created.
It is basically used to initialize your activity and to create user interface of your activity. Activity is a visual part which you can use again and again so.. I think your problem is not to recreate activity but to reinitialize all components of your activity. For that purpose you can create a method initialize_act() and call it from anywhere...
#OnCreate is only for initial creation, and thus should only be called once.
If you have any processing you wish to complete multiple times you should put it elsewhere, perhaps in the #OnResume method.
Recently i realized that onCreate is called on every screen orientation change (landscape/portrait). You should be aware of this while planning your initialization process.
Recreation can be suppressed in AndroidManifest.xml:
<activity
android:configChanges="keyboardHidden|orientation"
android:name=".testActivity"
android:label="#string/app_name"></activity>
Quick question: I have an activitygroup. Within that Activitygroup I have an activity. If I press back while inside this activity. the onBackPressed method of the activity is called - Not the Activitygroups onBackPressed - Why is that ?
EDIT: Got my answer but the problem remains. Here follows code and explanation of my original issue:
I am using ActivityGroups within a TabHost and as such have been "forced" into overriding onBackPressed. I can navigate through my application without issue by pressing back on my phone and by pressing the tabs on my tabhost. But I cannot interact with the interface after pressing Back.
Once I press one of the tabs on the tabhost again I can interact with everything like normal. Why is this happening? Do I need to override onResume?
Relevant code
SettingsActivityGroup :
public class SettingsActivityGroup extends ActivityGroup
{
// Keep this in a static variable to make it accessible for all the nested activities, lets them manipulate the view
public static SettingsActivityGroup group;
// Need to keep track of the history if you want the back-button to work properly, don't use this if your activities requires a lot of memory.
private ArrayList<View> history;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Allocate history
this.history = new ArrayList<View>();
// Set group
group = this;
// Start root (first) activity
Intent myIntent = new Intent(this, SettingsActivity.class); // Change to the first activity of your ActivityGroup
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
ReplaceView("SettingsActivity", myIntent);
}
/*
* Replace the activity with a new activity and add previous one to history
*/
public void ReplaceView(String pId, Intent pIntent)
{
Window window = getLocalActivityManager().startActivity(pId, pIntent);
View view = (window != null) ? window.getDecorView() : null;
// Add the old activity to the history
history.add(view);
// Set content view to new activity
setContentView(view);
}
/*
* Go back from previous activity or close application if there is no previous activity
*/
public void back()
{
if(history.size() > 1)
{
// Remove previous activity from history
history.remove(history.size()-1);
// Go to activity
View view = history.get(history.size() - 1);
Activity activity = (Activity) view.getContext();
// "Hack" used to determine when going back from a previous activity
// This is not necessary, if you don't need to redraw an activity when going back
activity.onWindowFocusChanged(true);
// Set content view to new activity
setContentView(view);
}
else
{
// Close the application
finish();
}
}
/*
* Overwrite the back button
*/
#Override
public void onBackPressed()
{
// Go one back, if the history is not empty
// If history is empty, close the application
SettingsActivityGroup.group.back();
return;
}
}
Arbitrary child of SettingsActivityGroup(CallForwardActivity)
public class CallForwardActivity extends ListActivity
{
....
#Override
public void onBackPressed()
{
// Go one back, if the history is not empty
// If history is empty, close the application
SettingsActivityGroup.group.back();
return;
}
}
Because I believe calling onBackPressed() of the currently selected activity is the desired behavior.
It's also worth noting that ActivityGroup is deprecated, but I assume you are coding for <3.0 and don't fancy working with the support libraries.
Regarding your edited question:
Another question on this site cites this article as a good ActivityGroup example, and I would agree http://ericharlow.blogspot.com/2010/09/experience-multiple-android-activities.html
This example just calls finish() on the current activity when back is pressed, and lets the os restart the previous activity, which is simpler than what you are doing, and will hopefully work! You can just call getParent() in your child activities too to avoid using that static reference (just seems easier to read to me that way!).
I am using android tab based application with TabGroupActivity, the application have a ListActivity "A" which drills down to a detail activity "D", on detail activity when i click device back button it navigate back to ListActivtiy "A" and relaods the data (ProgressBar shows progress on back button).
How can i avoid reloading of data on back buttuon ?
Any help would be appreciated.
Perhaps you can use onSaveInstanceState() to save what's needed to be saved and then restore your activity in onCreate()
If you look at the Activity LifeCycle :
When you go to another activity, and press back, it will call finish(). The first activity was on the onStop() method because it was no longer visible. So, when you come back to the first activity, it will call the method onStart(), and on Stop().
Try to overload this method without calling the super method (it might be a very bad idea).
You can also try to remember the position when you was (get the index of the current tab), and on the onResume() method, set the current tab to this index.
You just need an int attribute savedPosition in your listactivity.
private int savedPosition = -1;
#Override
protected void onPause() {
savedPosition = getListView().getFirstVisiblePosition();
super.onPause();
}
#Override
protected void onResume() {
super.onResume();
if(savedPosition != -1) {
getList().setSelection(savedPosition);
}
}