Android Fragments Fragment中的GlSurfaceView在恢复时冻结UI

bttbmeg0  于 12个月前  发布在  Android
关注(0)|答案(1)|浏览(167)

我有一个片段带有一个GlSurfaceView和一个按钮,可以导航到第二个片段。第二个片段也有一个GlSurfaceView和一个按钮,可以让你导航回第一个片段。
两个GlSurfaceView都使用相同类型的渲染器。这是一个简单的渲染器,只是改变背景颜色。
我的问题是,如果我在第二个片段上切换到另一个应用程序,然后切换回来,整个UI将冻结。这不会发生在第一个片段上,尽管它与第二个片段几乎相同。
为什么会发生这种情况,我能做些什么呢?我猜这是Android中的一个bug。这个bug只发生在Android 13(SDK 33)中,它在Android 12和14中工作正常。
我的渲染器在每一帧上清除帧500000次。如果我只清除一次帧,错误就不会发生。此外,如果我导航到第二帧而没有动画,它似乎也工作得很好。
这里有一个完整的示例项目:https://github.com/pekspro/GLSurfaceViewFreezeSample
这是渲染器的代码:

public final class OpenGLRenderer implements Renderer
{
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
    }

    public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
    }

    float color = 0.2f;

    public void onDrawFrame(GL10 gl) {
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);

        color += 0.01f;

        if (color > 0.7) {
            color = 0.02f;
        }

        for(int i = 0; i < 500000; i++) {
            gl.glClearColor(color * 0.8f, 0.0f, color, 1.0f);
        }
    }
}

字符串
这是第一个片段的代码:

public class FirstFragment extends Fragment {

    private FragmentFirstBinding binding;

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {
        binding = FragmentFirstBinding.inflate(inflater, container, false);

        return binding.getRoot();
    }

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        binding.OpenGlGameGraphics.setRenderer(new OpenGLRenderer());

        binding.buttonFirstWithAnimation.setOnClickListener(view1 -> {
            FragmentManager manager = getParentFragmentManager();

            FragmentTransaction ft = manager.beginTransaction();
            ft. Replace(R.id.FragmentMain, new SecondFragment());
            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
            ft.addToBackStack(null);

            ft. Commit();
        });

        binding.buttonFirstNoAnimation.setOnClickListener(view1 -> {
            FragmentManager manager = getParentFragmentManager();

            FragmentTransaction ft = manager.beginTransaction();
            ft. Replace(R.id.FragmentMain, new SecondFragment());
            ft.addToBackStack(null);

            ft. Commit();
        });
    }

    @Override
    public void onResume()
    {
        super.onResume();

        binding.OpenGlGameGraphics.onResume();
    }

    @Override
    public void onPause()
    {
        super.onPause();

        binding.OpenGlGameGraphics.onPause();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}


这是第二个片段的代码:

public class SecondFragment extends Fragment {

    private FragmentSecondBinding binding;

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState
    ) {

        binding = FragmentSecondBinding.inflate(inflater, container, false);

        return binding.getRoot();

    }

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        binding.OpenGlGameGraphics.setRenderer(new OpenGLRenderer());

        binding.buttonSecond.setOnClickListener(view1 -> {
            FragmentManager manager = getParentFragmentManager();
            manager.popBackStack();
        });
    }

    @Override
    public void onResume()
    {
        super.onResume();

        binding.OpenGlGameGraphics.onResume();
    }

    @Override
    public void onPause()
    {
        super.onPause();

        binding.OpenGlGameGraphics.onPause();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        binding = null;
    }
}

wpcxdonn

wpcxdonn1#

有一件事似乎是有效的,那就是在暂停时删除GlSurfaceView,然后延迟一段时间,在恢复时添加它。首先,将GlSurfaceView放入容器中:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">

    <FrameLayout
        android:id="@+id/OpenGlContainer"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <android.opengl.GLSurfaceView
            android:id="@+id/OpenGlGameGraphics"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
    </FrameLayout>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android: orientation="vertical">

        <Button
            android:id="@+id/button_second"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/previous"
            />

    </androidx.appcompat.widget.LinearLayoutCompat>
</FrameLayout>

字符串
然后像这样更新逻辑:

@Override
public void onResume() 
{
    super.onResume();

    if (binding.OpenGlContainer.getChildCount() == 0) {
        new Handler().postDelayed(() -> {
            binding.OpenGlContainer.addView(binding.OpenGlGameGraphics);
        }, 0);
    }

    binding.OpenGlGameGraphics.onResume();
}

@Override
public void onPause() {
    super.onPause();

    binding.OpenGlGameGraphics.onPause();
    binding.OpenGlContainer.removeView(binding.OpenGlGameGraphics);
}


我不明白为什么要这么做。但似乎很有效。
更多的人遇到了这个问题:
https://issuetracker.google.com/issues/269158607https://issuetracker.google.com/issues/263307511的数据库

相关问题