转载时注明地址:http://blog.csdn.net/xiaanming/article/details/9344703
我们开发Android应用的时候,当出现Crash的时候,系统弹出一个警告框,如下图一,有些手机会黑屏几秒钟然后还伴随着振动,作为我们开发人员,是很讨厌这样子的Crash,因为这意味着我们又要改bug,每个程序员都希望自己开发出来的东西bug少点,稳定点,但是没有bug的程序几乎是不可能的,作为用户,如果出现这样子的警告框,他的心情也会很不爽,也许还会破口大骂,如果用图二来提示用户是不是感觉会好一点
一句简简单单的“很抱歉,程序遭遇异常,即将退出”是不是更有人情味,人们对道歉的话是永远不会嫌腻的,哈哈!当然我们自定义Carsh处理类不仅仅是将系统警告框替换成Toast,还有更重要的原因,我们都知道市场上的Android设备和系统琳琅满目,参差不齐的,如果我们购买市场上每一种Android设备来测试,这是不现实的,况且这其中购买设备的资金也不小,所以我们重写Crash处理类,当我们的用户发生Crash的时候,我们将设备信息和错误信息保存起来,然后再上传到服务器中,这对于我们是极其有帮助的,特别是个人开发用户和小公司,因为他们的测试可能相对于大公司来说会显得不那么专业,就比如我们公司吧,自己测试,然后我们老大也要我做这个功能,我就将捕获异常和保存异常信息和大家分享下,上传到服务器功能的大家自行实现
先看下项目结构
1.我们新建一个CustomCrashHandler类 实现UncaughtExceptionHandler接口,重写回调方法void uncaughtException(Thread thread, Throwable ex)
-
<span style="font-size:12px;">package com.example.customcrash;
-
import java.io.File;
-
import java.io.FileOutputStream;
-
import java.io.PrintWriter;
-
import java.io.StringWriter;
-
import java.lang.Thread.UncaughtExceptionHandler;
-
import java.text.SimpleDateFormat;
-
import java.util.Date;
-
import java.util.HashMap;
-
import java.util.Map;
-
import java.util.TimeZone;
-
-
import android.content.Context;
-
import android.content.pm.PackageInfo;
-
import android.content.pm.PackageManager;
-
import android.content.pm.PackageManager.NameNotFoundException;
-
import android.os.Build;
-
import android.os.Environment;
-
import android.os.Looper;
-
import android.util.Log;
-
import android.widget.Toast;
-
-
-
-
-
-
-
-
public class CustomCrashHandler implements UncaughtExceptionHandler {
-
private static final String TAG = "Activity";
-
private Context mContext;
-
private static final String SDCARD_ROOT = Environment.getExternalStorageDirectory().toString();
-
private static CustomCrashHandler mInstance = new CustomCrashHandler();
-
-
-
private CustomCrashHandler(){}
-
-
-
-
-
public static CustomCrashHandler getInstance(){
-
return mInstance;
-
}
-
-
-
-
-
@Override
-
public void uncaughtException(Thread thread, Throwable ex) {
-
-
savaInfoToSD(mContext, ex);
-
-
-
showToast(mContext, "很抱歉,程序遭遇异常,即将退出!");
-
try {
-
thread.sleep(2000);
-
} catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
-
-
-
-
ExitAppUtils.getInstance().exit();
-
-
}
-
-
-
-
-
-
public void setCustomCrashHanler(Context context){
-
mContext = context;
-
Thread.setDefaultUncaughtExceptionHandler(this);
-
}
-
-
-
-
-
-
-
private void showToast(final Context context, final String msg){
-
new Thread(new Runnable() {
-
-
@Override
-
public void run() {
-
Looper.prepare();
-
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
-
Looper.loop();
-
}
-
}).start();
-
}
-
-
-
-
-
-
-
-
private HashMap<String, String> obtainSimpleInfo(Context context){
-
HashMap<String, String> map = new HashMap<String, String>();
-
PackageManager mPackageManager = context.getPackageManager();
-
PackageInfo mPackageInfo = null;
-
try {
-
mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
-
} catch (NameNotFoundException e) {
-
e.printStackTrace();
-
}
-
-
map.put("versionName", mPackageInfo.versionName);
-
map.put("versionCode", "" + mPackageInfo.versionCode);
-
-
map.put("MODEL", "" + Build.MODEL);
-
map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
-
map.put("PRODUCT", "" + Build.PRODUCT);
-
-
return map;
-
}
-
-
-
-
-
-
-
-
private String obtainExceptionInfo(Throwable throwable) {
-
StringWriter mStringWriter = new StringWriter();
-
PrintWriter mPrintWriter = new PrintWriter(mStringWriter);
-
throwable.printStackTrace(mPrintWriter);
-
mPrintWriter.close();
-
-
Log.e(TAG, mStringWriter.toString());
-
return mStringWriter.toString();
-
}
-
-
-
-
-
-
-
-
private String savaInfoToSD(Context context, Throwable ex){
-
String fileName = null;
-
StringBuffer sb = new StringBuffer();
-
-
for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) {
-
String key = entry.getKey();
-
String value = entry.getValue();
-
sb.append(key).append(" = ").append(value).append("\n");
-
}
-
-
sb.append(obtainExceptionInfo(ex));
-
-
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
-
File dir = new File(SDCARD_ROOT + File.separator + "crash" + File.separator);
-
if(! dir.exists()){
-
dir.mkdir();
-
}
-
-
try{
-
fileName = dir.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
-
FileOutputStream fos = new FileOutputStream(fileName);
-
fos.write(sb.toString().getBytes());
-
fos.flush();
-
fos.close();
-
}catch(Exception e){
-
e.printStackTrace();
-
}
-
-
}
-
-
return fileName;
-
-
}
-
-
-
-
-
-
-
-
private String paserTime(long milliseconds) {
-
System.setProperty("user.timezone", "Asia/Shanghai");
-
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
-
TimeZone.setDefault(tz);
-
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
-
String times = format.format(new Date(milliseconds));
-
-
return times;
-
}
-
}</span><span style="font-size: 14px;">
-
</span>
上面保存信息到SD卡中需要添加权限<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
2.我们需要重写Application类,在onCreate()方法中为它设置Crash处理类
-
package com.example.customcrash;
-
-
import android.app.Application;
-
-
public class CrashApplication extends Application {
-
-
@Override
-
public void onCreate() {
-
super.onCreate();
-
CustomCrashHandler mCustomCrashHandler = CustomCrashHandler.getInstance();
-
mCustomCrashHandler.setCustomCrashHanler(getApplicationContext());
-
}
-
-
}
我们需要在AndroidManifest.xml指定Application为CrashApplication,
3.接下来我们写一个MainActivity,它里面存在一个导致程序Crash的空指针异常
-
package com.example.customcrash;
-
-
import android.app.Activity;
-
import android.os.Bundle;
-
import android.widget.TextView;
-
-
public class MainActivity extends Activity {
-
TextView mTextView;
-
-
@Override
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
-
mTextView.setText("crash");
-
}
-
}
运行程序,你会发现一句很友好的“很抱歉,程序遭遇异常,即将退出”代替了冷冰冰的警告框,我们打开DDMS,在mnt/sdcard/Crash/目录下面发现了有一个文件,打开文件,我们可以看到
-
versionCode = 1
-
PRODUCT = sdk
-
MODEL = sdk
-
versionName = 1.0
-
SDK_INT = 8
-
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.customcrash/com.example.customcrash.MainActivity}: java.lang.NullPointerException
-
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663)
-
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679)
-
at android.app.ActivityThread.access$2300(ActivityThread.java:125)
-
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033)
-
at android.os.Handler.dispatchMessage(Handler.java:99)
-
at android.os.Looper.loop(Looper.java:123)
-
at android.app.ActivityThread.main(ActivityThread.java:4627)
-
at java.lang.reflect.Method.invokeNative(Native Method)
-
at java.lang.reflect.Method.invoke(Method.java:521)
-
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
-
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
-
at dalvik.system.NativeStart.main(Native Method)
-
Caused by: java.lang.NullPointerException
-
at com.example.customcrash.MainActivity.onCreate(MainActivity.java:15)
-
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
-
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
-
... 11 more
说到这里,算是说完了,接下来我们只需要将我们的错误文件上传到服务器就行了。
感谢4楼的朋友提出文章的bug,遭遇异常的时候程序退不出去,然后我写了一个程序退出的工具类,直接用这个工具类就能很好在任何地方退出程序,工具类如下
-
<span style="font-size:12px;">package com.example.customcrash;
-
-
import java.util.LinkedList;
-
import java.util.List;
-
-
import android.app.Activity;
-
-
-
-
-
-
-
-
-
-
public class ExitAppUtils {
-
-
-
-
private List<Activity> mActivityList = new LinkedList<Activity>();
-
private static ExitAppUtils instance = new ExitAppUtils();
-
-
-
-
-
private ExitAppUtils(){};
-
-
-
-
-
-
public static ExitAppUtils getInstance(){
-
return instance;
-
}
-
-
-
-
-
-
-
public void addActivity(Activity activity){
-
mActivityList.add(activity);
-
}
-
-
-
-
-
-
public void delActivity(Activity activity){
-
mActivityList.remove(activity);
-
}
-
-
-
-
-
-
public void exit(){
-
for(Activity activity : mActivityList){
-
activity.finish();
-
}
-
-
System.exit(0);
-
}
-
-
-
}</span><span style="font-size: 14px;">
-
</span>
退出工具类的使用我在代码中说的还比较清楚,相信你很容易使用,然后将CustomCrashHandler类uncaughtException(Thread thread, Throwable ex)方法中的
注:如果你的工程有很多个Activity,你需要在每一个Activity的onCreate()和onDestroy()调用addActivity()和delActivity()方法,这样子是不是显得很多余?你可以写一个BaseActivity继承Activity,然后再BaseActivity的onCreate()和onDestroy()调用addActivity()和delActivity()方法,其他的Activity继承BaseActivity就行了
-
android.os.Process.killProcess(android.os.Process.myPid());
-
System.exit(1);
替换成
-
ExitAppUtils.getInstance().exit();