Save the log file to the sdcard, directory: crash folder under the sdcard root directory
First, set permissions, no permissions to the final effect
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>Two classes must be created to collect error information for exceptionsHere is the CrashHandler class
/** * Created by BAIPEI on 2017/12/5. */ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import android.content.Context; 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 android.util.Log; import android.widget.Toast; public class CrashHandler implements UncaughtExceptionHandler{ private static final String TAG = "CrashHandler"; private Thread.UncaughtExceptionHandler mDefaultHandler;// System default UncaughtException processing class private static CrashHandler INSTANCE = new CrashHandler();// CrashHandler instance private Context mContext;// Context object for program private Map<String, String> info = new HashMap<String, String>();// Used to store device information and exception information private SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd-HH-mm-ss");// Used to format the date as part of the log file name /** Make sure there is only one instance of CrashHandler */ private CrashHandler() { } /** Get CrashHandler instance, singleton mode */ public static CrashHandler getInstance() { return INSTANCE; } /** * Initialization * * @param context */ public void init(Context context) { mContext = context; mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// Get the system default UncaughtException processor Thread.setDefaultUncaughtExceptionHandler(this);// Set this CrashHandler as the program's default processor } /** * When UncaughtException occurs, it goes into the overridden method to handle it */ public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { // Let the default exception handler of the system handle the custom unhandled mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(3000);// If processed, allow the program to continue running for 3 seconds before exiting, ensuring that the file is saved and uploaded to the server } catch (InterruptedException e) { e.printStackTrace(); } // Exit the program android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } /** * Customize error handling, collect error information and send error reports. * * @param ex * Exception Information * @return true If the exception information is handled; otherwise, false is returned. */ public boolean handleException(Throwable ex) { if (ex == null) return false; new Thread() { public void run() { Looper.prepare(); Toast.makeText(mContext, "I'm sorry,Program Exception,About to quit", Toast.LENGTH_SHORT).show(); Looper.loop(); } }.start(); // Collect device parameter information collectDeviceInfo(mContext); // Save Log File saveCrashInfo2File(ex); return true; } /** * Collect device parameter information * * @param context */ public void collectDeviceInfo(Context context) { try { PackageManager pm = context.getPackageManager();// Get Package Manager PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);// Get information about the app, the main Activity if (pi != null) { String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + ""; info.put("versionName", versionName); info.put("versionCode", versionCode); } } catch (NameNotFoundException e) { e.printStackTrace(); } Field[] fields = Build.class.getDeclaredFields();// Reflection mechanism for (Field field : fields) { try { field.setAccessible(true); info.put(field.getName(), field.get("").toString()); Log.d(TAG, field.getName() + ":" + field.get("")); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } private String saveCrashInfo2File(Throwable ex) { StringBuffer sb = new StringBuffer(); for (Map.Entry<String, String> entry : info.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + "=" + value + "\r\n"); } Writer writer = new StringWriter(); PrintWriter pw = new PrintWriter(writer); ex.printStackTrace(pw); Throwable cause = ex.getCause(); // Loop to write all exception information to the writer while (cause != null) { cause.printStackTrace(pw); cause = cause.getCause(); } pw.close();// Remember to close String result = writer.toString(); sb.append(result); // Save File long timetamp = System.currentTimeMillis(); String time = format.format(new Date()); String fileName = "crash-" + time + "-" + timetamp + ".log"; if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { try { File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "crash"); Log.i("CrashHandler", dir.toString()); if (!dir.exists()) dir.mkdir(); FileOutputStream fos = new FileOutputStream(new File(dir, fileName)); fos.write(sb.toString().getBytes()); fos.close(); return fileName; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return null; } }Here is the CrashApplication class
import android.app.Application; /** * Created by BAIPEI on 2017/12/5. */ public class CrashApplication extends Application { @Override public void onCreate() { super.onCreate(); CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(this); } }Here's MainActivity, because we want to make an exception, if we don't call that exception here,That exception won't work, and we won't get the results we want
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button mBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mBtn = (Button) findViewById(R.id.btn); mBtn.setOnClickListener(this); } @Override public void onClick(View v) { //Call this exception in a click event throw new RuntimeException("String"); } }Here is the layout file, which is also a click event
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="bwie.com.crashhandler_diqitian.MainActivity"> <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="click"/> </android.support.constraint.ConstraintLayout>Don't forget to put AndroidManifest.xml Change the application inside
<application android:name=".CrashApplication" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity"> ... </activity> </application>