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.mzph.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…