经典随机Crash之一:线程安全- http://mp.weixin.qq.com/s?__biz=MzAxMzYyNDkyNA==&mid=2651332781&idx=1&sn=f751d770f8853d503300b3c19abaea6c&chksm=8063339eb714ba88cd547f4e81981337a78da41563c01ef82b2e2e30450d84e5f0d308d86e4e&mpshare=1&scene=23&srcid=0405o4nlDjNFIS3GDESgT9L5#rd
-- Android捕获崩溃异常- https://github.com/shenhuniurou/BlogDemos/tree/master/CrashDemo
捕捉异常:try catch finally ;
try catch捕获不到的异常,使用:Thread.UncaughtExceptionHandler 。
-- Android 平台 Native 代码的崩溃捕获机制及实现- https://mp.weixin.qq.com/s/g-WzYF3wWAljok1XjPoo7w?
解决崩溃
1.优先级: 优先解决Top 崩溃或者对业务有重大影响;其次简单易排查的java崩溃,疑难杂症放到最后
2.尝试复现:针对疑难杂症,可以通过早先在日志中自定义的应用信息来复现崩溃过程
3.着眼硬件:排查是否是手机系统,内存,机型引起的。
4.系统错误:有些问题是Android系统不同版本之间存在的问题。
-- 捕获异常
1. 在4.1.1以上,5.0以下:使用安卓系统自带的libcorkscrew.so
2. 5.0以上:安卓系统中没有了libcorkscrew.so,使用自己编译的libunwind
在用户环境中的很多native crash单靠堆栈是解决不了的,logcat是非常重要的补充。好几例webview crash都是通过发生crash时的logcat定位的。
> Android Native Crash;APP java和native Crash;(libcorkscrew,libbacktrace,libunwind)
Android崩溃有两类,一类是Java Exception异常,一类是Native Signal异常。
4.1.1以上,5.0以下利用libcorkscrew.so来获取,但是libcorkscrew.so在Android4.4之后废弃了。当Android版本号大于4.4时,可以采用libbacktrace.so(5.0以上)来获取崩溃数据。
基于google-breakpad的方法体量较大,但获取的信息较全面。基于异常信号处理方法体量小,但信息不全面,只有崩溃线程的堆栈信息,不利于定位多线程崩溃的问题。
-- Android平台Native代码的崩溃捕获机制及实现- https://blog.csdn.net/mba16c35/article/details/54178067
5.0以下使用libcorkscrew,只在4.1.1以上,5.0以下的系统存在;5.0以上的系统使用了libunwind来代替libcorkscrew。
CoffeeCatch, a tiny native POSIX signal catcher (especially useful for JNI code on Android/Dalvik)- https://github.com/xroche/coffeecatch
-- Android 平台 Native 代码的崩溃捕获机制及实现- https://my.oschina.net/bugly/blog/1354954
在4.1.1以上,5.0以下:使用安卓系统自带的libcorkscrew.so;5.0以上:安卓系统中没有了libcorkscrew.so,使用自己编译的libunwind,libunwind是一个独立的开源库,高版本的安卓源码中也使用了libunwind作为解堆栈的工具,并针对安卓做了一些适配。
libunwind下载编译- http://download.savannah.nongnu.org/releases/libunwind/
-- native crash现有的方案:
1.Google Breakpad 权威,跨平台 代码体量较大
2.利用LogCat日志 利用安卓系统实现 需要在crash时启动新进程过滤logcat日志,不可靠
3.coffeecatch 实现简洁,改动容易 存在兼容性问题
综合考虑,可以基于coffeecatch + libunwind改进。
-- 移动应用崩溃日志收集工具对比- http://blog.csdn.net/chaosminds/article/details/56287953
1.腾讯bugly,应用崩溃分析能力优秀,并且提供了一定的运营数据统计能力。
2.友盟u-app,侧重于专业的运营数据的统计,但在崩溃分析方面,相比其他工具显得单一。
3.Crasheye,同样拥有优秀的崩溃分析能力,但不具备运营数据的统计。但在测试阶段,不需要统计运营数据时,其一行代码快速接入SDK的能力,方便快速开发,是其优势所在。
总而言之,这三款工具各自的优势和侧重点不同。根据具体场景的需要,可以选择合适的工具进行开发。
-- 常见的Android崩溃有两类,一类是Java Exception异常,一类是Native Signal异常。
Java的异常可以分为两类:Checked Exception和UnChecked Exception。
所有RuntimeException类及其子类的实例被称为Runtime异常,即UnChecked Exception;
Checked异常又称为编译时异常,即在编译阶段被处理的异常。RuntimeException类及其子类的异常实例则被称为Checked Exception。
Android平台的崩溃捕获机制及实现- http://blog.csdn.net/leeo1010/article/details/50522892
app崩溃日志收集以及上传- https://github.com/DrJia/AndroidLogCollector
在Android中自定义捕获Application全局异常- http://blog.csdn.net/qq_23547831/article/details/41725069
-- 美团外卖Android Crash治理之路- https://blog.csdn.net/MeituanTech/article/details/80701773
常见的Crash类型包括:空节点、角标越界、类型转换异常、实体对象没有序列化、数字转换异常、Activity或Service找不到等。这类Crash是App中最为常见的Crash,也是最容易反复出现的。在获取Crash堆栈信息后,解决这类Crash一般比较简单,更多考虑的应该是如何避免。
两个我们治理的量比较大的Crash:NullPointerException,IndexOutOfBoundsException
下面是常见的Crash解决思路:
1.尝试找到造成Crash的可疑代码,看是否有特异的API或者调用方式不当导致的,尝试修改代码逻辑来进行规避。
2.通过Hook来解决,Hook分为Java Hook和Native Hook。Java Hook主要靠反射或者动态代理来更改相应API的行为,需要尝试找到可以Hook的点,一般Hook的点多为静态变量,同时需要注意Android不同版本的API,类名、方法名和成员变量名都可能不一样,所以要做好兼容工作;Native Hook原理上是用更改后方法把旧方法在内存地址上进行替换,需要考虑到Dalvik和ART的差异;相对来说Native Hook的兼容性更差一点,所以用Native Hook的时候需要配合降级策略。
3.如果通过前两种方式都无法解决的话,我们只能尝试反编译ROM,寻找解决的办法。
> native crash
Android 平台 Native 代码的崩溃捕获机制及实现- http://geek.csdn.net/news/detail/210852
Android平台,native crash一直是crash里的大头:
1.Google Breakpad,权威,跨平台 代码体量较大
2.利用LogCat日志,利用安卓系统实现,需要在crash时启动新进程过滤logcat日志,不可靠
(推荐)3.coffeecatch,实现简洁,改动容易,存在兼容性问题
-- Native Crash在Android上的特点:
1.出错时界面不会弹出提示框提醒程序崩溃(Android 5.0以下);
2.出错时会弹出提示框提醒程序崩溃(Android 5.0以上);
3.程序会直接闪退到系统桌面;
4.这类错误一般是由C++层代码错误引起的;
5.绝大部分Crash工具不能够捕获;
-- Native崩溃时,Android系统同样会输出崩溃堆栈到LogCat,那么拿到了LogCat信息也就拿到了Native的崩溃堆栈。
Process process = Runtime.getRuntime().exec(new String[]{"logcat","-d","-v","threadtime"});
String logTxt = getSysLogInfo(process.getInputStream());
> java crash
通过UncaughtExceptionHandler来记录dump异常日志
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.Process;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
public class CrashHandler implements UncaughtExceptionHandler {
private static final String TAG = "CrashHandler";
private static final boolean DEBUG = true;
private static final String PATH = Environment.getExternalStorageDirectory() + "/CrashDemo/log/";
private static final String FILE_NAME = "crash";
private static final String FILE_NAME_SUFFIX = ".trace";
private static final String ABOLUTE_PATH = PATH + FILE_NAME + FILE_NAME_SUFFIX;
private String deviceToken;
private static CrashHandler sInstance = new CrashHandler();
private UncaughtExceptionHandler mDefaultCrashHandler;
private Context mContext;
private CrashHandler() {
}
public static CrashHandler getInstance() {
return sInstance;
}
public void init(Context context) {
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
}
/**
* 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法
* thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,我们就可以得到异常信息。
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
// 导出异常信息到SD卡中
dumpExceptionToSDCard(ex);
} catch (IOException e) {
e.printStackTrace();
}
ex.printStackTrace();
// 如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
if (mDefaultCrashHandler != null) {
mDefaultCrashHandler.uncaughtException(thread, ex);
} else {
Process.killProcess(Process.myPid());
}
}
private File dumpExceptionToSDCard(Throwable ex) throws IOException {
// 如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
if (!Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
if (DEBUG) {
Log.w(TAG, "sdcard unmounted,skip dump exception");
return null;
}
}
File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdirs();
}
long current = System.currentTimeMillis();
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
.format(new Date(current));
// File file = new File(PATH + FILE_NAME + time + "_"+ deviceToken +
// FILE_NAME_SUFFIX);
File file = new File(PATH + FILE_NAME + FILE_NAME_SUFFIX);
if (!file.exists()) {
file.createNewFile();
} else {
try {
// 追加内容
PrintWriter pw = new PrintWriter(new BufferedWriter(
new FileWriter(file, true)));
pw.println(time);
dumpPhoneInfo(pw);
pw.println();
ex.printStackTrace(pw);
pw.println("---------------------------------分割线----------------------------------");
pw.println();
pw.close();
} catch (Exception e) {
Log.e(TAG, "dump crash info failed");
}
}
return file;
}
private void dumpPhoneInfo(PrintWriter pw) throws 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("_");
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);
}
/**
* 提供方法上传异常信息到服务器
* @param log
*/
private void uploadExceptionToServer(File log) {
// TODO Upload Exception Message To Your Web Server
}
}
public class CrashApplication extends Application {
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(getApplicationContext());
}
}
-------------------------------
-- android和iOS平台的崩溃捕获和收集- https://www.cnblogs.com/sevenyuan/p/4347757.html
要实现崩溃捕获和收集的困难主要有这么几个:
1、如何捕获崩溃(比如c++常见的野指针错误或是内存读写越界,当发生这些情况时程序不是异常退出了吗,我们如何捕获它呢)
2、如何获取堆栈信息(告诉我们崩溃是哪个函数,甚至是第几行发生的,这样我们才可能重现并修改问题)
3、将错误日志上传到指定服务器(这个最好办)
会引发崩溃的代码本质上就两类,一个是c++语言层面的错误,比如野指针,除零,内存访问异常等等;另一类是未捕获异常(Uncaught Exception)
首先是java代码的崩溃捕获,这个可以仿照最下面的完整代码写一个UncaughtExceptionHandler,然后在所有的Activity的onCreate函数最开始调用
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(this));
这样,当发生崩溃的时候,就会自动调用UncaughtExceptionHandler的public void uncaughtException(Thread thread, Throwable exception)函数,其中的exception包含堆栈信息,我们可以在这个函数里面打印我们需要的信息,并且上传错误日志
然后是重中之重,jni的c++代码如何进行崩溃捕获。
#include
#include
static struct sigaction old_sa[NSIG];
void InitCrashReport()
{
CCLOG("InitCrashReport");
// Try to catch crashes...
struct sigaction handler;
memset(&handler, 0, sizeof(struct sigaction));
handler.sa_sigaction = android_sigaction;
handler.sa_flags = SA_RESETHAND;
#define CATCHSIG(X) sigaction(X, &handler, &old_sa[X])
CATCHSIG(SIGILL);
CATCHSIG(SIGABRT);
CATCHSIG(SIGBUS);
CATCHSIG(SIGFPE);
CATCHSIG(SIGSEGV);
CATCHSIG(SIGSTKFLT);
CATCHSIG(SIGPIPE);
}
通过singal的设置,当崩溃发生的时候就会调用android_sigaction函数。这同样是linux的信号机制。 此处设置信号回调函数的代码跟iOS有点不同,这个只是同一个功能的两种不同写法,没有本质区别。有兴趣的可以google下两者的区别。
jni的崩溃回调函数如下:
void android_sigaction(int signal, siginfo_t *info, void *reserved)
{
if (!g_env) {
return;
}
jclass classID = g_env->FindClass(CLASS_NAME);
if (!classID) {
return;
}
jmethodID methodID = g_env->GetStaticMethodID(classID, "onNativeCrashed", "()V");
if (!methodID) {
return;
}
g_env->CallStaticVoidMethod(classID, methodID);
old_sa[signal].sa_handler(signal);
}
java对应的函数实现如下:
public static void onNativeCrashed() {
// http://stackoverflow.com/questions/1083154/how-can-i-catch-sigsegv-segmentation-fault-and-get-a-stack-trace-under-jni-on-a
Log.e("handller", "handle");
new RuntimeException("crashed here (native trace should follow after the Java trace)").printStackTrace();
s_instance.startActivity(new Intent(s_instance, CrashHandler.class));
}
我们开启了一个新的activity,因为当jni发生崩溃的时候,原始的activity可能已经结束掉了。 这个新的activity实现如下:
publicclass CrashHandler extends Activity
{
publicstaticfinal String TAG = "CrashHandler";
protectedvoid onCreate(Bundle state)
{
super.onCreate(state);
setTitle(R.string.crash_title);
setContentView(R.layout.crashhandler);
TextView v = (TextView)findViewById(R.id.crashText);
v.setText(MessageFormat.format(getString(R.string.crashed), getString(R.string.app_name)));
final Button b = (Button)findViewById(R.id.report),
c = (Button)findViewById(R.id.close);
b.setOnClickListener(new View.OnClickListener(){
publicvoid onClick(View v){
final ProgressDialog progress = new ProgressDialog(CrashHandler.this);
progress.setMessage(getString(R.string.getting_log));
progress.setIndeterminate(true);
progress.setCancelable(false);
progress.show();
final AsyncTask task = new LogTask(CrashHandler.this, progress).execute();
b.postDelayed(new Runnable(){
publicvoid run(){
if (task.getStatus() == AsyncTask.Status.FINISHED)
return;
// It's probably one of these devices where some fool broke logcat.
progress.dismiss();
task.cancel(true);
new AlertDialog.Builder(CrashHandler.this)
.setMessage(MessageFormat.format(getString(R.string.get_log_failed), getString(R.string.author_email)))
.setCancelable(true)
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
}}, 3000);
}});
c.setOnClickListener(new View.OnClickListener(){
publicvoid onClick(View v){
finish();
}});
}
static String getVersion(Context c)
{
try {
return c.getPackageManager().getPackageInfo(c.getPackageName(),0).versionName;
} catch(Exception e) {
return c.getString(R.string.unknown_version);
}
}
}
class LogTask extends AsyncTask
{
Activity activity;
String logText;
Process process;
ProgressDialog progress;
LogTask(Activity a, ProgressDialog p) {
activity = a;
progress = p;
}
@Override
protected Void doInBackground(Void... v) {
try {
Log.e("crash", "doInBackground begin");
process = Runtime.getRuntime().exec(new String[]{"logcat","-d","-t","500","-v","threadtime"});
logText = UncaughtExceptionHandler.readFromLogcat(process.getInputStream());
Log.e("crash", "doInBackground end");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(activity, e.toString(), Toast.LENGTH_LONG).show();
}
returnnull;
}
@Override
protectedvoid onCancelled() {
Log.e("crash", "onCancelled");
process.destroy();
}
@Override
protectedvoid onPostExecute(Void v) {
Log.e("crash", "onPostExecute");
progress.setMessage(activity.getString(R.string.starting_email));
UncaughtExceptionHandler.sendLog(logText, activity);
progress.dismiss();
activity.finish();
Log.e("crash", "onPostExecute over");
}
获取到错误日志后,就可以写到sd卡(同样不要忘记添加权限),或者是上传。
-- Android崩溃SDK日志捕获(Checked、unChecked异常)
常见的Android崩溃有两类,一类是Java Exception异常,一类是Native Signal异常。对于很多基于Unity、Cocos平台的游戏,还会有C#、JavaScript、Lua等的异常。
Java的异常可以分为两类:Checked Exception和UnChecked Exception。所有RuntimeException类及其子类的实例被称为Runt ime异常,即UnChecked Exception,不是RuntimeException类及其子类的异常实例则被称为Checked Exception。
Checked异常又称为编译时异常,即在编译阶段被处理的异常。编译器会强制程序处理所有的Checked异常,也就是用try…catch显式的捕获并处理,因为Java认为这类异常都是可以被处理(修复)的。在Java API文档中,方法说明时,都会添加是否throw某个exception,这个exception就是Checked异常。如果没有try…catch这个异常,则编译出错,错误提示类似于“Unhandled
-- 该类异常捕获的流程是:
执行try块中的代码出现异常,系统会自动生成一个异常对象,并将该异常对象提交给Java运行环境,这个就是异常抛出(throw)阶段;
当Java运行环境收到异常对象时,会寻找最近的能够处理该异常对象的catch块,找到之后把该异常对象交给catch块处理,这个就是异常捕获(catch)阶段。
- Checked异常一般是不引起Android App Crash的,注意是“一般”,这里之所以介绍,有两个原因:
形成系统的了解,更好地对比理解UnCheckedException;
对于一些Checked Exception,虽然我们在程序里面已经捕获并处理了,但是如果能同时将该异常收集并发送到后台,将有助于提升App的健壮性。比如修改代码逻辑回避该异常,或者捕获后采用更好的方法去处理该异常。至于应该收集哪些Checked Exception,则取决于App的业务逻辑和开发者的经验了。
- UnChecked异常又称为运行时异常,即Runtime-Exception,最常见的莫过于NullPointerException。UnChecked异常发生时,由于没有相应的try…catch处理该异常对象,所以Java运行环境将会终止,程序将退出,也就是我们所说的Crash。当然,你可能会说,那我们把这些异常也try…catch住不就行了。理论上确实是可以的,但有两点会导致这种方案不可行:
无法将所有的代码都加上try…catch,这样对代码的效率和可读性将是毁灭性的;
UnChecked异常通常都是较为严重的异常,或者说已经破坏了运行环境的。比如内存地址,即使我们try…catch住了,也不能明确知道如何处理该异常,才能保证程序接下来的运行是正确的。
-- Java层异常捕获,Exception的分类及捕获:
static class MyCrashHandler implements UncaughtExceptionHandler {
private UncaughtExceptionHandler originalHandler;
private MyCrashHandler(Context context) {
originalHandler = Thread.getDefaultUncaughtExceptionHandler(); }
@Override
public void uncaughtException(Thread thread, final Throwable throwable) {
// Deal this exception
if (originalHandler != null) {
originalHandler.uncaughtException(thread, throwable);
}
}}
获取Exception崩溃堆栈:
捕获Exception之后,我们还需要知道崩溃堆栈的信息,这样有助于我们分析崩溃的原因,查找代码的Bug。异常对象的printStackTrace方法用于打印异常的堆栈信息,根据printStackTrace方法的输出结果,我们可以找到异常的源头,并跟踪到异常一路触发的过程。
public static String getStackTraceInfo(final Throwable throwable) {
String trace = "";
try {
Writer writer = new StringWriter();
PrintWriter pw = new PrintWriter(writer);
throwable.printStackTrace(pw);
trace = writer.toString();
pw.close();
} catch (Exception e) {
return "";
}
return trace;
}
-- Native崩溃分析与捕获
Linux进程对信号异常的响应可以归结为以下几类:
忽略信号:对信号不做任何处理,除了SIGKILL及SIGSTOP以外(超级用户杀掉进程时产生),其他都可以忽略;
捕获信号:注册信号处理函数,当信号发生时,执行相应的处理函数;
默认处理:执行内核分配的默认信号处理函数,大多数我们遇到的信号异常,默认处理是终止程序并生成core文件。
对Native代码的崩溃,可以通过调用sigaction()注册信号处理函数来完成。
#include
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact));
signum:代表信号编码,可以是除SIGKILL及SIGSTOP外的任何一个特定有效的信号,如果为这两个信号定义自己的处理函数,将导致信号安装错误。
act:指向结构体sigaction的一个实例的指针,该实例指定了对特定信号的处理,如果设置为空,进程会执行默认处理。
oldact:和参数act类似,只不过保存的是原来对相应信号的处理,也可设置为NULL。
Native代码崩溃(即信号异常)捕获的代码片段如下:
constint handledSignals[] = { SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS };
constint handledSignalsNum = sizeof(handledSignals) / sizeof(handledSignals[0]);
struct sigaction old_handlers[handledSignalsNum];
int nativeCrashHandler_onLoad(JNIEnv *env) {
struct sigaction handler;
memset(&handler,0,sizeof(sigaction));
handler.sa_sigaction = my_sigaction;
handler.sa_flags = SA_RESETHAND;
for (int i = 0; i < handledSignalsNum; ++i) { sigaction(handledSignals[i], &handler, &old_handlers[i]); }
return 1; }
当Android应用程序加载so库的时候,调用nativeCrashHandler_onLoad会为SIGSEGV、SIGABRT、SIGFPE、SIGILL、SIGBUS通过sigaction注册信号处理函数my_sigaction。当发生Native崩溃并且发生前面几个信号异常时,就会调用my_sigaction完成信号处理。
notifyNativeCrash = (*env)->GetMethodID(env, cls, "notifyNativeCrash", "()V");
void my_sigaction(int signal, siginfo_t *info, void *reserved) {
// Here catch the native crash
}
Android没有提供像throwable.printStackTrace一样的接口去获取Native崩溃后堆栈信息,所以我们需要自己想办法实现。这里有两种思路可以考虑。
-- 利用LogCat日志
在本地调试代码时,我们经常通过查看LogCat日志来分析解决问题。对于发布的应用,在代码中执行命令“logcat -d -v threadtime”也能达到同样的效果,只不过是获取到了用户手机的logcat。当Native崩溃时,Android系统同样会输出崩溃堆栈到LogCat,那么拿到了LogCat信息也就拿到了Native的崩溃堆栈。
Process process = Runtime.getRuntime().exec(new String[]{"logcat","-d","-v","threadtime"});
String logTxt = getSysLogInfo(process.getInputStream());
在my_sigaction捕获到异常信号后,通知Java层代码,在Java层启动新的进程,并在新的进程中完成上面的操作。这里注意一定要在新的进程中完成,因为原有的进程马上就会结束。
网络上有一些对应这种思路的代码,但是在很多手机上都无法获得Native的崩溃堆栈。原因是对崩溃堆栈产生了破坏,使得相关信息并没有输出到logcat中。研究一下Android backtrace的底层实现以及Google Breakpad的源码,会帮助你解决这个问题。
-- Google Breakpad
Linux提供了Core Dump机制,即操作系统会把程序崩溃时的内存内容dump出来,写入一个叫做core的文件里面。Google Breakpad作为跨平台的崩溃转储和分析模块(支持Windows、OS X、Linux、iOS和Android等),便是通过类似的MiniDump机制来获取崩溃堆栈的。
通过Google Breakpad捕获信号异常,并将堆栈信息写入你指定的本地MiniDump文件中。下次启动应用程序的时候,便可以读取该MiniDump文件进行相应的操作,比如上传到后台服务器。当然,也可以修改Google Breakpad的源码,不写MiniDump文件,而是通过dumpCallback直接获得堆栈信息,并将相关信息通知到Java层代码,做相应的处理。
Google Breakpad是权威的捕获Native崩溃的方法,相关的使用方法可以查看官网文档。由于它跨平台,代码体量较大,所以建议大家裁剪源码,只保留Android相关的功能,保持自己APK的小巧。