繁体   English   中英

微调器上 findViewById 的 Android NullPointerException

[英]Android NullPointerException for findViewById on spinner

我有一个 android 应用程序,它已在 Google Play 商店发布了一段时间。 它对 99.9% 的用户运行良好。 但是,Google Play 控制台告诉我,一些用户正在遭受由活动的 onCreate 例程中的 NullPointerException 引起的崩溃。 有问题的特定行是:

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

该例程似乎永远不会因例程中对findViewById的任何其他调用而崩溃,只是微调器的调用(而且很少)。 下面列出了我的布局文件,尽管我怀疑它是否相关。 恐怕我没有任何 logcat output,因为 Play 控制台不提供。

当该活动启动时,从活动内调用onCreate例程。 我在下面包含了完整的onCreate例程,但微调器的发现还很早。

有任何想法吗? 谢谢

<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() 是您要膨胀视图和/或初始化其他对象的地方。 我建议这样做:

  1. 为 choose_music 创建一个 class 变量
  2. 使用 findViewById() 在 onCreate() 中初始化对变量的引用
  3. 将其余代码移入 onStart() - 这可以确保视图全部膨胀,因为我相信视图可能在 onCreate() 生命周期中仍在呈现,这就是某些用户获得 NPE 的原因。 此外,使用 findViewById() 是可以为空的,所以我仍然会使用一些条件检查来包装所有逻辑,以确保调用它是安全的。

替代解决方案,使用 DataBinding,因为它是 null 安全的。

在那条线上使用 NPE 崩溃是没有意义的。 如果findViewById()在 NPE-worthy state 中(就像在活动附加到Window之前调用该方法),它上面会有 NPE 已经有很多行。

我怀疑行号报告略微偏离(它发生了),实际上是您的globals.music_track_list在某些情况下是null ,并且尝试读取它的length是这里的 NPE。

我还鼓励您使用比 Play 控制台更通用的崩溃报告服务。 例如,Crashlytics。 Play 控制台的问题之一是未报告异常消息和嵌套异常。

暂无
暂无

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

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