java - 微调器上 findViewById 的 Android NullPointerException
问题描述
我有一个 Android 应用程序已在 Google Play 商店发布了一段时间。它对 99.9% 的用户运行良好。但是,Google Play 控制台告诉我,一些用户正在遭受由活动的 onCreate 例程中的 NullPointerException 引起的崩溃。有问题的特定行是:
final Spinner choose_music = findViewById(R.id.music_list);
该例程似乎永远不会因例程findViewById
中的任何其他调用而崩溃,只是微调器的调用(而且很少)。下面列出了我的布局文件,尽管我怀疑它是否相关。恐怕我没有任何 logcat 输出,因为 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() 是您要膨胀视图和/或初始化其他对象的地方。我建议这样做:
- 为 choose_music 创建一个类变量
- 使用 findViewById() 在 onCreate() 中初始化对变量的引用
- 将剩余的代码移到 onStart() 中——这可以确保视图都被夸大了,因为我相信视图可能在 onCreate() 生命周期中仍在渲染,这就是一些用户获得 NPE 的原因。此外,使用 findViewById() 是可以为空的,所以我仍然会使用一些条件检查来包装所有逻辑,以确保调用它是安全的。
替代解决方案,使用 DataBinding,因为它是 null 安全的。
推荐阅读
- php - 如何计算带有尾随逗号的文本中所有输入值的总和?
- awk - 增加块数据集中的特定列值并循环多次
- c - 使用宏值初始化 C 结构
- javascript - 如何使用chart.js在没有绘图点的图表上绘制一条线
- java - 为什么我的一些 Java 代码被忽略了?问题:java.lang.ArrayIndexOutOfBoundsException
- java - 使用数据库值设置 TextView
- google-apps-script - 如何在带有独立脚本的 Google 文档上使用“getBody()”?
- javascript - Chrome 扩展 - 添加到 gmail 撰写页面的元素未显示
- c# - 以弧度旋转矢量,同时保持幅度
- javascript - 为什么“instanceof”会产生不同的结果?