Right now, I have an activity with method PrepareData(), used to prepare every data that needed by current activity, this called in OnCreate before I set everything. I call this method, and when find some issue I want to finish current activity.
So this is snippet of my code:
private void PrepareData()
{
try
{
//some code to prepare data here
}
catch(Exception ex)
{
Intent _startNewActivity = new Intent(this, ActivityB);
this.StartActivity(_startNewActivity);
this.Finish();
}
}
and OnCreate like this
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
SetContentView(Resource.Layout.ActivityA);
PrepareData()
toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
if (toolbar != null)
{
SetSupportActionBar(toolbar);
SupportActionBar.Title = "Activity A";
SupportActionBar.SetDisplayHomeAsUpEnabled(true);
}
}
Right Now, when app find error on PrepareData, intent is called and this.Finish() also called, but somehow app not finish Activity A immediately, it still set toolbar, and also call onResume.
I know there is activity lifecycle that onStop always called after onResume,But I want to know there is way to finish current activity immediately, without call next code?
An activity always calls through the first lifecycle, even if you call finish, e.g. onCreate --> onStart --> onResume. Finish call is only scheduled to be performed after onResume. If your only issue is to prevent some code from executing in onResume, define a flag where you call this.Finish(), for instance bool finishCalled = true; and then to prevent the toolbar from setting the title, just wrap the code inside that bool with if !(finishCalled).
That should do it.
Related
I'm using navigation in MainActivity, then I start SecondActivity (for result). After finish of SecondActivity I would like to continue with navigation in MainActivity, but FragmentManager has saved his state already.
On Navigation.findNavController(view).navigate(R.id.action_next, bundle) I receive log message:
Ignoring navigate() call: FragmentManager has already saved its state
How I can continue in navigation?
You must always call super.onActivityResult() in your Activity's onActivityResult. That is what:
Unlocks Fragments so they can do fragment transactions (i.e., avoid the state is already saved errors)
Dispatches onActivityResult callbacks to Fragments that called startActivityForResult.
Finally, I fix the issue by simple calling super.onPostResume() right before navigating to restore state.
I've solved this problem this way:
#Override
public void onActivityResult() { //inside my fragment that started activity for result
model.navigateToResults = true; //set flag, that navigation should be performed
}
and then
#Override
public void onResume() { //inside fragment that started activity for result
super.onResume();
if(model.navigateToResults){
model.navigateToResults = false;
navController.navigate(R.id.action_startFragment_to_resultsFragment);
}
}
not sure, if this is not a terrible hack, but it worked for me. FramgentManager state is restored at this point (onResume) and no problems with navigation occur.
I believe above solutions should work. But my problem was different. There was a third party sdk which was launching its activity using context provided by me and it was delivering the result on a listener which I had to implement.
So there was no option for me to work with onActivityResult :(
I used below hack to solve the issue:
private var runnable: Runnable? = null // Runnable object to contain the navigation code
override fun onResume() {
super.onResume()
// run any task waiting for this fragment to be resumed
runnable?.run()
}
override fun responseListener(response: Response) { // Function in which you are getting response
if (!isResumed) {
// add navigation to runnable as fragment is not resumed
runnable = Runnable {
navController.navigate(R.id.destination_to_navigate)
}
} else {
// navigate normally as fragment is already resumed
navController.navigate(R.id.destination_to_navigate)
}
}
Let me know if there is any better solution for this. Currently I found this very simple and easy to implement :)
call super.onPostResume() before navigation....It's working
I am creating an app having a navigation drawer activity with fragments. At every cold start of the app, I am executing some initialization code where I load the following things:
The user session(if the user is logged in or not)
Registering Retrofit services
Getting some data from the server to proceed with startup of the app.
This is the flow of my app when doing a cold start:
Starting MainActivity and verifying the user session.
If the session is valid, then we open the CoreActivity.
If not, then we open the LoginActivity.
When the app is brought to the foreground after some inactivity Android tries to restart the current Activity. This means my initialization code is bypassed and CoreActivity.onCreate() is executed.
All my activities(except MainActivity) are extending the following super activity:
public abstract class MasterActivity extends AppCompatActivity {
#Override
protected final void onCreate(Bundle savedInstanceState) {
this.supportRequestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
if (!CrmContext.getInstance().verifyContextSet(this)) {
return;
}
super.onCreate(savedInstanceState);
onCreateAfterContext(savedInstanceState);
}
In CrmContext:
public boolean verifyContextSet(final Context context) {
boolean isContextSet = applicationContext != null;
if (isContextSet) {
return true;
}
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
return false;
}
In verifyContextSet() I am doing some checks to be sure that the app has been correctly loaded. If the user session is not properly loaded.
My problem:
If the app is brought to the front the CoreActivity.onCreate() is executed and verifyContextSet() returns false. In this case I want to cancel the creation of CoreActivity and open MainActivity again.
When I do my verifyContextSet() before super.onCreate(), then I get this exception:
android.util.SuperNotCalledException: Activity {nl.realworks.crm/nl.realworks.crm.view.CoreActivity} did not call through to super.onCreate()
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2287)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2391)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1309)
I tried to execute super.onCreate() first, but then the Fragment inside the activity is created first. This means that my Fragment is recreated before my verifyContextSet() is executed.
So, If I try to cancel()/finish() the onCreate() before super.onCreate() has been called, then I get the SuperNotCalledException. If I execute super.onCreate() first, then the Fragment is initialized which is not allowed when verifyContextSet() returns false.
I want to do the following:
Bringing the app to the foreground
Check if the app has been initialized
If not, then finish() the current activity and then restart the app to open MainActivity.
put your checking/validating code in an Application sub class
public class MyApp extends Application {
//in your oncreate create sessions etc.
now whether MainActivity restarts or not, you have already validated.
Note: Application class' onCreate() is the firs to run before any body.
What you need to do is to have your onCreate like this
super.onCreate();
if(<activity is not valid>) {
startAnotherActivity()
finish()
return;
}
This will make sure that no other activity lifecycle method is called except onDestroy i.e. onResume, onPause, onStop, onStart.
I think code should look like that
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Your code
}
super.onCreate(savedInstanceState) allways as first line.
You can use ViewModel and Observer. Basically your remaining onCreate code will only execute if Observer triggered.
onResume {
// THIS WILL TRIGGER THE OBSERVER
viewModel._needVerification.value = true
}
onCreate {
super.onCreate()
... CREATE VIEWMODEL
// THIS NEEDED TO HANDLE RECREATED ACTIVITY CAUSE BY SCREEN ORIENTATION ETC
viewModel._verificationFinished.value = false
viewModel.needVerification.observe(this, Observer{
if (it == true) {
verifyContextSet(final Context context) {
if (isContextSet) {
if (viewModel.verificationFinished.value != true) {
... DO REMAINING ONCREATE CODE
viewModel._verificationFinished.value = true
}
} else {
Start MainActivity
}
}
}
})
}
Instead of only returning from onCreate you need to finish the Activity first to stop other initialization callbacks to be triggered.
Just change this code
if (!CrmContext.getInstance().verifyContextSet(this)) {
return;
}
to this
if (!CrmContext.getInstance().verifyContextSet(this)) {
finish();
return;
}
I have plugged in lock screen logic into my app. It shows ComfirmPatternActivity (above my MainActivity) which controls graphical pin code input to be correct.
When onCreate() method of MainActivity is call everything is OK. But I also want to lock screen when I simply turn app down not destroying MainActivity. So it goes from onRestart() -> onResume(). In onResume() I placed method handleLockScreen(); as in onCreate(). But for now I got into infinite loop hich shows me ComfirmPatternActivity screen for ages. It seamed out that the last command of code in ComfirmPatternActivity after user inputs correct pin - is Activity finish(). After that finish() Im redirected to MainActivity.onResume() - prev Activity on the stack - in which I again start ComfirmPatternActivity() and so on. I want resume logic only in case user pressed on app icon again, not in case top Activity is destroyed. How this can be handled? Thx in advance.
MainActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handleLockScreen();
setContentView(R.layout.activity_main);
}
protected void onResume() {
super.onResume();
handleLockScreen();
..
public void handleLockScreen(){
SharedPreferences prefs = this.getSharedPreferences("LOCK_SCREEN",
Context.MODE_PRIVATE);
String lock_screen_code = prefs.getString("LOCK_SCREEN_CODE","");
if (lock_screen_code.isEmpty()) {
Intent intent = new Intent(this, SampleSetPatternActivity.class);
this.startActivity(intent);
}
else{
Intent intent = new Intent(this, SampleConfirmPatternActivity.class);
this.startActivity(intent);
}
}
public class SampleConfirmPatternActivity extends ConfirmPatternActivity {
SharedPreferences prefs = this.getSharedPreferences("LOCK_SCREEN",
Context.MODE_PRIVATE);
String patternSha1 = prefs.getString("LOCK_SCREEN_CODE","");
return TextUtils.equals(PatternUtils.patternToSha1String(pattern), patternSha1);
... finish() is called further in this activity
}
This finish() returns to my onResume() which triggers all over again. And I want handle onResume() only in case smth external happend to my app like user returned to my app etc. I dont want get back to onResume() when pin code is checked and everything is OK.
You could possibly declare a boolean (global to application) in LockScreen Activity to fix this issue and tell(to the MainActivity) if it is coming to onResume from outside or from LockScreen (y)
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>