I have an AsyncTask which gets called onCreate() in my Main Activity. In the same Activity if the orientation changes the AsyncTask gets called again. How do I prevent this from happening or how do I restructure my program to avoid this happening?
public class Main extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pages);
StartProcess sProcess = new StartProcess();
sProcess.execute(this);
}
}
You should check Handling Run Time Changes
You can Handle either by using
Retain an object during a configuration change
Allow your activity to restart when a configuration changes, but carry a stateful Object to the new instance of your activity.
Handle the configuration change yourself
Prevent the system from restarting your activity during certain configuration changes, but receive a callback when the configurations do change, so that you can manually update your activity as necessary.
To retain an object during a runtime configuration change:
Override the onRetainNonConfigurationInstance() method to return the object you would like to retain.
When your activity is created again, call getLastNonConfigurationInstance() to recover your object.
#Override
public Object onRetainNonConfigurationInstance() {
final MyDataObject data = collectMyLoadedData();
return data;
}
Retain in OnCreate ;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
if (data == null) {
data = loadMyData();
}
...
}
Or simply add this code in you Manifest of you Activity
android:screenOrientation="portrait"
or
android:screenOrientation="landscape"
You can add android:configChanges="orientation" in the Activity manifest and manually set the contentView or change the layout by overriding the onConfigurationChanged method in your Activity.
Related
I have two layouts in layout and layout-land folders. The problem is when the orientation change, it will call onCreate method. How to prevent onCreate method called?
This is what I've done. But it didn't work at all.
AndroidManifest
<activity android:name=".MapsActivity"
android:configChanges="orientation|keyboardHidden">
MapsActivity.java
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
orientationChange = true;
//reload your ScrollBars by checking the newConfig
}
When orientation is changed it needs to load new layout with new configuration. it needs to recreate it. So, there is no way to exit this recreation.
There are some way to avoid it :
before activity destoryed it calls onsaveinstancestate. Where you can save your view state & in onCreate you can get the savedInstance & repopulate it.
Save Your Activity State
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
Restore Your Activity State
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
You can use Fragment. In which you need to set setRetainIntace(true). Then fragment will not destroyed while activity is destroyed.
Refs :
Recreating an Activity
Add this in manifest file for your Activity
android:configChanges="keyboardHidden|screenSize|orientation"
You can use
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
or
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
to lock the orientation. Because as you mentioned, the onCreate is called everytime the screen rotates.
But you have to call it before the setContentView(R.layout.main) Could look like this:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.main);
}
or android:screenOrientation="sensorPortrait"
like this in your AndroidManifest
<activity
android:name=".ActivitiesClasses.Login"
android:label="#string/app_name"
android:screenOrientation="sensorPortrait"
android:theme="#style/AppTheme.NoActionBar" />
I ran into an interesting problem, and I'm not sure how to go about fixing it. Consider the following code:
public class MainActivity extends AppCompatActivity {
private Bundle savedState;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
savedState = savedInstanceState;
Log.d("ON CREATE", "savedState is null: "+(savedState==null));
new CustomTask().execute();
}
public class CustomTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void v) {
Log.d("POST EXECUTE", "savedState is null: "+(savedState==null));
}
}
}
This code saves a reference to the savedInstanceState then runs an AsyncTask, which tries to access that variable 5 seconds later. If the user changes the orientation of the device before the AsyncTask finishes its work, the following output is produced:
ON CREATE: savedState is null: true //initial run
ON CREATE: savedState is null: false //re-created after orientation change
POST EXECUTE: savedState is null: true //first instance
POST EXECUTE: savedState is null: false //second instance
Both of the onPostExecute() methods fire after the orientation change, but they are seemingly accessing the same savedState variable, which I expected to be non-null in both cases since it was being accessed after the orientation change.
Apparently the first AsyncTask, which was started before the orientation change still references the savedState variable from before the change as well. So my questions are:
Why does it do this? After the app state is restored, I would expect the AsyncTask to simply access all class members in their current states, which means savedState would be non-null.
And how can I access the current savedInstanceState variable from the callback of an AsyncTask that was started before that variable was changed?
After the orientation change, there are two instances of MainActivity. The old one has gone through the tear-down lifecycle events (onStop(), onDestroy(), etc.) but has not been garbage collected because the inner-class CustomTask thread is still running and holds a reference to it. That instance of CustomTask sees the null savedState. It knows nothing of the new instance of MainActivity, created after the restart, and its non-null savedState.
Do you really want the original instance of CustomTask to continue running after restart? Maybe you should cancel it when the activity is destroyed. If you really need it to continue running and have access to state data that you now declare in the activity, you will need to move that state data out of the activity to somewhere else, such as a singleton object, subclass of Application or persistent storage.
Using a retained fragment might be another option for retaining state and background processing across restarts.
After a month of dealing with this issue, I finally found the solution to this. The key concept I was struggling with was how to update an (already running) AsyncTask with a reference to the current instance of the Activity it's working with. Here's how to do it.
The first step is to separate the AsyncTask into its own class file, and pass a reference to the Activity it's working with in through a constructor or a setter. So to use the same example I used in my original question, it would look something like this:
public class CustomTask extends AsyncTask<Void, Void, Void> {
//This could also be a reference to a callback interface
//implemented in an Activity
private Activity activity;
public CustomTask(Activity activity) {
this.activity = activity;
}
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Void v) {
Log.d("POST EXECUTE", "savedState is null: "+(savedState==null));
}
//newly added method
public void setActivity(Activity activity) {
this.activity = activity;
}
//newly added method
public void detachFromActivity() {
activity = null;
}
}
The next step is to save a reference to the running AsyncTask as a data member of the Activity. That's just a matter of creating a private variable private CustomTask customTask; in the Activity.
Next, and this is the important part, you need to override onRetainCustomNonConfigurationInstance() in your Activity, and in this method, detach the AsyncTask from its old Activity (which is destroyed on a screen rotation) and retain the reference to the task so we can work with it in the new instance of the Activity that will be re-created when the screen finishes rotating. So this method would look like this:
#Override
public Object onRetainCustomNonConfigurationInstance() {
if(customTask != null) {
customTask.detachFromActivity();
}
return customTask;
}
Now, after the screen rotation has completed and the Activity is re-created, we need to get the reference to our task that was passed through. So we call getLastCustomNonConfigurationInstance() and cast the Object it returns to our specific AsyncTask class type. We can do a null check here to see if we have a task that was passed through. If there is one, we set its listener to our CURRENT Activity reference, so that the callbacks come to the right Activity instance (and hence avoided NullPointerExceptions, IllegalStateExceptions, and other nasties). This bit should look like this:
customTask = (CustomTask) getLastCustomNonConfigurationInstance();
if(customTask != null) {
customTask.setListener(this);
}
Now our running AsyncTask has the correct reference to the current instance of our Activity, and it will properly deliver its callbacks to an up-to-date Activity.
For more information on the uses and limitations of this solution, please see the Android documentation here: http://developer.android.com/reference/android/app/Activity.html#onRetainNonConfigurationInstance()
I am using a View-less Fragment to store some data during orientation change of my Activity. It looks roughly like this:
public class BoardActivity extends BaseActivity {
private DataHandler mDataHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize the data handler
mDataHandler = (DataHandler)mFragmentManager.findFragmentByTag("data");
if (mDataHandler == null) {
mDataHandler = new DataHandler();
mFragmentManager.beginTransaction().add(mDataHandler, "data").commit();
// initialize the data
mDataHandler.mThreads = ...;
} else {
// here, the data is taken and the ListView is filled again.
fillView();
}
}
public static class DataHandler extends Fragment {
private Topic[] mThreads;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}
}
What happens is, that when the Activity is left (vor example with the home button) and for some reason is killed in the background, the app crashes upon restart of that Activity. The reason is that although the Fragment mDataHandler is found by the FragmentManager, its Member variable (mThreads) is null.
How come the Fragment itself can be retained but its variables are set to zero?
How come the Fragment itself can be retained but its variables are set to zero?
The fragment was not retained. Retained fragments are retained only for configuration changes. You did not go through a configuration change. Your process was terminated, because Android needed the RAM to support other apps.
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>
I have a View that was created on runtime then I draw some canvas on that View(runtime) after that I rotated my screen.All data was gone(reset).So I put the some code in AndroidManifest.xml like this
android:configChanges="keyboardHidden|orientation"
in my <activity> then I put a #Override function
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myPaint);
layout.addView(mView);
}
but everything couldn't solved my problem.I want to keep my data from View(runtime) on every single rotation.
That's my onCreate function.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = new MyView(this);
setContentView(mView);
mView.requestFocus();
setContentView(R.layout.main);
LinearLayout layout = (LinearLayout) findViewById(R.id.myPaint);
layout.addView(mView);
}
You need to save and load the data you want to retain. Even though you're handling the screen rotation yourself when you modified the Manifest the way you did, you're still reloading the view yourself. Reread the reference document on Handling Runtime Changes. You need to store your data and reload it accordingly. Otherwise it will be lost when the application restarts or when you reload your ContentView.
http://developer.android.com/guide/topics/resources/runtime-changes.html
You could approach this a few ways.
I assume MyView is your own class which extends View. If so there are two methods which you may care to know, onSaveInstanceState() and onRestoreInstanceState(). When saving you create a parcelable that will contain enough data for you to re-render your view if it were to be destroyed and recreated.
class MyView extends View {
private String mString;
onDraw(Canvas v) { ... }
Parcelable onSaveInstanceState() {
Bundle b = new Bundle();
bundle.putString("STRING", mString);
return b;
void onRestoreInstanceState(Parcelable c) {
Bundle b = (Bundle) c;
mString = bundle.getString("STRING", null);
}
}
Activity has similar state saving mechanics allowed in onCreate and onSaveInstanceState() (inside Activity, not View in this case) which will allow the activity to reset the state of it's view to the state it desires.
This should solve most of your worries. If you are wanting to use the onConfigurationChanged method, then you should reclarify your question as it is not clear what the current behavior is that you aren't expecting in each situation (only using onConfigurationChanged, or only using onCreate, or using both, etc).
I've just used my data-class as singleton (java-pattern).
And it works fine.
--> Application is a Stop-Timer for Racing, where i can stop time from different opponents on the track, so i need the data for longer time, also if the view is repainted.
regz
public class Drivers {
// this is my singleton data-class for timing
private static Drivers instance = null;
public static Drivers getInstance() {
if (instance == null) {
instance = new Drivers();
}
return instance;
}
// some Timer-Definitions.......
}
Then in MainActivity:
// now the class is static, and will alive during application is running
private Drivers drivers = Drivers.getInstance();
#Override
public void onClick(View v) {
if (v == runButton1) {
drivers.startTimer1();
// do some other crazy stuff ...
}
}
// here i put out the current timing every second
private myUpdateFunction(){
time01.setText(drivers.getTimer1());
// update other timers etc ...
}