• 欢迎浏览“String me = Creater\忠实的资深Linux玩家;”,请文明浏览,理性发言,有侵犯你的权益请邮件我(creater@vip.qq.com).
  • 把任何的失败都当作一次尝试,不要自卑;把所有的成功都想成是一种幸运,不要自傲。
  •    2年前 (2016-07-02)  java |   抢沙发  12 
    文章评分 0 次,平均分 0.0

    最近项目开发了一款android app,每次手动运行没有问题,但是让其自启动则奔溃,为了捕获异常,可以使用如下的方法来改进。
    下来了解两个类:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

    Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才会根据情况(Intent)来启动相应的Activity和Service。本示例中将在自定义加强版的Application中注册未捕获异常处理器。

    Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。
    大家刚才在项目的结构图中看到的CrashHandler.java实现了Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代码如下:

    package net.unix8.crash;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.lang.Thread.UncaughtExceptionHandler;
    import java.lang.reflect.Field;
    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    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;
    
    /**
     * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
     * 
     * @author user
     * 
     */
    public class CrashHandler implements UncaughtExceptionHandler {
    	
    	public static final String TAG = "CrashHandler";
    	
    	//系统默认的UncaughtException处理类 
    	private Thread.UncaughtExceptionHandler mDefaultHandler;
    	//CrashHandler实例
    	private static CrashHandler INSTANCE = new CrashHandler();
    	//程序的Context对象
    	private Context mContext;
    	//用来存储设备信息和异常信息
    	private Map<String, String> infos = new HashMap<String, String>();
    
    	//用于格式化日期,作为日志文件名的一部分
    	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发生时会转入该函数来处理
    	 */
    	@Override
    	public void uncaughtException(Thread thread, Throwable ex) {
    		if (!handleException(ex) && mDefaultHandler != null) {
    			//如果用户没有处理则让系统默认的异常处理器来处理
    			mDefaultHandler.uncaughtException(thread, ex);
    		} else {
    			try {
    				Thread.sleep(3000);
    			} catch (InterruptedException e) {
    				Log.e(TAG, "error : ", e);
    			}
    			//退出程序
    			android.os.Process.killProcess(android.os.Process.myPid());
    			System.exit(1);
    		}
    	}
    
    	/**
    	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
    	 * 
    	 * @param ex
    	 * @return true:如果处理了该异常信息;否则返回false.
    	 */
    	private boolean handleException(Throwable ex) {
    		if (ex == null) {
    			return false;
    		}
    		//使用Toast来显示异常信息
    		new Thread() {
    			@Override
    			public void run() {
    				Looper.prepare();
    				Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
    				Looper.loop();
    			}
    		}.start();
    		//收集设备参数信息 
    		collectDeviceInfo(mContext);
    		//保存日志文件 
    		saveCrashInfo2File(ex);
    		return true;
    	}
    	
    	/**
    	 * 收集设备参数信息
    	 * @param ctx
    	 */
    	public void collectDeviceInfo(Context ctx) {
    		try {
    			PackageManager pm = ctx.getPackageManager();
    			PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
    			if (pi != null) {
    				String versionName = pi.versionName == null ? "null" : pi.versionName;
    				String versionCode = pi.versionCode + "";
    				infos.put("versionName", versionName);
    				infos.put("versionCode", versionCode);
    			}
    		} catch (NameNotFoundException e) {
    			Log.e(TAG, "an error occured when collect package info", e);
    		}
    		Field[] fields = Build.class.getDeclaredFields();
    		for (Field field : fields) {
    			try {
    				field.setAccessible(true);
    				infos.put(field.getName(), field.get(null).toString());
    				Log.d(TAG, field.getName() + " : " + field.get(null));
    			} catch (Exception e) {
    				Log.e(TAG, "an error occured when collect crash info", e);
    			}
    		}
    	}
    
    	/**
    	 * 保存错误信息到文件中
    	 * 
    	 * @param ex
    	 * @return	返回文件名称,便于将文件传送到服务器
    	 */
    	private String saveCrashInfo2File(Throwable ex) {
    		
    		StringBuffer sb = new StringBuffer();
    		for (Map.Entry<String, String> entry : infos.entrySet()) {
    			String key = entry.getKey();
    			String value = entry.getValue();
    			sb.append(key + "=" + value + "\n");
    		}
    		
    		Writer writer = new StringWriter();
    		PrintWriter printWriter = new PrintWriter(writer);
    		ex.printStackTrace(printWriter);
    		Throwable cause = ex.getCause();
    		while (cause != null) {
    			cause.printStackTrace(printWriter);
    			cause = cause.getCause();
    		}
    		printWriter.close();
    		String result = writer.toString();
    		sb.append(result);
    		try {
    			long timestamp = System.currentTimeMillis();
    			String time = formatter.format(new Date());
    			String fileName = "crash-" + time + "-" + timestamp + ".log";
    			if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    				String path = "/sdcard/crash/";
    				File dir = new File(path);
    				if (!dir.exists()) {
    					dir.mkdirs();
    				}
    				FileOutputStream fos = new FileOutputStream(path + fileName);
    				fos.write(sb.toString().getBytes());
    				fos.close();
    			}
    			return fileName;
    		} catch (Exception e) {
    			Log.e(TAG, "an error occured while writing file...", e);
    		}
    		return null;
    	}
    }
    

    在收集异常信息时,朋友们也可以使用Properties,因为Properties有一个很便捷的方法properties.store(OutputStream out, String comments),用来将Properties实例中的键值对外输到输出流中,但是在使用的过程中发现生成的文件中异常信息打印在同一行,看起来极为费劲,所以换成Map来存放这些信息,然后生成文件时稍加了些操作。
    完成这个CrashHandler后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application,添加自己的代码,CrashApplication.java代码如下:

    package net.unix8.crash;
    
    import android.app.Application;
    
    public class CrashApplication extends Application {
    	@Override
    	public void onCreate() {
    		super.onCreate();
    		CrashHandler crashHandler = CrashHandler.getInstance();
    		crashHandler.init(getApplicationContext());
    	}
    }
    

    最后,为了让我们的CrashApplication取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:

    <application android:name=".CrashApplication" ...>  </application> 

    因为我们上面的CrashHandler中,遇到异常后要保存设备参数和具体异常信息到SDCARD,所以我们需要在AndroidManifest.xml中加入读写SDCARD权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  

    这样异常信息就被保存到程序中指明的sdcard路径里了

     

    除特别注明外,本站所有文章均为String me = "Creater\忠实的资深Linux玩家";原创,转载请注明出处来自http://unix8.net/home.php/4934.html

    关于

    发表评论

    暂无评论

    切换注册

    登录

    忘记密码 ?

    切换登录

    注册

    扫一扫二维码分享