I want to implement some sort of a monitor function, that tells me when my app is crashed or stopped or on pause. So I have 3 activities and I achieved so far that when in the mainActivity onPause is called it will send me a mail, however, I only want to know if someone stops the whole app and not just one of the activities (since the user jumps between them). Is there some kind of an overall onStop() Method or something that I can use?
Thanks !
This is my code
protected void onStop() {
super.onStop();
new Thread(new Runnable() {
public void run() {
try {
GMailSender sender = new GMailSender(
"email address",
"pw");
sender.sendMail("Pause", "The app has paused",
"email address",
"email address");
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_LONG).show();
}
}
}).start();
}
Maybe create three Booleans... activityOneAlive, activityTwoAlive, and activityThreeAlive. When each activity hits onStart(), it writes true, and right before switching to a different activity in your app, it sets its own Boolean to false. When any activity hits onPause(), and its Boolean is true, then send your email.
Add a private field to your Activity class:
private userIsLeavingActivity = false;
public onStart(){
...
userIsLeavingActivity = false;
...
}
public onbackPressed(){
...
userIsLeavingActivity = true;
...
}
public onUserLeaveHint(){
...
userIsLeavingActivity = true;
...
}
public onPause(){
if(isFinishing()){
// activity is finishing, not just paused.
}
}
public onStop()
{
if(userIsLeavingActivity){
// do what you want going to another activity or app
}
else{
// your activity is being stopped by something else
}
You could this into a master Activity then extend your other activities from this. Of course, I've left out the super() calls and so on. Needs some adaptation but you should be able to combine all of these to determine exactly why your activity is pausing or stopping.
The problem you now need to solve is a universal exception catch. See this question.
Create an Application class and in there, create an object that implements the ActivityLifecycleCallbacks interface. In that object, increment an integer in every onStart, and decrement it in every onStop. Also in onStop, if the integer is 0, then send your email.
http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html
package com.your.package;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.os.Build;
import android.os.Bundle;
public class ExampleApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
setupLifecycleCallbacks();
}
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
sendEmail();
}
});
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void setupLifecycleCallbacks() {
registerActivityLifecycleCallbacks( new ActivityLifecycleCallbacks() {
int mActivityCount = 0;
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(Activity activity) {
mActivityCount++;
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
mActivityCount--;
if (mActivityCount == 0){
sendEmail();
}
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
});
}
private void sendEmail() {
new Thread(new Runnable() {
public void run() {
try {
GMailSender sender = new GMailSender(
"email address",
"pw");
sender.sendMail("Pause", "The app has paused",
"email address",
"email address");
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_LONG).show();
}
}
}).start();
}
}
Related
I want to execute a function only when returning to the Application from the background.
I have included the method in onResume, and this does it to a certain extent. Problem is since onResume is fired even on creating the Activity and when returning to the activity from another activity (Ex: From pressing the back button), and the function is executed there as well.
How to avoid this and execute the function only when returning from background?
Ps: My application already has multiple places using startActivity so changing to startActivityForResult is a tedious task.
Also all my Activities are extending from a common BaseAppCompactActivity class and it's where my method is located, so this will apply to the whole application.
Edit 2:
My BaseAppCompactActivity is as below with LifecycleObserver implemented now. This doesn't seem to work though.
public class BaseAppCompactActivity extends AppCompatActivity implements LifecycleObserver {
private String TAG = BaseAppCompactActivity.class.getSimpleName();
#Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
#Override
protected void onPause() {
super.onPause();
stopService();
}
#Override
protected void onPostResume() {
super.onPostResume();
startService();
}
// #Override
// protected void onResume() {
// super.onResume();
//// updateLastAccessedDate();
// }
private void startService() {
startService(new Intent(this, BusinessCacheService.class));
}
private void stopService() {
stopService(new Intent(this, BusinessCacheService.class));
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
private void updateLastAccessedDate() {
//Do something
}
}
Although its a duplicate . Here is a Java implementation i am sharing for sake of help ..
public class MyApplication extends MultiDexApplication implements LifecycleObserver {
private boolean previouslyInBackground;
#Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
void onAppBackgrounded() {
previouslyInBackground=true;
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
void onAppForegrounded() {
if(previouslyInBackground){
// Do your stuff Here
}
previouslyInBackground=false;
}
}
Add the Gradle dependency from Lifecycle-aware components Documentation
You can use startActivityForResult instead of startActivity.
Then you can catch the returning inside onActivityResult method.
first set a global boolean variable like this:-
boolean isPaused = false;
now set a methods in your activity :-
#Override
protected void onUserLeaveHint() {
isPaused = true;
super.onUserLeaveHint();
}
or in your onResume method:-
#Override
protected void onResume() {
if(isPaused){
isPaused = false;
}
super.onResume();
}
Do like this
add these variable in your main activity
public static boolean isAppWentToBg = true;
public static boolean isWindowFocused = false;
public static boolean isBackPressed = false;
and also add these methods
private void applicationWillEnterForeground() {
if (isAppWentToBg) {
isAppWentToBg = false;
// Toast.makeText(getApplicationContext(), "App is in foreground", Toast.LENGTH_SHORT).show();
}
}
public void applicationdidenterbackground() {
if (!isWindowFocused) {
isAppWentToBg = true;
// Toast.makeText(getApplicationContext(), "App is Going to Background", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onBackPressed() {
isBackPressed = true;
Log.d(TAG, "onBackPressed " + isBackPressed + "" + this.getLocalClassName());
super.onBackPressed();
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
isWindowFocused = hasFocus;
if (isBackPressed && !hasFocus) {
isBackPressed = false;
isWindowFocused = true;
}
super.onWindowFocusChanged(hasFocus);
}
#Override
protected void onStart() {
Log.d(TAG, "onStart isAppWentToBg " + isAppWentToBg);
applicationWillEnterForeground();
super.onStart();
}
#Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop ");
applicationdidenterbackground();
}
What I would suggest is create a new boolean variable which say if that is created for the first time in resume and work on it.
Boolean isForeGround = true;
#Override
public void onPause() {
super.onPause();
isForeGround = false;
}
#Override
public void onResume() {
super.onPause();
if(!isForeGround){
isForeGround = true;
// write your code here
}
}
I am working on application, where in am getting a tasks from server and then user evaluate through Mobile Application(just like a quiz Application).
During Evaluation of Tasks, if the user presses home button then app goes to background. And when user back to application from recent background applications, then Application started from Splash screen.
I am confused that what price of code should I add, so that when user back to Application, then previous state must be shown to the user??
copy this class further i will tell you what to do
public class Foreground implements Application.ActivityLifecycleCallbacks {
public static final long CHECK_DELAY = 50;
public static final String TAG = Foreground.class.getName();
public interface Listener {
public void onBecameForeground();
public void onBecameBackground();
}
private static Foreground instance;
private boolean foreground = false, paused = true;
private Handler handler = new Handler();
private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
private Runnable check;
/**
* Its not strictly necessary to use this method - _usually_ invoking
* get with a Context gives us a path to retrieve the Application and
* initialise, but sometimes (e.g. in test harness) the ApplicationContext
* is != the Application, and the docs make no guarantees.
*
* #param application
* #return an initialised Foreground instance
*/
public static Foreground init(Application application){
if (instance == null) {
instance = new Foreground();
application.registerActivityLifecycleCallbacks(instance);
}
return instance;
}
public static Foreground get(Application application){
if (instance == null) {
init(application);
}
return instance;
}
public static Foreground get(Context ctx){
if (instance == null) {
Context appCtx = ctx.getApplicationContext();
if (appCtx instanceof Application) {
init((Application)appCtx);
}
throw new IllegalStateException(
"Foreground is not initialised and " +
"cannot obtain the Application object");
}
return instance;
}
public static Foreground get(){
if (instance == null) {
throw new IllegalStateException(
"Foreground is not initialised - invoke " +
"at least once with parameterised init/get");
}
return instance;
}
public boolean isForeground(){
return foreground;
}
public boolean isBackground(){
return !foreground;
}
public void addListener(Listener listener){
listeners.add(listener);
}
public void removeListener(Listener listener){
listeners.remove(listener);
}
#Override
public void onActivityResumed(Activity activity) {
paused = false;
boolean wasBackground = !foreground;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
if (wasBackground){
Log.i(TAG, "went foreground");
for (Listener l : listeners) {
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
#Override
public void onActivityPaused(Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = new Runnable(){
#Override
public void run() {
if (foreground && paused) {
foreground = false;
Log.i(TAG, "went background");
for (Listener l : listeners) {
try {
l.onBecameBackground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
}, CHECK_DELAY);
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
#Override
public void onActivityStarted(Activity activity) {}
#Override
public void onActivityStopped(Activity activity) {}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
#Override
public void onActivityDestroyed(Activity activity) {}
}
add this in onCreate of year Application Class
Foreground foreground = Foreground.init(this);
final Foreground.Listener myListener = new Foreground.Listener()
{
public void onBecameForeground()
{
Log.d("TAG", "FOREGROUND");
}
public void onBecameBackground()
{
//registerActivityLifecycleCallbacks(new MyLifecycleHandler());
Intent i = new Intent("android.intent.action.MAIN").putExtra("some_msg", "I will be sent!");
sendBroadcast(i);
}
};
foreground.addListener(myListener);
add this code in onCreate of your Base Activity ok ?
IntentFilter intentFilter = new IntentFilter(
"android.intent.action.MAIN");
mReceiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
//extract our message from intent
String msg_for_me = intent.getStringExtra("some_msg");
//log our message value
Log.i("InchooTutorial", msg_for_me);
finish();
}
};
//registering our receiver
this.registerReceiver(mReceiver, intentFilter);
not this this is your override onDestroy method copy outside the oncreate
#Override
protected void onDestroy()
{
super.onDestroy();
unregisterReceiver(mReceiver);
}
Overide the methods onStop(), onPause(), onResume() in your main activity.
I'm trying to write an app that will launch, or show a notification or a popup when another specific app goes to the background.
For example:
User launches app A
User uses app A
User puts app A in the background, either by pressing the home button or back button or launching another app
My app detects that and launches itself or shows a popup or whatever
Is there a way to do this and if there is, without killing the battery?
In your app A put the below code in a class:
public class Foreground implements Application.ActivityLifecycleCallbacks {
public static final long CHECK_DELAY = 500;
public static final String TAG = Foreground.class.getName();
public interface Listener {
public void onBecameForeground();
public void onBecameBackground();
}
private static Foreground instance;
private boolean foreground = false, paused = true;
private Handler handler = new Handler();
private List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
private Runnable check;
/**
* Its not strictly necessary to use this method - _usually_ invoking
* get with a Context gives us a path to retrieve the Application and
* initialise, but sometimes (e.g. in test harness) the ApplicationContext
* is != the Application, and the docs make no guarantees.
*
* #param application
* #return an initialised Foreground instance
*/
public static Foreground init(Application application) {
if (instance == null) {
instance = new Foreground();
application.registerActivityLifecycleCallbacks(instance);
}
return instance;
}
public static Foreground get(Application application) {
if (instance == null) {
init(application);
}
return instance;
}
public static Foreground get(Context ctx) {
if (instance == null) {
Context appCtx = ctx.getApplicationContext();
if (appCtx instanceof Application) {
init((Application) appCtx);
}
throw new IllegalStateException(
"Foreground is not initialised and " +
"cannot obtain the Application object");
}
return instance;
}
public static Foreground get() {
if (instance == null) {
throw new IllegalStateException(
"Foreground is not initialised - invoke " +
"at least once with parameterised init/get");
}
return instance;
}
public boolean isForeground() {
return foreground;
}
public boolean isBackground() {
return !foreground;
}
public void addListener(Listener listener) {
listeners.add(listener);
}
public void removeListener(Listener listener) {
listeners.remove(listener);
}
#Override
public void onActivityResumed(Activity activity) {
paused = false;
boolean wasBackground = !foreground;
foreground = true;
if (check != null)
handler.removeCallbacks(check);
if (wasBackground) {
Log.i(TAG, "went foreground");
for (Listener l : listeners) {
try {
l.onBecameForeground();
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
#Override
public void onActivityPaused(Activity activity) {
paused = true;
if (check != null)
handler.removeCallbacks(check);
handler.postDelayed(check = new Runnable() {
#Override
public void run() {
if (foreground && paused) {
foreground = false;
Log.i(TAG, "went background");
for (Listener l : listeners) {
try {
l.onBecameBackground();
Intent intent = getPackageManager().getLaunchIntentForPackage("com.package.name");
if (intent != null) {
// We found the activity now start the activity
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else {
// Bring user to the market or let them choose an app?
intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse("market://details?id=" + "com.package.name"));
startActivity(intent);
}
} catch (Exception exc) {
Log.e(TAG, "Listener threw exception!", exc);
}
}
} else {
Log.i(TAG, "still foreground");
}
}
}, CHECK_DELAY);
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
}
Now in onCreate method of Application.class put the code:
#Override
public void onCreate() {
super.onCreate();
Foreground.init(this);
}
You have to set your app's package name in onActivityPaused() method.
Thanks
Do the code in onPause
#Override
protected void onPause() {
Toast.makeText(getApplicationContext(),"Background",Toast.LENGTH_LONG).show();
super.onPause();
}
So if the app goes in background, you will get toast.
Hope it helps..All the best
Try using Services. Read more on http://developer.android.com/guide/components/services.html
Okay this is driving me nuts. I have a worker thread, (Network call) that needs to run separate of the UI thread, (Its actually a ThreadPoolExecutor but I simplified it to prove my point). If you run this code in portrait, without rotation, the text updates. I put in the delay there to allow for rotations to show my issue. If you start in portrait and before text updates rotation to landscape the text does not update. If you comment the code you can see the listener fire but the text never updates.
I am trying to simulate a custom network call running in a separate thread that may take some time to come back if the user rotates in between then the data gets lost. We are trying to prevent multiple network calls to save data usage on a phone.
package com.example.test;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.res.Configuration;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int orientation = getResources().getConfiguration().orientation;
if (Configuration.ORIENTATION_LANDSCAPE == orientation) {
//Do SomeThing; // Landscape
} else {
startBackgroundWork();
//Do SomeThing; // Portrait
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private Handler mHandler = new Handler();
public void startBackgroundWork() {
new WorkingThread(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
}).start();
}
public interface SomeListener {
public void onSomethingDone(Object result);
}
public class WorkingThread extends Thread {
private SomeListener mListener;
public WorkingThread(SomeListener listener) {
mListener = listener;
}
public void run() {
/* do some work */
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mListener.onSomethingDone("New Text");
}
}
}
It's because on rotation, the activity is re created, therefore all your code is binded to the old activity. You should make a reference to your working thread :
private static WorkingThread mWorkingThread;
public void startBackgroundWork() {
mWorkingThread = new WorkingThread(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
}).start();
}
then onCreate update it :
public class WorkingThread extends Thread {
private SomeListener mListener;
public WorkingThread(SomeListener listener) {
mListener = listener;
}
public void run() {
/* do some work */
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mListener.onSomethingDone("New Text");
}
public void updateListener(SomeListener listener) {
mListener = listener;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startBackgroundWork();
}
public void startBackgroundWork() {
if (mWorkingThread == null || mWorkingThread.isFinished()) { // Use a boolean to know if it still running
mWorkingThread = new WorkingThread(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
});
mWorkingThread.start();
} else {
mWorkingThread.updateListener(new SomeListener() {
public void onSomethingDone(final Object result) {
mHandler.post(new Runnable() {
public void run() {
((TextView)findViewById(R.id.text)).setText((String)result);
//showMyDialog(result);
}
});
}
});
}
}
But there is some improvments you could make :
The WorkingThread class should be static to avoid direct reference to the old activity : Java: Static vs non static inner class
Then make a reference to the current activity, and update it when it is recreated
Make a method for update of the text, instead of having the code directly in the listener
I used Toast to make notification, but it seems it will appear even its activity is not in the current screen and some other activity has been started.
I want to check this situation, when the activity is not the current one, I'd not send the Toast notification. But how to do ?
When your Activity comes to the foreground, its onResume() method will be invoked. When another Activity comes in front of your Activity, its onPause() method will be invoked. So all you need to do is implement a boolean indicating if your Activity is in the foreground:
private boolean isInFront;
#Override
public void onResume() {
super.onResume();
isInFront = true;
}
#Override
public void onPause() {
super.onPause();
isInFront = false;
}
ArrayList<String> runningactivities = new ArrayList<String>();
ActivityManager activityManager = (ActivityManager)getBaseContext().getSystemService (Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager.getRunningTasks(Integer.MAX_VALUE);
for (int i1 = 0; i1 < services.size(); i1++) {
runningactivities.add(0,services.get(i1).topActivity.toString());
}
if(runningactivities.contains("ComponentInfo{com.app/com.app.main.MyActivity}")==true){
Toast.makeText(getBaseContext(),"Activity is in foreground, active",1000).show();
}
This way you will know if the pointed activity is the current visible activity.
I prefer not to handle the state by myself, so I have implemented a class that does this for me.
package mypackage;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
// Mine extends AppCompatActivity - your's might need to extend Activity, depending on whether
// you use the support library or not.
public class StateTrackingActivity extends AppCompatActivity {
public enum ActivityState {
CREATED, RESUMED, STARTED, PAUSED, STOPPED, DESTROYED
}
private ActivityState _activityState;
protected ActivityState getActivityState() { return _activityState; }
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_activityState = ActivityState.CREATED;
}
#Override
protected void onResume() {
super.onResume();
_activityState = ActivityState.RESUMED;
}
#Override
protected void onStart() {
super.onStart();
_activityState = ActivityState.STARTED;
}
#Override
protected void onPause() {
super.onPause();
_activityState = ActivityState.PAUSED;
}
#Override
protected void onStop() {
super.onStop();
_activityState = ActivityState.STOPPED;
}
#Override
protected void onDestroy() {
super.onDestroy();
_activityState = ActivityState.DESTROYED;
}
}
Then your activity can extend this one and you can get the state by calling getActivityState().
This is my ultimate isActivityVisible function.
protected boolean isActivityVisible() {
if (this.mActivity != null) {
Class klass = this.mActivity.getClass();
while (klass != null) {
try {
Field field = klass.getDeclaredField("mResumed");
field.setAccessible(true);
Object obj = field.get(this.mActivity);
return (Boolean)obj;
} catch (NoSuchFieldException exception1) {
// Log.e(TAG, exception1.toString());
} catch (IllegalAccessException exception2) {
// Log.e(TAG, exception2.toString());
}
klass = klass.getSuperclass();
}
}
return false;
}
if (BaseActivity.this instanceof Faq)
{
Toast.makeText(BaseActivity.this, "You are in the Same Page", Toast.LENGTH_SHORT).show();
}else {
Intent intent = new Intent(BaseActivity.this, Faq.class);
startActivity(intent);
drawer.closeDrawer(GravityCompat.START);
}
//// here am All my activities are extending on Activity called BaseActivity
There is Activity#isTaskRoot() method
if ( getActivity() instanceof ManageCardActivity){
// your code
}