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

Android异常处理框架

2014-12-01 09:47 120 查看
Android系统的“程序异常退出”,给应用的用户体验造成不良影响。为了捕获应用运行时异常并给出友好提示,便可继承UncaughtExceptionHandler类来处理。

思路:

1.此类主要完成一下几个任务

1.1 提示友好的错误信息

1.1.1网络异常,错误码:%d

1.1.2网络异常,请求超时

1.1.3网络异常,读取数据超时

1.1.4网络连接失败,请检查网络设置

1.1.5数据解析异常

1.1.6数据解析异常

1.1.7文件流异常

1.1.8应用程序运行时异常

1.2 保存异常日志

1.2.1判断是否挂载了SD卡

1.2.2没有挂载SD卡,无法写文件,直接return

1.2.3将异常写入文件

1.3 自定义异常处理:收集错误信息&发送错误报告

现在来研究一下 开源中国 异常处理框架。

/**
* 应用程序异常类:用于捕获异常和提示错误信息
* @author liux (http://my.oschina.net/liux)
* @version 1.0
* @created 2012-3-21
*/
public class AppException extends Exception implements UncaughtExceptionHandler{

private final static boolean Debug = false;//是否保存错误日志

/** 定义异常类型 */
public final static byte TYPE_NETWORK 	= 0x01;
public final static byte TYPE_SOCKET	= 0x02;
public final static byte TYPE_HTTP_CODE	= 0x03;
public final static byte TYPE_HTTP_ERROR= 0x04;
public final static byte TYPE_XML	 	= 0x05;
public final static byte TYPE_IO	 	= 0x06;
public final static byte TYPE_RUN	 	= 0x07;
public final static byte TYPE_JSON	 	= 0x08;

private byte type;
private int code;

/** 系统默认的UncaughtException处理类 */
private Thread.UncaughtExceptionHandler mDefaultHandler;

private AppException(){
this.mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
}

private AppException(byte type, int code, Exception excp) {
super(excp);
this.type = type;
this.code = code;
if(Debug){
this.saveErrorLog(excp);
}
}
public int getCode() {
return this.code;
}
public int getType() {
return this.type;
}

/**
* 提示友好的错误信息
* @param ctx
*/
public void makeToast(Context ctx){
switch(this.getType()){
case TYPE_HTTP_CODE:
String err = ctx.getString(R.string.http_status_code_error, this.getCode());
Toast.makeText(ctx, err, Toast.LENGTH_SHORT).show();
break;
case TYPE_HTTP_ERROR:
Toast.makeText(ctx, R.string.http_exception_error, Toast.LENGTH_SHORT).show();
break;
case TYPE_SOCKET:
Toast.makeText(ctx, R.string.socket_exception_error, Toast.LENGTH_SHORT).show();
break;
case TYPE_NETWORK:
Toast.makeText(ctx, R.string.network_not_connected, Toast.LENGTH_SHORT).show();
break;
case TYPE_XML:
Toast.makeText(ctx, R.string.xml_parser_failed, Toast.LENGTH_SHORT).show();
break;
case TYPE_JSON:
Toast.makeText(ctx, R.string.xml_parser_failed, Toast.LENGTH_SHORT).show();
break;
case TYPE_IO:
Toast.makeText(ctx, R.string.io_exception_error, Toast.LENGTH_SHORT).show();
break;
case TYPE_RUN:
Toast.makeText(ctx, R.string.app_run_code_error, Toast.LENGTH_SHORT).show();
break;
}
}

/**
* 保存异常日志
* @param excp
*/
public void saveErrorLog(Exception excp) {
String errorlog = "errorlog.txt";
String savePath = "";
String logFilePath = "";
FileWriter fw = null;
PrintWriter pw = null;
try {
//判断是否挂载了SD卡
String storageState = Environment.getExternalStorageState();
if(storageState.equals(Environment.MEDIA_MOUNTED)){
savePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/OSChina/Log/";
File file = new File(savePath);
if(!file.exists()){
file.mkdirs();
}
logFilePath = savePath + errorlog;
}
//没有挂载SD卡,无法写文件
if(logFilePath == ""){
return;
}
File logFile = new File(logFilePath);
if (!logFile.exists()) {
logFile.createNewFile();
}
fw = new FileWriter(logFile,true);
pw = new PrintWriter(fw);
pw.println("--------------------"+(new Date().toLocaleString())+"---------------------");
excp.printStackTrace(pw);
pw.close();
fw.close();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(pw != null){ pw.close(); }
if(fw != null){ try { fw.close(); } catch (IOException e) { }}
}

}

public static AppException http(int code) {
return new AppException(TYPE_HTTP_CODE, code, null);
}

public static AppException http(Exception e) {
return new AppException(TYPE_HTTP_ERROR, 0 ,e);
}

public static AppException socket(Exception e) {
return new AppException(TYPE_SOCKET, 0 ,e);
}

public static AppException io(Exception e) {
if(e instanceof UnknownHostException || e instanceof ConnectException){
return new AppException(TYPE_NETWORK, 0, e);
}
else if(e instanceof IOException){
return new AppException(TYPE_IO, 0 ,e);
}
return run(e);
}

public static AppException xml(Exception e) {
return new AppException(TYPE_XML, 0, e);
}

public static AppException json(Exception e) {
return new AppException(TYPE_JSON, 0, e);
}

public static AppException network(Exception e) {
if(e instanceof UnknownHostException || e instanceof ConnectException){
return new AppException(TYPE_NETWORK, 0, e);
}
else if(e instanceof HttpException){
return http(e);
}
else if(e instanceof SocketException){
return socket(e);
}
return http(e);
}

public static AppException run(Exception e) {
return new AppException(TYPE_RUN, 0, e);
}

/**
* 获取APP异常崩溃处理对象
* @param context
* @return
*/
public static AppException getAppExceptionHandler(){
return new AppException();
}

@Override
public void uncaughtException(Thread thread, Throwable ex) {

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 Context context = AppManager.getAppManager().currentActivity();

if(context == null) {
return false;
}

final String crashReport = getCrashReport(context, ex);
//显示异常信息&发送报告
new Thread() {
public void run() {
Looper.prepare();
UIHelper.sendAppCrashReport(context, crashReport);
Looper.loop();
}

}.start();
return true;
}
/**
* 获取APP崩溃异常报告
* @param ex
* @return
*/
private String getCrashReport(Context context, Throwable ex) {
PackageInfo pinfo = ((AppContext)context.getApplicationContext()).getPackageInfo();
StringBuffer exceptionStr = new StringBuffer();
exceptionStr.append("Version: "+pinfo.versionName+"("+pinfo.versionCode+")\n");
exceptionStr.append("Android: "+android.os.Build.VERSION.RELEASE+"("+android.os.Build.MODEL+")\n");
exceptionStr.append("Exception: "+ex.getMessage()+"\n");
StackTraceElement[] elements = ex.getStackTrace();
for (int i = 0; i < elements.length; i++) {
exceptionStr.append(elements[i].toString()+"\n");
}
return exceptionStr.toString();
}
}


使用:
1.在Application子类,注册App异常崩溃处理器。

application是用来保存全局变量的,并且是在package创建的时候就跟着存在了。

Thread.setDefaultUncaughtExceptionHandler()方法将异常处理类设置到线程上。

@Override
public void onCreate() {
super.onCreate();
Thread.setDefaultUncaughtExceptionHandler(AppException.getAppExceptionHandler());

}


2. 例:检查更新版本

/**
* 检查版本更新
* @param url
* @return
*/
public static Update checkVersion(AppContext appContext) throws AppException {
try{
return Update.parse(http_get(appContext, URLs.UPDATE_VERSION));
}catch(Exception e){
if(e instanceof AppException)
throw (AppException)e;
throw AppException.network(e);
}
}


public static Update parse(InputStream inputStream) throws IOException, AppException {
Update update = null;

try {
//.......
} catch (XmlPullParserException e) {
throw AppException.xml(e);
} finally {
inputStream.close();
}
return update;
}


UpdateManager.java

new Thread(){
public void run() {
Message msg = new Message();
try {
Update update = ApiClient.checkVersion((AppContext)mContext.getApplicationContext());
msg.what = 1;
msg.obj = update;
} catch (AppException e) {
e.printStackTrace();
}
handler.sendMessage(msg);
}
}.start();


3.普通异常处理

/**
* 下载图片-可指定显示图片的高宽
* @param url
* @param width
* @param height
*/
private Bitmap downloadBitmap(String url, int width, int height) {
Bitmap bitmap = null;
try {
//http加载图片
bitmap = ApiClient.getNetBitmap(url);
if(width > 0 && height > 0) {
//指定显示图片的高宽
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
}
//放入缓存
cache.put(url, new SoftReference<Bitmap>(bitmap));
} catch (AppException e) {
e.printStackTrace();
}
return bitmap;
}


4.发送toast消息

final Handler handler = new Handler(){
public void handleMessage(Message msg) {
if(mProgress!=null)mProgress.dismiss();
if(msg.what == 1){
Result res = (Result)msg.obj;
UIHelper.ToastMessage(QuestionPub.this, res.getErrorMessage());
if(res.OK()){
//发送通知广播
if(res.getNotice() != null){
UIHelper.sendBroadCast(QuestionPub.this, res.getNotice());
}
//清除之前保存的编辑内容
ac.removeProperty(AppConfig.TEMP_POST_TITLE,AppConfig.TEMP_POST_CATALOG,AppConfig.TEMP_POST_CONTENT);
//跳转到文章详情
finish();
}
}
else {
((AppException)msg.obj).makeToast(QuestionPub.this);
}
}
};
new Thread(){
public void run() {
Message msg = new Message();
try {
Result res = ac.pubPost(post);
msg.what = 1;
msg.obj = res;
} catch (AppException e) {
e.printStackTrace();
msg.what = -1;
msg.obj = e;
}
handler.sendMessage(msg);
}
}.start();


((AppException)msg.obj).makeToast(QuestionPub.this);

msg.obj为Exception ,强制转换到AppException,得到异常类型,Toast.

/**
* 发送App异常崩溃报告
*
* @param cont
* @param crashReport
*/
public static void sendAppCrashReport(final Context cont,
final String crashReport) {
AlertDialog.Builder builder = new AlertDialog.Builder(cont);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setTitle(R.string.app_error);
builder.setMessage(R.string.app_error_message);
builder.setPositiveButton(R.string.submit_report,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
// 发送异常报告
Intent i = new Intent(Intent.ACTION_SEND);
// i.setType("text/plain"); //模拟器
i.setType("message/rfc822"); // 真机
i.putExtra(Intent.EXTRA_EMAIL,
new String[] { "zhangdeyi@oschina.net" });
i.putExtra(Intent.EXTRA_SUBJECT,
"开源中国Android客户端 - 错误报告");
i.putExtra(Intent.EXTRA_TEXT, crashReport);
cont.startActivity(Intent.createChooser(i, "发送错误报告"));
// 退出
AppManager.getAppManager().AppExit(cont);
}
});
builder.setNegativeButton(R.string.sure,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
// 退出
AppManager.getAppManager().AppExit(cont);
}
});
builder.show();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: