I have an app and if the app crashes in a particular activity, it restarts at one of the intermediate parent activities.
This is a problem for me since I have some inputted user info that is lost upon crash.
Is there any way to force the app to start from the launcher screen after restarting from a crash?
Thanks.
Proposed Solution 1 -
Add this tag android:clearTaskOnLaunch="true" in the manifest.xml file to your main activity which should always launch.
Probable Reason why it did not work
When the application crashes, it throws an Exception and we need to handle the Exception and otherwise we would not get the expected behavior
Proposed Solution 2
Try to handle any uncaught Exception and tell the system what to do. To implement this, try the below steps.
Create a class extending Application Class
Handle uncaughtException in your Application subclass.
In your launcher Activity, call your Application class.
After catching an Exception, start your main Activity (as per your requirement).
Code Sample
Step 1 and 2
package com.casestudy.intentsandfilter;
import android.app.Application;
import android.content.Intent;
public class MyApplication extends Application
{
#Override
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException (Thread thread, Throwable e) {
handleUncaughtException (thread, e);
}
});
}
private void handleUncaughtException (Thread thread, Throwable e) {
// The following shows what I'd like, though it won't work like this.
Intent intent = new Intent (getApplicationContext(),DrawView.class);
startActivity(intent);
// Add some code logic if needed based on your requirement
}
}
Step 3
public class MainActivity extends Activity {
protected MyApplication app;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Get the application instance
app = (MyApplication)getApplication();
.............
}
}
Step 4
Modify the below method as per your requirement
private void handleUncaughtException (Thread thread, Throwable e) {
// The following shows what I'd like, though it won't work like this.
Intent intent = new Intent (getApplicationContext(), HomeActivity.class);
startActivity(intent);
// Add some code logic if needed based on your requirement
}
I would recommend using library such as
https://github.com/Ereza/CustomActivityOnCrash
As the library takes care of other stuff along with different versions of android.
First, create and set the App class in your AndroidManifest.xml and
android:name=".App"
android:clearTaskOnLaunch="true"
then put this code in the App class
public class App extends Application {
#Override
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable e) {
Log.d("AppCrash", "Error just lunched ");
}
});
}}
Debug Log Screenshot:
Maybe there's no way to do that but you can flag it so you know if the activity was started through user action or if it was just started after a crash.
i.e when you start the parent activity, pass something into the startActivity intent. If that isn't there then it was started after the crash.
I managed to start my main activity with intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); like this:
private void handleUncaughtException (Thread thread, Throwable e)
{
Intent intent = new Intent (getApplicationContext(), MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
Related
I did a bit of coding to test out Thread.UncaughtExceptionHandler for a specific use case (explained below).
Firstly, I have a BaseActivity implemented this way.
public class BaseActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
Log.e("CRASH_REPORT", "Activity crashed!");
System.exit(0);
}
});
}
}
By extending BaseActivity, any uncaught exception will end up being caught by the handler. System.exit(0) will terminate the VM belongs to the app.
Now, I created 2 activities with this hierarchy (both extending BaseActivity).
ParentActivity -> SubActivity
In ParentActivity, I have only 1 button that will start SubActivity on clicked (code omitted).
In SubActivity.onCreate(...), I purposely inject an exception to trigger uncaughtException(...).
public class SubActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int crash = 1 / 0;
}
}
When SubActivity executed, uncaughtException(...) triggered and the app stopped (obviously without the well known app stopped dialog).
What I want to know is whether its possible to just terminate the triggered Activity (SubActivity in this case) and app will roll-back (sort of) to its previous state (ParentActivity)?
Any advice is appreciated. Thank you.
After some research, I believe there is NO WAY to return to previous Activity when uncaughtException(...) triggered as main thread has been stopped since.
Here, I will list down the ideas I have on "countermeasure" this issue.
1. Declare each Activity in different process (not recommended)
In the manifest file, add android:process=dedicated_process_name under each activity tag. Doing this will make each Activity running in its own process, thus ensuring crashing on 1 process does not affect another. This is not recommended, though.
<activity
android:name=".ParentActivity"
android:process="::parent_process" />
2. Force stop the app (System.exit(code)) and provide a callback where each Activity can define its own handling.
Create a BaseActivity with a onCrashed(...) callback.
public class BaseActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread paramThread, Throwable paramThrowable) {
Log.e("CRASH_REPORT", "Activity crashed!");
onCrashed(thread, throwable);
System.exit(0);
}
});
}
protected void onCrashed(Thread thread, Throwable throwable) {
// space for rent
}
}
All Activity extending from BaseActivity can decide what they want to do on crash. An example is to schedule to start the application again.
public class SubActivity extends BaseActivity
#Override
protected void onCrashed(Thread thread, Throwable throwable) {
Intent i = new Intent(this, ParentActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pi);
}
Alternatively, we can choose to implement uncaughtException(...) at Application level and Application will decide what to do next. (eg. restart current Activity)
I was wondering how to keep a record of launched activites for logging purposes. what broadcast receiver I have to subscribe to intercept this intent? or what intent-filter to use? I figure that I must use some type of long-running service in the background.
My first objetive is to track main-focus applications, some sort of history.
Want to get finally some similar to:
- Launched app com.android.xxx
- Launched app xx.yy.zz
- App xx.yy.zz lost focus
Thanks in advance
EDIT - Just see that app MyAppRank , that does exactly what i mean
What i'm able to figure out from your question is that you want to keep track of all the activities when they are launched in your application. If that is correct, the solution may work for you:
Crate a BaseActivity which all of your Activities should extend
public class BaseActivity extends Activity
{
private Activity activity;
public static final String INTENTFILTER_TRACK_MY_ACTIVITIES="INTENTFILTER_TRACK_MY_ACTIVITIES";
public static final String INTENTFILTER_REMOVE_MY_ACTIVITIES="INTENTFILTER_REMOVE_MY_ACTIVITIES";
public void setActivity(Activity act)
{
activity = act;
}
public Activity getActivity()
{
return activity;
}
#Override protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Intent intent = new Intent();
intent.setAction(INTENTFILTER_TRACK_MY_ACTIVITIES);
intent.putExtra("activityName", activity.getClass().getSimpleName());
sendBroadcast(intent);
}
#Override protected void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
Intent intent = new Intent();
intent.setAction(INTENTFILTER_REMOVE_MY_ACTIVITIES);
intent.putExtra("activityName", activity.getClass().getSimpleName());
sendBroadcast(intent);
setActivity(null);
}
}
Now extend above BaseActivity for all your activities. i.e instead of extending your Activities should extend BaseActivity and call setActivity(this); in onCreate like below:
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setActivity(this);
//write your other code form here
}
}
3.Then write a BroadcastReceiver like below:
class TrackActivitiesReceiver extends BroadcastReceiver
{
private static final Object SEPERATOR = ",";// use , as seperator
String sb="";
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equalsIgnoreCase(BaseActivity.INTENTFILTER_TRACK_MY_ACTIVITIES))
{
sb+=intent.getStringExtra("activityName");
sb+=SEPERATOR;
}
else if(intent.getAction().equalsIgnoreCase(BaseActivity.INTENTFILTER_REMOVE_MY_ACTIVITIES))
{
sb=sb.replace(intent.getStringExtra("activityName")+SEPERATOR, "");
}
}}
4Finally, Register above Receiver in your AndroidManifest.xml
<receiver
android:name="TrackActivitiesReceiver"
android:exported="false" >
<intent-filter>
<action android:name="INTENTFILTER_TRACK_MY_ACTIVITIES" />
</intent-filter>
</receiver>
Hope this solves your problem. cheers!
There are no Intents broadcast when applications are started or when applications come to the foreground. There isn't anything that you can hook into as a listener to get these events.
The way you can do this (which is the way apps like MyAppRank do it) is to use the methods of the ActivityManager:
getRunningTasks()
getRunningAppProcesses()
getRecentTasks()
You create a Service which runs all the time and at regular intervals calls methods of the ActvityManager to determine which task is in the foreground and you can "infer" what the user has done (or is doing). It isn't an exact science.
Note: You will need android.permission.GET_TASKS and none of this works anymore as of API 21 (Android 5, Lollipop). As of API 21 the security has been tightened and an application can only get information about its own tasks, not other tasks in the system.
I have tried almost all the solutions from SO but no success :(.
I have a simple myJavaClass.java with a couple of functions.
One of the functions in myJavaClass : startActivity() starts MyCustomActivity
public startActivity(Context context)
{
Intent intent = new Intent(context, MyCustomActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
This launches MyCustomActivity() as expected.
Now I have another function in myJavaClass.java to close/finish MyCustomActivity but it is not able to do so!
I have tried
Making MyCustomActivity SingleTop in manifest and creating the activity via an intent as above
Passing an activity instance to "this" in onCreate() of MyCustomActivity and calling MyCustomActivity.activity.finish() from myJava.class but that doesnt work as well
Please help me. I have been stuck here for hours now. I know the solution is very simple and conceptual but I am a newbie. Just building Java/Android concepts!
EDIT
MyCustomActivity
public Activity activity;
OnCreate()
{
...
this = activity;
}
MyJavaClass
public closeActivity(Context context)
{
Activity customActivity = MyCustomActivity.activity;
customActivity.finish();
}
I think that what you are trying to do is fundamentally bad. For a start, outside of the Activity code, there are no guarantees that the activity still exists - the memory manager may have cleaned it up, the user may have pressed Back etc. Think of Activities as independent entities - you can start them, and you can optionally get a result back when they finish what they're doing, but that's it.
Think about whether you really have to programmatically close the activity from outside it - I'd say this is an unusual design, but there are circumstances where it may be appropriate.
If so, what I think you want is a publish/subscribe system whereby MyCustomActivity can register a listener with MyJavaClass, and then receive a callback whereupon it can 'finish' itself.
public Activity activity implements FinishListener
{
public void onCreate(...)
{
//where does MyJavaClass come from? see in a minute
MyJavaClass myjava = getMyJavaclass();
myJava.addFinishListener( this );
}
public void onFinishCallback()
{
this.finish();
}
}
and
public class MyJavaClass
{
private List<FinishListener> finishListeners = ...;
public void addFinishListener( FinishListener fl )
{
this.finishListeners.add(fl);
}
public closeActivity(Context context)
{
for ( FinishListener fl : finishListeners )
{
fl.onFinishCallback();
}
}
}
and
public interface FinishListener
{
void onFinishCallback();
}
Now the only remaining issue is how to get MyJavaClass from the Activity. That's up to you - you may already know how, you may be able to put it in your Application implementation, it could be a singleton (bad), the listeners could be static (bad) or various other options.
Oh, and don't forget to remove the listener again in the Activity's onDestroy() method!
Just try this....
public closeActivity(Activity _activity)
{
_activity.finish();
}
you can't finish activity from other class until you have the reference of instance of Activity in that class, give the reference in that class and call finish() method to stop the activity.
activity.finish();
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Using global exception handling with “setUncaughtExceptionHandler” and “Toast”
I have implemented UncaughtExceptionHandler in onCreate() in one of my activities.
In uncaughtException() method I am trying to open another activity ErrorActivity with an extra parameter (error msg and stacktrace). That activity should only show (ment globaly) AlertDialog and handle logs etc.
Can some one tell me why the ErrorActivity doesnt open while the code in oncoughtException gets executed? I suspect the problem is Thread related.
Here is my first activity (simulating exception in onCreate())
public class MainActivity extends Activity {
GlobalSettings settings;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread.currentThread().setUncaughtExceptionHandler(
new UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
Intent intent = new Intent(MainActivity.this,
ErrorActivity.class);
Bundle bundle = new Bundle();
bundle.putString("ERROR", ex.getMessage());
intent.putExtras(bundle);
startActivity(intent);
}
} );
settings = (GlobalSettings) getApplication();
settings = null;
settings.getApplicationContext();
setContentView(R.layout.main);
}
}
And my second activity that should handle errors:
public class ErrorActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getIntent().getExtras();
String name = bundle.getString("ERROR");
ShowAlertDialog(name);
}
}
You can add attribute android:process=":report_process" to the <activity> element which refers to your bug report activity in AndroidManifest.xml.
By default, activities belong to the same appliction would run in the same process identified by your package name. By setting android:process attribute, you can override this. android:process starting with : refers to a private identifier within your package, so that you can start the activity in a new process without conflicting other packages' process.
Refer Using global exception handling with “setUncaughtExceptionHandler” and “Toast”
There Qberticus told
You're not seeing anything because the
exception happened on your UI thread
and the stack unrolled all the way. So
there is no more Looper and there is
no support there that is used to
display the Toast.
Since the exception happens on UI we cannot do an UI operation :(
I am calling a subactivity from main activity. This subactivity should take few numbers from user (i'm using Edit text control to achieve this), save them to static variable in another class and terminate. I want main activity to wait for subactivity but both are just running simultaneously. Even doing sth like that doesn't help:
Thread t = new Thread(new Runnable(){
public void run(){
Log.v("==================", "run "+new Date());
startActivityForResult(new Intent(ctx,myCustomSubactivity.class),1);
} });
Log.v("==================", "calling run "+new Date());
t.start();
try {
t.join();
} catch (InterruptedException e) {Log.v("==================", "can't join");}
Log.v("==================", "back from activity "+new Date());
do you know how to force main activity to wait? Thread.wait() method is not supported in Android(program throws error).
May be I'm missing something but why don't just use startActivityForResult and onActivityResult mechanism? You could get result from you subactivity from intent it was resulted with.
Edit: BTW as far as I understand, if you will run Object.wait() from Activity code if will hold UI tread whitch can result in Application not responding error.
I agree with Nikolay this is definitely the android way to do this.
Start the subactivity with startActivityForResult in the sub activity use setResult to add an result code and an intent with all the numbers you need in the data bundle.
In your first activity overwrite onActivityResult and retrieve the numbers from the Intent.
If you use the static variable this seems easier in the first moment but it is very insecure and there are some cases this may not work. If your program is send to the background your activities will be saved but if the phone runs low on memory the system will close your program and after the user resumes it everything looks like the moment the user left it but the static variables will be recreated to their initialization value.
Try to get used to the way the android activity lifecycle works. Using this approach will result in fewer used memory and a much better user experience.
Check out the Notepad example, it covers exactly this situation. And as others have said, the Android way is to have your first activity start up your second activity (not sub-activity!) and asynchronously listen for a response (not pause or wait, no need for joining, etc.).
Well... you can do it like this (btw, there's not straight forward way):
Have a singleton class, let's call it Monitor:
public class Singleton
{
private Singleton() { }
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
public class ParentActivity extends Activity
{
private void startAndWait()
{
Intent i = new Intent();
// initialize i
startActivityForResult(i);
Singleton si = Singleton.getInstance();
synchronized(si)
{
si.wait();
}
//do remaining work
}
}
public class ChildActivity extends Activity
{
protected void onCreate(Bundle savedInstance)
{
//do all the work
Singleton si = Singleton.getInstance();
synchronized(si)
{
si.notify();
}
}
}
I'm not here to judge if it's a good pattern or not but if you really need an activity to wait for a sub-activity, you can try this approach:
define an object (lock) over which the two activities get synchronized; this can (should) also work as the object to exchange data between those two activities and thus should be defined as static
in parent activity, start an async task (as the UI main thread cannot be in waiting state)
in the async task, start your sub-activity
the async task waits on the lock till it gets notified
the sub-activity does whatever it needs and notifies the waiting thread when it finishes
I did a similar thing in my app and IMHO had a good reason for this (not to bother a user with login screen upon app start or resume, the app tries to re-use credentials stored in a secured place and only in case it fails, it shows this login screen. So yes, basically any activity in my app can get "paused" and waits till the user provides correct credentials in the login activity upon which the login screen finishes and the app continues exactly where it got paused (in the parent activity).
In the code it would be something like this:
ParentActivity:
public class ParentActivity extends Activity {
private static final String TAG = ParentActivity.class.getSimpleName();
public static class Lock {
private boolean condition;
public boolean conditionMet() {
return condition;
}
public void setCondition(boolean condition) {
this.condition = condition;
}
}
public static final Lock LOCK = new Lock();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parent_layout);
// do whatever logic you need and anytime you need to stat sub-activity
new ParentAsyncTask().execute(false);
}
private class ParentAsyncTask extends AsyncTask<Boolean, Void, Boolean> {
#Override
protected Boolean doInBackground(Boolean... params) {
// do what you need and if you decide to stop this activity and wait for the sub-activity, do this
Intent i = new Intent(ParentActivity.this, ChildActivity.class);
startActivity(i);
synchronized (LOCK) {
while (!LOCK.conditionMet()) {
try {
LOCK.wait();
} catch (InterruptedException e) {
Log.e(TAG, "Exception when waiting for condition", e);
return false;
}
}
}
return true;
}
}
}
ChildActivity:
public class ChildActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.child_layout);
// do whatever you need in child activity, but once you want to finish, do this and continue in parent activity
synchronized (ParentActivity.LOCK) {
ParentActivity.LOCK.setCondition(true);
ParentActivity.LOCK.notifyAll();
}
finish();
// if you need the stuff to run in background, use AsyncTask again, just please note that you need to
// start the async task using executeOnExecutor method as you need more executors (one is already occupied), like this:
// new ChildAsyncTask().executeOnExecutor(ChildAsyncTask.THREAD_POOL_EXECUTOR, false);
}
}