【問題描述】
音樂組同事反饋了一個必現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,也不會存在野指針問題。
?