E AndroidRuntime: FATAL EXCEPTION: main E AndroidRuntime: Process: me.hjhl.app, PID: 10740 E AndroidRuntime: java.lang.NullPointerException: Missing required view with ID: me.hjhl.app:id/my_gl_surface_view E AndroidRuntime: at me.hjhl.app.databinding.FragmentGlesDemoBinding.bind(FragmentGlesDemoBinding.java:67) E AndroidRuntime: at me.hjhl.app.databinding.FragmentGlesDemoBinding.inflate(FragmentGlesDemoBinding.java:49) E AndroidRuntime: at me.hjhl.app.demo.GLESDemoFragment.onCreateView(GLESDemoFragment.kt:31) E AndroidRuntime: at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2995) E AndroidRuntime: at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:523) E AndroidRuntime: at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) E AndroidRuntime: at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1840) E AndroidRuntime: at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1764) E AndroidRuntime: at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1701) E AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2849) E AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2784) E AndroidRuntime: at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:262) E AndroidRuntime: at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:478) E AndroidRuntime: at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246) E AndroidRuntime: at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1433) E AndroidRuntime: at android.app.Activity.performStart(Activity.java:7923) E AndroidRuntime: at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3337) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2049) E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107) E AndroidRuntime: at android.os.Looper.loop(Looper.java:228) E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7589) E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)
整体代码逻辑大致是:Activity 创建时跳转到一个 Fragment,这个 Fragment 对应的 XML 里很简单——FrameLayout 里套了一个自定义 View。错误堆栈提示找不到资源 id my_gl_surface_view。这让我不禁怀疑是不是自定义 View 导致的。
二、分析过程
2.1 找到 ViewBinding 生成的 UI 类
ViewBinding 的大致原理是在编译时,把开启了 ViewBinding 功能的布局资源 XML 生成对应的 Java 类。例如在这个 case 中,对应的类文件位置为:app/build/generated/data_binding_base_class_source_out/debug/out/me/ljh/app/databinding/FragmentGlesDemoBinding.java。
// file: app/build/generated/data_binding_base_class_source_out/debug/out/me/ljh/app/databinding/FragmentGlesDemoBinding.java publicfinalclassFragmentGlesDemoBindingimplementsViewBinding { @NonNull publicstatic FragmentGlesDemoBinding bind(@NonNull View rootView) { // The body of this method is generated in a way you would not otherwise write. // This is done to optimize the compiled bytecode for size and performance. int id; missingId: { id = R.id.my_gl_surface_view; MyGLSurfaceViewmyGlSurfaceView= ViewBindings.findChildViewById(rootView, id); if (myGlSurfaceView == null) { // 无法找到 view,则 break 准备抛出异常 break missingId; } // 递归处理 R.id.my_gl_surface_view 的子 view returnnewFragmentGlesDemoBinding((FrameLayout) rootView, myGlSurfaceView); } StringmissingId= rootView.getResources().getResourceName(id); thrownewNullPointerException("Missing required view with ID: ".concat(missingId)); } }
E AndroidRuntime: FATAL EXCEPTION: main E AndroidRuntime: Process: me.ljh.app, PID: 16008 E AndroidRuntime: android.view.InflateException: Binary XML file line #13 in me.ljh.app:layout/fragment_gles_demo: Binary XML file line #13 in me.ljh.app:layout/fragment_gles_demo: Error inflating class me.ljh.app.widget.MyGLSurfaceView E AndroidRuntime: Caused by: android.view.InflateException: Binary XML file line #13 in me.ljh.app:layout/fragment_gles_demo: Error inflating class me.ljh.app.widget.MyGLSurfaceView E AndroidRuntime: Caused by: java.lang.NoSuchMethodException: me.ljh.app.widget.MyGLSurfaceView.<init> [class android.content.Context, interface android.util.AttributeSet] E AndroidRuntime: at java.lang.Class.getConstructor0(Class.java:2332) E AndroidRuntime: at java.lang.Class.getConstructor(Class.java:1728) E AndroidRuntime: at android.view.LayoutInflater.createView(LayoutInflater.java:828) E AndroidRuntime: at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1010) E AndroidRuntime: at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:965) E AndroidRuntime: at android.view.LayoutInflater.rInflate(LayoutInflater.java:1127) E AndroidRuntime: at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1088) E AndroidRuntime: at android.view.LayoutInflater.inflate(LayoutInflater.java:686) E AndroidRuntime: at android.view.LayoutInflater.inflate(LayoutInflater.java:538) E AndroidRuntime: at me.ljh.app.demo.GLESDemoFragment.onCreateView(GLESDemoFragment.kt:34) E AndroidRuntime: at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2995) E AndroidRuntime: at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:523) E AndroidRuntime: at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) E AndroidRuntime: at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1840) E AndroidRuntime: at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1764) E AndroidRuntime: at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1701) E AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2849) E AndroidRuntime: at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2784) E AndroidRuntime: at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:262) E AndroidRuntime: at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:478) E AndroidRuntime: at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246) E AndroidRuntime: at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1433) E AndroidRuntime: at android.app.Activity.performStart(Activity.java:7923) E AndroidRuntime: at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3337) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2049) E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:107) E AndroidRuntime: at android.os.Looper.loop(Looper.java:228) E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7589) E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:953)
/** * A collection of attributes, as found associated with a tag in an XML * document. Often you will not want to use this interface directly, instead * passing it to {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int) * Resources.Theme.obtainStyledAttributes()} * which will take care of parsing the attributes for you. In particular, * the Resources API will convert resource references (attribute values such as * "@string/my_label" in the original XML) to the desired type * for you; if you use AttributeSet directly then you will need to manually * check for resource references * (with {@link #getAttributeResourceValue(int, int)}) and do the resource * lookup yourself if needed. Direct use of AttributeSet also prevents the * application of themes and styles when retrieving attribute values. * * <p>This interface provides an efficient mechanism for retrieving * data from compiled XML files, which can be retrieved for a particular * XmlPullParser through {@link Xml#asAttributeSet * Xml.asAttributeSet()}. Normally this will return an implementation * of the interface that works on top of a generic XmlPullParser, however it * is more useful in conjunction with compiled XML resources: * * <pre> * XmlPullParser parser = resources.getXml(myResource); * AttributeSet attributes = Xml.asAttributeSet(parser);</pre> * * <p>The implementation returned here, unlike using * the implementation on top of a generic XmlPullParser, * is highly optimized by retrieving pre-computed information that was * generated by aapt when compiling your resources. For example, * the {@link #getAttributeFloatValue(int, float)} method returns a floating * point number previous stored in the compiled resource instead of parsing * at runtime the string originally in the XML file. * * <p>This interface also provides additional information contained in the * compiled XML resource that is not available in a normal XML file, such * as {@link #getAttributeNameResource(int)} which returns the resource * identifier associated with a particular XML attribute name. * * @see XmlPullParser */
一大串有点长,总结就是:这是 XML 里面,对一个 View 配置的属性集合。比如android:id="@+id/my_gl_surface_view"、android:layout_width="match_parent"、android:layout_height="match_parent",它们会被 XML Parser 解析成一个个 Java 对象,AttributeSet 就是这些对象的集合。
这就回答了前面的 1、2、3、4 个问题:
这个参数,用于 XML 转换为 Java 对象时,传递 XML 中关于 view 实例的描述信息,包括它的id、宽、高……