View野指針問題分析報告

【問題描述】java

音樂組同事反饋了一個必現Native Crash問題,tombstone以下:android

pid: 5028, tid: 5028, name: com.miui.player  >>> com.miui.player <<<
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 79801f28
    r0 7ac59c98  r1 00000000  r2 bea7b174  r3 400fc1b8
    r4 774c4c88  r5 79801f28  r6 bea7b478  r7 40c12bb8
    r8 7c1b68e8  r9 778781e8  sl bea7b478  fp bea7b414
    ip 00000001  sp bea7b148  lr 40c07031  pc 79801f28  cpsr 600f0010
backtrace:
    #00  pc 0000bf28  <unknown>
    #01  pc 0002302f  /system/lib/libhwui.so (android::uirenderer::OpenGLRenderer::callDrawGLFunction(android::Functor*, android::uirenderer::Rect&)+322)
    #02  pc 00015d91  /system/lib/libhwui.so (android::uirenderer::DrawFunctorOp::applyDraw(android::uirenderer::OpenGLRenderer&, android::uirenderer::Rect&)+28)
    #03  pc 00014527  /system/lib/libhwui.so (android::uirenderer::DrawBatch::replay(android::uirenderer::OpenGLRenderer&, android::uirenderer::Rect&, int)+74)
    #04  pc 00014413  /system/lib/libhwui.so (android::uirenderer::DeferredDisplayList::flush(android::uirenderer::OpenGLRenderer&, android::uirenderer::Rect&)+218)
    #05  pc 0001d1cf  /system/lib/libhwui.so (_ZN7android10uirenderer14OpenGLRenderer15drawDisplayListEPNS0_11DisplayListERNS0_4RectEi.part.47+230)
    #06  pc 0006820d  /system/lib/libandroid_runtime.so

崩潰的緣由是pc指向了一個沒有可執行權限的內存地址上。c++

 

【問題分析】web

對應的代碼以下:app

status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;

    detachFunctor(functor);
       ...
    interrupt();

    // call functor immediately after GL state setup
    status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);  

其中,Functor類重載了()操做符:函數

class Functor {
public:
    Functor() {}
    virtual ~Functor() {}
    virtual status_t operator ()(int /*what*/, void* /*data*/) { return NO_ERROR; }
};

所以,()操做其實就是調用了Functor類的一個虛函數,它的具體實現目前還不清楚。工具

對應的彙編代碼以下:oop

   23028:       aa0b            add     r2, sp, #44
   2302a:       6803            ldr     r3, [r0, #0]          ; r0是functor,r3 = [r0] = functor.vtlb
   2302c:       689d            ldr     r5, [r3, #8]          ; r5 = [r3 + 8] = [functor.vtlb + 8] = Functor.operator()
   2302e:       47a8            blx     r5                    ; call Functor.operator()

崩潰時的寄存器值以下:ui

    r0 7ac59c98  r1 00000000  r2 bea7b174  r3 400fc1b8
    r4 774c4c88  r5 79801f28  r6 bea7b478  r7 40c12bb8
    r8 7c1b68e8  r9 778781e8  sl bea7b478  fp bea7b414
    ip 00000001  sp bea7b148  lr 40c07031  pc 79801f28  cpsr 600f0010

能夠看到r5和pc值是相等的,確實是崩潰在2302e這一行彙編代碼中。spa

而查看寄存器對應的內存值,發現有點問題:

memory near r0:
    7ac59c78 00000018 0000001b 735a9b38 23831ef0  
    7ac59c88 23831ef0 735a9b50 00000018 00000011  
    7ac59c98 79822328 77768698 00000010 00000022  
    7ac59ca8 00000000 00000000 00000000 00000003  

memory near r3:
    400fc198 7c74c000 00200000 00000077 0d44acd8  
    400fc1a8 00000000 00000000 400fc1a8 400fc1a8  
    400fc1b8 400fc1b0 400fc1b0 7c04acb8 7c78f008  
    400fc1c8 7c021d98 7c78ffc0 7983bbf0 7c04bfa8 

崩潰前:

   2302a:       6803            ldr     r3, [r0, #0] 

但崩潰後tombstone打印內存值時,發現[r0] = 0x79822328,這與r3值0x400fc1b8不相同!

[r3+8] = [400fc1b8 + 8]  = 7c04acb8,這個值也和r5值(79801f28)不同。

這在平時的tombstone裏是很是少見的!

乍一看很是難以想象,但仔細想一想tombstone的生成過程,就能發現其中的問題。

原來寄存器信息是錯位崩潰時的cpu context,保存在崩潰時的線程私有的信號棧和內核棧中,直到debuggerd去獲取這個值,它是不會被修改。

而內存是進程中的各個線程共享的,因此在發生異常到debuggerd打印內存信息這段過程當中(實際上是相對很長的一個過程),別的線程是有可能修改內存值的。

爲了證實別的線程在改這個內存值,在callDrawGLFunction()函數中的若干處打印了Functor和它的vtbl值:

 

status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
    AOGI("functor=%p,vtbl=%p");
    sleep(1);
    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
    AOGI("functor=%p,vtbl=%p");
    sleep(1);
    detachFunctor(functor);
    ... 

    AOGI("functor=%p,vtbl=%p");
    sleep(1);
    interrupt();
    AOGI("functor=%p,vtbl=%p");
    sleep(1);
    // call functor immediately after GL state setup
    status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);   

 

抓到的log以下:

10-27 21:19:45.794 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x73648de0
10-27 21:19:47.801 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x73648de0
10-27 21:19:48.801 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x73648de0
10-27 21:19:49.801 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x73648de0
10-27 21:19:50.804 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x73648de0
10-27 21:19:51.804 8027 8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x400fc1b8

能夠肯定確實有別的線程在修改這個值。

 

這裏就存在兩個可能性了:

一、別的線程也持有functor指針,並修改內容

二、functor是野指針,對應的內存已經還回系統,其餘模塊可任意使用。

而對象的vtbl通常是不會修改的,因此2的可能性更大一些。

 

爲了查明是哪一個線程在改,對functor指向的內存作了寫保護操做:

static int** s_saved_vtbl = NULL;
static void* s_saved_functor = NULL;

static void  mprotect_local(int** p) {
    // 一旦發現vtbl有變化就將對應內存設置爲只讀
    if(p != s_saved_vtbl) { 
        mprotect((void*)((unsigned int)s_saved_functor&0xfffff000), 4096, PROT_READ);
    }
    sleep(1);
}

status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
    int* ptr = (int*)functor;
    s_saved_functor = (void*)ptr;
    s_saved_vtbl = (int**)*ptr;

    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; 

 mprotect_local((int**)*ptr);
    detachFunctor(functor);
 mprotect_local((int**)*ptr);
    ...
 mprotect_local((int**)*ptr);
    interrupt();
 
    // call functor immediately after GL state setup
    status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);   

push到手機中復現問題,很容易抓到crash,

而每次的crash的線程和位置都不同,也就是不一樣的線程在不一樣的函數中讀寫這個地址。

這樣基本上就肯定是野指針問題,進入下一階段的分析。

 

被析構的對象是Functor類的對象,

因爲它的vtbl地址咱們可以從log中獲取到,而vtbl通常指向定義了該類的so中,

因此用vtbl值(0x73648de0)去map表中找,就能肯定是哪一個so了。

...
73635000-73646000 rw-p 00000000 00:00 0 
73646000-73648000 r-xp 00000000 b3:18 1287       /system/lib/libwebviewchromium_plat_support.so
73648000-73649000 r--p 00001000 b3:18 1287       /system/lib/libwebviewchromium_plat_support.so
73649000-7364a000 rw-p 00002000 b3:18 1287       /system/lib/libwebviewchromium_plat_support.so

而須要注意的是,C++對象的釋放是delete函數,libwebviewchromium_plat_support.so不會直接調用libc的free函數,而是調用libc++.so中的delete函數。

先確認libwebviewchromium_plat_support.so是否依賴了delete函數:

 

 

$ readelf -s libwebviewchromium_plat_support.so |grep UND
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize
     2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit
     4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0
     5: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr1
     6: 00000000     0 FUNC    GLOBAL DEFAULT  UND getrlimit
     7: 00000000     0 FUNC    GLOBAL DEFAULT  UND setrlimit
     8: 00000000     0 FUNC    GLOBAL DEFAULT  UND __errno
     9: 00000000     0 FUNC    GLOBAL DEFAULT  UND strerror
    10: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print
    11: 00000000     0 FUNC    GLOBAL DEFAULT  UND _Znwj
    12: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZdlPv
    14: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_assert
    ...
    51: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_llsr
    52: 00000000     0 OBJECT  GLOBAL DEFAULT  UND __popcount_tab

 

其中_Znwj是new的符號,_ZdlPv是delete的符號。

接下來就用工具hook libwebviewchromium_plat_support.so的delete函數:

 

extern void _ZdlPv(void *);
void inject__ZdlPv(void* ptr) {
    LOGD("delete %p",ptr);
    dumpNativeStack();
    dumpJavaStack();
    _ZdlPv(ptr);
}

hook後復現問題,抓到的log以下:

10-27 21:19:52.961  8027  8027 D ObserverLayout: onStop: clz=com.miui.player.display.view.DisplayFragmentLayout{45665838 V.E..... ........ 0,0-1080,1920 #7f080039 app:id/content}
10-27 21:19:52.965  8027  8027 I MusicBaseFragment: onDestroyView  the view is still attached, delay destroy
10-27 21:19:52.966  8027  8027 D INJECT  : delete 0x7a7b8530
10-27 21:19:52.986  8027  8027 D INJECT  : #00  pc 000015f6  /system/lib/libinject.so (inject__ZdlPv+21)
10-27 21:19:52.986  8027  8027 D INJECT  : #01  pc 00001134  /system/lib/libwebviewchromium_plat_supp
10-27 21:19:52.986  8027  8027 D INJECT  : #02  pc 00001088  /system/lib/libwebviewchromium_plat_supp
10-27 21:19:52.987  8027  8027 D INJECT  : #03  pc 0001d30c  /system/lib/libdvm.so (dvmPlatformInvoke+112)
10-27 21:19:52.987  8027  8027 D INJECT  : #04  pc 0004d8da  /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JV+397)
10-27 21:19:52.987  8027  8027 D INJECT  : #05  pc 00026720  /system/lib/libdvm.so
10-27 21:19:52.987  8027  8027 D INJECT  : #06  pc 0002d790  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
10-27 21:19:52.987  8027  8027 D INJECT  : #07  pc 0002adf4  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JVa+184)
10-27 21:19:52.988  8027  8027 D INJECT  : #08  pc 00060058  /system/lib/libdvm.so (dvmInvokeMethod(Object*, Method const*, +391)
10-27 21:19:52.988  8027  8027 D INJECT  : #09  pc 00067ff6  /system/lib/libdvm.so
10-27 21:19:52.988  8027  8027 D INJECT  : #10  pc 00026720  /system/lib/libdvm.so
10-27 21:19:52.988  8027  8027 D INJECT  : #11  pc 0002d790  /system/lib/libdvm.so (dvmMterpStd(Thread*)+76)
10-27 21:19:52.988  8027  8027 D INJECT  : #12  pc 0002adf4  /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JVa+184)
10-27 21:19:52.988  8027  8027 D INJECT  : #13  pc 0005fd74  /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, O+335)
10-27 21:19:52.988  8027  8027 D INJECT  : #14  pc 000494c2  /system/lib/libdvm.so
10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor.nativeDestroyGLFunctor(Native Method)
10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor.access$000(DrawGLFunctor.java:31)
10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor$DestroyRunnable.run(DrawGLFunctor.java:91)
10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.runCleanupTaskInternal(CleanupReference.java:159)
10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.access$300(CleanupReference.java:32)
10-27 21:19:52.989  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference$LazyHolder$1.handleMessage(CleanupReference.java:93)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.handleOnUiThread(CleanupReference.java:147)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.org.chromium.content.common.CleanupReference.cleanupNow(CleanupReference.java:141)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.webview.chromium.DrawGLFunctor.destroy(DrawGLFunctor.java:46)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.android.webview.chromium.WebViewChromium.destroy(WebViewChromium.java:430)
10-27 21:19:52.990  8027  8027 D INJECT  :   at android.webkit.WebView.destroy(WebView.java:667)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:64)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:115)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.miui.player.component.MusicBaseFragment.onDestroyView(MusicBaseFragment.java:216)
10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.Fragment.performDestroyView(Fragment.java:1898)
10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:954)
10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167)
10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:715)
10-27 21:19:52.991  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1544)
10-27 21:19:52.992  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl$3.run(FragmentManager.java:502)
10-27 21:19:52.992  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1449)
10-27 21:19:52.992  8027  8027 D INJECT  :   at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)
10-27 21:19:52.992  8027  8027 D INJECT  :   at android.os.Handler.handleCallback(Handler.java:733)
10-27 21:19:52.992  8027  8027 D INJECT  :   at android.os.Handler.dispatchMessage(Handler.java:95)
10-27 21:19:52.992  8027  8027 D INJECT  :   at android.os.Looper.loop(Looper.java:136)
10-27 21:19:52.993  8027  8027 D INJECT  :   at android.app.ActivityThread.main(ActivityThread.java:5016)
10-27 21:19:52.993  8027  8027 D INJECT  :   at java.lang.reflect.Method.invokeNative(Native Method)
10-27 21:19:52.993  8027  8027 D INJECT  :   at java.lang.reflect.Method.invoke(Method.java:515)
10-27 21:19:52.993  8027  8027 D INJECT  :   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
10-27 21:19:52.993  8027  8027 D INJECT  :   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
10-27 21:19:52.993  8027  8027 D INJECT  :   at dalvik.system.NativeStart.main(Native Method)
10-27 21:19:53.020  8027  8027 I OpenGLRenderer: functor=0x7a7b8530,vtbl=0x400fc1b8

從log中能夠看到,確實是在distroy view的時候釋放了Functor,而隨後再Renderer中又使用了這個Functor。

打印崩潰時的java調用棧以下:

10-27 21:19:53.274  8027  8027 I dalvikvm: "main" prio=5 tid=1 TIMED_WAIT
10-27 21:19:53.279  8027  8027 I dalvikvm:   | group="main" sCount=0 dsCount=0 obj=0x41716ca8 self=0x415344f8
10-27 21:19:53.279  8027  8027 I dalvikvm:   | sysTid=6895 nice=-6 sched=0/0 cgrp=apps handle=1074409812
10-27 21:19:53.280  8027  8027 I dalvikvm:   | state=R schedstat=( 0 0 0 ) utm=184 stm=61 core=3
10-27 21:19:53.280  8027  8027 I dalvikvm:   at android.view.GLES20Canvas.nDrawDisplayList(Native Method)
10-27 21:19:53.281  8027  8027 I dalvikvm:   at android.view.GLES20Canvas.drawDisplayList(GLES20Canvas.java:420)
10-27 21:19:53.281  8027  8027 I dalvikvm:   at android.view.HardwareRenderer$GlRenderer.drawDisplayList(HardwareRenderer.java:1709)
10-27 21:19:53.281  8027  8027 I dalvikvm:   at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:1525)
10-27 21:19:53.282  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.draw(ViewRootImpl.java:2475)
10-27 21:19:53.282  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2347)
10-27 21:19:53.283  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1977)
10-27 21:19:53.284  8027  8027 I dalvikvm:   at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1094)
10-27 21:19:53.285  8027  8027 I dalvikvm:   at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5703)
10-27 21:19:53.285  8027  8027 I dalvikvm:   at android.view.Choreographer$CallbackRecord.run(Choreographer.java:764)
10-27 21:19:53.286  8027  8027 I dalvikvm:   at android.view.Choreographer.doCallbacks(Choreographer.java:577)
10-27 21:19:53.287  8027  8027 I dalvikvm:   at android.view.Choreographer.doFrame(Choreographer.java:547)
10-27 21:19:53.288  8027  8027 I dalvikvm:   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:750)
10-27 21:19:53.289  8027  8027 I dalvikvm:   at android.os.Handler.handleCallback(Handler.java:733)
10-27 21:19:53.289  8027  8027 I dalvikvm:   at android.os.Handler.dispatchMessage(Handler.java:95)
10-27 21:19:53.290  8027  8027 I dalvikvm:   at android.os.Looper.loop(Looper.java:136)
10-27 21:19:53.291  8027  8027 I dalvikvm:   at android.app.ActivityThread.main(ActivityThread.java:5016)
10-27 21:19:53.291  8027  8027 I dalvikvm:   at java.lang.reflect.Method.invokeNative(Native Method)
10-27 21:19:53.292  8027  8027 I dalvikvm:   at java.lang.reflect.Method.invoke(Method.java:515)
10-27 21:19:53.293  8027  8027 I dalvikvm:   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
10-27 21:19:53.293  8027  8027 I dalvikvm:   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
10-27 21:19:53.293  8027  8027 I dalvikvm:   at dalvik.system.NativeStart.main(Native Method)

正常狀況下,view在被destroy後不該該再被繪製,這種狀況多是view在destroy前沒有remove致使的。

 

又發現delete時的調用棧中有兩行特別的:

10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:64)
10-27 21:19:52.990  8027  8027 D INJECT  :   at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:115)

這個是應用的代碼,這個問題只有在這個應用上出現過,因此極可能是應用的代碼引發的,因此查了下opengrok中的代碼,發現有兩處實現:

@packages/apps/MiuiMusic/common/music_sdk/hybrid/src/com/xiaomi/music/hybrid/HybridFragment.java
    private void destroyHybridView() {
        for (HybridView view : mHybridViews) {
            if (view != null) {
                view.destroy();
            }
        }
        mHybridViews.clear();
    } 

@packages/apps/MiuiSdk/library/src/java/miui/hybrid/HybridFragment.java
    private void destroyHybridView() {
        for (HybridView view : mHybridViews) {
            if (view != null) {
                if (view.getParent() != null) {
                    ((ViewGroup) view.getParent()).removeView(view);
                }
                view.destroy();
            }
        }
        mHybridViews.clear();
    }

跟應用的同事溝通後得知,音樂應用是用上面的代碼,也就是沒有removeView的代碼。

 

【解決方案】

將上面代碼中添加removeView的邏輯後再也不復現問題。

 

雖然問題獲得解決,但還不清楚爲何沒有removeView會致使野指針。

爲了找到根源仔細閱讀了相關代碼,發現代碼中Render中有detachFunctor的代碼:

class GLES20Canvas extends HardwareCanvas {
    ...
    public void detachFunctor(int functor) {
        nDetachFunctor(mRenderer, functor);
    }

在這個代碼中設置斷點,用studio獲得以下調用棧:

  java.lang.Thread.State: RUNNABLE
      at android.view.GLES20Canvas.detachFunctor(GLES20Canvas.java:321)
      at android.view.HardwareRenderer$GlRenderer.detachFunctor(HardwareRenderer.java:1791)
      at android.view.ViewRootImpl.detachFunctor(ViewRootImpl.java:744)
      at com.android.webview.chromium.DrawGLFunctor$DestroyRunnable.detachNativeFunctor(DrawGLFunctor.java:97)
      at com.android.webview.chromium.DrawGLFunctor.detach(DrawGLFunctor.java:53)
      at com.android.webview.chromium.WebViewChromium.onDetachedFromWindow(WebViewChromium.java:1718)
      at android.webkit.WebView.onDetachedFromWindow(WebView.java:2108)
      at android.view.View.dispatchDetachedFromWindow(View.java:12631)
      at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:2587)
      at android.view.ViewGroup.removeViewInternal(ViewGroup.java:3845)
      at android.view.ViewGroup.removeViewInternal(ViewGroup.java:3818)
      at android.view.ViewGroup.removeView(ViewGroup.java:3750)
      at com.xiaomi.music.hybrid.HybridFragment.destroyHybridView(HybridFragment.java:66)
      at com.xiaomi.music.hybrid.HybridFragment.onDestroyView(HybridFragment.java:119)
      at com.miui.player.component.MusicBaseFragment.onDestroyView(MusicBaseFragment.java:216)
      at android.app.Fragment.performDestroyView(Fragment.java:1898)
      at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:954)
      at android.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1167)
      at android.app.BackStackRecord.popFromBackStack(BackStackRecord.java:715)
      at android.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1544)
      at android.app.FragmentManagerImpl$3.run(FragmentManager.java:502)
      at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1449)
      at android.app.FragmentManagerImpl$1.run(FragmentManager.java:443)
      at android.os.Handler.handleCallback(Handler.java:733)
      at android.os.Handler.dispatchMessage(Handler.java:95)
      at android.os.Looper.loop(Looper.java:136)
      at android.app.ActivityThread.main(ActivityThread.java:5016)
      at java.lang.reflect.Method.invokeNative(Method.java:-1)
      at java.lang.reflect.Method.invoke(Method.java:515)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
      at dalvik.system.NativeStart.main(NativeStart.java:-1)

加了removeView後,會從Render中刪除Functor,這樣Render在繪製時,再也不調用這個Functor。

這個問題只會在KK上有,L之後對Render作的很大改動,即便不作removeView,也不會存在野指針問題。

相關文章
相關標籤/搜索