简体   繁体   English

微调器上 findViewById 的 Android NullPointerException

[英]Android NullPointerException for findViewById on spinner

I have an android app which has been released on the Google Play store for some time.我有一个 android 应用程序,它已在 Google Play 商店发布了一段时间。 It runs fine for 99.9% of users.它对 99.9% 的用户运行良好。 However, the Google Play Console tells me that a few users are suffering from a crash caused by a NullPointerException in the onCreate routine for an activity.但是,Google Play 控制台告诉我,一些用户正在遭受由活动的 onCreate 例程中的 NullPointerException 引起的崩溃。 The particular line in question is:有问题的特定行是:

final Spinner choose_music = findViewById(R.id.music_list);

The routine never seems to crash with any other call to findViewById in the routine, just the one for the spinner (and only very rarely).该例程似乎永远不会因例程中对findViewById的任何其他调用而崩溃,只是微调器的调用(而且很少)。 My layout file is listed below, although I doubt that it's relevant.下面列出了我的布局文件,尽管我怀疑它是否相关。 I'm afraid I don't have any logcat output, since the Play Console doesn't provide that.恐怕我没有任何 logcat output,因为 Play 控制台不提供。

The onCreate routine is called from within an activity, when that activity launches.当该活动启动时,从活动内调用onCreate例程。 I've included the complete onCreate routine below, but the finding of the spinner is fairly early on.我在下面包含了完整的onCreate例程,但微调器的发现还很早。

Any ideas?有任何想法吗? Thanks谢谢

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:background="@color/light_grey"
    tools:context=".PlayActivity" >

<RelativeLayout
    android:id="@+id/main_canvas"
    android:layout_weight="90"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorAccent">
</RelativeLayout>

<RelativeLayout
    android:id="@+id/bottom_panel"
    android:layout_weight="90"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <RelativeLayout
        android:id="@+id/layout_choose"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/layout_volume"
        android:layout_alignBottom="@id/layout_volume"
        android:background="@color/mid_grey"
        android:layout_alignParentLeft="true">
        <TextView
            android:id="@+id/choose_music"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:text="Music"
            android:padding="5sp"
            android:textSize="20sp"/>
        <Spinner
            android:id="@+id/music_list"
            android:layout_alignParentLeft="true"
            android:layout_width="wrap_content"
            android:layout_below="@id/choose_music"
            android:layout_height="wrap_content"></Spinner>
    </RelativeLayout>
    <TextView
        android:id="@+id/padding_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/layout_choose"
        android:text="."
        android:textColor="@color/light_grey"
        android:padding="2sp"
        android:textSize="20sp"/>
    <RelativeLayout
        android:id="@+id/layout_volume"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@color/mid_grey"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@id/padding_text">
        <TextView
            android:id="@+id/choose_volume"
            android:layout_width="match_parent"
            android:gravity="center"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_alignParentRight="true"
            android:text="Relative volume"
            android:padding="0sp"
            android:textSize="20sp"/>
        <TextView
            android:id="@+id/volume_music"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/choose_volume"
            android:layout_alignParentLeft="true"
            android:text="Music"
            android:padding="5sp"
            android:textSize="20sp"/>
        <TextView
            android:id="@+id/volume_speech"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/choose_volume"
            android:layout_alignParentRight="true"
            android:text="Speech"
            android:padding="5sp"
            android:textSize="20sp"/>
        <android.support.v7.widget.AppCompatSeekBar
            android:id="@+id/relative_volume"
            android:padding="10sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_below="@id/volume_speech"/>
    </RelativeLayout>
</RelativeLayout>

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_play);
    my_view = (RelativeLayout) findViewById(R.id.main_canvas);
    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    int height = displayMetrics.heightPixels;
    int width = displayMetrics.widthPixels;
    view = new CircularMusicController(PlayActivity.this, height, width);
    my_view.addView(view, new ActionBar.LayoutParams(
            RelativeLayout.LayoutParams.WRAP_CONTENT,
            RelativeLayout.LayoutParams.WRAP_CONTENT));
    globals.crash_log("Layout dimensions before: " + String.valueOf(my_view.getWidth() + ", "
            + String.valueOf(my_view.getHeight())));
    my_view.setLayoutParams(new LinearLayout.LayoutParams(view.get_width(), view.get_height()));

    // Get ListView object from xml
    final Spinner choose_music = findViewById(R.id.music_list);

    int music_index = 0;
    for (int i=0; i<globals.music_track_list.length; i+=1) {
        if (globals.appState.get_music().equals(globals.music_track_list[i])) {
            music_index = i;
        }
    }
    chosen_music = music_index;
    // ListView Item Click Listener
    ArrayAdapter<CharSequence> music_adapter = new ArrayAdapter<CharSequence>(this, R.layout.activity_listview, globals.music_names);
    choose_music.setAdapter(music_adapter);
    choose_music.setSelection(music_index);
    choose_music.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
            globals.crash_log("I have chosen " + String.valueOf(i));
            if (chosen_music > 0 && musicBound && musicSrv != null) {
                musicSrv.halt();
            }
            chosen_music = i;
            if (musicBound) {
                if (i > 0) {
                    musicSrv.setSong(globals.music_locs[i]);
                    if (isPlaying()) {
                        musicSrv.playSong(isPlaying());
                    } else {
                        choice_while_paused = true;
                    }
                }
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> adapterView) {
        }
    });

    globals.crash_log("Layout dimensions: " + String.valueOf(my_view.getWidth() + ", "
            + String.valueOf(my_view.getHeight())));

    volume_control = findViewById(R.id.relative_volume);
    volume_control.setProgress(globals.appState.get_volume());
    volume_control.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int volume, boolean from_user) {
            if (from_user) {
                int focus_vol = globals.roc_function(volume);
                try {
                    if (speechSrv != null) {
                        speechSrv.set_volume(focus_vol);
                    }
                    if (musicSrv != null) {
                        musicSrv.set_volume(100 - focus_vol);
                    }
                } catch (Exception err) {
                    globals.crash_log("Failed to set volume");
                    globals.crash_exception(err);
                }
            }
            globals.crash_log("SeekBar: new volume is " + String.valueOf(volume) + "   " +
                    String.valueOf(from_user));
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    });

    // Get any information passed down from the parent activity
    Intent intent = getIntent();
    track_name = intent.getStringExtra("name");
    track_location = intent.getStringExtra("location");
    res_location = intent.getIntExtra("res_location", 0);
    activity_name = intent.getStringExtra("activity_name");
    globals.crash_log("PlayActivity: onCreate: activity_name is " + activity_name);
    globals.crash_log("PlayActivity: onCreate: track_name is " + track_name);
    if ( ! (track_location == null) ) {
        globals.crash_log("PlayActivity: onCreate: track_loc is " + track_location);
    }
    globals.crash_log("PlayActivity: onCreate: res_location is "+String.valueOf(res_location));

    // Setup the action bar, and start the broadcast receiver.
    ActionBar action_bar = getSupportActionBar();
    action_bar.setTitle(track_name);
    action_bar.setDisplayHomeAsUpEnabled(true);

    IntentFilter filter = new IntentFilter("PLAY_ACTIVITY");
    this.registerReceiver(_refreshReceiver, filter);
}

onCreate() is where you want to inflate views and/or initialize other objects. onCreate() 是您要膨胀视图和/或初始化其他对象的地方。 I would recommend doing this:我建议这样做:

  1. Create a class variable for choose_music为 choose_music 创建一个 class 变量
  2. initialize the reference to the variable in onCreate() with findViewById()使用 findViewById() 在 onCreate() 中初始化对变量的引用
  3. Move the remainder of the code into onStart() - this ensures that the views are all inflated, because I believe it's possible that the views are still rendering while in the onCreate() lifecycle and that's why some users are getting NPE.将其余代码移入 onStart() - 这可以确保视图全部膨胀,因为我相信视图可能在 onCreate() 生命周期中仍在呈现,这就是某些用户获得 NPE 的原因。 Also, using findViewById() is nullable, so I would still wrap all logic with some conditional checks to ensure it is safe to call.此外,使用 findViewById() 是可以为空的,所以我仍然会使用一些条件检查来包装所有逻辑,以确保调用它是安全的。

Alternative solution, use DataBinding as that is null-safe.替代解决方案,使用 DataBinding,因为它是 null 安全的。

Crashing with NPE on that line does not make sense.在那条线上使用 NPE 崩溃是没有意义的。 If findViewById() was in a NPE-worthy state (like calling the method before the activity was attached to a Window ), it would have NPE'd already many lines above it.如果findViewById()在 NPE-worthy state 中(就像在活动附加到Window之前调用该方法),它上面会有 NPE 已经有很多行。

I suspect the line number reporting is slightly off (it happens) and it is in fact your globals.music_track_list that is null in some scenarios, and attempting to read its length is the NPE here.我怀疑行号报告略微偏离(它发生了),实际上是您的globals.music_track_list在某些情况下是null ,并且尝试读取它的length是这里的 NPE。

I also encourage you to use a more versatile crash reporting service than Play console.我还鼓励您使用比 Play 控制台更通用的崩溃报告服务。 For example, Crashlytics.例如,Crashlytics。 One of the issues with Play console is that exception messages and nested exceptions are not reported. Play 控制台的问题之一是未报告异常消息和嵌套异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM