Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[安卓原生]线上版本出现大量的 java 的 NullPointer异常引起的闪退问题. #17910

Closed
finscn opened this issue Nov 23, 2024 · 12 comments
Assignees
Labels
Bug Env: Android Env: Native General issue on all native platforms including iOS, Android, MacOS, Windows Needs Triage Needs to be assigned by the team
Milestone

Comments

@finscn
Copy link
Contributor

finscn commented Nov 23, 2024

Cocos Creator version

3.8.4

System information

android

Issue description

错误信息如下:

java.lang.NullPointerException
Attempt to invoke virtual method 'int android.view.InputDevice.getSources()' on a null object reference

at com.google.android.games.paddleboat.GameControllerManager.isDeviceOfSource(Unknown Source:4)
at com.google.android.games.paddleboat.GameControllerManager.getIsGameController(Unknown Source:3)
at com.google.android.games.paddleboat.GameControllerManager.onInputDeviceChanged(Unknown Source:0)
at com.google.android.games.paddleboat.GameControllerThread.onInputDeviceChanged(Unknown Source:24)
at android.hardware.input.InputManager$InputDeviceListenerDelegate.handleMessage(InputManager.java:1708)
at android.os.Handler.dispatchMessage(Handler.java:117)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:293)
at com.google.android.games.paddleboat.GameControllerThread.run(Unknown Source:17)

还有另外一种

java.lang.NullPointerException
Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference

at com.cocos.lib.CocosDownloader$c$c.a(Unknown Source:33)
at k1.w$b.e(Unknown Source:19)
at l1.b.run(Unknown Source:17)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at java.lang.Thread.run(Thread.java:1012)

可能是某些安卓设备的特殊性导致的? cocos是否考虑在某些地方做一些 null check ?

还有一种不是 null pointer 异常, 但是也是 java 的

java.lang.RuntimeException
Unbalanced drawPending/pendingDrawFinished calls

at android.view.ViewRootImpl.pendingDrawFinished(ViewRootImpl.java:4087)
at android.view.ViewRootImpl.lambda$performDraw$1$ViewRootImpl(ViewRootImpl.java:4168)
at android.view.-$$Lambda$ViewRootImpl$7A_3tkr_Kw4TZAeIUGVlOoTcZhg.run(Unknown Source:4)
at android.os.Handler.handleCallback(Handler.java:900)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:219)
at android.os.HandlerThread.run(HandlerThread.java:67)

Relevant error log output

No response

Steps to reproduce

.1

Minimal reproduction project

No response

@finscn finscn added Bug Needs Triage Needs to be assigned by the team labels Nov 23, 2024
@finscn
Copy link
Contributor Author

finscn commented Nov 24, 2024

@longchuan 我知道getSharedPreferences是干啥的. 这个问题和 getSharedPreferences 无关. 是 context丢失引起的. 我在另一个issue里详细谈了这个问题.

@minggo
Copy link
Contributor

minggo commented Nov 25, 2024

从这些信息很难判断出是什么问题。context 丢失是怎么造成的呢?

@finscn
Copy link
Contributor Author

finscn commented Nov 25, 2024

从这些信息很难判断出是什么问题。context 丢失是怎么造成的呢?

@minggo

GlobalObject 中的 context 是一个 Activity 的context , 而不是 Application的context.
Activity的context在某些情况下会被销毁重建.
比如某些手机启动游戏之后,走第三方登录, 或者游戏过程中调用第三方支付, 此时游戏本体的Activity会被切到后台.

虽然 cocos的代码中, 会在Activity重建时,重新给 GlobalObject中的context赋值.
但是如果在 销毁之后,重建之前, 这段时间里, 执行了 代码所在的 run task , 就会出错.
所以建议在 CocosDownloader.java 中的 那段调用 getSharedPreferences() 方法的 run task 里, 对 context 做 null check.
或者在 Activity销毁后, 确保该 run task 不被执行.

@minggo minggo added Env: Native General issue on all native platforms including iOS, Android, MacOS, Windows Env: Android labels Nov 26, 2024
@minggo
Copy link
Contributor

minggo commented Nov 26, 2024

@finscn 我们会加更多的保护,不过对于 activity 被销毁重建的逻辑只能是游戏重新开始,没法继续。引擎目前对于重启也还做得不是很好,代码中会用到一些静态和全局变量,这些可能会导致问题。

@finscn
Copy link
Contributor Author

finscn commented Nov 26, 2024

@finscn 我们会加更多的保护,不过对于 activity 被销毁重建的逻辑只能是游戏重新开始,没法继续。引擎目前对于重启也还做得不是很好,代码中会用到一些静态和全局变量,这些可能会导致问题。

现在的情况是 我们游戏上线后 , 大概 2%的用户会有 闪退(有的是真闪退, 直接退到桌面, 有的其实是自动重启游戏)的情况. 这个闪退率挺高的.
我们自己开发环境没法重现 (我们自己手里有10多种不同的安卓手机) , TapTap后台上报的错误信息都很抽象, 我们现在也是没办法, 属于病急乱投医了.

另外 那个 android.view.InputDevice.getSources() 相关的 NullPointer 异常 都是 华为 MatePad 报的.
但是我们自己游戏肯定没有调用相关的逻辑, 我搜了cocos引擎, 也没有调用过 InputDevice.getSources. 真的是奇了怪了.
你们和华为有着密切合作, 不知道能否帮忙问问.

@minggo
Copy link
Contributor

minggo commented Nov 26, 2024

华为的问题我们可以问一下。

@finscn
Copy link
Contributor Author

finscn commented Nov 26, 2024

华为的问题我们可以问一下。

@minggo 还有个华为的问题, 大量华为设备出现崩溃错误.

部分出错设备:
image

错误信息如下.
image

此错误, 还有前面提到的 InputDevice.getSources 错误, 都是有且只有华为设备报错.

@finscn
Copy link
Contributor Author

finscn commented Nov 26, 2024

这个 libart.so 相关的错误 也是 崩溃的重灾区.

#00 0x00000000001a8c40 libart.so art::ArtMethod::PrettyMethod(bool)+92
    #01 0x00000000001a997c libart.so art::FindOatMethodFor(art::ArtMethod*, art::PointerSize, bool*) [clone .llvm.1419196823647339138]+552
    #02 0x00000000001a95f4 libart.so art::ArtMethod::GetOatQuickMethodHeader(unsigned long)+332
    #03 0x000000000057a87c libart.so void art::StackVisitor::WalkStack<(art::StackVisitor::CountTransitions)1>(bool)+552
    #04 0x00000000005af568 libart.so void art::Thread::VisitRoots<false>(art::RootVisitor*)+1288
    #05 0x0000000000267c54 libart.so art::gc::collector::ConcurrentCopying::ThreadFlipVisitor::Run(art::Thread*)+296
    #06 0x00000000005a46b4 libart.so art::Thread::FullSuspendCheck()+752
    #07 0x00000000001797c0 libart.so void art::interpreter::ExecuteSwitchImplCpp<true, false>(art::interpreter::SwitchImplContext*)+64892
    #08 0x000000000013f7dc libart.so ExecuteSwitchImplAsm+8
    #09 0x000000000030552c libart.so art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) [clone .llvm.2780203035745748192]+532
    #10 0x000000000030d2e0 libart.so art::interpreter::ArtInterpreterToInterpreterBridge(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame*, art::JValue*)+200
    #11 0x000000000030dc84 libart.so bool art::interpreter::DoCall<false, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+884
    #12 0x0000000000673d9c libart.so MterpInvokeStatic+536
    #13 0x000000000012d998 libart.so mterp_op_invoke_static+20
    #14 0x0000000000305424 libart.so art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) [clone .llvm.2780203035745748192]+268
    #15 0x000000000065fc14 libart.so artQuickToInterpreterBridge+760
    #16 0x000000000013cffc libart.so art_quick_to_interpreter_bridge+88
    #17 0x00000000001337ec libart.so art_quick_invoke_static_stub+568
    #18 0x00000000001a8a8c libart.so art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+228
    #19 0x000000000054aee4 libart.so art::JValue art::InvokeWithVarArgs<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, std::__va_list)+448
    #20 0x000000000054b3a0 libart.so art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)+92
    #21 0x00000000003c87dc libart.so art::JNI<false>::CallStaticIntMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)+636
    #22 0x00000000000801b8 libsandbox_ext.so
    #23 0x0000000000080120 libsandbox_ext.so
    #24 0x000000000059f134 boot-framework.oat android.content.ContentProvider.enforceReadPermissionInner+112
    #25 0x00000000005fde28 boot-framework.oat android.content.ContentProvider$Transport.enforceReadPermission+196
    #26 0x00000000006004e4 boot-framework.oat android.content.ContentProvider$Transport.query+672
    #27 0x0000000000487ed8 boot-framework.oat android.content.ContentResolver.query+868
    #28 0x00000000004885e0 boot-framework.oat android.content.ContentResolver.query+124
    #29 0x0000000000133568 libart.so art_quick_invoke_stub+548
    #30 0x00000000001a8a70 libart.so art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200
    #31 0x0000000000317bdc libart.so art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+376
    #32 0x000000000030ecfc libart.so bool art::interpreter::DoCall<true, false>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, art::JValue*)+696
    #33 0x00000000006747bc libart.so MterpInvokeVirtualRange+704
    #34 0x000000000012db18 libart.so mterp_op_invoke_virtual_range+20
    #35 0x00000000006771ac libart.so MterpInvokeStaticRange+820
    #36 0x000000000012dc98 libart.so mterp_op_invoke_static_range+20
    #37 0x0000000000674964 libart.so MterpInvokeVirtualRange+1128
    #38 0x000000000012db18 libart.so mterp_op_invoke_virtual_range+20
    #39 0x00000000006771ac libart.so MterpInvokeStaticRange+820
    #40 0x000000000012dc98 libart.so mterp_op_invoke_static_range+20
    #41 0x00000000006771ac libart.so MterpInvokeStaticRange+820
    #42 0x000000000012dc98 libart.so mterp_op_invoke_static_range+20
    #43 0x0000000000675fbc libart.so MterpInvokeInterfaceRange+1396
    #44 0x000000000012dd18 libart.so mterp_op_invoke_interface_range+20
    #45 0x0000000000671110 libart.so MterpInvokeVirtual+1512
    #46 0x000000000012d818 libart.so mterp_op_invoke_virtual+20
    #47 0x000000000067376c libart.so MterpInvokeDirect+1240
    #48 0x000000000012d918 libart.so mterp_op_invoke_direct+20
    #49 0x0000000000676a90 libart.so MterpInvokeDirectRange+844
    #50 0x000000000012dc18 libart.so mterp_op_invoke_direct_range+20
    #51 0x0000000000676a90 libart.so MterpInvokeDirectRange+844
    #52 0x000000000012dc18 libart.so mterp_op_invoke_direct_range+20
    #53 0x00000000006771ac libart.so MterpInvokeStaticRange+820
    #54 0x000000000012dc98 libart.so mterp_op_invoke_static_range+20
    #55 0x0000000000672b30 libart.so MterpInvokeInterface+1796
    #56 0x000000000012da18 libart.so mterp_op_invoke_interface+20
    #57 0x0000000000305424 libart.so art::interpreter::Execute(art::Thread*, art::CodeItemDataAccessor const&, art::ShadowFrame&, art::JValue, bool, bool) [clone .llvm.2780203035745748192]+268
    #58 0x000000000065fc14 libart.so artQuickToInterpreterBridge+760
    #59 0x000000000013cffc libart.so art_quick_to_interpreter_bridge+88
    #60 0x00000000002a080c boot.oat java.util.concurrent.ThreadPoolExecutor.runWorker+984
    #61 0x000000000029da04 boot.oat java.util.concurrent.ThreadPoolExecutor$Worker.run+64
    #62 0x000000000015c35c boot.oat java.lang.Thread.run+72
    #63 0x0000000000133568 libart.so art_quick_invoke_stub+548
    #64 0x00000000001a8a70 libart.so art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+200
    #65 0x000000000054ba24 libart.so art::JValue art::InvokeVirtualOrInterfaceWithJValues<art::ArtMethod*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, art::ArtMethod*, jvalue const*)+460
    #66 0x000000000059a184 libart.so art::Thread::CreateCallback(void*)+1288
    #67 0x00000000000eb50c libc.so __pthread_start(void*)+64
    #68 0x000000000008bb10 libc.so __start_thread+64

@zhitaocai
Copy link

从这些信息很难判断出是什么问题。context 丢失是怎么造成的呢?

@minggo

GlobalObject 中的 context 是一个 Activity 的context , 而不是 Application的context. Activity的context在某些情况下会被销毁重建. 比如某些手机启动游戏之后,走第三方登录, 或者游戏过程中调用第三方支付, 此时游戏本体的Activity会被切到后台.

虽然 cocos的代码中, 会在Activity重建时,重新给 GlobalObject中的context赋值. 但是如果在 销毁之后,重建之前, 这段时间里, 执行了 代码所在的 run task , 就会出错. 所以建议在 CocosDownloader.java 中的 那段调用 getSharedPreferences() 方法的 run task 里, 对 context 做 null check. 或者在 Activity销毁后, 确保该 run task 不被执行.

或者一种最快见效的方法:直接将 GlobalObject 的 sContext 从 Activity 转换为 Application 的上下文

QQ_1732629354617

这样子即便是 Activity 退居后台,由于系统内存不足而被销毁,但依旧不影响 OKHttp 创建的下载任务继续在执行,并且此时依旧能通过 Application 的上下文打开 SharedPreferences

@finscn
Copy link
Contributor Author

finscn commented Nov 27, 2024

从这些信息很难判断出是什么问题。context 丢失是怎么造成的呢?

@minggo
GlobalObject 中的 context 是一个 Activity 的context , 而不是 Application的context. Activity的context在某些情况下会被销毁重建. 比如某些手机启动游戏之后,走第三方登录, 或者游戏过程中调用第三方支付, 此时游戏本体的Activity会被切到后台.
虽然 cocos的代码中, 会在Activity重建时,重新给 GlobalObject中的context赋值. 但是如果在 销毁之后,重建之前, 这段时间里, 执行了 代码所在的 run task , 就会出错. 所以建议在 CocosDownloader.java 中的 那段调用 getSharedPreferences() 方法的 run task 里, 对 context 做 null check. 或者在 Activity销毁后, 确保该 run task 不被执行.

或者一种最快见效的方法:直接将 GlobalObject 的 sContext 从 Activity 转换为 Application 的上下文

QQ_1732629354617

这样子即便是 Activity 退居后台,由于系统内存不足而被销毁,但依旧不影响 OKHttp 创建的下载任务继续在执行,并且此时依旧能通过 Application 的上下文打开 SharedPreferences

我咨询了一些安卓开发的朋友, 他们也给出了类似的建议. 但是这个地方涉及到深度修改 cocos代码, 我没这个能力. 也不敢轻易改.
cocos用 Activity 可能有它自己的理由吧.

@minggo , 我们自己用自定义原生引擎, 做了 #17911 这个修改, CocosDownloader相关的 null pointer 错误几乎消失了.
但是我不确定玩家那边是否能正常游戏, 有时候不报错 不意味着正确.

@zhitaocai
Copy link

zhitaocai commented Nov 27, 2024

我看 GlobalObject.init(Context context, Activity activity) 调用的地方,传入的 context 和 activity 都是同一个 activity 对象

而 GlobalObject 本来提供了 getContextgetActivity 方法,明显是要区分 Context 和 Activity 的,但是目前其实都是返回 Activity

我感觉这看起来更多是写错之类,一般来说这里的 getContext 应该就是要返回 Application 的上下文

QQ_1732673777961

不过最终还是看引擎组同学怎么看吧

我是觉得这里就是写错了,init 的时候, sContext 应该就是要取 Application 的 Context,这样子,GlobalObject.getContext 才好返回 Application 的 Context

@MrKylinGithub
Copy link

自己工作遇到的问题,然后拿出来给别人解决,好像别人应该给你打工一样。

谢谢 @longchuan 同学在各个issue里的热心回答,为我们引擎组分担了不少压力。 但还请保持冷静,不要过于激动,哈哈哈!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Env: Android Env: Native General issue on all native platforms including iOS, Android, MacOS, Windows Needs Triage Needs to be assigned by the team
Projects
None yet
Development

No branches or pull requests

5 participants