Whenever app crashes it's better to get the information about the crash, so that we can fix it and give user better experience. After googling and taking reference I write down the below code.
Project hierarchy |
1) Make UtilFunctions.java class in this we will write a function that reads Log of the project, which is sent in the mail along with crash detail so that we can know what had happened before crash.
UtilFunctions.java
import
java.io.BufferedReader;
import
java.io.IOException;
import
java.io.InputStreamReader;
import
android.content.Context;
public class UtilFunctions {
public static String
readLog(Context context) {
String
command = "logcat -d -v time";
Process
mLogcatProc = null;
BufferedReader
reader = null;
StringBuilder
log = null;
try {
mLogcatProc
= Runtime.getRuntime().exec(command);
reader
= new BufferedReader(new
InputStreamReader(mLogcatProc.getInputStream()));
String
line;
log
= new
StringBuilder();
String
separator = System.getProperty("line.separator");
while ((line =
reader.readLine())
!= null) {
log.append(line);
log.append(separator);
}
}
catch (IOException e) {
}
finally {
if (reader != null) {
try {
reader.close();
}
catch (IOException e)
{}
}
}
return log.toString();
}
}
|
2) Now make DeviceInfo.java class this will have functions which reads device information, which is sent in the mail along with crash detail(like version name, package name, model, display etc).
DeviceInfo.java
import java.io.File;
import
android.content.Context;
import
android.content.pm.PackageInfo;
import
android.content.pm.PackageManager;
import
android.content.pm.PackageManager.NameNotFoundException;
import
android.os.Environment;
import
android.os.StatFs;
public class DeviceInfo {
String
VersionName;
String
PackageName;
String
FilePath;
String
PhoneModel;
String
AndroidVersion;
String
Board;
String
Brand;
String
Device;
String
Display;
String
FingerPrint;
String
Host;
String
ID;
String
Model;
String
Product;
String
Tags;
long Time;
String
Type;
String
User;
Context
context;
public
DeviceInfo(Context context){
this.context = context;
}
void
collectInformation(Context context) {
PackageManager
pm = context.getPackageManager();
try {
PackageInfo
pi;
pi
= pm.getPackageInfo(context.getPackageName(), 0);
VersionName = pi.versionName;
PackageName = pi.packageName;
FilePath =
context.getFilesDir().getAbsolutePath();
PhoneModel =
android.os.Build.MODEL;
AndroidVersion =
android.os.Build.VERSION.RELEASE;
Board =
android.os.Build.BOARD;
Brand =
android.os.Build.BRAND;
Device =
android.os.Build.DEVICE;
Display =
android.os.Build.DISPLAY;
FingerPrint =
android.os.Build.FINGERPRINT;
Host =
android.os.Build.HOST;
ID =
android.os.Build.ID;
Model =
android.os.Build.MODEL;
Product =
android.os.Build.PRODUCT;
Tags =
android.os.Build.TAGS;
Time =
android.os.Build.TIME;
Type =
android.os.Build.TYPE;
User =
android.os.Build.USER;
}
catch
(NameNotFoundException e) {
e.printStackTrace();
}
}
public long
getAvailableInternalMemorySize() {
File
path = Environment.getDataDirectory();
StatFs
stat = new StatFs(path.getPath());
long blockSize =
stat.getBlockSize();
long availableBlocks
= stat.getAvailableBlocks();
return availableBlocks
* blockSize;
}
public long
getTotalInternalMemorySize() {
File
path = Environment.getDataDirectory();
StatFs
stat = new StatFs(path.getPath());
long blockSize =
stat.getBlockSize();
long totalBlocks =
stat.getBlockCount();
return totalBlocks *
blockSize;
}
public String
CreateInformationString() {
collectInformation(context);
String
separator = System.getProperty("line.separator");
StringBuilder
sb = new StringBuilder();
sb.append("Version
: " + VersionName + separator);
sb.append("Package
: " + PackageName + separator + separator);
sb.append("FilePath
: " + FilePath + separator);
sb.append("Phone
Model : " + PhoneModel + separator);
sb.append("Android
Version : " + AndroidVersion + separator);
sb.append("Board :
" + Board + separator);
sb.append("Brand :
" + Brand + separator);
sb.append("Device :
" + Device + separator);
sb.append("Display
: " + Display + separator);
sb.append("Finger
Print : " + FingerPrint + separator);
sb.append("Host :
" + Host + separator);
sb.append("ID :
" + ID + separator);
sb.append("Model :
" + Model + separator);
sb.append("Product
: " + Product + separator);
sb.append("Tags :
" + Tags + separator);
sb.append("Time :
" + Time + separator);
sb.append("Type :
" + Type + separator);
sb.append("User :
" + User + separator);
sb.append("Total
Internal memory : " + getTotalInternalMemorySize() + separator);
sb.append("Available
Internal memory : " + getAvailableInternalMemorySize() + separator);
return sb.toString();
}
public String
getFilePath(){
return context.getFilesDir().getAbsolutePath();
}
}
|
3) And lastly make CrashReporter.java class which will monitor crash.
CrashReporter.java
import
java.io.BufferedReader;
import java.io.File;
import
java.io.FileOutputStream;
import
java.io.FileReader;
import
java.io.FilenameFilter;
import
java.io.IOException;
import
java.io.PrintWriter;
import
java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import android.content.Context;
import
android.content.Intent;
public class CrashReporter implements
Thread.UncaughtExceptionHandler {
private
Thread.UncaughtExceptionHandler perviousHandler;
private static CrashReporter instance;
private Context mContext;
String
filePath;
DeviceInfo
deviceInfo;
String
separator;
public static CrashReporter
getInstance() {
if (instance == null)
instance = new
CrashReporter();
return instance;
}
public void init(Context
context) {
perviousHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
deviceInfo = new
DeviceInfo(context);
filePath = deviceInfo.getFilePath();
separator = System.getProperty("line.separator");
mContext = context;
}
public void
uncaughtException(Thread t, Throwable e) {
StringBuilder
infoString = new StringBuilder();
String
time = new Date().toString();
String
deviceInformation = deviceInfo.CreateInformationString();
String
stackTrace = readStackTrace(e);
String
stackCause = getStackCause(e);
String
logcat = UtilFunctions.readLog(mContext);
infoString.append("**** Start of current Report ***");
infoString.append("Error
Report collected on : " + time + separator);
infoString.append("Informations
:" + separator + "=>" + separator +
deviceInformation + separator);
infoString.append("StackTrace
: " + separator + "=>" + separator + stackTrace + separator);
infoString.append("StackCause
: " + separator + "=>" + separator + stackCause + separator);
infoString.append("Logcat :
" + separator + "=>" + separator + logcat + separator);
infoString.append("**** End of current Report ***");
saveAsFile(infoString.toString());
perviousHandler.uncaughtException(t,
e);
}
private String
readStackTrace(Throwable e) {
final Writer result =
new StringWriter();
final PrintWriter
printWriter = new PrintWriter(result);
e.printStackTrace(printWriter);
return
result.toString();
}
private String
getStackCause(Throwable e) {
StringBuffer
sb = new StringBuffer();
Throwable
cause = e.getCause();
while (cause != null) {
sb.append(readStackTrace(cause));
sb.append(separator);
cause
= cause.getCause();
}
return sb.toString();
}
private void
sendErrorMail(String ErrorContent) {
Intent
sendIntent = new Intent(Intent.ACTION_SEND);
String
subject = "Error Report";
String
body = ErrorContent;
// sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {Constant.EMAIL_ID});
sendIntent.putExtra(Intent.EXTRA_TEXT, body);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sendIntent.setType("message/rfc822");
mContext.startActivity(Intent.createChooser(sendIntent,
"Title:"));
}
private void
saveAsFile(String ErrorContent) {
try {
long random =
System.currentTimeMillis();
String
FileName = "stack-" + random + ".stacktrace";
FileOutputStream
trace = mContext.openFileOutput(FileName, Context.MODE_PRIVATE);
trace.write(ErrorContent.getBytes());
trace.close();
}
catch (IOException e)
{}
}
public String[]
getErrorFileList() {
File
dir = new File(filePath + "/");
dir.mkdir();
FilenameFilter
filter = new FilenameFilter() {
public boolean accept(File
dir, String name) {
return name.endsWith(".stacktrace");
}
};
return
dir.list(filter);
}
public boolean
isThereAnyErrorFile() {
return
getErrorFileList().length > 0;
}
public void
checkErrorAndSendMail() {
try {
if
(isThereAnyErrorFile()) {
String
wholeErrorText = "";
String[]
ErrorFileList = getErrorFileList();
int curIndex = 0;
final int MaxSendMail =
5;
for (String
curString : ErrorFileList) {
if (curIndex++
<= MaxSendMail) {
wholeErrorText
+= "Trace
collected :" + separator;
wholeErrorText
+= "=====================" + separator;
String
fPath = filePath + "/" + curString;
BufferedReader
input = new BufferedReader( new FileReader(fPath));
String
line;
while ((line = input.readLine())
!= null) {
wholeErrorText
+= line + "\n";
}
input.close();
}
File
curFile = new File(filePath + "/" + curString);
curFile.delete();
}
sendErrorMail(wholeErrorText);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
|
- getInstance() - Make the single instance of CrashReporter class.
- init() - Initialize
context, Thread.setDefaultUncaughtExceptionHandler(this), filePath;
- uncaughtException() - Crash is noticed and saved in a file.
- readStackTrace() - Read the crashed stack trace.
- getStackCause() - Read the Cause of crash.
- sendErrorMail() - Prompt to send error mail.
- saveAsFile() - Save the crash file so that next time on launch it will send.
- getErrorFileList() - Get the list of crash files.
- isThereAnyErrorFile() - Check if any crash file is present or not.
- checkErrorAndSendMail() - Read the crash file, prompt to send then delete the same.
4) Now make a activity in which on clicking on button application crashed, and opening the application next time it will ask to send error mail.
activity_main.xml
<?xml version="1.0"
encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:gravity="center"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/btnCrash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Crash"
/>
</LinearLayout>
|
MainActivity.java
import
android.app.Activity;
import
android.app.AlertDialog;
import
android.content.DialogInterface;
import
android.os.Bundle;
import
android.view.View;
import
android.view.View.OnClickListener;
import
android.widget.Button;
import
com.tutorialsface.errorhandler.crash.CrashReporter;
public class MainActivity extends Activity{
Button
btnCrash;
CrashReporter
reporter;
@Override
protected void onCreate(Bundle
savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnCrash = (Button)
findViewById(R.id.btnCrash);
reporter =
CrashReporter.getInstance();
reporter.init(this);
if (reporter.isThereAnyErrorFile())
{
showAlertDialog();
}
btnCrash.setOnClickListener(new
OnClickListener() {
@Override
public void onClick(View v)
{
int i = 1 /
0;
}
});
}
private void
showAlertDialog(){
AlertDialog.Builder
alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("Send
Error Log?");
alertDialogBuilder.setMessage(
"A
previous crash was reported. Would you like to send"
+
"
the developer the error log to fix this issue in the future?")
.setCancelable(false)
.setPositiveButton("OK",new
DialogInterface.OnClickListener() {
public void
onClick(DialogInterface dialog,int id) {
reporter.checkErrorAndSendMail();
}
})
.setNegativeButton("No",new
DialogInterface.OnClickListener() {
public void
onClick(DialogInterface dialog,int id) {
dialog.dismiss();
}
});
AlertDialog
alertDialog = alertDialogBuilder.create();
alertDialog.show();
}
}
|
Initialize the CrashReporter by writing-
CrashReporter reporter = CrashReporter.getInstance();
reporter.init(this);
Then check if any crash file is present by -
reporter.isThereAnyErrorFile()
If yes then show the alert dialog for sending report.
its too nice
ReplyDelete