Crash 監(jiān)控

Crash(應(yīng)用崩潰)是由于代碼異常而導(dǎo)致 App 非正常退出僻爽,導(dǎo)致應(yīng)用程序無(wú)法繼續(xù)使用顿锰,所有工作都停止的現(xiàn)象蛔糯。發(fā)生 Crash 后需要重新啟動(dòng)應(yīng)用(有些情況會(huì)自動(dòng)重啟)陈瘦。
在 Android 應(yīng)用中發(fā)生的 Crash 有兩種類型幌甘,Java 層的 Crash 和 Native 層 Crash。這兩種Crash 的監(jiān)控和獲取堆棧信息有所不同痊项。

Java Crash

Java 的 Crash 監(jiān)控比較簡(jiǎn)單锅风,Java 中的 Thread 定義了一個(gè)接口UncaughtExceptionHandler ;用于處理未捕獲的異常導(dǎo)致線程的終止(注意:catch了的是捕獲不到的)鞍泉,當(dāng)我們的應(yīng)用 crash 的時(shí)候皱埠,就會(huì)走 UncaughtExceptionHandler.uncaughtException ,在該方法中可以獲取到異常的信息咖驮,我們通過(guò) Thread.setDefaultUncaughtExceptionHandler該方法來(lái)設(shè)置線程的默認(rèn)異常處理器边器,我們可以將異常信息保存到本地或者是上傳到服務(wù)器训枢,方便我們快速的定位問(wèn)題。

/**
 * UncaughtException處理類, 當(dāng)程序發(fā)生Uncaught異常的時(shí)候, 有該類來(lái)接管程序, 并記錄發(fā)送錯(cuò)誤報(bào)告
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

  private static final String FILE_NAME_SUFFIX = ".trace";
  /**
   * 系統(tǒng)默認(rèn)的UncaughtException處理類
   */
  private static Thread.UncaughtExceptionHandler mDefaultCrashHandler;
  private static Context mContext;

  private volatile static CrashHandler mInstance = null;

  private CrashHandler() {
  }

  public static CrashHandler getInstance() {
    if (mInstance == null) {
      synchronized (CrashHandler.class) {
        if (mInstance == null) {
          mInstance = new CrashHandler();
        }
      }
    }
    return mInstance;
  }

  public void init(Context context) {
    // 獲取系統(tǒng)默認(rèn)的UncaughtException處理器
    //默認(rèn)為:RuntimeInit#KillApplicationHandler
    mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
    //設(shè)置該CrashHandler為程序的默認(rèn)處理器
    Thread.setDefaultUncaughtExceptionHandler(this);
    mContext = context.getApplicationContext();
  }

  /***
   * 當(dāng)程序中有未被捕獲的異常忘巧,系統(tǒng)將會(huì)調(diào)用這個(gè)方法
   * @param thread 出現(xiàn)未捕獲異常的線程
   * @param throwable 得到異常信息
   */
  @Override
  public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
    try {
      //自行處理:保存本地
      File file = dealException(thread, throwable);
      //上傳服務(wù)器
      //......
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      //交給系統(tǒng)默認(rèn)程序處理
      if (mDefaultCrashHandler != null) {
        // 如果用戶沒(méi)有處理則讓系統(tǒng)默認(rèn)的異常處理器來(lái)處理
        mDefaultCrashHandler.uncaughtException(thread, throwable);
      }
    }
  }

  /**
   * 導(dǎo)出異常信息到SD卡
   */
  private File dealException(Thread thread, Throwable e) throws Exception {
    //存儲(chǔ)位置:sdcard->Android->data->包名->cache->crash_info
    File dir = new File(mContext.getExternalCacheDir(), "crash_info");
    if (!dir.exists()) {
      dir.mkdirs();
    }
    long timeMillis = System.currentTimeMillis();
    File file = new File(dir, timeMillis + ".txt");
    String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
    // //往文件中寫入數(shù)據(jù)
    PrintWriter pw = new PrintWriter(new FileWriter(file));
    pw.println(time);
    pw.println("Thread: " + thread.getName());
    pw.println(getPhoneInfo());
    //寫入crash堆棧
    e.printStackTrace(pw);
    Throwable mThrowable = e.getCause();
    // 迭代棧隊(duì)列把所有的異常信息寫入writer中
    while (mThrowable != null) {
      mThrowable.printStackTrace(pw);
      // 換行 每個(gè)個(gè)異常棧之間換行
      pw.append("\r\n");
      mThrowable = mThrowable.getCause();
    }
    pw.close();
    return file;
  }

  /**
   * 記錄手機(jī)信息
   */
  private String getPhoneInfo() throws PackageManager.NameNotFoundException {
    PackageManager pm = mContext.getPackageManager();
    PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
    StringBuilder sb = new StringBuilder();
    //App版本
    sb.append("App Version: ");
    sb.append(pi.versionName);
    sb.append("_");
    sb.append(pi.versionCode).append("\n");
    //Android版本號(hào)
    sb.append("OS Version: ");
    sb.append(Build.VERSION.RELEASE);
    sb.append("_");
    sb.append(Build.VERSION.SDK_INT).append("\n");
    //手機(jī)制造商
    sb.append("Vendor: ");
    sb.append(Build.MANUFACTURER).append("\n");
    //手機(jī)型號(hào)
    sb.append("Model: ");
    sb.append(Build.MODEL).append("\n");
    //CPU架構(gòu)
    sb.append("CPU: ");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      sb.append(Arrays.toString(Build.SUPPORTED_ABIS)).append("\n");
    } else {
      sb.append(Build.CPU_ABI).append("\n");
    }
    return sb.toString();
  }
}

NDK Crash

相對(duì)于 Java 的 Crash恒界,NDK 的錯(cuò)誤無(wú)疑更加讓人頭疼。

Linux信號(hào)機(jī)制

信號(hào)機(jī)制是 Linux 進(jìn)程間通信的一種重要方式砚嘴,Linux 信號(hào)一方面用于正常的進(jìn)程間通信和同步十酣,另一方面它還負(fù)責(zé)監(jiān)控系統(tǒng)異常及中斷。當(dāng)應(yīng)用程序運(yùn)行異常時(shí)际长,Linux內(nèi)核將產(chǎn)生錯(cuò)誤信號(hào)并通知當(dāng)前進(jìn)程耸采。當(dāng)前進(jìn)程在接收到該錯(cuò)誤信號(hào)后,可以有三種不同的處理方式也颤。

  • 忽略該信號(hào)洋幻;
  • 捕捉該信號(hào)并執(zhí)行對(duì)應(yīng)的信號(hào)處理函數(shù)(信號(hào)處理程序);
  • 執(zhí)行該信號(hào)的缺省操作(如終止進(jìn)程)翅娶;

當(dāng) Linux 應(yīng)用程序在執(zhí)行時(shí)發(fā)生嚴(yán)重錯(cuò)誤文留,一般會(huì)導(dǎo)致程序崩潰。其中竭沫,Linux 專門提供了一類 crash 信號(hào)燥翅,在程序接收到此類信號(hào)時(shí),缺省操作是將崩潰的現(xiàn)場(chǎng)信息記錄到核心文件蜕提,然后終止進(jìn)程森书。

常見(jiàn)崩潰信號(hào)列表:

信號(hào) 描述
SIGSEGV 內(nèi)存引用無(wú)效。
SIGBUS 訪問(wèn)內(nèi)存對(duì)象的未定義部分谎势。
SIGFPE 算術(shù)運(yùn)算錯(cuò)誤凛膏,除以零。
SIGILL 非法指令脏榆,如執(zhí)行垃圾或特權(quán)指令
SIGSYS 糟糕的系統(tǒng)調(diào)用
SIGXCPU 超過(guò)CPU時(shí)間限制猖毫。
SIGXFSZ 文件大小限制。

一般的出現(xiàn)崩潰信號(hào)须喂,Android 系統(tǒng)默認(rèn)缺省操作是直接退出我們的程序吁断。但是系統(tǒng)允許我們給某一個(gè)進(jìn)程的某一個(gè)特定信號(hào)注冊(cè)一個(gè)相應(yīng)的處理函數(shù)(signal),即對(duì)該信號(hào)的默認(rèn)處理動(dòng)作進(jìn)行修改坞生。因此 NDK Crash 的監(jiān)控可以采用這種信號(hào)機(jī)制仔役,捕獲崩潰信號(hào)執(zhí)行我們自己的信號(hào)處理函數(shù)從而捕獲 NDK Crash。

墓碑

普通應(yīng)用無(wú)權(quán)限讀取墓碑文件是己,墓碑文件位于路徑/data/tombstones/下又兵。解析墓碑文件與后面的 breakPad 都可使用 addr2line 工具。

Android 本機(jī)程序本質(zhì)上就是一個(gè) Linux 程序卒废,當(dāng)它在執(zhí)行時(shí)發(fā)生嚴(yán)重錯(cuò)誤寒波,也會(huì)導(dǎo)致程序崩潰乘盼,然后產(chǎn)生一個(gè)記錄崩潰的現(xiàn)場(chǎng)信息的文件,而這個(gè)文件在 Android 系統(tǒng)中就是 tombstones 墓碑文件。

BreakPad

Google breakpad 是一個(gè)跨平臺(tái)的崩潰轉(zhuǎn)儲(chǔ)和分析框架和工具集合慨丐,其開(kāi)源地址是:https://github.com/google/breakpad叮趴。breakpad 在 Linux 中的實(shí)現(xiàn)就是借助了 Linux 信號(hào)捕獲機(jī)制實(shí)現(xiàn)的。因?yàn)槠鋵?shí)現(xiàn)為 C++希停,因此在 Android 中使用,必須借助 NDK 工具。

引入項(xiàng)目

將Breakpad源碼下載解壓辰企,首先查看 README.ANDROID 文件。

打開(kāi) README.ANDROID

按照文檔中的介紹况鸣,如果我們使用 Android.mk 非常簡(jiǎn)單就能夠引入到我們工程中牢贸,但是目前 NDK 默認(rèn)的構(gòu)建工具為:CMake,因此我們做一次移植镐捧。查看android/google_breakpad/Android.mk

LOCAL_PATH := $(call my-dir)/../..
include $(CLEAR_VARS)

#最后編譯出 libbreakpad_client.a
LOCAL_MODULE := breakpad_client
#指定c++源文件后綴名
LOCAL_CPP_EXTENSION := .cc
# 強(qiáng)制構(gòu)建系統(tǒng)以 32 位 arm 模式生成模塊的對(duì)象文件
LOCAL_ARM_MODE := arm

# 需要編譯的源碼
LOCAL_SRC_FILES := \
    src/client/linux/crash_generation/crash_generation_client.cc \
    src/client/linux/dump_writer_common/thread_info.cc \
    src/client/linux/dump_writer_common/ucontext_reader.cc \
    src/client/linux/handler/exception_handler.cc \
    src/client/linux/handler/minidump_descriptor.cc \
    src/client/linux/log/log.cc \
    src/client/linux/microdump_writer/microdump_writer.cc \
    src/client/linux/minidump_writer/linux_dumper.cc \
    src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
    src/client/linux/minidump_writer/minidump_writer.cc \
    src/client/minidump_file_writer.cc \
    src/common/convert_UTF.cc \
    src/common/md5.cc \
    src/common/string_conversion.cc \
    src/common/linux/breakpad_getcontext.S \
    src/common/linux/elfutils.cc \
    src/common/linux/file_id.cc \
    src/common/linux/guid_creator.cc \
    src/common/linux/linux_libc_support.cc \
    src/common/linux/memory_mapped_file.cc \
    src/common/linux/safe_readlink.cc

#導(dǎo)入頭文件
LOCAL_C_INCLUDES        := $(LOCAL_PATH)/src/common/android/include \
                           $(LOCAL_PATH)/src \
                           $(LSS_PATH) #注意這個(gè)目錄

#導(dǎo)出頭文件
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
#使用android ndk中的日志庫(kù)log
LOCAL_EXPORT_LDLIBS     := -llog

#編譯static靜態(tài)庫(kù)-》類似java的jar包
include $(BUILD_STATIC_LIBRARY)

注意:mk文件中 LOCAL_C_INCLUDES 的 LSS_PATH 是個(gè)坑

對(duì)照 Android.mk 文件潜索,我們?cè)谧约喉?xiàng)目的 cpp(工程中C/C++源碼)目錄下創(chuàng)建 breakpad 目錄,并將下載的 breakpad 源碼根目錄下的 src 目錄全部復(fù)制到我們的項(xiàng)目中:


需要在 build.gradle 中配置

接下來(lái)在 breakpad 目錄下創(chuàng)建 CMakeLists.txt 文件( AS 安裝 CMake Simple highlighter 插件使 CMakeLists.txt 高亮顯示):
具體可以參照NDK配置 CMake

cmake_minimum_required(VERSION 3.4.1)
#對(duì)應(yīng)android.mk中的 LOCAL_C_INCLUDES 
include_directories(breakpad/src breakpad/src/common/android/include) 
#開(kāi)啟arm匯編支持懂酱,因?yàn)樵谠创a中有 .S文件(匯編源碼) enable_language(ASM)

#生成 libbreakpad.a 并指定源碼竹习,對(duì)應(yīng)android.mk中 LOCAL_SRC_FILES+LOCAL_MODULE
add_library(breakpad STATIC 
        src/client/linux/crash_generation/crash_generation_client.cc 
        src/client/linux/dump_writer_common/thread_info.cc 
        src/client/linux/dump_writer_common/ucontext_reader.cc 
        src/client/linux/handler/exception_handler.cc
        src/client/linux/handler/minidump_descriptor.cc src/client/linux/log/log.cc 
        src/client/linux/microdump_writer/microdump_writer.cc 
        src/client/linux/minidump_writer/linux_dumper.cc 
        src/client/linux/minidump_writer/linux_ptrace_dumper.cc 
        src/client/linux/minidump_writer/minidump_writer.cc 
        src/client/minidump_file_writer.cc src/common/convert_UTF.cc 
        src/common/md5.cc src/common/string_conversion.cc 
        src/common/linux/breakpad_getcontext.S src/common/linux/elfutils.cc 
        src/common/linux/file_id.cc src/common/linux/guid_creator.cc 
        src/common/linux/linux_libc_support.cc 
        src/common/linux/memory_mapped_file.cc 
        src/common/linux/safe_readlink.cc)
#鏈接 log庫(kù),對(duì)應(yīng)android.mk中 LOCAL_EXPORT_LDLIBS
target_link_libraries(breakpad log)

在 cpp 目錄下(breakpad同級(jí))還有一個(gè) CMakeLists.txt 文件列牺,它的內(nèi)容是:

cmake_minimum_required(VERSION 3.4.1)
#引入breakpad的頭文件(api的定義)
include_directories(breakpad/src breakpad/src/common/android/include)
#引入breakpad的cmakelist整陌,執(zhí)行并生成libbreakpad.a (api的實(shí)現(xiàn),類似java的jar包)
add_subdirectory(breakpad)

#生成libbugly.so 源碼是:bugly.cpp(我們自己的源碼瞎领,要使用breakpad)
add_library(bugly SHARED bugly.cpp)
# 鏈接ndk中的log庫(kù)
target_link_libraries(
        bugly
        breakpad#引入breakpad的庫(kù)文件(api的實(shí)現(xiàn))
        log)

此時(shí)執(zhí)行編譯泌辫,會(huì)在 #include "third_party/lss/linux_syscall_support.h" 報(bào)錯(cuò),無(wú)法找到頭文件九默。此文件從:https://chromium.googlesource.com/external/linux-syscall-support/+/refs/heads/master 下載(需要翻墻)放到工程對(duì)應(yīng)目錄即可震放。
bugly.cpp 源文件中的實(shí)現(xiàn)為:

#include <jni.h> 
#include <android/log.h>
#include "breakpad/src/client/linux/handler/minidump_descriptor.h" 
#include "breakpad/src/client/linux/handler/exception_handler.h"

bool DumpCallback(const google_breakpad::MinidumpDescriptor &descriptor, 
                  void *context,
                  bool succeeded) { 
    __android_log_print(ANDROID_LOG_ERROR, "ndk_crash", "Dump path: %s", descriptor.path()); 
    //如果回調(diào)返回true,Breakpad將把異常視為已完全處理荤西,禁止任何其他處理程序收到異常通知澜搅。 
    //如果回調(diào)返回false,Breakpad會(huì)將異常視為未處理邪锌,并允許其他處理程序處理它勉躺。 
    return false; 
}

extern "C" 
JNIEXPORT void JNICALL 
Java_com_wuc_crash_CrashReport_initBreakpad(JNIEnv *env, jclass type, jstring path_) {
   const char *path = env->GetStringUTFChars(path_, 0); 
   //開(kāi)啟crash監(jiān)控 
   google_breakpad::MinidumpDescriptor descriptor(path); 
   static google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback, NULL, true, -1); 
   env->ReleaseStringUTFChars(path_, path);
 }

//測(cè)試用 
extern "C" 
JNIEXPORT void JNICALL 
Java_com_wuc_crash_CrashReport_testNativeCrash(JNIEnv *env, jclass clazz) { 
    int *i = NULL;
    *i = 1; 
}

注意 JNI 方法的方法名對(duì)應(yīng)了 java 類,創(chuàng)建 Java 源文件: com.wuc.crash.CrashReport

package com.wuc.crash;

import android.content.Context;
import java.io.File;
public class CrashReport {

  static {
    System.loadLibrary("bugly");
  }

  public static void init(Context context) {
    //開(kāi)啟java監(jiān)控
    Context applicationContext = context.getApplicationContext();
    CrashHandler.getInstance().init(applicationContext);

    //開(kāi)啟ndk監(jiān)控
    File file = new File(context.getExternalCacheDir(), "native_crash");
    if (!file.exists()) {
      file.mkdirs();
    }
    initBreakpad(file.getAbsolutePath());
  }

  // C++: Java_com_enjoy_crash_CrashReport_initBreakpad
  private static native void initBreakpad(String path);

  // C++: Java_com_enjoy_crash_CrashReport_testNativeCrash
  public static native void testNativeCrash();

  public static void testJavaCrash() {
    int i = 1 / 0;
  }
}

此時(shí)觅丰,如果出現(xiàn) NDK Crash饵溅,會(huì)在我們指定的目
錄: /sdcard/Android/Data/[packageName]/cache/native_crash 下生成 NDK Crash 信息文件。

Crash 解析

采集到的 Crash 信息記錄在 minidump 文件中妇萄。minidump 是由微軟開(kāi)發(fā)的用于崩潰上傳的文件格式蜕企。我們可以將此文件上傳到服務(wù)器完成上報(bào)咬荷,但是此文件沒(méi)有可讀性可言,要將文件解析為可讀的崩潰堆棧需要按照 breakpad 文檔編譯minidump_stackwalk 工具轻掩。不過(guò)好在幸乒,無(wú)論你是 Mac、windows 還是 ubuntu 在 Android Studio 的安裝目錄下的 bin\lldb\bin 面就存在一個(gè)對(duì)應(yīng)平臺(tái)的 minidump_stackwalk 唇牧。

使用這里的工具執(zhí)行:

minidump_stackwalk xxxx.dump > crash.txt

打開(kāi) crash.txt 內(nèi)容為:

Operating system: Android
                  0.0.0 Linux 5.4.61-android11-0-00791-gbad091cc4bf3-ab6833933 #1 SMP PREEMPT 2020-09-14 14:42:20 i686
CPU: x86  // abi類型
     GenuineIntel family 6 model 142 stepping 10
     4 CPUs

Crash reason:  SIGSEGV   //內(nèi)存引用無(wú)效 信號(hào)
Crash address: 0x0
Process uptime: not available

Thread 0 (crashed) //crashed:出現(xiàn)crash的線程
 0  libbugly.so + 0x1fee4  //crash的so與寄存器信息
    eip = 0xdfea8ee4   esp = 0xff81d6c0   ebp = 0xff81d6f8   ebx = 0xdff23460
    esi = 0xdff037f8   edi = 0xdff037ff   eax = 0x00000001   ecx = 0x00000000
    edx = 0x00000001   efl = 0x00010246
    Found by: given as instruction pointer in context
 1  libart.so + 0x142133
    eip = 0xe3292133   esp = 0xff81d700   ebp = 0xff81d720
    Found by: previous frame's frame pointer

Thread 1
...

接下來(lái)使用 Android NDK 里面提供的 addr2line 工具將寄存器地址轉(zhuǎn)換為對(duì)應(yīng)符號(hào)罕扎。addr2line 要用和自己 so 的 ABI 匹配的目錄,同時(shí)需要使用有符號(hào)信息的so(一般debug的就有)丐重。

因?yàn)槲沂褂玫氖悄M器x86架構(gòu)腔召,因此 addr2line 位于:
android/android-sdk-macosx/ndk/21.1.6352462/toolchains/x86-4.9/prebuilt/darwin-x86_64/bin/i686-linux-android-addr2line

i686-linux-android-addr2line -f -C -e libbugly.so 0x1fee4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扮惦,隨后出現(xiàn)的幾起案子臀蛛,更是在濱河造成了極大的恐慌,老刑警劉巖崖蜜,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浊仆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡纳猪,警方通過(guò)查閱死者的電腦和手機(jī)氧卧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氏堤,“玉大人沙绝,你說(shuō)我怎么就攤上這事∈笮猓” “怎么了闪檬?”我有些...
    開(kāi)封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)购笆。 經(jīng)常有香客問(wèn)我粗悯,道長(zhǎng),這世上最難降的妖魔是什么同欠? 我笑而不...
    開(kāi)封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任样傍,我火速辦了婚禮,結(jié)果婚禮上铺遂,老公的妹妹穿的比我還像新娘衫哥。我一直安慰自己,他們只是感情好襟锐,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布撤逢。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚊荣。 梳的紋絲不亂的頭發(fā)上初狰,一...
    開(kāi)封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音互例,去河邊找鬼奢入。 笑死,一個(gè)胖子當(dāng)著我的面吹牛媳叨,可吹牛的內(nèi)容都是我干的俊马。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼肩杈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了解寝?” 一聲冷哼從身側(cè)響起扩然,我...
    開(kāi)封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聋伦,沒(méi)想到半個(gè)月后夫偶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡觉增,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年兵拢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逾礁。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡说铃,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘹履,到底是詐尸還是另有隱情腻扇,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布砾嫉,位于F島的核電站幼苛,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏焕刮。R本人自食惡果不足惜舶沿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望配并。 院中可真熱鬧括荡,春花似錦、人聲如沸荐绝。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至召夹,卻和暖如春岩喷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背监憎。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工纱意, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鲸阔。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓偷霉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親褐筛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子类少,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Crash(應(yīng)用崩潰)是由于代碼異常而導(dǎo)致 App 非正常退出,導(dǎo)致應(yīng)用程序無(wú)法繼續(xù)使用渔扎,所有工作都 停止的現(xiàn)象硫狞。...
    zcwfeng閱讀 698評(píng)論 0 2
  • 原文[http://www.reibang.com/p/4278915847b6]源碼[https://down...
    WaterYuan閱讀 2,099評(píng)論 1 2
  • Android異常監(jiān)控 Crash就是由于代碼異常而導(dǎo)致App非正常退出現(xiàn)象,也就是我們常說(shuō)的『崩潰』 通常情況下...
    Heezier閱讀 1,115評(píng)論 2 6
  • 背景: 支付SDK面向游戲提供支付服務(wù)晃痴,高效的游戲引擎一般都會(huì)C++編寫的残吩,通過(guò)NDK編譯成so文件在Androi...
    jpchen_hn閱讀 4,957評(píng)論 0 6
  • 1. 找到未strip的, 符號(hào)表完整的so庫(kù)文件 在Android Studio 3.2.1: strip之前的...
    hjm1fb閱讀 4,289評(píng)論 3 3