博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android热修复之 收集崩溃信息上传服务器
阅读量:5884 次
发布时间:2019-06-19

本文共 8218 字,大约阅读时间需要 27 分钟。

1.概述


  开始想收集崩溃信息是因为测试的哥们老是说崩了,但是一过来就开始拍脑袋说 我*怎么好了?所以后来上网查了很多信息,找到了一种方法。大致的流程就是在用户崩溃的时候,我们获取崩溃信息、应用当前的信息和手机信息,然后把它保存到手机内存卡,再找我就直接找出来看看。后来衍生到上线后某些奇葩机型会有部分问题,所以不得不上传到服务器,后来发现居然可以配合热修复一步一步如此神奇,接下来我们来玩一玩,如何才能把用户的崩溃信息上传到服务器。大家也可以去找腾讯他有现成的:https://bugly.qq.com/v2/index 友盟也有现成的:http://www.umeng.com/ 实现的原理都类似。

  视频讲解:http://pan.baidu.com/s/1i5woOtR

  相关文章:

        

  

  

  

       

2.实现


   2.1 拦截闪退信息      如何去收集我们的闪退信息?我们需要认识一下这个类Thread.UncaughtExceptionHandler,一言不和就看源码,这个可以不看,且看我是如何写的。

/** * Created by Darren on 2017/2/8. * Email: 240336124@qq.com * Description: 拦截应用的闪退信息 */public class ExceptionCrashHandler implements Thread.UncaughtExceptionHandler {    private static final String TAG = "ExceptionCrashHandler";    // 单例设计模式    private static ExceptionCrashHandler mInstance;    // 留下原来的,便于开发的时候调试    private Thread.UncaughtExceptionHandler mDefaultHandler;    // 上下文  获取版本信息和手机信息    private Context mContext;    public static ExceptionCrashHandler getInstance() {        if (mInstance == null) {            synchronized (ExceptionCrashHandler.class) {                if (mInstance == null) {                    mInstance = new ExceptionCrashHandler();                }            }        }        return mInstance;    }    private ExceptionCrashHandler() {    }    public void init(Context context) {        /**        * 官方解释        * Set the handler invoked when this thread abruptly terminates        * due to an uncaught exception.         **/        Thread.currentThread().setUncaughtExceptionHandler(this);        // 获取系统默认的UncaughtException处理器        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();        this.mContext = context;    }    @Override    public void uncaughtException(Thread t, Throwable ex) {        Log.e(TAG, "到拦截闪退信息");    }}复制代码

在Application的onCreate()中配置一下,然后在任何一个地方写一个异常试一试:

/** * Created by Darren on 2017/2/8. * Email: 240336124@qq.com * Description: BaseApplication */public class BaseApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        ExceptionCrashHandler.getInstance().init(this);    }}复制代码

2.2 收集闪退信息

  这样每次崩溃的时候都会进入uncaughtException(),这个时候我们只需要收集信息写入本地文件就好了,收集的信息肯定需要包含好几个部分:当前崩溃信息,当前应用的版本信息,当前手机的信息,有的时候我们还需要其他部分,这里大概就只收集这三部分。为什么收集收集手机信息呢?因为有的时候是由于某些特定手机引起的Bug,若怪罪下来的话我们要甩锅给他。

@Override    public void uncaughtException(Thread t, Throwable ex) {        Log.e(TAG, "捕捉到了异常");        // 1. 获取信息        // 1.1 崩溃信息        // 1.2 手机信息        // 1.3 版本信息        // 2.写入文件        String crashFileName = saveInfoToSD(ex);        Log.e(TAG, "fileName --> " + crashFileName);        // 3. 缓存崩溃日志文件        cacheCrashFile(crashFileName);        // 系统默认处理        mDefaultHandler.uncaughtException(t, ex);    }    /**     * 缓存崩溃日志文件     *     * @param fileName     */    private void cacheCrashFile(String fileName) {        SharedPreferences sp = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE);        sp.edit().putString("CRASH_FILE_NAME", fileName).commit();    }    /**     * 获取崩溃文件名称     *     * @return     */    public File getCrashFile() {        String crashFileName = mContext.getSharedPreferences("crash",                Context.MODE_PRIVATE).getString("CRASH_FILE_NAME", "");        return new File(crashFileName);    }    /**     * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中     *     * @param ex     * @return     */    private String saveInfoToSD(Throwable ex) {        String fileName = null;        StringBuffer sb = new StringBuffer();        for (Map.Entry
entry : obtainSimpleInfo(mContext) .entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key).append(" = ").append(value).append("\n"); } sb.append(obtainExceptionInfo(ex)); if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { File dir = new File(mContext.getFilesDir() + File.separator + "crash" + File.separator); // 先删除之前的异常信息 if (dir.exists()) { deleteDir(dir); } // 再从新创建文件夹 if (!dir.exists()) { dir.mkdir(); } try { fileName = dir.toString() + File.separator + getAssignTime("yyyy_MM_dd_HH_mm") + ".txt"; FileOutputStream fos = new FileOutputStream(fileName); fos.write(sb.toString().getBytes()); fos.flush(); fos.close(); } catch (Exception e) { e.printStackTrace(); } } return fileName; } /** * 返回当前日期根据格式 **/ private String getAssignTime(String dateFormatStr) { DateFormat dataFormat = new SimpleDateFormat(dateFormatStr); long currentTime = System.currentTimeMillis(); return dataFormat.format(currentTime); } /** * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中 * * @return */ private HashMap
obtainSimpleInfo(Context context) { HashMap
map = new HashMap<>(); PackageManager mPackageManager = context.getPackageManager(); PackageInfo mPackageInfo = null; try { mPackageInfo = mPackageManager.getPackageInfo( context.getPackageName(), PackageManager.GET_ACTIVITIES); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } map.put("versionName", mPackageInfo.versionName); map.put("versionCode", "" + mPackageInfo.versionCode); map.put("MODEL", "" + Build.MODEL); map.put("SDK_INT", "" + Build.VERSION.SDK_INT); map.put("PRODUCT", "" + Build.PRODUCT); map.put("MOBLE_INFO", getMobileInfo()); return map; } /** * Cell phone information * * @return */ public static String getMobileInfo() { StringBuffer sb = new StringBuffer(); try { Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); String name = field.getName(); String value = field.get(null).toString(); sb.append(name + "=" + value); sb.append("\n"); } } catch (Exception e) { e.printStackTrace(); } return sb.toString(); } /** * 获取系统未捕捉的错误信息 * * @param throwable * @return */ private String obtainExceptionInfo(Throwable throwable) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); throwable.printStackTrace(printWriter); printWriter.close(); return stringWriter.toString(); } /** * 递归删除目录下的所有文件及子目录下所有文件 * * @param dir 将要删除的文件目录 * @return boolean Returns "true" if all deletions were successful. If a * deletion fails, the method stops attempting to delete and returns * "false". */ private boolean deleteDir(File dir) { if (dir.isDirectory()) { String[] children = dir.list(); // 递归删除目录中的子目录下 for (int i = 0; i < children.length; i++) { boolean success = deleteDir(new File(dir, children[i])); if (!success) { return false; } } } // 目录此时为空,可以删除 return true; }复制代码

  保存的路径最好不要在用户的外部存储卡中,因为6.0的时候如果访问外部存储卡需要动态的申请权限,那这个时候信息是获取到了但是GG。都蹦了还拖着我不放,还需要申请权限,纳尼???

2.2 上传闪退信息

  每次启动应用的时候就获取上次闪退的信息日志,然后上传到服务器。上传就先不上传了,等内涵段子的系统架构和功能都完毕后我们再来上传,然后打包但是不能上线,要不然要背官司的。

/** * Created by Darren on 2017/2/8. * Email: 240336124@qq.com * Description: 主页面MainActivity */public class MainActivity extends BaseActivity {    @Override    protected void initData() {        // 获取上次的崩溃信息        File crashFile = ExceptionCrashHandler.getInstance().getCrashFile();        // 上传到服务器,后面再说.......    }    @Override    protected void initView() {    }    @Override    protected void setContentView() {        setContentView(R.layout.activity_main);    }    @Override    protected void initTitle() {    }}复制代码

  视频讲解:http://pan.baidu.com/s/1i5woOtR

  相关文章:

        

  

  

转载地址:http://xqlix.baihongyu.com/

你可能感兴趣的文章
数据驱动销售——个性化推荐引擎
查看>>
C语言标准库函数qsort那点小事
查看>>
HL7 CDA高级培训
查看>>
Android 调用照相机拍照
查看>>
linux的C获取shell执行返回的结果
查看>>
关于spring mybateis 定义resultType="java.util.HashMap"
查看>>
程序员怎么留住健康?
查看>>
(转)C# 把我所积累的类库全部分享给博友(附件已经上传)
查看>>
Silverlight5 无法切换输入法,无法输入中文的原因及解决初探
查看>>
游戏开发基础:方向键的组合,八方向实现
查看>>
黑书-DP-方块消除 ****
查看>>
MySQL 分区
查看>>
我的架构经验系列文章 - 后端架构 - 语言层面
查看>>
DEFERRED_SEGMENT_CREATION
查看>>
读取手机硬件信息
查看>>
一致哈希
查看>>
The connection to adb is down, and a severe error has occured. 问题解决
查看>>
在Jenkins中配置运行远程shell命令
查看>>
代码杂记
查看>>
linux中防CC攻击两种实现方法(转)
查看>>