Android Vitals报告中的Google Play控制台为我提供了此信息
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2955)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3030)
at android.app.ActivityThread.-wrap11 (Unknown Source)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1696)
at android.os.Handler.dispatchMessage (Handler.java:105)
at android.os.Looper.loop (Looper.java:164)
at android.app.ActivityThread.main (ActivityThread.java:6938)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run (Zygote.java:327)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1374)
Caused by: java.lang.NullPointerException:
at com.golendukhin.whitenoise.GridAdapter.getCount (GridAdapter.java:52)
at android.widget.GridView.setAdapter (GridView.java:243)
at com.golendukhin.whitenoise.FoldersGridActivity.onCreate (FoldersGridActivity.java:60)
at android.app.Activity.performCreate (Activity.java:7183)
at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1220)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2908)
据我所知,这是因为网格适配器的getCount方法返回空值。提示,网格适配器不起作用。诚然,没有创造出全部的狂热。只有三星设备才有这样的问题。
首先,我尝试创建具有相同Android版本,RAM和ARM的相同仿真设备。仿真器的工作原理绝对出色。没错。
然后我添加了一些测试,以确保getCount可以正常工作并创建活动。
@RunWith(AndroidJUnit4.class)
public class GridViewTest {
@Rule
public ActivityTestRule<FoldersGridActivity> foldersGridActivityTestRule = new ActivityTestRule<>(FoldersGridActivity.class);
@Test
public void ensureGridAdapterIsNotNull() {
FoldersGridActivity foldersGridActivity = foldersGridActivityTestRule.getActivity();
View view = foldersGridActivity.findViewById(R.id.grid_view);
GridView gridView = (GridView) view;
GridAdapter gridAdapter = (GridAdapter) gridView.getAdapter();
assertThat(gridAdapter, notNullValue());
}
@Test
public void ensureArrayAdapterInGridActivityIsNotEmpty() {
FoldersGridActivity foldersGridActivity = foldersGridActivityTestRule.getActivity();
View view = foldersGridActivity.findViewById(R.id.grid_view);
GridView gridView = (GridView) view;
GridAdapter gridAdapter = (GridAdapter) gridView.getAdapter();
assertTrue(gridAdapter.getCount() > 0 );
}
@Test
public void gridViewIsVisibleAndClickable() {
onData(anything()).inAdapterView(withId(R.id.grid_view)).atPosition(0).perform(click());
onView(withId(R.id.toolbar_title_text_view)).check(matches(withText("rain")));
}
}
所有仿真设备再次成功通过测试。 然后,我添加了FireBase TestLab,并在装有Android 8的Galaxy S9真实设备上启动了测试。
适配器的代码。是的,即使它没有意义,我也重写了getCount()方法。
public class GridAdapter extends ArrayAdapter<TracksFolder> {
@BindView(R.id.folder_text_view) TextView textView;
@BindView(R.id.grid_item_image_view) ImageView imageView;
private ArrayList<TracksFolder> tracksFolders;
private Context context;
GridAdapter(Context context, int resource, ArrayList<TracksFolder> tracksFolders) {
super(context, resource, tracksFolders);
this.tracksFolders = tracksFolders;
this.context = context;
}
/**
* @return grid item, customized via TracksFolder class instance
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.grid_item, parent, false);
}
ButterKnife.bind(this, convertView);
String folderName = tracksFolders.get(position).getFolderName();
int imageResource = tracksFolders.get(position).getImageResource();
textView.setText(folderName);
imageView.setImageResource(imageResource);
return convertView;
}
/**
* Added to possibly avoid NPE on multiple devices
* Could not reproduce this error
* @return size of tracksFolders
*/
@Override
public int getCount() {
return tracksFolders.size();
}
}
所以,我有几个问题: 首先,此崩溃报告是否意味着每次Sumsung所有者启动应用程序时都会发生此崩溃?由于某些三星设备仍在统计中,因此我认为没有。 其次,我应该怎么做才能以某种方式重现此错误并最终修复该错误。
我知道问题太广泛了,但是我将非常感谢您的帮助,因为我绝对不知道下一步该怎么做。
进入类别。使用适配器
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.GridView;
import com.google.firebase.crash.FirebaseCrash;
import java.util.ArrayList;
import butterknife.BindArray;
import butterknife.BindView;
import butterknife.ButterKnife;
import static com.golendukhin.whitenoise.SharedPreferencesUtils.writeToSharedPreferences;
public class FoldersGridActivity extends AppCompatActivity {
@BindView(R.id.grid_view) GridView gridView;
@BindArray(R.array.labels) String[] foldersName;
@BindView(R.id.toolbar_like_button) Button toolbarLikeButton;
@BindView(R.id.toolbar_back_button) Button toolbarBackButton;
private ArrayList<TracksFolder> tracksFolders;
/**
* Binds all UI views
* Defines variables to feed adapter and listeners
* Initializes grid adapter, sets number of columns via phone in portrait or landscape orientation
* Sets onclick listener for every grid item
* Sets onclick for toolbar "like" button
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.folders_grid_view);
ButterKnife.bind(this);
if (getIntent().getExtras() != null) { //activity is triggered via on back pressed
Bundle bundle = getIntent().getExtras();
tracksFolders = (ArrayList<TracksFolder>) bundle.getSerializable("tracksFolders");
} else if (savedInstanceState != null) { //this happens after screen is rotated
tracksFolders = (ArrayList<TracksFolder>) savedInstanceState.getSerializable("tracksFolders");
} else { //this happens if app is launched by user
tracksFolders = getTracksFolders();
initService();
}
toolbarBackButton.setVisibility(View.GONE);
GridAdapter gridAdapter;
gridAdapter = new GridAdapter(this, R.id.grid_view, tracksFolders);
gridView.setNumColumns(getResources().getInteger(R.integer.columns_number));
gridView.setAdapter(gridAdapter);
FirebaseCrash.log("Activity created");
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Bundle bundle = new Bundle();
bundle.putSerializable("tracksFolders", tracksFolders);
bundle.putInt("folderPosition", position);
Intent intent = new Intent(FoldersGridActivity.this, TracksListActivity.class);
intent.putExtras(bundle);
startActivity(intent);
}
});
toolbarLikeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putSerializable("tracksFolders", tracksFolders);
bundle.putInt("activityFrom", Constants.FROM_FOLDERS_GRID_ACTIVITY);
Intent intent = new Intent(FoldersGridActivity.this, LikedTracksListActivity.class);
intent.putExtras(bundle);
startActivity(intent);
}
});
}
/**
* Saves tracksFolders array list to rebuild activity after rotation
*/
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putSerializable("tracksFolders", tracksFolders);
writeToSharedPreferences(this, tracksFolders);
super.onSaveInstanceState(outState);
}
@Override
public void onBackPressed() {
super.onBackPressed();
moveTaskToBack(true);
}
/**
* After app is launched need to initialize audio player service
*/
private void initService() {
Intent intent = new Intent(FoldersGridActivity.this, AudioPlayerService.class);
startService(intent);
}
/**
* If tracksFolders is not stored in shared preferences, it is initialized from resources.
* @return arrayList of folders with arrayList of tracks belonging to every folder
*/
private ArrayList<TracksFolder> getTracksFolders() {
Resources resources = getResources();
TypedArray pictures = resources.obtainTypedArray(R.array.pictures);
ArrayList<TracksFolder> tracksFolders = new ArrayList<>();
for (int i = 0; i < foldersName.length; i++) {
String folderName = foldersName[i];
int imageResource = pictures.getResourceId(i, -1);
ArrayList<Track> tracks = getArrayListOfTracks(resources, i, imageResource);
tracksFolders.add(new TracksFolder(folderName, imageResource, tracks));
}
pictures.recycle();
return tracksFolders;
}
/**
* @param resources need to get typedArray to get array of audio files names
* @param folderPosition passed to Track constructor
* @param imageResource passed to Track constructor
* Liked tracks are obtained from shared preferences
* @return array list of tracks for every folder
*/
private ArrayList<Track> getArrayListOfTracks(Resources resources, int folderPosition, int imageResource) {
TypedArray audioTracksTypedArray = resources.obtainTypedArray(R.array.audio_tracks);
int audioTracksId = audioTracksTypedArray.getResourceId(folderPosition, 0);
String[] audio = resources.getStringArray(audioTracksId);
audioTracksTypedArray.recycle();
TypedArray trackNamesTypedArray = resources.obtainTypedArray(R.array.track_names);
int trackNamesId = trackNamesTypedArray.getResourceId(folderPosition, 0);
String[] trackNames = resources.getStringArray(trackNamesId);
trackNamesTypedArray.recycle();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
ArrayList<Track> tracksInFolder = new ArrayList<>();
for (int i = 0; i < trackNames.length; i++) {
int audioResource = resources.getIdentifier(audio[i], "raw", this.getPackageName());
String trackName = trackNames[i].replace("_", " ");
boolean isLiked = sharedPreferences.getBoolean(String.valueOf(audioResource), false);
tracksInFolder.add(new Track(audioResource, trackName, imageResource, isLiked));
}
return tracksInFolder;
}
//todo убрать полосы в списках треков
}
更新的课程:
@NonNull
private ArrayList<TracksFolder> getTracksFoldersAndInitService(Bundle savedInstanceState) {
if (getIntent().getExtras() != null) { //activity is triggered via on back pressed
Bundle bundle = getIntent().getExtras();
tracksFolders = (ArrayList<TracksFolder>) bundle.getSerializable("tracksFolders");
} else if (savedInstanceState != null) { //this happens after screen is rotated
tracksFolders = (ArrayList<TracksFolder>) savedInstanceState.getSerializable("tracksFolders");
} else { //this happens if app is launched by user
tracksFolders = getTracksFoldersFromResources();
initService();
}
return tracksFolders;
}
@Nick Fortescue,@ Ranjan,谢谢你们两个的建议,但是tracksFolders绝不能为NULL。我已经编辑了类(您可以在“更新的类:”下面看到它),并将tracksFolders实例化为带有@NonNull批注的另一个方法。我已经使用此更改更新了应用程序,后来又获得了具有相同日志的相同NPE。我知道这是在用户上测试应用程序的最糟糕的方法,但是我别无选择。如果您是对的,则日志会说getTracksFoldersAndInitService方法中引发了Exception。但事实并非如此。因此,适配器存在问题。如果我错了请纠正我。
答案 0 :(得分:0)
在GridAdapter.getCount()
中发生的NullPointerException并不意味着getCount()返回null。这意味着它正在访问一个空值。由于您上传了代码(太好了!),我们可以看到只有两个可能的问题:
trackFolders
是null
,因此访问它会得到NPE trackFolders
返回一个null
整数,因此当它转换为int
时,您会得到一个NPE 第二个选项是不可能的,因为.size()
上的ArrayList
也会返回一个int,因此它必须是第一个选项。
trackFolders
是在构造函数中设置的,该构造函数是在onCreate()
方法中从捆绑软件,已保存状态或getTracksFolders()
中设置的。看来getTracksFolders()
不能返回null,所以我猜测您的活动是从将其设置为null的意图开始的。我会在您的onCreate()方法中再次检查,如果发生这种情况,请采取适当的措施,也许将其替换为new ArrayList()
?