• Android热修复之


    1.概述

      大致的流程就是在用户崩溃的时候,我们获取崩溃信息、应用当前的信息和手机信息,然后把它保存到手机内存卡,再找我就直接找出来看看。后来衍生到上线后某些奇葩机型会有部分问题,所以不得不上传到服务器,后来发现居然可以配合热修复一步一步如此神奇,接下来我们来玩一玩,如何才能把用户的崩溃信息上传到服务器。大家也可以去找腾讯他有现成的:https://bugly.qq.com/v2/index 友盟也有现成的:http://www.umeng.com/ 实现的原理都类似。

    2.实现


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

    拦截应用的闪退信息

     1 public class ExceptionCrashHandler implements Thread.UncaughtExceptionHandler {
     2 
     3     private static final String TAG = "ExceptionCrashHandler";
     4     // 单例设计模式
     5     private static ExceptionCrashHandler mInstance;
     6     // 留下原来的,便于开发的时候调试
     7     private Thread.UncaughtExceptionHandler mDefaultHandler;
     8     // 上下文  获取版本信息和手机信息
     9     private Context mContext;
    10 
    11     public static ExceptionCrashHandler getInstance() {
    12         if (mInstance == null) {
    13             synchronized (ExceptionCrashHandler.class) {
    14                 if (mInstance == null) {
    15                     mInstance = new ExceptionCrashHandler();
    16                 }
    17             }
    18         }
    19         return mInstance;
    20     }
    21 
    22     private ExceptionCrashHandler() {
    23 
    24     }
    25 
    26     public void init(Context context) {
    27         /**
    28         * 官方解释
    29         * Set the handler invoked when this thread abruptly terminates
    30         * due to an uncaught exception.
    31          **/
    32         Thread.currentThread().setUncaughtExceptionHandler(this);
    33         // 获取系统默认的UncaughtException处理器
    34         mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
    35         this.mContext = context;
    36     }
    37 
    38     @Override
    39     public void uncaughtException(Thread t, Throwable ex) {
    40         Log.e(TAG, "到拦截闪退信息");
    41     }
    42 
    43 }

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

    public class BaseApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            ExceptionCrashHandler.getInstance().init(this);
        }
    }

    2.2 收集闪退信息

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

      1  @Override
      2     public void uncaughtException(Thread t, Throwable ex) {
      3         Log.e(TAG, "捕捉到了异常");
      4         // 1. 获取信息
      5         // 1.1 崩溃信息
      6         // 1.2 手机信息
      7         // 1.3 版本信息
      8         // 2.写入文件
      9         String crashFileName = saveInfoToSD(ex);
     10 
     11         Log.e(TAG, "fileName --> " + crashFileName);
     12 
     13         // 3. 缓存崩溃日志文件
     14         cacheCrashFile(crashFileName);
     15         // 系统默认处理
     16         mDefaultHandler.uncaughtException(t, ex);
     17     }
     18 
     19     /**
     20      * 缓存崩溃日志文件
     21      *
     22      * @param fileName
     23      */
     24     private void cacheCrashFile(String fileName) {
     25         SharedPreferences sp = mContext.getSharedPreferences("crash", Context.MODE_PRIVATE);
     26         sp.edit().putString("CRASH_FILE_NAME", fileName).commit();
     27     }
     28 
     29 
     30     /**
     31      * 获取崩溃文件名称
     32      *
     33      * @return
     34      */
     35     public File getCrashFile() {
     36         String crashFileName = mContext.getSharedPreferences("crash",
     37                 Context.MODE_PRIVATE).getString("CRASH_FILE_NAME", "");
     38         return new File(crashFileName);
     39     }
     40 
     41     /**
     42      * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中
     43      *
     44      * @param ex
     45      * @return
     46      */
     47     private String saveInfoToSD(Throwable ex) {
     48         String fileName = null;
     49         StringBuffer sb = new StringBuffer();
     50 
     51         for (Map.Entry<String, String> entry : obtainSimpleInfo(mContext)
     52                 .entrySet()) {
     53             String key = entry.getKey();
     54             String value = entry.getValue();
     55             sb.append(key).append(" = ").append(value).append("
    ");
     56         }
     57 
     58         sb.append(obtainExceptionInfo(ex));
     59 
     60         if (Environment.getExternalStorageState().equals(
     61                 Environment.MEDIA_MOUNTED)) {
     62             File dir = new File(mContext.getFilesDir() + File.separator + "crash"
     63                     + File.separator);
     64 
     65             // 先删除之前的异常信息
     66             if (dir.exists()) {
     67                 deleteDir(dir);
     68             }
     69 
     70             // 再从新创建文件夹
     71             if (!dir.exists()) {
     72                 dir.mkdir();
     73             }
     74             try {
     75                 fileName = dir.toString()
     76                         + File.separator
     77                         + getAssignTime("yyyy_MM_dd_HH_mm") + ".txt";
     78                 FileOutputStream fos = new FileOutputStream(fileName);
     79                 fos.write(sb.toString().getBytes());
     80                 fos.flush();
     81                 fos.close();
     82             } catch (Exception e) {
     83                 e.printStackTrace();
     84             }
     85         }
     86         return fileName;
     87     }
     88 
     89     /**
     90     * 返回当前日期根据格式
     91     **/
     92     private String getAssignTime(String dateFormatStr) {
     93         DateFormat dataFormat = new SimpleDateFormat(dateFormatStr);
     94         long currentTime = System.currentTimeMillis();
     95         return dataFormat.format(currentTime);
     96     }
     97 
     98 
     99     /**
    100      * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中
    101      *
    102      * @return
    103      */
    104     private HashMap<String, String> obtainSimpleInfo(Context context) {
    105         HashMap<String, String> map = new HashMap<>();
    106         PackageManager mPackageManager = context.getPackageManager();
    107         PackageInfo mPackageInfo = null;
    108         try {
    109             mPackageInfo = mPackageManager.getPackageInfo(
    110                     context.getPackageName(), PackageManager.GET_ACTIVITIES);
    111         } catch (PackageManager.NameNotFoundException e) {
    112             e.printStackTrace();
    113         }
    114         map.put("versionName", mPackageInfo.versionName);
    115         map.put("versionCode", "" + mPackageInfo.versionCode);
    116         map.put("MODEL", "" + Build.MODEL);
    117         map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
    118         map.put("PRODUCT", "" + Build.PRODUCT);
    119         map.put("MOBLE_INFO", getMobileInfo());
    120         return map;
    121     }
    122 
    123 
    124     /**
    125      * Cell phone information
    126      *
    127      * @return
    128      */
    129     public static String getMobileInfo() {
    130         StringBuffer sb = new StringBuffer();
    131         try {
    132             Field[] fields = Build.class.getDeclaredFields();
    133             for (Field field : fields) {
    134                 field.setAccessible(true);
    135                 String name = field.getName();
    136                 String value = field.get(null).toString();
    137                 sb.append(name + "=" + value);
    138                 sb.append("
    ");
    139             }
    140         } catch (Exception e) {
    141             e.printStackTrace();
    142         }
    143         return sb.toString();
    144     }
    145 
    146 
    147     /**
    148      * 获取系统未捕捉的错误信息
    149      *
    150      * @param throwable
    151      * @return
    152      */
    153     private String obtainExceptionInfo(Throwable throwable) {
    154         StringWriter stringWriter = new StringWriter();
    155         PrintWriter printWriter = new PrintWriter(stringWriter);
    156         throwable.printStackTrace(printWriter);
    157         printWriter.close();
    158         return stringWriter.toString();
    159     }
    160 
    161 
    162     /**
    163      * 递归删除目录下的所有文件及子目录下所有文件
    164      *
    165      * @param dir 将要删除的文件目录
    166      * @return boolean Returns "true" if all deletions were successful. If a
    167      * deletion fails, the method stops attempting to delete and returns
    168      * "false".
    169      */
    170     private boolean deleteDir(File dir) {
    171         if (dir.isDirectory()) {
    172             String[] children = dir.list();
    173             // 递归删除目录中的子目录下
    174             for (int i = 0; i < children.length; i++) {
    175                 boolean success = deleteDir(new File(dir, children[i]));
    176                 if (!success) {
    177                     return false;
    178                 }
    179             }
    180         }
    181         // 目录此时为空,可以删除
    182         return true;
    183     }

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

    2.2 上传闪退信息

    每次启动应用的时候就获取上次闪退的信息日志,然后上传到服务器。

    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() {
    
        }
    }


  • 相关阅读:
    CREATE VIEW
    CREATE USER
    安全层次
    PHP json_decode 函数解析 json 结果为 NULL 的解决方法
    Java实现 LeetCode 7整数反转
    Java实现 LeetCode 6 Z字形变换
    Java实现 LeetCode 6 Z字形变换
    Java实现 LeetCode 6 Z字形变换
    Java实现 LeetCode 5 最长回文子串
    Java实现 LeetCode 5 最长回文子串
  • 原文地址:https://www.cnblogs.com/ganchuanpu/p/8196771.html
Copyright © 2020-2023  润新知