应用发布之后,总有人反馈说发生crash,但是由于拿不到log,我无法定位问题。后来发现,我们应该收集crash日志,并上传到服务器。
国内有很多的三方机构提供了崩溃收集的sdk,我们可以直接拿来使用,比如,我之前做的app使用的是bugHD(http://bughd.com/)提供的服务。
但是崩溃收集的原理是什么呢?搜索了一下,发现使用的是java中的uncaughtExceptionHandler,我们可以通过Thread.setDefautUncaughtExceptionHandler()设置我们自己的UncaughtExceptionHandler。它其实是一个接口,实现方法即可。
我写的一个demo:
import android.os.Looper;
import android.widget.Toast;
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.Date;
import java.util.Locale;
import java.util.Map;
/**
* Created by Rowandjj on 2015/5/19.
*
* 崩溃日志收集
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler
{
private static CrashHandler sInstance = null;
private static Object lock = new Object();
private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
private CrashHandler()
{
}
public static CrashHandler getInstance()
{
if (sInstance == null)
{
synchronized (lock)
{
if (sInstance == null)
sInstance = new CrashHandler();
}
}
return sInstance;
}
public void register()
{
mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread thread, Throwable ex)
{
//生成日志
final String filename = cacheLog(ex);
new Thread(new Runnable()
{
@Override
public void run()
{
Looper.prepare();
if(filename != null)
Toast.makeText(AppEnv.getAppContext(),"程序崩溃了:( \n崩溃日志已保存到"+filename+"",Toast.LENGTH_SHORT).show();
Looper.loop();
}
}).start();
try
{
Thread.sleep(2000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
mDefaultExceptionHandler.uncaughtException(thread,ex);
}
private String cacheLog(Throwable ex)
{
if(ex == null)
return null;
Map<String, String> hardwareInfo = HardwareUtils.getHardwareInfo();
StringBuilder buffer = new StringBuilder();
for (Map.Entry<String, String> me : hardwareInfo.entrySet())
{
buffer.append(me.getKey() + ":" + me.getValue() + "\n");
}
buffer.append("packname:" + AppEnv.getPackageName() + "\n");
buffer.append("versionname:" + AppEnv.getVersionName() + "\n");
buffer.append("versioncode:" + AppEnv.getVersionCode() + "\n");
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null)
{
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
buffer.append(result);
long timestamp = System.currentTimeMillis();
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.CHINA);
String time = formatter.format(new Date());
String filename = "crash-" + time + "-" + timestamp + ".log";
File file = new File(BasicUtils.getStoreDir(), filename);
try
{
if (!file.exists())
file.createNewFile();
FileOutputStream out = new FileOutputStream(file);
out.write(buffer.toString().getBytes());
out.close();
Logger.d(this,file.getAbsolutePath());
return file.getAbsolutePath();
} catch (IOException e)
{
e.printStackTrace();
}
return null;
}
}
然后在你应用的Application中调用
CrashHandler.getInstance().register();
即可。
应用出现crash时,会把log保存到本地。如果希望上传到服务器,加上一个http上传模块即可。
部分代码没有贴出,大家自己脑补。