精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

得物 Android Crash 治理實踐

移動開發 Android
得物Android端的Crash監控體系得到顯著增強,使得歷史Crash數據的完整捕獲能力得到系統性改善,相應Crash指標也有所上升,經過架構以及各團隊的共同努力下,崩潰率已從最高的萬2降至目前的萬1.1到萬1.5,其中疑難問題占比約90%、因系統bug導致的Crash占比約40%,在本文中將簡要介紹一些較典型的系統Crash的治理過程。

目錄

一、前言

二、DNS解析崩潰

    1. 背景

    2. 問題分析

    3. 解決過程

三、MediaCodec 狀態異常崩潰

    1. 背景

    2. 問題分析

    3. 解決過程

四、bio多線程環境崩潰

    1. 背景

    2. 問題分析

    3. 解決過程

五、小米Android15 焦點處理空指針崩潰

    1. 背景

    2. 問題分析

    3. 解決過程

六、總結

一、前言

通過修復歷史遺留的Crash漏報問題(包括端側SDK采集的兼容性優化及Crash平臺的數據消費機制完善),得物Android端的Crash監控體系得到顯著增強,使得歷史Crash數據的完整捕獲能力得到系統性改善,相應Crash指標也有所上升,經過架構以及各團隊的共同努力下,崩潰率已從最高的萬2降至目前的萬1.1到萬1.5,其中疑難問題占比約90%、因系統bug導致的Crash占比約40%,在本文中將簡要介紹一些較典型的系統Crash的治理過程。

二、DNS解析崩潰

背景

Android11及以下版本在DNS解析過程中的有幾率產生野指針問題導致的Native Crash,其中Android9占比最高。 

堆棧與上報趨勢

at libcore.io.Linux.android_getaddrinfo(Linux.java)
at libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:172)
at java.net.InetAddress.parseNumericAddressNoThrow(InetAddress.java:1631)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:96)
at java.net.InetAddress.getAllByName(InetAddress.java:1154)


#00 pc 000000000003b938  /system/lib64/libc.so (android_detectaddrtype+1164)
#01 pc 000000000003b454  /system/lib64/libc.so (android_getaddrinfofornet+72)
#02 pc 000000000002b5f4  /system/lib64/libjavacore.so (_ZL25Linux_android_getaddrinfoP7_JNIEnvP8_jobjectP8_jstringS2_i+336)

圖片圖片

問題分析

崩潰入口方法InetAddress.getAllByName用于根據指定的主機名返回與之關聯的所有 IP 地址,它會根據系統配置的名稱服務進行解析,沿著調用鏈查看源碼發現在parseNumericAddressNoThrow方法內部調用Libcore.os.android_getaddrinfo時中有try catch的容錯邏輯,繼續查看后續調用的c++的源碼,在調用android_getaddrinfofornet函數返回值不為0時拋出GaiException異常。

https://cs.android.com/android/platform/superproject/+/android-9.0.0_r49:libcore/ojluni/src/main/java/java/net/InetAddress.java
 
 static InetAddress parseNumericAddressNoThrow(String address) {
        // Accept IPv6 addresses (only) in square brackets for compatibility.
        if (address.startsWith("[") && address.endsWith("]") && address.indexOf(':') != -1) {
            address = address.substring(1, address.length() - 1);
        }
        StructAddrinfo hints = new StructAddrinfo();
        hints.ai_flags = AI_NUMERICHOST;
        InetAddress[] addresses = null;
        try {
            addresses = Libcore.os.android_getaddrinfo(address, hints, NETID_UNSET);
        } catch (GaiException ignored) {
        }
        return (addresses != null) ? addresses[0] : null;
    }
https://cs.android.com/android/platform/superproject/+/master:libcore/luni/src/main/native/libcore_io_Linux.cpp?q=Linux_android_getaddrinfo&ss=android%2Fplatform%2Fsuperproject


static jobjectArray Linux_android_getaddrinfo(JNIEnv* env, jobject, jstring javaNode,
        jobject javaHints, jint netId) {
    ......
    int rc = android_getaddrinfofornet(node.c_str(), NULL, &hints, netId, 0, &addressList);
    std::unique_ptr<addrinfo, addrinfo_deleter> addressListDeleter(addressList);
    if (rc != 0) {
        throwGaiException(env, "android_getaddrinfo", rc);
        return NULL;
    }
    ......
    return result;
}

解決過程

解決思路是代理android_getaddrinfofornet函數,捕捉調用原函數過程中出現的段錯誤信號,接著吃掉這個信號并返回-1,使之轉換為JAVA異常進而走進parseNumericAddressNoThrow方法的容錯邏輯,和負責網絡的同學提前做了溝通,確定此流程對業務沒有影響后開始解決。

首先使用inline-hook代理了android_getaddrinfofornet函數,接著使用字節封裝好的native try catch工具做吃掉段錯誤信號并返回-1的,字節工具內部原理是在try塊的開始使用sigsetjmp打個錨點并快照當前寄存器的值,然后設置信號量處理器并關聯當前線程,在catch塊中解綁線程與信號的關聯并執行業務兜底代碼,在捕捉到信號時通過siglongjmp函數長跳轉到catch塊中,感興趣的同學可以用下面精簡后的demo試試,以下代碼保存為mem_err.c,執行gcc ./mem_err.c;./a.out

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>


struct sigaction old;
static sigjmp_buf buf;


void SIGSEGV_handler(int sig, siginfo_t *info, void *ucontext) {
    printf("信號處理 sig: %d, code: %d\n", sig, info->si_code);
    siglongjmp(buf, -1);
}


int main() {
    if (!sigsetjmp(buf, 0)) {
        struct sigaction sa;


        sa.sa_sigaction = SIGSEGV_handler;
        sigaction(SIGSEGV, &sa, &old);


        printf("try exec\n");
        //產生段錯誤
        int *ptr = NULL;
        *ptr = 1;
        printf("try-block end\n");//走不到
    } else {
        printf("catch exec\n");
        sigaction(SIGSEGV, &old, NULL);
    }
    printf("main func end\n");
    return 0;
}


//輸出以下日志
//try exec
//信號處理 sig: 11, code: 2
//catch exec
//main func end

inline-hook庫: https://github.com/bytedance/android-inline-hook

字節native try catch工具: https://github.com/bytedance/android-inline-hook/blob/main/shadowhook/src/main/cpp/common/bytesig.c

三、MediaCodec 狀態異常崩潰

背景

在Android 11系統庫的音視頻播放過程中,偶爾會出現因狀態異常導致的SIGABRT崩潰。音視頻團隊反饋指出,這是Android 11的一個系統bug。隨后,我們協助音視頻團隊通過hook解決了這一問題。

堆棧與上報趨勢

#00 pc 0000000000089b1c  /apex/com.android.runtime/lib64/bionic/libc.so (abort+164)
#01 pc 000000000055ed78  /apex/com.android.art/lib64/libart.so (_ZN3art7Runtime5AbortEPKc+2308)
#02 pc 0000000000013978  /system/lib64/libbase.so (_ZZN7android4base10SetAborterEONSt3__18functionIFvPKcEEEEN3$_38__invokeES4_+76)
#03 pc 0000000000006e30  /system/lib64/liblog.so (__android_log_assert+336)
#04 pc 0000000000122074  /system/lib64/libstagefright.so (_ZN7android10MediaCodec37postPendingRepliesAndDeferredMessagesENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEERKNS_2spINS_8AMessageEEE+720)
#05 pc 00000000001215cc  /system/lib64/libstagefright.so (_ZN7android10MediaCodec37postPendingRepliesAndDeferredMessagesENSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEi+244)
#06 pc 000000000011c308  /system/lib64/libstagefright.so (_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE+8752)
#07 pc 0000000000017814  /system/lib64/libstagefright_foundation.so (_ZN7android8AHandler14deliverMessageERKNS_2spINS_8AMessageEEE+84)
#08 pc 000000000001d9cc  /system/lib64/libstagefright_foundation.so (_ZN7android8AMessage7deliverEv+188)
#09 pc 0000000000018b48  /system/lib64/libstagefright_foundation.so (_ZN7android7ALooper4loopEv+572)
#10 pc 0000000000015598  /system/lib64/libutils.so (_ZN7android6Thread11_threadLoopEPv+460)
#11 pc 00000000000a1d6c  /system/lib64/libandroid_runtime.so (_ZN7android14AndroidRuntime15javaThreadShellEPv+144)
#12 pc 0000000000014d94  /system/lib64/libutils.so (_ZN13thread_data_t10trampolineEPKS_+412)
#13 pc 00000000000eba94  /apex/com.android.runtime/lib64/bionic/libc.so (_ZL15__pthread_startPv+64)
#14 pc 000000000008bd80  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64)

圖片圖片

問題分析

根據堆棧內容分析Android11的源碼以及結合SIGABRT信號采集到的信息(postPendingRepliesAndDeferredMessages: mReplyID == null, from kWhatRelease:STOPPING following kWhatError:STOPPING),找到崩潰發生在onMessageReceived函數處理kWhatRelease類型消息的過程中,onMessageReceived函數連續收到兩條消息,第一條是kWhatError:STOPPING,第二條是kWhatRelease:STOPPING此時因mReplyID已經被置為空,因此走到判空拋異常的邏輯。

https://cs.android.com/android/_/android/platform/frameworks/av/+/refs/tags/android-11.0.0_r48:media/libstagefright/MediaCodec.cpp;l=2280;drc=789055bbcb4560b42faf19103b1cda5534e8f9cb;bpv=0;bpt=0

圖片圖片

圖片圖片

圖片圖片

圖片圖片

對比Android12的源碼,在處理kWhatRelease事件且狀態為STOPPING拋異常前,增加了對mReplyID不為空的判斷來規避這個問題。

https://cs.android.com/android/_/android/platform/frameworks/av/+/ca0c3286a4790a4de2d90cb275ae89a9601b805b:media/libstagefright/MediaCodec.cpp;dlc=7327aab894f6c456ea16c95b64134841da8d5737

圖片圖片

解決過程

Android12的修復方式意味著上述三個條件結合下吃掉異常是符合預期的,接下來就是想辦法通過hook Android11使邏輯對齊Android12。

  • 【初探】最先想到的辦法是代理相關函數通過判斷走到這個場景時提前return出去來規避,音視頻的同學嘗試后發現不可行,原因如下:

a.void MediaCodec::postPendingRepliesAndDeferredMessages(std::string origin, status_t err): 匹配origin是否為特征字符串(postPendingRepliesAndDeferredMessages: mReplyID == null, from kWhatRelease:STOPPING following kWhatError:STOPPING);很多設備找不到這個符號不可行;

b.void MediaCodec::onMessageReceived(const sp&msg): 已知MediaCodec實例的內存首地址,需要通過hardcode偏移量來獲取mReplay、mState兩個字段,這里又缺少可供校驗正確性的特征,風險略大擔心有不同機型的兼容性問題(不同機型新增、刪除字段導致偏移量不準)。

  • 【踩坑】接著嘗試使用與修復DNS崩潰類似思路的保護方案,使用inline-hook代理onMessageReceived函數調用原函數時使用setjmp打錨點,然后使用plt hook代理_android_log_assert函數并在內部檢測錯誤信息為特征字符串時通過longjmp跳轉到onMessageReceived函數的錨點并作return操作,精簡后的demo如下:

Plt-hook 庫: https://github.com/iqiyi/xHook

#include <iostream>
#include <setjmp.h>
#include <csignal>


static thread_local jmp_buf _buf;
void *origin_onMessageReceived = nullptr;
void *origin__android_log_assert = nullptr;


void _android_log_assert_proxy(const char* cond, const char *tag, const char* fmt, ...) {
    //模擬liblog.so的__android_log_assert函數
    std::cout << "__android_log_assert start" << std::endl;
    if (!strncmp(fmt, "postPendingRepliesAndDeferredMessages: mReplyID == null", 55)) {
        longjmp(_buf, -1);
    }
    //模擬調用origin__android_log_assert,產生崩潰 
    raise(SIGABRT);
}


void onMessageReceived_proxy(void *thiz, void *msg) {
    std::cout << "onMessageReceived_proxy start" << std::endl;
    if (!setjmp(_buf)) {
        //模擬調用onMessageReceived原函數(origin_onMessageReceived)進入崩潰流程
        std::cout << "onMessageReceived_proxy 1" << std::endl;
        _android_log_assert_proxy(nullptr, nullptr, "postPendingRepliesAndDeferredMessages: mReplyID == null, from kWhatRelease:STOPPING following kWhatError:STOPPING");
        std::cout << "onMessageReceived_proxy 2" << std::endl;//走不到
    } else {
        //保護后從此處返回
        std::cout << "onMessageReceived_proxy 3" << std::endl;
    }
    std::cout << "onMessageReceived_proxy end" << std::endl;
}


int main() {
    std::cout << "main func start" << std::endl;
    /**
     inline-hook: shadowhook_hook_sym_name("libstagefright.so","_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE",(void *) onMessageReceived_proxy, (void **) &origin_onMessageReceived);
     plhook: xh_core_register("libstagefright.so", "__android_log_assert", (void *) (_android_log_assert_proxy), (void **) (&origin__android_log_assert));
     */
    //模擬調用libstagefright.so的_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE函數
    onMessageReceived_proxy(nullptr, nullptr);
    std::cout << "main func end" << std::endl;
    return 0;
}


/**
日志輸出
 main func start
onMessageReceived_proxy start
onMessageReceived_proxy 1
__android_log_assert start
onMessageReceived_proxy 3
onMessageReceived_proxy end
main func end
*/

線下一陣操作猛如虎經測試保護邏輯符合預期,但是在灰度期間踩到棧溢出保護導致錯誤轉移的坑,堆棧如下:

#00 pc 000000000004e40c  /apex/com.android.runtime/lib64/bionic/libc.so (abort+164)
#01 pc 0000000000062730  /apex/com.android.runtime/lib64/bionic/libc.so (__stack_chk_fail+20)
#02 pc 000000000000a768 /data/app/~~JaQm4SU8wxP7T2GaSWxYkQ==/com.shizhuang.duapp-N5RFIB8WurdccMgAVsBang==/lib/arm64/libduhook.so (_ZN25CrashMediaCodecProtection5proxyEPvS0_)
#03 pc 0000000001091c0c  [anon:scudo:primary]

關于棧溢出保護機制感興趣的同學可以參考這篇文章https://bbs.kanxue.com/thread-221762-1.htm

(CSPP 第3版 “3.10.3 內存越界引用和緩沖區溢出”章節講的更詳細)

longjmp函數只是恢復寄存器的值后從錨點處再次返回,過程中也唯一可能會操作棧禎只有inline-hook,當時懷疑是與setjmp/longjmp機制不兼容,由于inline-hook內部邏輯大量使用匯編來實現排查起來比較困難,因此這個問題困擾比較久,網上的資料提到可以使用代理出錯函數(__stack_chk_fail)或者編譯so時增加參數不讓編譯器生成保護代碼來繞過,這兩種方式影響面都比較大所以未采用。有了前面的懷疑點想到使用c++的try catch機制來做跨函數域的跳轉,大致的思路同上只是把setjmp替換為c++的try catch,把longjmp替換為throw exception,精簡后的demo如下:

c++異常機制介紹: https://baiy.cn/doc/cpp/inside_exception.htm

#include <iostream>
#include <csignal>


void *origin_onMessageReceived = nullptr;
void *origin__android_log_assert = nullptr;


class MyCustomException : public std::exception {
public:
    explicit MyCustomException(const std::string& message)
            : msg_(message) {}


    virtual const char* what() const noexcept override {
        return msg_.c_str();
    }


private:
    std::string msg_;
};


void _android_log_assert_proxy(const char* cond, const char *tag, const char* fmt, ...) {
    //模擬liblog.so的__android_log_assert函數
    std::cout << "__android_log_assert start" << std::endl;
    if (!strncmp(fmt, "postPendingRepliesAndDeferredMessages: mReplyID == null", 55)) {
        throw MyCustomException("postPendingRepliesAndDeferredMessages: mReplyID == null");
    }
    //模擬調用origin__android_log_assert,產生崩潰
    raise(SIGABRT);
}


void onMessageReceived_proxy(void *thiz, void *msg) {
    std::cout << "onMessageReceived_proxy start" << std::endl;
    try {
        //模擬調用onMessageReceived原函數(origin_onMessageReceived)進入崩潰流程
        std::cout << "onMessageReceived_proxy 1" << std::endl;
        _android_log_assert_proxy(nullptr, nullptr, "postPendingRepliesAndDeferredMessages: mReplyID == null, from kWhatRelease:STOPPING following kWhatError:STOPPING");
        std::cout << "onMessageReceived_proxy 2" << std::endl;//走不到
    } catch (const MyCustomException& e) {
        //保護后從此處返回
        std::cout << "onMessageReceived_proxy 3" << std::endl;
    }
    std::cout << "onMessageReceived_proxy end" << std::endl;
}


int main() {
    std::cout << "main func start" << std::endl;
    /**
     inline-hook: shadowhook_hook_sym_name("libstagefright.so","_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE",(void *) onMessageReceived_proxy, (void **) &origin_onMessageReceived);
     plhook: xh_core_register("libstagefright.so", "__android_log_assert", (void *) (_android_log_assert_proxy), (void **) (&origin__android_log_assert));
     */
    //模擬調用libstagefright.so的_ZN7android10MediaCodec17onMessageReceivedERKNS_2spINS_8AMessageEEE函數
    onMessageReceived_proxy(nullptr, nullptr);
    std::cout << "main func end" << std::endl;
    return 0;
}


/**
日志輸出
 main func start
onMessageReceived_proxy start
onMessageReceived_proxy 1
__android_log_assert start
onMessageReceived_proxy 3
onMessageReceived_proxy end
main func end
*/

灰度上線后發現有設備走到了_android_log_assert代理函數中的throw邏輯,但是未按預期走到catch塊而是把錯誤又轉移為" terminating with uncaught exception of type" ,有點搞心態啊。

  • 【柳暗花明】C++的異常處理機制在throw執行時,會開始在調用棧中向上查找匹配的catch塊,檢查每一個函數直到找到一個具有合適類型的catch塊,上述的錯誤信息代表未找到匹配的catch塊。從轉移的堆棧中注意到沒有onMessageReceived代理函數的堆棧,此時基于inline-hook的原理(修改原函數前面的匯編代碼跳轉到代理函數)又懷疑到它身上,再次排查代碼時發現代理函數開頭漏寫了一個宏,在inline-hook中SHADOWHOOK_STACK_SCOPE就是來管理棧禎的,因此出現找不到catch塊以及前面longjmp的問題就不奇怪了。加上這個宏以后柳暗花明,重新放量后保護邏輯按預期執行并且保護生效后視頻播放正常。和音視頻的小伙伴一努力下,經歷了幾個版本終于解決了這個系統bug,目前僅剩老版本App有零星的上報。

四、bio多線程環境崩潰

背景

Android 11  Socket close過程中在多線程場景下有幾率產生野指針問題導致Native Crash,現象是多個線程同時close連接時,一個線程已銷毀了bio的上下文,另外一個線程仍執行close并在此過程中嘗試獲取這個bio有多少未寫出去的字節數時出現野指針導致的段錯誤。此問題從21年首次上報以來在得物的Crash列表中一直處于較前的位置。

堆棧與上報趨勢

at com.android.org.conscrypt.NativeCrypto.SSL_pending_written_bytes_in_BIO(Native method)
at com.android.org.conscrypt.NativeSsl$BioWrapper.getPendingWrittenBytes(NativeSsl.java:660)
at com.android.org.conscrypt.ConscryptEngine.pendingOutboundEncryptedBytes(ConscryptEngine.java:566)
at com.android.org.conscrypt.ConscryptEngineSocket.drainOutgoingQueue(ConscryptEngineSocket.java:584)
at com.android.org.conscrypt.ConscryptEngineSocket.close(ConscryptEngineSocket.java:480)
at okhttp3.internal.Util.closeQuietly_aroundBody0(Util.java:1)
at okhttp3.internal.Util$AjcClosure1.run(Util.java:1)
at org.aspectj.runtime.reflect.JoinPointImpl.proceed(JoinPointImpl.java:3)
at com.shizhuang.duapp.common.aspect.ThirdSdkAspect.t(ThirdSdkAspect.java:1)
at okhttp3.internal.Util.closeQuietly(Util.java:3)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.java:42)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.java:1)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.java:6)
at okhttp3.internal.connection.Transmitter.newExchange(Transmitter.java:5)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:5)


#00 pc 0000000000064060  /system/lib64/libcrypto.so (bio_ctrl+144)
#01 pc 00000000000615d8  /system/lib64/libcrypto.so (BIO_ctrl_pending+40)
#02 pc 00000000000387dc  /apex/com.android.conscrypt/lib64/libjavacrypto.so (_ZL45NativeCrypto_SSL_pending_written_bytes_in_BIOP7_JNIEnvP7_jclassl+20)

圖片圖片

問題分析

從設備分布上看,出問題都全是Android 11且各個國內廠商的設備都有,懷疑是Android 11引入的bug,對比了Android 11 和 Android 12的源碼,發現在Android12 崩潰堆棧中的相關類 com.android.org.conscrypt.NativeSsl$BioWrapper有四個方法增加了讀寫鎖,此時懷疑是多線程問題,通過搜索Android源碼的相關issue以及差異代碼的MR描述信息,進一步確認此結論。通過源碼進一步分析發現NativeSsl的所有加鎖的方法,會分發到NativeCrypto.java中的native方法,最終調用到native_crypto.cc中的JNI函數,如果能hook到相關的native函數并在Native層實現與Android12相同的讀寫鎖邏輯,這個問題就可以解決了。

https://cs.android.com/android/platform/superproject/+/android-12.0.0_r1:external/conscrypt/repackaged/common/src/main/java/com/android/org/conscrypt/NativeSsl.java

https://cs.android.com/android/platform/superproject/+/android-11.0.0_r48:external/conscrypt/repackaged/common/src/main/java/com/android/org/conscrypt/NativeCrypto.java

https://cs.android.com/android/platform/superproject/+/android-11.0.0_r48:external/conscrypt/common/src/jni/main/cpp/conscrypt/native_crypto.cc

解決過程

通過JNI hook代理Android12中增加鎖的相關函數,當走到代理函數中時,先分發到JAVA層通過反射獲取ReadWriteLock實例并上鎖再通過跳板函數調用原來的JNI函數,此時就完成了對Android12 增量鎖邏輯的復刻。經歷了兩個版本的灰度hook方案已穩定在線上運行,期間無因hook導致的網絡不可用和其它崩潰問題,目前開關放全量的版本崩潰設備數已降為0。

圖片圖片

JNI hook原理,以及詳細修復過程:  https://blog.dewu-inc.com/article/MTMwNDU?fromType=personal_blog

五、小米Android15 焦點處理空指針崩潰

背景

隨著Android15開放公測,焦點處理過程中發生的空指針問題逐步增多,并在1月份上升到Top。

堆棧與上報趨勢

java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.ViewGroup$LayoutParams android.view.View.getLayoutParams()' on a null object reference
at android.view.ViewRootImpl.handleWindowFocusChanged(ViewRootImpl.java:5307)
at android.view.ViewRootImpl.-$$Nest$mhandleWindowFocusChanged(Unknown Source:0)
at android.view.ViewRootImpl$ViewRootHandler.handleMessageImpl(ViewRootImpl.java:7715)
at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:7611)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loopOnce(Looper.java:249)
at android.os.Looper.loop(Looper.java:337)
at android.app.ActivityThread.main(ActivityThread.java:9568)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)

圖片圖片

問題分析

通過分析ASOP的源碼,崩潰的觸發點是mView字段為空。

https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/ViewRootImpl.java;drc=98e96368cc73432efbacd6fbcf61fe789dcec0ee;l=7243?q=ViewRootImpl

圖片圖片

源碼中mView為空的情況有兩種:

  • 未調用setView方法前觸發窗口焦點變化事件(只有setView方法才會給mView賦不為空的值)。
  • 先正常調用setView使mView不為空,其它地方置為空。

結合前置判斷了mAdded為true才會走到崩潰點,在源碼中尋找到只有先正常調用setView以后在調用dispatchDetachedFromWindow時才滿足mAdded=true、mView=null的條件,從采集的logcat日志中可以證明這一點,此時基本可以定位根因是窗口銷毀與焦點事件處理的時序問題。

圖片圖片

圖片圖片

解決過程

在問題初期,嘗試通過 Hook 攔截 handleWindowFocusChanged 方法增加防御:當檢測到 mView 為空時直接中斷后續邏輯執行。本地驗證階段,通過在 Android 15 設備上高頻觸發商詳頁 Dialog 彈窗的焦點獲取與關閉操作,未復現線上崩潰問題。考慮到 Hook 方案的侵入性風險 ,且無法本地測試,最終放棄此方案上線。

通過崩潰日志分析發現,問題設備100% 集中在小米/紅米機型,而該品牌在 Android 15 DAU中僅占 36% ,因此懷疑是MIUI對Android15某些定制功能有bug。經與小米技術團隊數周的溝通與聯合排查,最終小米在v2.0.28版本修復了此問題,需要用戶升級ROM解決,目前>=2.0.28的MIUI設備無此問題的上報。

六、總結

通過上述問題的治理,系統bug類的崩潰顯著減少,希望這些經驗對大家有所幫助。

責任編輯:武曉燕 來源: 得物技術
相關推薦

2025-11-11 01:55:00

2023-03-30 18:39:36

2024-06-06 10:39:32

2023-07-19 22:17:21

Android資源優化

2023-10-09 18:35:37

得物Redis架構

2023-11-27 18:38:57

得物商家測試

2023-02-08 18:33:49

SRE探索業務

2022-12-14 18:40:04

得物染色環境

2023-08-09 20:43:32

2022-10-26 18:44:33

藍紙箱設計數據

2025-07-31 00:00:25

2022-10-20 14:35:48

用戶畫像離線

2023-03-13 18:35:33

灰度環境golang編排等

2025-03-20 10:47:15

2023-11-29 18:41:35

模型數據

2023-02-01 18:33:44

得物商家客服

2022-12-09 18:58:10

2023-12-27 18:46:05

云原生容器技術

2023-02-06 18:35:05

架構探測技術

2023-03-31 18:36:00

點贊
收藏

51CTO技術棧公眾號

亚洲激情77| 亚洲区一区二区三区| 一区二区不卡在线观看| 91精品国自产| 亚洲另类av| 欧美日韩高清一区二区不卡| 91国内在线视频| 亚洲精品乱码久久久久久9色| 国产区av在线| 国产a久久精品一区二区三区| 亚洲最新在线观看| 成人黄色大片在线免费观看| 一级黄色性视频| 综合另类专区| 久久久久久久久一| 日本一区二区不卡| 人妻大战黑人白浆狂泄| 国产激情视频在线看| 成人深夜视频在线观看| 欧美大肥婆大肥bbbbb| 中文字幕22页| 欧美激情免费| 国产在线精品不卡| 久热精品在线视频| 又黄又爽又色的视频| 成人无遮挡免费网站视频在线观看| 日本一不卡视频| 中文日韩电影网站| 午夜免费看毛片| 色在线中文字幕| 久久久亚洲精品石原莉奈| 91丝袜美腿美女视频网站| 日韩高清dvd碟片| 日韩成人在线观看视频| 一区二区三区中文字幕精品精品| 91黄色精品| 日韩精品国产一区二区| 欧美色资源站| 欧美色综合久久| 日本不卡一区二区三区四区| 国产日本精品视频| 激情欧美亚洲| 亚洲女人天堂色在线7777| 国产精品亚洲二区在线观看| 无码国产色欲xxxx视频| 久久久噜噜噜久久狠狠50岁| 在线视频欧美日韩精品| 日本黄色特级片| 国产精品一区二区三区美女| 欧美视频精品一区| 亚洲欧美日韩不卡一区二区三区| 日本在线视频1区| 毛片基地黄久久久久久天堂| 欧美第一黄色网| 大又大又粗又硬又爽少妇毛片| 日韩制服诱惑| 亚洲国产精品麻豆| 亚洲日本精品一区| 午夜视频在线| 99精品在线观看视频| 国产精品一区二区久久久久| 激情四射综合网| 国产欧美一区| 一区二区三区黄色| 一级全黄裸体片| 日韩成人亚洲| 亚洲高清久久久| av在线观看地址| 2019中文字幕在线视频| 成人av在线资源网站| 国产精品盗摄久久久| 激情五月婷婷在线| 亚洲高清网站| 热99久久精品| 国产稀缺真实呦乱在线| 伊人久久综合| 欧美有码在线观看| 婷婷色中文字幕| 成人看的羞羞网站| 亚洲国产精品国自产拍av秋霞 | 国产在线观看无码免费视频| 欧洲亚洲精品久久久久| 天天综合色天天综合| 一区二区三区一级片| h视频在线免费观看| 亚洲小说欧美激情另类| 在线综合视频网站| 免费在线观看一级毛片| 成人激情免费网站| 免费看国产精品一二区视频| 午夜精品一二三区| 精品写真视频在线观看| 日本亚洲精品在线观看| 亚洲一区二区影视| 日本不卡视频在线| 亚洲一区二区三区久久| 国产裸体美女永久免费无遮挡| 99视频一区| 韩国三级日本三级少妇99| 我要看黄色一级片| 亚洲精品激情| 韩国19禁主播vip福利视频| 亚洲熟女毛茸茸| 日韩精品欧美| 亚洲男人天堂视频| 粉嫩av性色av蜜臀av网站| 欧美韩日一区| 日韩亚洲欧美成人| 国产一区二区三区四区在线| 国产麻豆一区二区三区精品视频| 精品国产自在精品国产浪潮| 成人午夜福利一区二区| 91精品国产乱码久久久久久| 自拍视频国产精品| 日本中文字幕免费观看| 另类成人小视频在线| 国产综合色一区二区三区| 亚洲国产精品久久久久久久 | 国产精品r级在线| 九九热精品视频在线| 日韩视频在线一区二区三区| 国产精品丝袜久久久久久不卡| 免费国产黄色片| 99天天综合性| 欧美日本韩国一区二区三区| 欧美精品少妇| 亚洲国产精品一区二区久久| mm131国产精品| av日韩久久| 欧美成人官网二区| 国产二级一片内射视频播放 | 日韩精品五月天| 国产成人亚洲综合91精品| 好吊视频一区二区三区| jvid福利写真一区二区三区| 日韩中文在线字幕| 1区2区3区在线| 色婷婷一区二区| 亚洲天堂2018av| av资源久久| 欧美wwwxxxx| 一级片免费网站| 亚洲国产精品精华液ab| 五月天综合婷婷| 国产在视频一区二区三区吞精| 日韩欧美一级在线播放| 久久人人爽人人爽人人片| 悠悠资源网久久精品| 97se亚洲综合在线| seseavlu视频在线| 一区二区高清免费观看影视大全| 日本在线播放一区二区| 欧美激情777| 成人欧美一区二区三区黑人| 国产视频三级在线观看播放| 91福利社在线观看| 国产一级片中文字幕| 999国产精品999久久久久久| 国产一区在线播放| 少妇又色又爽又黄的视频| 国产欧美视频一区二区三区| 9191国产视频| 日本少妇精品亚洲第一区| 欧美精品一二区| 国产高清第一页| 国产日韩精品一区二区三区 | 999国产在线视频| 欧美日韩三级一区二区| 97精品在线播放| 国产精品一区二区久激情瑜伽| 久久久久欧美| 中文字幕中文字幕在线十八区| 欧美日韩国产精品一区二区不卡中文| 中文字幕22页| 欧美另类综合| 国产精品私拍pans大尺度在线| 日韩在线观看www| 日韩视频中午一区| 成年人在线免费看片| 欧美色123| 精品国产aⅴ麻豆| av免费在线观看网址| 亚洲精品在线免费播放| 777777国产7777777| 国产精品白丝jk黑袜喷水| 中文字幕无码精品亚洲资源网久久| 欧美成人毛片| 欧美激情图片区| 国产女人18毛片水真多| 性久久久久久久久久久久| 手机在线免费毛片| 国产午夜精品一区二区三区欧美| 91亚洲精品久久久| 麻豆国产在线| xvideos成人免费中文版| 日本xxxxwww| 欧美日韩大陆一区二区| 久草视频精品在线| 国产成a人亚洲精| 午夜探花在线观看| 中文成人在线| 欧美性视频网站| www.久久ai| 亚洲视频999| 啪啪小视频网站| 亚洲一区二区三区视频在线 | 亚洲精品伦理在线| 少妇真人直播免费视频| 国产精品18久久久久| 中文字幕久精品免| 日韩精品免费一区二区三区竹菊 | 成人国产精品入口免费视频| 亚洲精品日韩在线| 东京热一区二区三区四区| 99麻豆久久久国产精品免费| 91av视频免费观看| 久久成人国产| 国产日韩欧美精品在线观看| 亚洲a一区二区三区| 欧美一区免费视频| 69堂精品视频在线播放| 久久久久国产视频| 国产盗摄在线观看| 日韩在线视频免费观看| 97人人爽人人爽人人爽| 亚洲欧美怡红院| 少妇愉情理伦片bd| 久草这里只有精品视频| 麻豆视频传媒入口| 成人嫩草影院| 日本10禁啪啪无遮挡免费一区二区| 成人av资源网址| 欧美综合在线观看| 91福利在线尤物| 欧美精品videosex牲欧美| 国产在线看片| 亚洲高清一二三区| www.国产.com| 日韩一区二区免费视频| 一区二区三区午夜| 欧美日韩国产一二三| 中文字幕一二三四| 亚洲国产另类av| 麻豆疯狂做受xxxx高潮视频| 亚洲色欲色欲www| 亚洲精品乱码久久久久久不卡| 国产成人精品免费一区二区| 国内精品国产三级国产aⅴ久| 久久精品国内一区二区三区| 99sesese| 极品少妇一区二区三区精品视频| 三级av免费观看| 久久99国产精品成人| 天天久久综合网| 国产情侣久久| 免费黄色日本网站| 91精品成人| 欧美a级黄色大片| 欧美日韩三级| www.99热这里只有精品| 日韩在线观看一区| 一级做a爰片久久| 91精品成人| 国产精品又粗又长| 男女av一区三区二区色多| 少妇黄色一级片| 日韩午夜在线| 男人操女人免费| 激情欧美丁香| 天天摸天天碰天天添| 日韩电影在线一区二区三区| www.com黄色片| 国产一区二区伦理| 国产毛片毛片毛片毛片毛片毛片| 韩国精品一区二区| 欧美图片自拍偷拍| 精品一区二区影视| 亚洲一二三四五| 久久综合色8888| 婷婷五月精品中文字幕| 九九久久精品视频| 国产精品熟妇一区二区三区四区| 26uuu国产在线精品一区二区| www日本在线观看| 91在线观看污| 国产人与禽zoz0性伦| 久久久五月婷婷| 欧美一级片在线视频| 亚洲www啪成人一区二区麻豆| 丁香社区五月天| 欧美视频裸体精品| 国产三级午夜理伦三级| 亚洲国产日韩一区| 亚洲乱码在线观看| 怡红院精品视频| av老司机在线观看| 国产欧美日韩中文字幕在线| a级日韩大片| 午夜精品美女久久久久av福利| 婷婷精品视频| 在线观看18视频网站| 久久福利影视| 午夜福利三级理论电影| www国产成人| 欧美卡一卡二卡三| 欧美无乱码久久久免费午夜一区| 无码视频在线观看| 欧美一二三四在线| 91社区在线观看| 日韩女在线观看| 国内精品偷拍| 91麻豆天美传媒在线| 日韩精品免费专区| 亚洲熟妇一区二区| 亚洲欧美一区二区三区极速播放| 精品不卡一区二区| 欧美精品一区二区久久婷婷| 日本在线免费| 国产精品久久久久久久app | 欧美精品一区二区三区在线看午夜 | 久久国内精品自在自线400部| 双性尿奴穿贞c带憋尿| 亚洲一区二区三区四区在线 | 亚洲男人的天堂在线| 国产色婷婷在线| 午夜精品久久久久久久久久久久久| 日韩福利在线观看| 亚洲成人自拍| 久久亚洲美女| 国产三级国产精品国产专区50| 26uuu亚洲综合色欧美| 国产亚洲精品成人| 欧美日韩一区二区在线播放| a级片在线免费看| 久久精品最新地址| 57pao成人永久免费| 亚洲精品视频一二三| 日韩成人av影视| 亚洲av成人无码久久精品| 欧美日韩一区二区三区 | av色综合久久天堂av综合| 国产一级一片免费播放放a| 日韩视频在线永久播放| 中国av在线播放| 97人人做人人人难人人做| 一区二区三区国产精华| 欧美午夜小视频| 99在线精品视频| 国产精品777777| 亚洲欧美一区二区精品久久久| 深夜在线视频| 日本不卡一区二区三区在线观看| 久久精选视频| 丰满的亚洲女人毛茸茸| 欧美三级在线看| 久久国产精品一区| 91成人免费视频| 激情欧美日韩| 欧美多人猛交狂配| 在线观看欧美精品| 成人午夜视频一区二区播放| 色综合久久久久久中文网| 精品国产免费人成网站| 日本电影一区二区三区| 美国一区二区三区在线播放 | 欧美色播在线播放| 可以在线观看的av网站| 国产精品入口尤物| 午夜a一级毛片亚洲欧洲| 91传媒久久久| 国产精品天天摸av网| 91亚洲欧美激情| 欧美猛少妇色xxxxx| 日韩美脚连裤袜丝袜在线| 久久精品网站视频| 亚洲三级理论片| 五月婷婷六月丁香综合| 国产精品扒开腿做爽爽爽男男 | 久久久99久久| 97免费观看视频| 456亚洲影院| 久久一区二区三区电影| 在线播放第一页| 欧洲视频一区二区| 日本成人不卡| 91精品国产综合久久香蕉| 欧美日韩理论| 中国女人特级毛片| 欧美成人三级电影在线| 日本高清不卡一区二区三区视频| 丰满女人性猛交| 久久综合999| 精品久久久无码中文字幕| 日本午夜在线亚洲.国产| 欧美va天堂在线| 又色又爽又黄18网站| 在线亚洲一区二区| 黄色成人在线网| 一本一道久久a久久综合精品| 岛国精品在线播放|