Android stop activity in onCreate() before calling super.OnCreate() - android

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;
}

Related

Finish Current Activity Immediately

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.

Ignoring navigate() call: FragmentManager has already saved its state

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

How to check activity has been destroyed or no more running

I am working on an android app and have an activity. I have written a code in my activity that will start a new activity after getting response from server, this code is getting executed even after I press back button on my activity.
So, I want to check that if my current activity is not active anymore, then the code should not run.
How can I check that activity is not running or in existence any more.
Please help me if anyone know how to do this.
Thanks a lot in advanced.
Activity is still in memory that's why your code is executed to finish it completed call finish() after starting another activity.
To check if current activity is there or not you have to override onDestroy() method which is called everytime when your activity is completely destroyed.
For checking activity is running or not follow this question
just call finish() method when you starts a new Activity
like
Intent intent = new Intent(this, NextActivity.class);
startActivity(intent);
finish();//this activity has been finish and the code will not execute
you can check if Activity is destroyed or not.
override this method
public void onDestroy() {
super.onDestroy();
Log.d("Activity name,"destroyed");
}
Try like this
class MyActivity extends Activity {
static boolean isActive = false;
#Override
public void onStart() {
super.onStart();
isActive = true;
}
#Override
public void onStop() {
super.onStop();
isActive = false;
}
}
Check here : Proper way to know whether an Activity has been destroyed
The question have your answer and as the solution provided just use the SharedPrefrence to store the variable.

About launcher intent

I'm trying to make a certain function to start only when a user,
Opens the app for the first time,
Goes back to an app from home.
But not start if the user switches between activities within the app.
I have looked through this topic,and the best answer is to use singleTask with onNewIntent(). So, if a user is goes back to the app from Home, a onNewIntent call with the launcher intent passed to it can be used.
However, here is my code:
public class AdMobSDK_DFP_Interstitial extends Activity implements AdListener {
private static final String MOBMAX_INTERSTITIAL_AD_UNIT_ID = "/7732/test_portal7/android_app1_test_portal7/splash_banner_android_app1_test_portal7";
private DfpInterstitialAd interstitialAd;
private int num = 0;
public void onNewIntent(Intent intent){
super.onNewIntent(intent);
Log.d("flow", "onNewIntent");
}
If I switch between different activities in the app, onNewIntent() is always called, which is the same as I go back to the app from Home.
First thing you can do is to implement your own "Application" object and have it run the needed function when it is created.
public class MyApplication extends Application {
#Override
public void onCreate() {
// Call your function
}
}
Your application object will be live as long as your app is alive (any activity/service is still running), but note that the Application object is not destroyed immediately when the user presses "Home", and might stay alive for a while and a user can return to it without the function being called.
If you need this function to run as part of your main activity, just save a flag in your Application context :
public boolean alreadyDisplayed = false; and then in your activity's onStart you can just call
if ((MyApplication)getApplication().alreadyDisplayed ) {
// Call your function
(MyApplication)getApplication().alreadyDisplayed = true;
}
** If this solution is not enough for you and you need to call your function every time your main activity is displayed from the home page you'll need to do something not as nice... one suggestion I can give you is to implement the same Application object but this time with an "open activity" counter:
public class MyApplication extends Application {
public int mActivityCounter = 0;
}
Then you can increment this counter on every onStart of activity in your app and decrement on every onStop (of course this can be done by implementing a class MyActivity and make all your relevant activities inherit it. Then you can use this counter to know if there are any other activities opened. Note that you'll have to make sure the access to this counter is synchronized and work your way with it as you need.
I hope this helps...

Cleanly binding/unbinding to a Service in an Application

I have an Android application that is binding to a persistent service (once started with startService()).
The service is an integral part of the application and thus is used in almost every Activity. Hence I want to bind to the service just once (instead of binding/unbinding in every Activity) and keep the binding during the lifetime of my application.
I've extended from Application and bind to the service in Application#onCreate(). However I now have the problem that I don't know when my application exists since Application#onTerminate() is never called, see JavaDoc:
This method is for use in emulated process environments. It will never
be called on a production Android device, where processes are removed
by simply killing them; no user code (including this callback) is
executed when doing so.
So how do I cleanly unbind from a service bound in Application?
I solved this problem by counting the references to the service binding in the Application. Every Activity has to call acquireBinding() in their onCreate() methods and call releaseBinding() in onDestroy(). If the reference counter reaches zero the binding is released.
Here's an example:
class MyApp extends Application {
private final AtomicInteger refCount = new AtomicInteger();
private Binding binding;
#Override
public void onCreate() {
// create service binding here
}
public Binding acquireBinding() {
refCount.incrementAndGet();
return binding;
}
public void releaseBinding() {
if (refCount.get() == 0 || refCount.decrementAndGet() == 0) {
// release binding
}
}
}
// Base Activity for all other Activities
abstract class MyBaseActivity extend Activity {
protected MyApp app;
protected Binding binding;
#Override
public void onCreate(Bundle savedBundleState) {
super.onCreate(savedBundleState);
this.app = (MyApp) getApplication();
this.binding = this.app.acquireBinding();
}
#Override
public void onDestroy() {
super.onDestroy();
this.app.releaseBinding();
}
}
From Sven's answer:
I solved this problem by counting the references to the service
binding in the Application. Every Activity has to call
acquireBinding() in their onCreate() methods and call releaseBinding()
in onDestroy(). If the reference counter reaches zero the binding is
released.
I agree, BUT you shouldn't do it in onDestroy - that will often not get called.
Instead I suggest the following (based on your code sample)...
// Base Activity for all other Activities
abstract class MyBaseActivity extend Activity {
protected MyApp app;
protected Binding binding;
#Override
public void onCreate(Bundle savedBundleState) {
super.onCreate(savedBundleState);
this.app = (MyApp) getApplication();
this.binding = this.app.acquireBinding();
}
#Override
protected void onPause() {
super.onPause();
// Pre-HC, activity is killable after this.
if ((11 > Build.VERSION.SDK_INT) && (isFinishing()))
onFinishing();
}
#Override
protected void onStop() {
super.onStop();
if ((10 < Build.VERSION.SDK_INT) && (isFinishing()))
onFinishing();
}
protected void onFinishing() {
// Do all activity clean-up here.
this.app.releaseBinding();
}
}
BUT, my use of isFinishing() is just a thought - I'm not certain that it is reliable. Perhaps onPause/onStop get called with isFinishing() false, but then the activity gets killed - and your releaseBinding() never gets called.
If you get rid of the isFinishing check I think you need to move the acquireBinding() call from onCreate to onStart/onResume (depending on sdk version), to ensure that your ref count doesn't get messed up.
Who knew that releasing your app's service would be so complicated!
Is unbinding necessary at all in this case? The application gets killed anyway. I tried implementing a sample application doing this without unbinding and it seems to work properly.

Categories

Resources