【问题标题】:Calling ViewGroup#addView and ViewGroup#removeView causes NullPointerException调用 ViewGroup#addView 和 ViewGroup#removeView 导致 NullPointerException
【发布时间】:2023-03-11 21:58:01
【问题描述】:

在方法内部,我调用addViewremoveView如下:

public class EnclosingClass extends FrameLayout {
    ...
    void fooMethod() {
        ...
        viewGroup1.addView(this);
        viewGroup2.removeView(this);
        ...
    }
    ...
}

这在纵向模式下运行良好(它被多次调用,没有任何问题),但是一旦我将屏幕的方向更改为横向,我就会得到一个 NullPointerException:(仅供参考,fooMethod 正在被调用来自surfaceChanged 在方向变化期间):

 18376         AndroidRuntime  E  FATAL EXCEPTION: main
 18376         AndroidRuntime  E  Process: com.google.android.apps.chrome, PID: 18376
 18376         AndroidRuntime  E  java.lang.NullPointerException
 18376         AndroidRuntime  E  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:405)
 18376         AndroidRuntime  E  at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 18376         AndroidRuntime  E  at android.view.View.layout(View.java:14817)
 18376         AndroidRuntime  E  at android.view.ViewGroup.layout(ViewGroup.java:4631)
 18376         AndroidRuntime  E  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 18376         AndroidRuntime  E  at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 18376         AndroidRuntime  E  at android.view.View.layout(View.java:14817)
 18376         AndroidRuntime  E  at android.view.ViewGroup.layout(ViewGroup.java:4631)
 18376         AndroidRuntime  E  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
 18376         AndroidRuntime  E  at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
 18376         AndroidRuntime  E  at android.view.View.layout(View.java:14817)
 18376         AndroidRuntime  E  at android.view.ViewGroup.layout(ViewGroup.java:4631)
 18376         AndroidRuntime  E  at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1987)
 18376         AndroidRuntime  E  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1744)
 18376         AndroidRuntime  E  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
 18376         AndroidRuntime  E  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
 18376         AndroidRuntime  E  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
 18376         AndroidRuntime  E  at android.view.Choreographer.doCallbacks(Choreographer.java:574)
 18376         AndroidRuntime  E  at android.view.Choreographer.doFrame(Choreographer.java:544)
 18376         AndroidRuntime  E  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
 18376         AndroidRuntime  E  at android.os.Handler.handleCallback(Handler.java:733)
 18376         AndroidRuntime  E  at android.os.Handler.dispatchMessage(Handler.java:95)
 18376         AndroidRuntime  E  at android.os.Looper.loop(Looper.java:136)
 18376         AndroidRuntime  E  at android.app.ActivityThread.main(ActivityThread.java:5017)
 18376         AndroidRuntime  E  at java.lang.reflect.Method.invokeNative(Native Method)
 18376         AndroidRuntime  E  at java.lang.reflect.Method.invoke(Method.java:515)
 18376         AndroidRuntime  E  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
 18376         AndroidRuntime  E  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
 18376         AndroidRuntime  E  at dalvik.system.NativeStart.main(Native Method)

为了解决这个问题,我把addViewremoveView放在Runnable里面如下:

void fooMethod() {
    ...
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            viewGroup1.addView(EnclosingClass.this);
            viewGroup2.removeView(EnclosingClass.this);
    });
    ...
}

这确实解决了问题,一切似乎都运行良好,但我不明白为什么。将方法放入消息队列中如何帮助解决问题?另外,为什么我在堆栈跟踪中看不到对我的代码的任何引用?逐渐注释掉代码发现了问题。

【问题讨论】:

    标签: android android-view android-framelayout


    【解决方案1】:
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }`enter code here`
    }
    

    因为你在真正的 UIThread 上使用 runOnUiThread,所以代码在 else 分支上运行:action.run() 这是一个串行操作。如果其他 api 仍然在具有本地 mChildCount 的 UI Thread 上运行,则会遇到 NULL 指针,(UI线程不安全)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多