想必凡是使用安卓手机的童鞋们都碰到过这样的情况,不知道是内存还是什么的原因,弹出个提示框,某某应用已终止,对这类的应用,我们会没来由的觉得烦躁,严重点就会删除,所以作为程序员,我们在开发应用的时候,可以选择通过让用户选择是否上传错误信息,来增加交互不显得那么生硬,另外,就是因为不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,能及时获取在该设备上导致崩溃的信息,这对于下一个版本的bug修复帮助极大,废话不多说,先上图吧
欢迎转载,请加地址http://blog.csdn.net/jing110fei/article/details/38797599
如果用户点击取消则直接关闭程序,如果用户点击上传,则将错误信息上传至服务器,有关上传至服务器的代码我就不在这里过多描述了,重点说下怎么实现获取异常信息,和弹出Dialog。
首先,我们先人为的创建个错误异常,用来测试。
public class DemoMainActivity extends Activity { private String string; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_demo_main); System.out.println(string.equals("any string")); } }
创建CrashHandler类是专门用来处理异常,我自己根据需要,上传的信息包括,时间-软件版本-手机型号-还有错误信息。
public class CrashHandler implements UncaughtExceptionHandler{ public static final String TAG = "CrashHandler"; StringBuffer sbs = new StringBuffer(); //系统默认的UncaughtException处理类 private Thread.UncaughtExceptionHandler mDefaultHandler; //CrashHandler实例 private static CrashHandler INSTANCE = new CrashHandler(); //程序的Context对象 private Context mContext; //用于格式化日期 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); /** 保证只有一个CrashHandler实例 */ private CrashHandler() { } /** 获取CrashHandler实例 ,单例模式 */ public static CrashHandler getInstance() { return INSTANCE; } /** * 初始化 * * @param context */ public void init(Context context) { mContext = context; //获取系统默认的UncaughtException处理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 当UncaughtException发生时会转入该函数来处理 */ public void uncaughtException(Thread thread, Throwable ex) { // TODO Auto-generated method stub if (!handleException(ex) && mDefaultHandler != null) { //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, ex); } } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. * * @param ex * @return true:如果处理了该异常信息;否则返回false. */ private boolean handleException(Throwable ex) { if (ex == null) { return false; } final Throwable exsThrowable=ex; new Thread() { @Override public void run() { Looper.prepare(); showDialog(mContext); Looper.loop(); } }.start(); // 1.获取软件版本信息 String versioninfo = getVersionInfo(); // 2.获取手机的硬件信息. String mobileInfo = getMobileInfo(); // 3.把错误的堆栈信息 获取出来 String errorinfo = getErrorInfo(ex); String time = formatter.format(new Date()); sbs.append(time); sbs.append("\n"); sbs.append("软件版本:"+versioninfo); sbs.append("\n"); sbs.append("手机型号"+mobileInfo); sbs.append("\n"); sbs.append(errorinfo); return true; } private void showDialog(final Context context) { final Dialog dialog; AlertDialog.Builder buder=new AlertDialog.Builder(context); buder.setTitle("温馨提示"); buder.setMessage("由于发生了一个未知错误,应用已关闭,我们对此引起的不便表示抱歉!" + "您可以将错误信息上传到我们的服务器,帮助我们尽快解决该问题,谢谢!"); buder.setPositiveButton("上传", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub //上传处理---我这里没写,大家根据实际情况自己补上,我这里是一个Toast提示,提示内容就是我们要上传的信息 Log.e(TAG, "---"+sbs.toString()); Toast.makeText(mContext, sbs.toString(), Toast.LENGTH_LONG).show(); new Thread(new Runnable() { public void run() { // TODO Auto-generated method stub try { Thread.sleep(3000); // mDefaultHandler.uncaughtException(thread, ex); } catch (InterruptedException e) { Log.e(TAG, "error : ", e); } //退出程序 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } }).start(); } }); buder.setNegativeButton("取消", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { // TODO Auto-generated method stub android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } }); dialog=buder.create(); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); dialog.setCanceledOnTouchOutside(false);//设置点击屏幕其他地方,dialog不消失 dialog.setCancelable(false);//设置点击返回键和HOme键,dialog不消失 dialog.show(); Log.i("PLog", "2"); } //打印错误信息 private String getErrorInfo(Throwable arg1) { Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); arg1.printStackTrace(printWriter); Throwable cause = arg1.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String error = writer.toString(); return error; } /** * 获取手机的硬件信息 * @return */ private String getMobileInfo() { StringBuffer sb = new StringBuffer(); sb.append(""+android.os.Build.BRAND + android.os.Build.MODEL); sb.append("\n"); return sb.toString(); } /** * 获取软件的版本信息 * @return */ private String getVersionInfo(){ try { PackageManager pm = mContext.getPackageManager(); PackageInfo info =pm.getPackageInfo(mContext.getPackageName(), 0); return info.versionName; } catch (Exception e) { e.printStackTrace(); return "版本号未知"; } } }
完成这个CrashHandler后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application,添加自己的代码,CrashApplication.java代码如下:
public class CrashApplication extends Application{ @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(getApplicationContext()); } }
最后,为了让我们的CrashApplication取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:
<application android:name=".CrashApplication" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".DemoMainActivity" android:label="@string/title_activity_demo_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
还有,为了能使AlertDialog能够正常弹出,我们还需要加上权限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
有些童鞋可能觉得弹出的AlertDialog的样子不太满意,想自己实现,在这里我也是尝试了好久,一开始以为不能自定义,事实上是可以实现的,R.layout.sports_dialog就是我自定义的布局,用下列代码替换上面CrashHandler中showDialog()方法
final Dialog dialog; dialog = new Dialog(mContext,R.style.sports_dialog); LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); View v = inflater.inflate(R.layout.sports_dialog, null); ((TextView) v.findViewById(R.id.message)).setText("由于发生了一个未知错误,应用已关闭,我们对此引起的不便表示抱歉!" + "您可以将错误信息上传到我们的服务器,帮助我们尽快解决该问题,谢谢!"); TextView tv = (TextView)v.findViewById(R.id.title); tv.setText("温馨提示"); v.findViewById(R.id.bt_ok).setOnClickListener( new View.OnClickListener() { public void onClick(View arg0) { dialog.dismiss(); Toast.makeText(mContext, sbs.toString(), Toast.LENGTH_LONG).show(); new Thread(new Runnable() { public void run() { // TODO Auto-generated method stub try { Thread.sleep(3000); // mDefaultHandler.uncaughtException(thread, ex); } catch (InterruptedException e) { Log.e(TAG, "error : ", e); } //退出程序 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } }).start(); } }); v.findViewById(R.id.bt_cancel).setOnClickListener( new View.OnClickListener() { public void onClick(View arg0) { android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } }); dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); dialog.setCanceledOnTouchOutside(false);//设置点击屏幕其他地方,dialog不消失 dialog.setCancelable(false);//设置点击返回键和HOme键,dialog不消失 dialog.setContentView(v); dialog.show();
OK,就是这样,需要的可以直接用的,嘿嘿。
PS:如何在这弹出Dialog是自己处理的,
崩溃异常捕获部分参考自:http://blog.csdn.net/liuhe688/article/details/6584143