doum
昨天 6c12dd77bc481aeabec568bfed3dd68e81b80f8b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
package com.doumee.keyCabinet.utils;
 
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
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 com.doumee.keyCabinet.ui.guide.GuideActivity;
import com.doumee.lib_coremodel.util.SpUtil;
 
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
 
/**
 * 
 * 
 * UncaughtExceptionHandler:线程未捕获异常控制器是用来处理未捕获异常的。 如果程序出现了未捕获异常默认情况下则会出现强行关闭对话框
 * 实现该接口并注册为程序中的默认未捕获异常处理 这样当未捕获异常发生时,就可以做些异常处理操作 例如:收集异常信息,发送错误报告 等。
 * 
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 */
public class CrashHandler implements UncaughtExceptionHandler {
    private static final String TAG = "CrashHandler";
    private static String PATH = Environment.getExternalStorageDirectory().getPath() + "/log/";
    public static final String FILE_NAME = "crash";
    //log文件的后缀名
    private static final String FILE_NAME_SUFFIX = ".txt";
 
    public static final String CLIENT_VERSION = "clientVersion";
 
    /** CrashHandler实例 */
    private static CrashHandler INSTANCE;
    /** 程序的Context对象 */
    private Context mContext;
    /** 系统默认的UncaughtException处理类 */
    private UncaughtExceptionHandler mDefaultHandler;
 
    /** 使用Properties来保存设备的信息和错误堆栈信息 */
    private Properties mDeviceCrashInfo = new Properties();
 
    /** 保证只有一个CrashHandler实例 */
    private CrashHandler() {
    }
 
    /** 获取CrashHandler实例 ,单例模式 */
    public static CrashHandler getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new CrashHandler();
        }
        return INSTANCE;
    }
 
    /**
     * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器
     *
     * @param ctx
     */
    public void init(Context ctx) {
        mContext = ctx;
        //PATH = ctx.getFilesDir().getParentFile().getAbsolutePath()+ "/log/";
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
        // 收集设备信息
        collectCrashDeviceInfo(mContext);
    }
 
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        try {
            //导出异常信息到SD卡中
            dumpExceptionToSDCard(ex);
            //System.out.println("重启应用");
            // 创建一个新的启动意图
            Intent intent = new Intent(mContext, GuideActivity.class);
            // 设置FLAG_ACTIVITY_CLEAR_TASK标志位
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            // 使用PendingIntent包装启动意图
            PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
            // 获取AlarmManager实例
            AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
            // 在500毫秒后触发重启操作
            alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 10, pendingIntent);
 
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
            //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("错误日志1"+e.getMessage());
        }
        //打印出当前调用栈信息
        ex.printStackTrace();
        //如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
        if (mDefaultHandler != null) {
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            android.os.Process.killProcess(android.os.Process.myPid());
        }
 
        if (!handleException(ex) && mDefaultHandler != null) {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            /*try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Log.e(TAG, "error : ", e);
            }*/
            /*System.out.println("重启应用");
            // 创建一个新的启动意图
            Intent intent = new Intent(mContext, GuideActivity.class);
            // 设置FLAG_ACTIVITY_CLEAR_TASK标志位
            intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            // 使用PendingIntent包装启动意图
            PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
            // 获取AlarmManager实例
            AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
            // 在500毫秒后触发重启操作
            alarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + 10, pendingIntent);
 
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);*/
            /*// 未捕获异常写文件
            writeCrashInfoToFile(ex);
            // 退出整个应用
            if (mDefaultHandler != null) {
                mDefaultHandler.uncaughtException(thread, ex);
            } else {
                System.exit(0);
            }*/
        }
    }
 
    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        // 使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                //T.ss("很抱歉,程序出现异常,即将退出");
                Looper.loop();
            }
        }.start();
        // 未捕获异常写文件
        writeCrashInfoToFile(ex);
        return true;
    }
 
    /**
     * 收集程序崩溃的设备信息
     *
     * @param ctx
     */
    private void collectCrashDeviceInfo(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
 
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
                    PackageManager.GET_ACTIVITIES);
            if (pi != null) {
                mDeviceCrashInfo.setProperty(CLIENT_VERSION,
                        pi.versionName == null ? "not set" : pi.versionName);
            }
        } catch (NameNotFoundException e) {
            e.printStackTrace();
            System.out.println("错误日志3"+e.getMessage());
        }
    }
 
    /**
     * 将异常信息写入日志文件
     *
     * @param ex
     * @return
     */
    private String writeCrashInfoToFile(Throwable ex) {
        Writer info = new StringWriter();
        PrintWriter printWriter = new PrintWriter(info);
        // printStackTrace(PrintWriter s)
        // 将此 throwable 及其追踪输出到指定的 PrintWriter
        ex.printStackTrace(printWriter);
 
        // getCause() 返回此 throwable 的 cause;如果 cause 不存在或未知,则返回 null。
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
 
        // toString() 以字符串的形式返回该缓冲区的当前值。
        String result = info.toString();
        printWriter.close();
 
        String errorLog = String
                .format("FINGERPRINT=%s|"
                        + "STACK_TRACE=%s|versionName=%s|MODEL=%s|MANUFACTURER=%s|BRAND=%s|RELEASE=%s",
                // 硬件名称
                        Build.FINGERPRINT,
                        // 异常信息
                        result,
                        // 应用版本名称
                        mDeviceCrashInfo.getProperty(CLIENT_VERSION),
                        // 设备名称
                        Build.MODEL,
                        // 硬件制造商
                        Build.MANUFACTURER,
                        // android系统定制商
                        Build.BRAND,
                        // android SDK版本
                        Build.VERSION.RELEASE);
        //sp.setString(Constants.SP_CRASH_EXCEPTION, errorLog);
        return null;
    }
 
    private void dumpExceptionToSDCard(Throwable ex) throws IOException {
        //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            /*if (Const.DEBUG) {
                LogUtils.w(TAG, "sdcard unmounted,skip dump exception");
                return;
            }*/
            return;
        }
        /*File dir = new File(PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }*/
        long current = System.currentTimeMillis();
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            PrintStream print = new PrintStream(out);
            //导出发生异常的时间
            print.println(time);
            //导出手机信息
            dumpPhoneInfo(print);
            print.println();
            ex.printStackTrace(print);
            SpUtil.saveString(FILE_NAME+time,new String(out.toByteArray()));
            print.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("错误日志2"+e.getMessage());
            //LogUtils.e(TAG, "dump crash info failed");
        }
        //以当前时间创建log文件
        /*File file = new File(PATH , FILE_NAME + time + FILE_NAME_SUFFIX);
        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            //导出发生异常的时间
            pw.println(time);
            //导出手机信息
            dumpPhoneInfo(pw);
            pw.println();
            //导出异常的调用栈信息
            ex.printStackTrace(pw);
            pw.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("错误日志2"+e.getMessage());
            //LogUtils.e(TAG, "dump crash info failed");
        }*/
    }
    private void dumpPhoneInfo(PrintStream pw) throws NameNotFoundException {
        //应用的版本名称和版本号
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
        pw.print("App Version: ");
        pw.print(pi.versionName);
        pw.print('_');
        pw.println(pi.versionCode);
        //android版本号
        pw.print("OS Version: ");
        pw.print(Build.VERSION.RELEASE);
        pw.print("_");
        pw.println(Build.VERSION.SDK_INT);
        //手机制造商
        pw.print("Vendor: ");
        pw.println(Build.MANUFACTURER);
        //手机型号     pw.print("Model: ");
        pw.println(Build.MODEL);
        //cpu架构
        pw.print("CPU ABI: ");
        pw.println(Build.CPU_ABI);   }
}