Android开发中的异常统一处理
实际开发中为了防止程序异常奔溃,而使得开发人员不知道奔溃原因,且影响用户体验:所以我们应该在app中统一处理异常,拦截异常信息,上报服务器。
一自定义异常拦截实现Thread.UncaughtExceptionHandler重写拦截异常方法
public class CrashHandler implements Thread.UncaughtExceptionHandler{
public static final String TAG = "CrashHandler";
//系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
//CrashHandler实例
private static volatile CrashHandler instance;
//程序的Context对象
private Context mContext;
//创建警告对话框
private Dialog dialog;
//异常文件存储路径
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/myExceptionCatch/log/";
//异常文件存储名字
private static final String FILE_NAME = "catch";
//异常文件存储后缀
private static final String FLIE_NAME_SUFFIX = ".log";
/**
* 无参构造
*/
public CrashHandler() {
}
/**
* 获取CrashHandler实例 ,单例模式
*/
public static CrashHandler getInstance() {
if (instance == null)
synchronized (CrashHandler.class) {
if (instance == null) {
instance = new CrashHandler();
}
}
return instance;
}
/**
* 初始化
*/
public void init(Context context) {
mContext = context.getApplicationContext();
//获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 程序崩溃会被该方法捕获到 在此添加逻辑是自己处理还是交给系统异常处理
* handleException是自己处理异常的方法
*/
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
if (!handleException(throwable) && mDefaultHandler != null) {
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, throwable);
} else {
Log.d("flag","----------------获取到异常了else");
// try {
// Thread.sleep(3000);
// } catch (InterruptedException e) {
// Log.e(TAG, "error : ", e);
// }
// //退出程序
// android.os.Process.killProcess(android.os.Process.myPid());
// System.exit(1);
}
}
/**
* 添加自己处理异常的方法 在这里收集收集的设备信息 保存异常文件
*/
private boolean handleException(final Throwable ex) {
if (ex == null || mContext == null) {
return false;
}
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
try {
//先获取手机的基本信息 将异常信息一起写入文件 上传服务器
savcExceptionToSDCard(ex);
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_SHORT).show();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, "error : ", e);
}
//退出程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(1);
Looper.loop();
}
}).start();
return true;
}
/**
* 出现异常写到内存卡上 然后读文件上传到服务器
*/
private void savcExceptionToSDCard(final Throwable ex) throws IOException {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Log.w("TAG", "sdcard unmounted,skip save exception");
}
File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdirs();
}
long current = System.currentTimeMillis();
final String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
File file = new File(PATH + FILE_NAME + FLIE_NAME_SUFFIX);
try {
//写入时间
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
pw.println(time);
//写入手机信息
savePhoneInfo(pw, ex);
} catch (Exception e) {
}
//上传服务器
uploadToServer(file);
}
/**
* 写入手机基本信息 异常机型 异常原因等
*/
private void savePhoneInfo(PrintWriter pw, Throwable ex) throws PackageManager.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(" _ sdk: ");
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);
pw.println();
//异常信息
ex.printStackTrace(pw);
pw.close();
}
/**
* 读取文件转换成字符串
* 参数一 输入文件file
*
* @return
*/
public String readFromFile(File file) {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(file));
StringBuilder stringBuilder = new StringBuilder();
String content;
while ((content = bufferedReader.readLine()) != null) {
stringBuilder.append(content);
}
return stringBuilder.toString();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void uploadToServer(File file) {
String ec=readFromFile(file);
OkhttpUtils.upEcInfo(ec,"服务器的url");
}
}
二为了实现app全局监听 在App的application里进行注册
public class MyApplication extends Application {
private static MyApplication instance;
@Override
public void onCreate() {
super.onCreate();
//获取CrashHandler实例并初始化CrashHandler
CrashHandler.getInstance().init(getApplicationContext());
}
public static MyApplication getInstance() {
if (instance == null) {
instance = new MyApplication();
}
return instance;
}
}
三清单文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.wbxu.myexceptiondemo">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
四上传工具本文使用okhttp
public class OkhttpUtils {
//上传字符串
private static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");
public static OkHttpClient mOkHttpClient;
// public static String SessionKey;
// 1静态代码块初始化OkHttpClient
static {
// 2创建OkHttpClient构建器
OkHttpClient.Builder builder = new OkHttpClient.Builder();
// 3设置网络的链接超时时间
builder.connectTimeout(50, TimeUnit.SECONDS);
// 4使用构建器 构建出OkHttpClient
mOkHttpClient = builder.build();
}
//获取OkHttpClient的方法
public static OkHttpClient getOkHttpClient(){
return mOkHttpClient;
}
public static void upEcInfo(String ecInfo,String url){
RequestBody requestBody = RequestBody.create(MEDIA_TYPE_MARKDOWN, ecInfo);
//4创建请求对象
Request request = new Request.Builder()
.url(url)
.post(requestBody)
.build();
//5执行请求 enqueue异步请求
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("Flag","------上传异常失败错误信息为:e.getLocalizedMessage() = " + e.getLocalizedMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String strInfo = response.body().string();
Log.d("flag", "----------------门店检查图片信息上传返回的请求体为" + strInfo);
}
});
}
}
五mainActivity里创建异常进行测试
public class MainActivity extends AppCompatActivity {
private TextView ecTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ecTextView= (TextView) findViewById(R.id.tv);
}
/**
* 制造异常
*/
public void buildEc(View view) {
new Thread(new Runnable() {
@Override
public void run() {
ecTextView.setText("异常捕获");
}
}).start();
}
}