1. 创建类继承 UncaughtExceptionHandler
import android.content.Context;
import android.os.Environment;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.emh.pdavoicecall4.constant.Constants;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
public class MyCrashHandler implements Thread.UncaughtExceptionHandler {
private Context context;
private static final String APP_NAME = Constants.APP_PACKAGE_NAME;
private String logFilePath = Environment.getExternalStorageDirectory().getPath()
+ File.separator + APP_NAME + File.separator + "Log" + File.separator + "crashlog";
public MyCrashHandler(Context context) {
this.context = context;
}
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull final Throwable ex) {
Log.e("程序出现异常", "Thread = " + thread.getName() + "\nThrowable = " + ex.getMessage());
String stackTraceInfo = getStackTraceInfo(ex);
Log.e("stackTraceInfo", stackTraceInfo);
saveThrowableMessage(stackTraceInfo);
new Thread() {
public void run() {
Looper.prepare();
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_SHORT).show();
Looper.loop();
};
}.start();
}
private String getStackTraceInfo(final Throwable t) {
PrintWriter pw = null;
Writer writer = new StringWriter();
try {
pw = new PrintWriter(writer);
t.printStackTrace(pw);
} catch (Exception e) {
return "";
} finally {
if (pw != null) {
pw.close();
}
}
return writer.toString();
}
private void saveThrowableMessage(String errorMessage) {
if (TextUtils.isEmpty(errorMessage)) {
return;
}
File file = new File(logFilePath);
if (!file.exists()) {
boolean mkdirs = file.mkdirs();
if (mkdirs) {
writeStringToFile(errorMessage, file);
}
} else {
writeStringToFile(errorMessage, file);
}
}
private void writeStringToFile(final String errorMessage, final File file) {
FileOutputStream fos = null;
try {
String content = errorMessage + "[" + Utils.getCurrentDateTime() + "]\r\n";
ByteArrayInputStream bais = new ByteArrayInputStream(content.getBytes("UTF-8"));
File crashLog = createCrashLogFile(file);
fos = new FileOutputStream(crashLog, true);
int len = 0;
byte[] bytes = new byte[1024];
while ((len = bais.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.flush();
Log.e("程序出现异常", "写入本地文件成功:" + file.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private List getAllCrashFiles() {
File dir = new File(logFilePath);
File[] files = dir.listFiles();
List fileNames = new ArrayList<>();
for (int i = 0; i < files.length; i++) {
if (files[i].isFile()) {
fileNames.add(files[i].getName());
}
}
return fileNames;
}
private File createCrashLogFile(File file) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
String currentDate = sdf.format(new Date());
File crashLog = new File(file, currentDate + ".txt");
if (!crashLog.exists()) {
crashLog.createNewFile();
List crashLogNames = getAllCrashFiles();
if (crashLogNames.size() > 10) {
long currentTimeMilSeconds = new Date().getTime();
String earliestDate = sdf.format(new Date(currentTimeMilSeconds - 24 * 60 * 60 * 10 * 1000));
deleteFile(earliestDate + ".txt");
}
}
return crashLog;
}
private void deleteFile(String fileName) {
File file = new File(logFilePath + File.separator + fileName);
if (file.exists()) {
file.delete();
}
}
}
2. 创建类继承Application,初始化MyCrashHandler
import android.app.Application;
public class MyApp extends Application {
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
MyCrashHandler handler = new MyCrashHandler(getApplicationContext());
Thread.setDefaultUncaughtExceptionHandler(handler);
}
}
3. 测试
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button test_crash_btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
test_crash_btn = (Button) findViewById(R.id.test_crash_btn);
test_crash_btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
testThreadException();
}
});
}
private void testUIThreadException() {
String string = null;
char[] chars = string.toCharArray();
}
private void testThreadException() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
int s = 10 / i;
}
});
thread.start();
}
}