android全局捕获异常信息
由于最近在做一个国外的项目,所以客户测试时,有些简单的问题直接可以改改。但是当客户拿着国外的手机,或者一些各种机型的手机出现不同的问题时,真心不知道咋解决了。所以如果在程序出现异常时,需要及时捕获到并上传到服务器。这样我们就能够看到异常日志信息了。
1.首先介绍工具类:CrashUtils和xjzUtils
可以在github上找到:https://github.com/Blankj/AndroidUtilCode这个里面封装了很多工具类。
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.util.Log;
import com.ztxx.customer.http.HttpUtil;
import com.ztxx.customer.http.MyCallBack;
import com.ztxx.customer.http.UrlConst;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* <pre>
* author: Blankj
* blog : http://blankj.com
* time : 2016/09/27
* desc : 崩溃相关工具类
* </pre>
*/
public final class CrashUtils {
private static String defaultDir;
private static String dir;
private static String versionName;
private static int versionCode;
private static ExecutorService sExecutor;
private static final String FILE_SEP = System.getProperty("file.separator");
private static final Format FORMAT = new SimpleDateFormat("MM-dd HH-mm-ss", Locale.getDefault());
private static final String CRASH_HEAD;
private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER;
private static final UncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER;
static {
try {
PackageInfo pi = xjzUtils.getApp().getPackageManager().getPackageInfo(xjzUtils.getApp().getPackageName(), 0);
if (pi != null) {
versionName = pi.versionName;
versionCode = pi.versionCode;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
CRASH_HEAD = "\n************* Crash Log Head ****************" +
"\nDevice Manufacturer: " + Build.MANUFACTURER +// 设备厂商
"\nDevice Model : " + Build.MODEL +// 设备型号
"\nAndroid Version : " + Build.VERSION.RELEASE +// 系统版本
"\nAndroid SDK : " + Build.VERSION.SDK_INT +// SDK版本
"\nApp VersionName : " + versionName +
"\nApp VersionCode : " + versionCode +
"\n************* Crash Log Head ****************\n\n";
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER = Thread.getDefaultUncaughtExceptionHandler();
UNCAUGHT_EXCEPTION_HANDLER = new UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
Log.i("path", "抓住了异常");
if (e == null) {
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
return;
}
Date now = new Date(System.currentTimeMillis());
// fileName = FORMAT.format(now) + ".txt"
fileName = "Exception.txt";
fullPath = (dir == null ? defaultDir : dir) + fileName;
if (!createOrExistsFile(fullPath)) return;
if (sExecutor == null) {
sExecutor = Executors.newSingleThreadExecutor();
}
sExecutor.execute(new Runnable() {
@Override
public void run() {
PrintWriter pw = null;
try {
pw = new PrintWriter(new FileWriter(fullPath, true));
pw.write(CRASH_HEAD);
e.printStackTrace(pw);
Throwable cause = e.getCause();
while (cause != null) {
cause.printStackTrace(pw);
cause = cause.getCause();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
}
}
});
if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {
DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, e);
}
}
};
}
public static String fileName;
private static String fullPath;
private static void uploadCrash(String fullPath) {
HttpUtil.uploadCrashFile(UrlConst.UPLOADURL, fullPath, new MyCallBack<String>() {
@Override
public void onSuccess(String result) {
super.onSuccess(result);
Log.i("path", result);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
super.onError(ex, isOnCallback);
Log.i("path", ex.toString());
}
});
}
private CrashUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 初始化
* <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
*/
public static void init() {
init("");
}
/**
* 初始化
* <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
*
* @param crashDir 崩溃文件存储目录
*/
public static void init(@NonNull final File crashDir) {
init(crashDir.getAbsolutePath());
}
/**
* 初始化
* <p>需添加权限 {@code <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>}</p>
*
* @param crashDir 崩溃文件存储目录
*/
public static void init(final String crashDir) {
if (isSpace(crashDir)) {
dir = null;
} else {
dir = crashDir.endsWith(FILE_SEP) ? crashDir : crashDir + FILE_SEP;
}
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
&& xjzUtils.getApp().getExternalCacheDir() != null)
defaultDir = xjzUtils.getApp().getExternalCacheDir() + FILE_SEP + "crash" + FILE_SEP;
else {
defaultDir = xjzUtils.getApp().getCacheDir() + FILE_SEP + "crash" + FILE_SEP;
}
Thread.setDefaultUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
}
private static boolean createOrExistsFile(final String filePath) {
File file = new File(filePath);
if (file.exists()) return file.isFile();
if (!createOrExistsDir(file.getParentFile())) return false;
try {
return file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
private static boolean createOrExistsDir(final File file) {
return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
}
private static boolean isSpace(final String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
}
xjzUtils工具类:
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.support.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.List;
public final class xjzUtils {
@SuppressLint("StaticFieldLeak")
private static Application sApplication;
static WeakReference<Activity> sTopActivityWeakRef;
static List<Activity> sActivityList = new LinkedList<>();
private static Application.ActivityLifecycleCallbacks mCallbacks = new Application.ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
sActivityList.add(activity);
setTopActivityWeakRef(activity);
}
@Override
public void onActivityStarted(Activity activity) {
setTopActivityWeakRef(activity);
}
@Override
public void onActivityResumed(Activity activity) {
setTopActivityWeakRef(activity);
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
sActivityList.remove(activity);
}
};
private xjzUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 初始化工具类
*
* @param app 应用
*/
public static void init(@NonNull final Application app) {
xjzUtils.sApplication = app;
app.registerActivityLifecycleCallbacks(mCallbacks);
}
/**
* 获取Application
*
* @return Application
*/
public static Application getApp() {
if (sApplication != null) return sApplication;
throw new NullPointerException("u should init first");
}
private static void setTopActivityWeakRef(Activity activity) {
if (sTopActivityWeakRef == null || !activity.equals(sTopActivityWeakRef.get())) {
sTopActivityWeakRef = new WeakReference<>(activity);
}
}
}
2.在Application中初始化。
/**
*初始化APP
*/
public class CreateApp extends Application {
private static Context mContext;
public static Gson gsonInstance;
@Override
public void onCreate() {
super.onCreate();
// 异常信息初始化
xjzUtils.init(this);
CrashUtils.init(Environment.getExternalStorageDirectory().getAbsolutePath());
}
}
3.在AndroidManifest.xml中赋值
<application
android:name=".application.MyApp"
android:allowBackup="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/android_template"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/TranslucentTheme">
写完上面的程序之后,当程序发生异常错误时,就可以捕获了。
4.将异常信息上传到服务器。
思路:当异常信息发生后,将异常信息保存到手机上。每次发生异常时,都会添加到Exception.txt文件中。在我们需要上传异常文件的地方,首先判断异常文件是否存在,然后请求服务器上传文件。上传成功之后可以及时将异常文件从客户手机中删除。
上传文件的方法:我是封装的xutils这个联网框架:
public static <T> Cancelable uploadCrashFile(String url, String path,
CommonCallback<T> callback) {
RequestParams params = new RequestParams(url);
params.setHeader("Content-Type", "application/json;charest=utf-8");
params.setMultipart(true);
params.addBodyParameter("files", new File(path), "*/*", path);
Cancelable cancelable = x.http().post(params, callback);
return cancelable;
}
//我是在主界面上传异常信息的。判断是否要上传
String filePath = Environment.getExternalStorageDirectory().
getAbsolutePath() + File.separator + "Exception.txt";
if (new File(filePath).exists()) {
uploadCrash(filePath);
}
上传文件
private void uploadCrash(String fullPath) {
HttpUtil.uploadCrashFile(UrlConst.UPLOADCRASH, fullPath, new MyCallBack<String>() {
@Override
public void onSuccess(String result) {
super.onSuccess(result);
// {"success":true,"code":"","message":"上传成功","data":"appuploadFiles/file/Exception.txt"}
JSONObject jsonObject = null;
try {
jsonObject = new JSONObject(result);
boolean success = jsonObject.getBoolean("success");
if (success){
File file=new File(filePath);
if (file.exists()){
file.delete();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
super.onError(ex, isOnCallback);
}
});
}
写完上面的就可以将异常文件上传到服务器了。