View野指針問題分析報告

【問題描述】

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

pid: 5028, tid: 5028, name: com.miui.player  >>> com.miui.player <<<
signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 79801f28r0 7ac59c98  r1 00000000  r2 bea7b174  r3 400fc1b8r4 774c4c88  r5 79801f28  r6 bea7b478  r7 40c12bb8r8 7c1b68e8  r9 778781e8  sl bea7b478  fp bea7b414ip 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指向了一個沒有可執行權限的內存地址上。

?

【問題分析】

對應的代碼如下:

status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;detachFunctor(functor);...interrupt();// call functor immediately after GL state setupstatus_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類的一個虛函數,它的具體實現目前還不清楚。

對應的匯編代碼如下:

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

崩潰時的寄存器值如下:

    r0 7ac59c98  r1 00000000  r2 bea7b174  r3 400fc1b8r4 774c4c88  r5 79801f28  r6 bea7b478  r7 40c12bb8r8 7c1b68e8  r9 778781e8  sl bea7b478  fp bea7b414ip 00000001  sp bea7b148  lr 40c07031  pc 79801f28  cpsr 600f0010

可以看到r5和pc值是相等的,確實是崩潰在2302e這一行匯編代碼中。

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

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 setupstatus_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

可以確定確實有別的線程在修改這個值。

?

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

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

2、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 setupstatus_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 UND0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_finalize2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit4: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr05: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr16: 00000000     0 FUNC    GLOBAL DEFAULT  UND getrlimit7: 00000000     0 FUNC    GLOBAL DEFAULT  UND setrlimit8: 00000000     0 FUNC    GLOBAL DEFAULT  UND __errno9: 00000000     0 FUNC    GLOBAL DEFAULT  UND strerror10: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print
    11: 00000000     0 FUNC    GLOBAL DEFAULT  UND _Znwj
    12: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZdlPv14: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_assert...51: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_llsr52: 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.javaprivate void destroyHybridView() {for (HybridView view : mHybridViews) {if (view != null) {view.destroy();}}mHybridViews.clear();} @packages/apps/MiuiSdk/library/src/java/miui/hybrid/HybridFragment.javaprivate 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: RUNNABLEat 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,也不會存在野指針問題。

?

轉載于:https://www.cnblogs.com/YYPapa/p/6850939.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/395802.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/395802.shtml
英文地址,請注明出處:http://en.pswp.cn/news/395802.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

SicilyFunny Game

一、題目描述 Two players, Singa and Suny, play, starting with two natural numbers. Singa, the first player, subtracts any positive multiple of the lesser of the two numbers from the greater of the two numbers, provided that the resulting number must be non…

java 分布式同步_Java Web分布式集群搭建(三)——Session同步

對于一個業務系統的Tomcat集群來說&#xff0c;必須保證同一個用戶訪問到任一臺服務器上都可以維持之前操作的身份。比如在服務器A進行了登陸&#xff0c;那么在服務器B中也要同步該用戶已登錄的狀態&#xff0c;這里就用到了Session的同步。同步方式sticky模式、復制模式、Ter…

移動應用程序和網頁應用程序_如何不完全破壞您的移動應用程序的用戶界面

移動應用程序和網頁應用程序by Luke Konior盧克科尼爾(Luke Konior) 如何不完全破壞您的移動應用程序的用戶界面 (How to not utterly ruin your mobile app’s user interface) There’s no single universal formula for designing a great user interface (if you discover…

logging記錄日志

日志是一個系統的重要組成部分&#xff0c;用以記錄用戶操作、系統運行狀態和錯誤信息。日志記錄的好壞直接關系到系統出現問題時定位的速度。logging模塊Python2.3版本開始成為Python標準庫的一部分。 日志級別 在最簡單的使用中&#xff0c;我們直接導入logging模塊&#xff…

C#編程之接口

1.定義 接口是把公共方法和屬性組合起來&#xff0c;以封裝特定功能的一個集合。&#xff08;一旦定義了接口&#xff0c;就可以在類中實現它。這樣類就可以支持接口所指定的所有屬性和成員&#xff09; 注意1&#xff1a;接口不能單獨存在。不能像實例化一個類那樣實例化一個接…

supervisor守護進程

2019獨角獸企業重金招聘Python工程師標準>>> supervisor 是一個client/server系統,把不是守護進程的進程變成守護進程,并監控和控制類 Unix 操作系統上的進程。 upervisor就是用Python開發的一套通用的進程管理程序&#xff0c;能將一個普通的命令行進程變為后臺dae…

神經網絡算法 java 源代碼_神經網絡算法與實現 ——基于Java語言 代碼實例

【實例簡介】Neural Network Programming with Java_ISBN 978-7-115-46093-6【實例截圖】【核心代碼】NeuralNetworkProgrammingwithJava_code└── Neural Network Programming with Java_code├── Chapter1│ ├── HiddenLayer.java│ ├── InputLayer.java│ ├…

javascript面試_在編碼面試中需要注意的3個JavaScript問題

javascript面試JavaScript is the official language of all modern web browsers. As such, JavaScript questions come up in all sorts of developer interviews.JavaScript是所有現代Web瀏覽器的官方語言。 因此&#xff0c;各種開發人員訪談中都會出現JavaScript問題。 T…

【學習筆記】深入理解js原型和閉包(11)——執行上下文棧

繼續上文的內容。 執行全局代碼時&#xff0c;會產生一個執行上下文環境&#xff0c;每次調用函數都又會產生執行上下文環境。當函數調用完成時&#xff0c;這個上下文環境以及其中的數據都會被消除&#xff0c;再重新回到全局上下文環境。處于活動狀態的執行上下文環境只有一個…

Java基礎--訪問權限控制符

今天我們來探討一下訪問權限控制符。 使用場景一&#xff1a;攻城獅A編寫了ClassA&#xff0c;但是他不想所有的攻城獅都可以使用該類&#xff0c;應該怎么辦&#xff1f; 使用場景二&#xff1a;攻城獅A編寫了ClassA&#xff0c;里面有func1方法和func2方法&#xff0c;但是他…

css繪制正方體_設計師僅使用CSS繪制了8個標志性X戰警

css繪制正方體Here are three links worth your time:這是三個值得您花費時間的鏈接&#xff1a; A designer drew 8 iconic X-Men using nothing but CSS (1 minute interactive) 一位設計師僅用CSS繪制了8個標志性的X戰警( 互動時間為1分鐘 ) Raspberry Pi just turned 5. H…

Dubbo簡單介紹及實例

1、概念 Dubbo是一個分布式服務框架&#xff0c;以及阿里巴巴內部的SOA服務化治理方案的核心框架。其功能主要包含&#xff1a;高性能NIO通訊及多協議集成。服務動態尋址與路由。軟負載均衡與容錯&#xff0c;依賴分析與降級等。 說通俗點&#xff0c;就是首先將程序組件化成一…

Oracle 10.2.0.5升級至11.2.0.4

參照MOS 官方文檔Complete Checklist for Manual Upgrade to Oracle Database 11gR2 (11.2) (Doc ID 837570.1)一、升級前的準備1、復制utlu112i.sql腳本從11G數據庫復制$ORACLE_HOME/rdbms/admin/utlu112i.sql 腳本至10g 數據庫臨時目錄&#xff0c;準備執行如果不在10g數據庫…

脫殼_詳細_使用的方法_01

ZC: 如何確定被調試程序已經來到了 未加殼的程序中&#xff1f; ZC:  視頻中是使用判斷集中語言的特征 ZC:  我的方法&#xff1a;上面的方式 ESP平衡 1、第1課 (1)、單步跟蹤&#xff08;原則&#xff1a;向下的跳轉>正常F8&#xff0c;向上的跳轉>F4跳過(或者用F2…

android 函數式編程_Android開發人員的函數式編程-第1部分

android 函數式編程by Anup Cowkur通過安納普考庫(Anup Cowkur) Android開發人員的函數式編程-第1部分 (Functional Programming for Android Developers — Part 1) Lately, I’ve been spending a lot of time learning Elixir, an awesome functional programming language…

java編程 內存_Java編程技術之淺析JVM內存

JVMJVM->Java Virtual Machine:Java虛擬機,是一種用于計算設備的規范&#xff0c;它是一個虛構出來的計算機&#xff0c;是通過在實際的計算機上仿真模擬各種計算機功能來實現的。基本認知&#xff1a;1.JVM是用于運行Java代碼的假象計算機&#xff0c;主要有一套字節碼指令…

bzoj1116: [POI2008]CLO

傳送門&#xff1a;http://www.lydsy.com/JudgeOnline/problem.php?id1116 題目大意&#xff1a;Byteotia城市有n個 towns m條雙向roads. 每條 road 連接 兩個不同的 towns ,沒有重復的road. 你要把其中一些road變成單向邊使得&#xff1a;每個town都有且只有一個入度 題解&am…

java排序算法大全_各種排序算法的分析及java實現

排序一直以來都是讓我很頭疼的事&#xff0c;以前上《數據結構》打醬油去了&#xff0c;整個學期下來才勉強能寫出個冒泡排序。由于要找工作了&#xff0c;也知道排序算法的重要性(據說是面試必問的知識點)&#xff0c;所以又花了點時間重新研究了一下。排序大的分類可以分為兩…

Cocos2d-x 3.0 簡捷的物理引擎

Cocos2d-x 3.0 開發&#xff08;九&#xff09;使用Physicals取代Box2D和chipmunk http://www.cocos2d-x.org/docs/manual/framework/native/physics/physics-integration/zh -- 官網Demo 水墨魚的專欄 http://www.cocos2d-x.org/docs/catalog/zh --- 官方 搭“server” 須要哪…

google i/o_Google I / O 2017最有希望的突破

google i/oby Aravind Putrevu通過Aravind Putrevu Google I / O 2017最有希望的突破 (The most promising breakthroughs from Google I/O 2017) Google I/O is one of the biggest developer conferences. This year was particularly exciting. There were two keynotes: o…