您的位置:首页 > 移动开发 > Android开发

Android 崩溃异常时弹出Dialog让用户选择是否上传错误信息

2014-08-24 17:48 519 查看
想必凡是使用安卓手机的童鞋们都碰到过这样的情况,不知道是内存还是什么的原因,弹出个提示框,某某应用已终止,对这类的应用,我们会没来由的觉得烦躁,严重点就会删除,所以作为程序员,我们在开发应用的时候,可以选择通过让用户选择是否上传错误信息,来增加交互不显得那么生硬,另外,就是因为不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,能及时获取在该设备上导致崩溃的信息,这对于下一个版本的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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息