成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

得物 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類的崩潰顯著減少,希望這些經驗對大家有所幫助。

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

2024-06-06 10:39:32

2023-03-30 18:39:36

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

藍紙箱設計數據

2022-10-20 14:35:48

用戶畫像離線

2023-03-13 18:35:33

灰度環境golang編排等

2025-03-20 10:47:15

2022-12-09 18:58:10

2023-02-01 18:33:44

得物商家客服

2023-11-29 18:41:35

模型數據

2023-12-27 18:46:05

云原生容器技術

2023-02-06 18:35:05

架構探測技術

2023-03-31 18:36:00

2023-05-12 18:42:13

得物AI平臺

2022-12-12 18:56:04

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九九热re | 97视频在线观看网站 | 国产片侵犯亲女视频播放 | 亚洲精品久久久久久首妖 | 国产精品一区二区免费 | 黄色片免费在线观看 | 亚洲一区在线播放 | 黄色毛片在线看 | 中文一区 | 国产一伦一伦一伦 | 99免费视频 | 久久久久一区二区三区四区 | 欧美一区二区三区在线观看 | 亚洲精品一区二区三区中文字幕 | 国产一区二区av | 成人在线免费视频观看 | 在线一区视频 | 一区二区三区四区av | 自拍 亚洲 欧美 老师 丝袜 | 日韩免费高清视频 | 在线观看免费av网 | 色婷婷综合网 | 成人二区三区 | 亚洲精品一区二区三区蜜桃久 | 日韩美女一区二区三区在线观看 | 黄色电影在线免费观看 | 成人一级视频在线观看 | 亚洲精品久 | 国产日韩一区二区三区 | 免费二区| 欧美成视频在线观看 | 欧美xxxx色视频在线观看免费 | a级大片免费观看 | 干干干日日日 | 九九九久久国产免费 | 中文字幕一区在线观看视频 | 中文在线视频 | 91在线一区 | 91精品国产综合久久久久 | 精品久久久久久久久久久久久久 | 精品国产区|