最近公司的项目出现了一点棘手的问题,由于某种未知的原因,该 App 在网络连接上出现了问题,只能用轮询来进行网络请求。但是这种方式会使得 App 每隔半小时就崩溃一次,最后无奈之下,决定让 App 在崩溃后自动重启。
当然了,这只是无法可想之下的应急手段,如果可以解决网络推送的毛病就好了……
下面是对于网络上资料的总结。
捕捉异常处理类
网络上对于程序崩溃的处理已经说得很清楚了(虽然看起来都是同一个人写的代码),就是利用 Thread.UncaughtExceptionHandler 类,重写其中的 uncaughtException(Thread thread, Throwable ex) 方法。
自定义异常处理类
如上所述,关键在于重写 uncaughtException(Thread thread, Throwable ex):
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mUncaughtExceptionHandler != null) {
// 若是用户没有设置异常处理,则让系统默认的类来处理异常
mUncaughtExceptionHandler.uncaughtException(thread, ex);
} else {
// 进行自定义的方法
// 设置定时任务,1秒后重启此 App
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent, 0);
AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent);
// 移除当前任务
myApplication.finishActivity();
}
}
其中有一个 handleException(Throwable ex) 方法,同样是自己定义的,意在对崩溃是抛出的异常进行处理,例如打印日志、上传崩溃信息等等。
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
// 此处可以进行一些更为复杂的请求,例如打印崩溃信息日志等
// 这里弹出提示信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context, "很抱歉,程序崩溃了,一秒钟后重启。", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
return true;
}
自定义 Application
异常处理类里需要用到的 Context 对象,应当为 Application。
网上很多的代码都是使用 getApplicationContext() 或是 Application.getBaseContext() ,但是我这里在创建 Toast 、Intent 的时候就会出现无法创建的情况,导致后续代码不能执行,异常处理类自然不能正常工作。
因此,建议在程序入口传入 Application 的 Context 对象,后续直接使用此对象。
@Override
public void onCreate() {
super.onCreate();
// 将自定义的异常处理类设为默认
MyUncaughtExceptionHandler catchException = new MyUncaughtExceptionHandler(getApplicationContext());
Thread.setDefaultUncaughtExceptionHandler(catchException);
}
自定义的 Application 很简单,声明了使用自定义的类来捕获崩溃异常。
最后,别忘了在 Manifest 里对 Application 进行初始化。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mouzhai.test">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
完整代码
MainActivity.class
这里简单地设置了一个按钮,点击按钮就会使程序崩溃。
private MyApplication myApplication;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myApplication = new MyApplication();
myApplication.addActivity(this);
btnCrash = (Button) findViewById(R.id.btn_crash);
btnCrash.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 在子线程中改变 UI,会导致程序崩溃
new Thread(new Runnable() {
@Override
public void run() {
btnCrash.setText("ccccccccccccccccc");
}
}).start();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
myApplication.removeActivity(this);
}
MyApplication.class
/**
* 在此初始化一些必要的逻辑,并定义了一些管理 Activity 的方法
* 需要获取 Application 级别 Context 对象的方法可以设置在这里
* <p>
* Created by Mouzhai on 2017/4/11.
*/
public class MyApplication extends Application {
List<Activity> list = new ArrayList<>();
@Override
public void onCreate() {
super.onCreate();
// 将自定义的异常处理类设为默认
MyUncaughtExceptionHandler catchException = new MyUncaughtExceptionHandler(getApplicationContext());
Thread.setDefaultUncaughtExceptionHandler(catchException);
}
/**
* 当 Activity 结束时,从列表中移除
*/
public void removeActivity(Activity a) {
list.remove(a);
}
/**
* 向列表中添加 Activity
*/
public void addActivity(Activity a) {
list.add(a);
}
/**
* 结束列表中的所有 Activity
*/
public void finishActivity() {
for (Activity activity : list) {
if (null != activity) {
activity.finish();
}
}
// 结束当前进程
android.os.Process.killProcess(android.os.Process.myPid());
}
}
MyUncaughtExceptionHandler.class
/**
* 自定义异常处理类
* <p>
* Created by Mouzhai on 2017/4/11.
*/
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private Context context;
private Thread.UncaughtExceptionHandler mUncaughtExceptionHandler;
private MyApplication myApplication;
public MyUncaughtExceptionHandler(Context context) {
this.context = context;
mUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的异常处理类
myApplication = new MyApplication();
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mUncaughtExceptionHandler != null) {
// 若是用户没有设置异常处理,则让系统默认的类来处理异常
mUncaughtExceptionHandler.uncaughtException(thread, ex);
} else {
// 进行自定义的方法
// 设置定时任务,1秒后重启此 App
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent, 0);
AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent);
// 移除当前任务
myApplication.finishActivity();
}
}
/**
* 对于抛出的异常进行处理,例如打印日志、发送文件给服务器等等
*
* @param ex 导致崩溃的异常
* @return 若在此处理了异常,则返回 true,否则返回 false
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
// 此处可以进行一些更为复杂的请求,例如打印崩溃信息日志等
// 这里弹出提示信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context, "很抱歉,程序崩溃了,一秒钟后重启。", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
return true;
}
}
大功告成!不过最后还是想给自己的项目烧柱香……