package net.oschina.gitapp; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.net.ConnectException; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Date; import org.apache.commons.httpclient.HttpException; import com.umeng.analytics.MobclickAgent; import net.oschina.gitapp.common.UIHelper; import android.content.Context; import android.content.pm.PackageInfo; import android.os.Environment; import android.os.Looper; import android.widget.Toast; /** * 应用程序异常类:用于捕获异常和提示错误信息 * @author liux (http://my.oschina.net/liux) * @version 1.0 * @created 2012-3-21 */ @SuppressWarnings("serial") 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; public final static byte TYPE_FILENOTFOUND = 0x09; private byte type;// 异常的类型 // 异常的状态码,这里一般是网络请求的状态码 private int code; /** 系统默认的UncaughtException处理类 */ private Thread.UncaughtExceptionHandler mDefaultHandler; private AppContext mContext; private AppException(Context context){ this.mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); this.mContext = (AppContext) context; } private AppException(byte type, int code, Exception excp) { super(excp); this.type = type; this.code = code; // 保存debug的 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; case TYPE_FILENOTFOUND:// 文件没有找到 Toast.makeText(ctx, R.string.app_run_code_error, Toast.LENGTH_SHORT).show(); break; default: Toast.makeText(ctx, "抱歉,发生异常", Toast.LENGTH_SHORT).show(); break; } } /** * 保存异常日志 * @param excp */ @SuppressWarnings("deprecation") 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 file(Exception e) { return new AppException(TYPE_FILENOTFOUND, 0 ,e); } // io异常 public static AppException io(Exception e) { return io(e, 0); } // io异常 public static AppException io(Exception e, int code) { if(e instanceof UnknownHostException || e instanceof ConnectException){ return new AppException(TYPE_NETWORK, code, e); } else if(e instanceof IOException){ return new AppException(TYPE_IO, code ,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(Context context){ return new AppException(context.getApplicationContext()); } @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(final Throwable ex) { if(ex == null) { return false; } if(mContext == null) { return false; } final Context context = AppManager.getAppManager().currentActivity(); final String crashReport = getCrashReport(context, ex); //显示异常信息&发送报告 new Thread() { public void run() { Looper.prepare(); // 上传错误信息到友盟的后台 MobclickAgent.reportError(mContext, ex); 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("API Level: "+android.os.Build.VERSION.SDK_INT + "\n"); exceptionStr.append("Android: "+android.os.Build.VERSION.RELEASE+" ("+android.os.Build.MODEL+")\n\n\n"); exceptionStr.append("异常信息: \n"); exceptionStr.append("Exception: "+ex.getMessage()+"\n\n\n"); exceptionStr.append("堆栈信息: \n"); StackTraceElement[] elements = ex.getStackTrace(); for (int i = 0; i < elements.length; i++) { exceptionStr.append(elements[i].toString()+"\n"); } return exceptionStr.toString(); } }