I am trying to solve a problem. In my application I need to determine if onStop method was called because of starting a new activity or it was called after user had clicked on the home button or had switched to another app.
I have BaseActivity class, and I need to check it here.
I have tried to find a way to do this, but unfortunately still no solution is found.
Maybe there is a workaround for that.
The idea is to differentiate the initiator of onStop method call.
I would be grateful for any help.
A possible solution would be to register an ActivityLifecycleCallbacks and save the reference name of the last activity that called onResume:
public class ActivityChecker implements Application.ActivityLifecycleCallbacks {
private static ActivityChecker mChecker;
private String mCurrentResumedActivity = "";
public static ActivityChecker getInstance() {
return mChecker = mChecker == null ? new ActivityChecker() : mChecker;
}
// If you press the home button or navigate to another app, the onStop callback will be called without touching the mCurrentResumedActivity property.
// When a new activity is open, its onResume method will be called before the onStop from the current activity.
#Override
public void onActivityResumed(Activity activity) {
// I prefer to save the toString() instead of the activity to avoid complications with memory leaks.
mCurrentResumedActivity = activity.toString();
}
public boolean isTheLastResumedActivity(#NonNull Activity activity) {
return activity.toString().equals(mCurrentResumedActivity);
}
// [...] All other lifecycle callbacks were left empty
}
The ActivityLifecycleCallbacks can be registered in your Application class:
public class App extends Application {
public App() {
registerActivityLifecycleCallbacks(ActivityChecker.getInstance());
}
}
Don't forget to register it in your manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="your.package.name">
<application
...
android:name=".App"
> ...
</application>
</manifest>
Then, you can use it in your base Activity.
public class MyBaseActivity {
#Override protected void onStop() {
if(ActivityChecker.getInstance().isTheLastResumedActivity(this)) {
// Home button touched or other application is being open.
}
}
}
References:
Registering your custom Application class and the ActivityLifecycleCallbacks: https://developer.android.com/reference/android/app/Application.html
After writing this I found this link with some other options to retrieve the current resumed activity: How to get current foreground activity context in android?.
You can use SharedPreferences to check it:
#Override
protected void onStop() {
super.onStop();
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
pref.edit().putBoolean("IfOnStopCalled", true).apply();
}
Check in your BaseActivity:
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
Boolean IfOnStopCalled = pref.getBoolean("IfOnStopCalled",false);
if(IfOnStopCalled){
//Do your action
}
else{
//Do your action
}
Related
I'm trying to determine when my app is being resumed after the user closed it, in any way, pressing home button, back button or switching to another app.
What I need to do is to set a boolean when the app goes in background, so, when it is resumed, I know that it was in background before and I can act accordingly.
I tried to use onResume and onPause methods in activities to know when the app goes in background and it is then resumed, but as only one activity can be alive at at time, I had no success. When an activity is paused, this doesn't mean that the app went to background, because another activity could have been launched, but the onResume event of that activity will trigger only after the previous one has paused.
I've also tried to list all the apps in foreground, but with no success, if I put my app in background resuming another app, my app always results to be in the foreground.
I read that since Android 4 there is a new method to know when the app is in foreground, but I need my app to be compatible with Android 3.0 devices too.
Here is the code I tried putting in every single activity (MyApp is my Application name):
#Override
protected void onResume() {
super.onResume();
MyApp.isPaused = false;
}
#Override
protected void onPause() {
super.onPause();
MyApp.isPaused = true;
}
This is also my attempt to list all the apps in foreground:
ActivityManager activityManager = (ActivityManager)((Activity) currentContext).getSystemService( ACTIVITY_SERVICE );
List<RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
for(RunningAppProcessInfo appProcess : appProcesses){
if(appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND){
if(appProcess.processName.equals("com.xxx.myapp")) {
Log.i("MyApp", "it's in foreground");
}
Log.i("MyApp", appProcess.processName);
}
}
This class provides a singleton to determine "the activity in background" status. It uses a timer with a threshold(i.e. 0.3s) to determine the activity is went to background or not.
One thing has to point out is that if the user resumes to the activity within the threshold (i.e. 0.3s), this test will be failed.
If you have a better solution, please share with us :)
Ref: https://gist.github.com/steveliles/11116937
You are absolutely correct :) Because only one activity can be alive at a time so you need something which remains alive through out the application life cycle :) like Application instance itself or you can also make use of shared preference for that matter. But seriously using shared prefference for checking lifecycle is wrong choice if you ask me.
If I was in your position I would have gone for Application class :) Here is code if you want to do the same :)
import android.app.Application;
/**
* Created by sandeepbhandari on 3/3/16.
*/
public class AppService extends Application{
private static AppService sInstance;
public static boolean isGoingToBackGround=false;
#Override
public void onCreate() {
super.onCreate();
sInstance = this;
}
public static AppService getInstance() {
return sInstance;
}
}
In all your activities onPause just set
AppService service = AppService.getInstance();
service.isGoingToBackGround =true;
And in onResume check the same variablethats all :) and yeah if you want to use your application class rather than default Application you have to make change to manifest.xml
<application
android:name=".AppService"
Thats all :)
Override onTrimMemory(int level) in your Application. Might not be the prettiest way, but it has worked for me.
You will get
TRIM_MEMORY_BACKGROUND = 40;
when your application went into the Background.
You can make Application class inside your project to save state of your project. When any activity goes to pause call on pause respectively while on resume call on resume method and save state of the inside this class. Even if one activity goes on pause another on resume your class will know exact state of the application. Or another way you can save applicaton state in shared preference in each activity can change its value.
i trust there is no need for u to post a code... that being said...
start by logging every implemented methods onCreate(), onPause(), onDestroy(), and other well reputed Activity methods...
but back button does not just pause it kills, thus onCreate is called most
and check onStart() too.
public class CustomApplication extends Application {
private static boolean activityVisible;
#Override
public void onCreate() {
super.onCreate();
}
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
}
and in your all activities set
#Override
protected void onResume() {
super.onResume();
CustomApplication.activityResumed();
}
#Override
protected void onPause() {
super.onPause();
CustomApplication.activityPaused();
}
and in your manifest
<application
android:name=".CustomApplication"
I have an Activity with a private OnSharedPreferenceChangeListener, the listener's work is defined on the onCreate() method of the Activity. The listener is registered to the sharedPreferences of the application.
The change itself is triggered by a Service in response to an sms received intent.
Will the listener receive the callback when the Activity itself has died? are there cases where it will not?
The listener is defined (roughly):
private OnSharedPreferenceChangeListener _sharedPreferenceListener;
public void onCreate(Bundle bundle){
...
_prefs = PreferenceManager.getDefaultSharedPreferences(this);
_prefs.registerOnSharedPreferenceChangeListener(_sharedPreferenceListener);
...
_sharedPreferenceListener = new SharedPreferences.OnSharedPreferenceChangeListener(){ /*doing some work here*/};
...
}
please igonre the logic here if correct or not, assume that the code works, my main concern is how the listener reacts to changes in the lifecycle of the activity.
Thanks,
actually, since the listener doesn't know anything about the activity (and as such you can use it anywhere , not just in an activity), you will get notified no matter where you use it.
Also, since you can't know for sure what it does with the context , you should use the application context instead in this case (so that you won't have memory leaks, though I doubt it needs a reference to the activity).
Of course, if the listener itself is referenced by weak reference, and the activity doesn't have any reference to itself on any other class, the listener can be GC-ed too. You can see in the code of Android (or at least of API 19) that in the class "android.app.SharedPreferencesImpl" (example link here) , you have a WeakHashMap of listeners, so it might mean that the activity that hosts the listener can be GC-ed and so the listener will stop from being called. Here is the relavant code of Android:
private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
...
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(this) {
mListeners.put(listener, mContent);
}
}
So, as I've written, best if you just put the application context in case you wish to keep listening to this event.
Or, in case you do wish to stop listening to this event, just unregister it when the activity is being destroyed.
to prove it, you can simply run your app...
here's my proof app:
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
preferences.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
android.util.Log.d("AppLog", "changed!");
}
});
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startActivity(new Intent(MainActivity.this, Activity2.class));
}
}, 1000);
finish();
}
}
Activity2.java
public class Activity2 extends Activity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activity2);
//if you call here System.gc(); , you have a good chance that the listener won't be called
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//this may or may not cause the listener to write to the log
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(Activity2.this);
preferences.edit().putBoolean("test", true).commit();
}
}, 1000);
}
}
Will the listener receive the callback when the Activity itself has died?
-> No, it won't. Because when your activity dies, the _prefs and _sharedPreferenceListener fields will be destroyed.
You could check this question for more details on OnSharedPreferenceChangeListener :
SharedPreferences.onSharedPreferenceChangeListener not being called consistently
You must un-register the listener in onDestroy() of activity, else Activity object will stay in memory.
How to know app state background to foreground in android?
I had extends my activities from one Baseactivity call and Baseactivity class extends android Activity.
I put code appcomeForeground() into base activity on onRestart() but its call when we navigate activity into our foreground app also.
Please suggest way to get call back only when app comes foreground.
Thanks in advance.
to check whether your application is in background of foreground you can do the following.
Declare a class which will maintain the state
public class ApplicationState {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private volatile static boolean activityVisible;
}
in the onResume method of every activity of your application call
ApplicationState.activityResumed()
and in onPause method of every activity of your application call
ApplicationState.activityPaused()
Now at anytime you can check the foreground/background state of your application by just calling
ApplicationState.isActivityVisible()
Maintain a boolean variable in Baseactivity,
i.e.:
private boolean isForeground;
Inside onResume() of Baseactivity make isForeground = true
and inside onPause() method of Baseactivity make isForeground = false
and whenever you want to know the status,check that boolean variable and apply your further logic accordingly.
There's no framework-provided way to do this. I've described my own solution here: https://stackoverflow.com/a/14734761/1207921
Another way to solve is to call putExtra on the intents which let the user navigate between the app's activities. If onRestart/onResume does not receive this Extra, the app was just coming into foreground.
I have three activities ActivityA, ActivityB, ActivityC.
Suppose in ActivityA, there is some code like...
if(someCondition()){
gotoActivityB();
}
else{
gotoActivityC();
}
Now, If user goes to ActivityB, ActivityA should not be finished.
If he goes to ActivityC, it should be finished.
Adding noHistory in manifest file doesn't work.
Also, finish()in if condition doesn't work, As there are many activities after ActivityC in which ActivityA should be in background.
I don't want to call startActivity(context,ActivityA.class)in those activities onBackPressed() because, it will again execute code of onCreate() in ActivityA.
So, is there a way, where i can remove ActivityA from the stack when user presses back button in ActivityB?
may be something like this:?
ActivityB.this.finish();
ActivityA.finish(); //some code to finish ActivityA
Okay, here is one way you can accomplish your goal. You will need to pass around the Activity context to wherever you need it in order to call finish() on it. I used the Application class to do this. I only used two classes to do it for the sake of time, but it should work just fine for your purposes. Here is how I did it:
This is the first class. It is the Activity that we want to close from another Activity.
public class MainActivity extends Activity implements OnClickListener {
private Button button;
// application instance
private MainApplication mainApplication;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainApplication = (MainApplication) getApplicationContext();
// set the Activity's context for later usage. Doing this determines which
// Activity can be closed from another Activity.
mainApplication.setActivityContext(this);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
Intent i = new Intent(this, SecondActivity.class);
startActivity(i);
break;
}
}
}
This is the Second Activity. Exiting out of it will also cause finish() to be called on the first class:
public class SecondActivity extends Activity {
private Activity activityContext;
private MainApplication mainApplication;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_activity_layout);
mainApplication = (MainApplication) getApplicationContext();
// get the Activity context you stored in the MainApplication class
// so you can call finish on it.
activityContext = mainApplication.getActivityContext();
}
#Override
protected void onPause() {
super.onPause();
// closes your defined Activity. If you press the back button you will find
// that you exit right out of the app as the other Activity gets popped off
// the stack.
activityContext.finish();
}
}
And the Application class:
public class MainApplication extends Application {
private Activity activityContext;
public Activity getActivityContext() {
return activityContext;
}
public void setActivityContext(Activity activityContext) {
this.activityContext = activityContext;
}
}
And of course make sure to declare your MainApplication class in the AndroidManifest:
<application
android:name=".MainApplication"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
This is a sort of hacky way to do this. There may be better ways. But regardless, you have to pass around the context of the Activity that you want to call finish() on. Then you can close it from anywhere.
Hi you can finish your activity in current activity itself based on the condition. or use StartActivityforResult based on the result you can finish your activity.
hope this will help you.
You can try this in another way, like i do.
Create a static instance variable of the activity in the beginning.
private static Activity1 thisAct = null; // Activity1 is name of class
Now initialize this variable in onCreate() method
thisAct = this;
Create a static method which will finish this activity
public static void finishActivity()
{
thisAct.finish();
}
While going to Activity C, clear the FLAG :
Intent cIntent = new Intent(view.getContext(), cActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(cIntent);
Actually i have created an singleton class. Now my singleton class extends Activity, and i have write onCreate() and onStart() method on this class. But it is never called.The code i have used is shown below. If anyone knows help me to solve these out.
Code
public class cycleManager
{
private static CycleManager m_cycleManagerObj;
private CycleManager()
{
// Initialise Variable
onInitialization();
readData(this); // show error when call from here
}
public static synchronized CycleManager getSingletonObject()
{
if (m_cycleManagerObj == null)
{
m_cycleManagerObj = new CycleManager();
}
return m_cycleManagerObj;
}
public Object clone() throws CloneNotSupportedException
{
throw new CloneNotSupportedException();
}
public void writeData(Context c)
{
SharedPreferences preferencesWrite = c.getSharedPreferences("myPreferences", 0);
SharedPreferences.Editor editor = preferencesWrite.edit();
// work to be done
}
public void readData(Context c)
{
SharedPreferences preferencesRead = c.getSharedPreferences("myPreferences", 0);
// work to be done
}
}
The thing is Android manages activities in its own manner: from calling a constructor to calling all lifecycle methods. So if you declare your Activity's constructor as private then Android will not be able to manage this activity.
Why do you need singleton Activity-class? Consider different launch modes
check your activity in the AndroidManifest.xml.
<activity
android:configChanges="orientation|keyboardHidden"
android:name=".ActivityName">
They are not public method.They are protected method.You should override existing method.try like the following.
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
The key here is that Android is supposed to be managing your activity lifecycle, not you.
onCreate and onStart (along with onPause, onDestroy and all the other android activity lifecycle functions) are called by the looper on Android's main thread.
How did you start this activity? Was it declared in your manifest as your main activity and launcher? Did you call startActivity and pass the class name?
The fact that you are creating a singleton instance of your activity, and that its constructor is private, suggests to me that Android would be unable to start this activity when you want it to, though some function for passing an existing activity to be managed may exist, and I've just never seen it.
If onCreate and onStart are never being called, it means Android doesn't know it is supposed to be running your activity.
You get an error because your class is not a subclass of Context. Add Context attribute to getSingletonObject(Context context) method and pass it to CycleManager(Context context) constructor.