我正在构建一个应用程序,该应用程序可以解析来自api的JSON数据,但是由于某些原因,它一直崩溃。我收到由以下原因引起的错误:java.lang.NullPointerException:尝试在null对象引用上调用虚拟方法'int java.lang.String.length()',它指向我定义JSON对象的区域在MainActivity中:
//Create the network request to download the JSON data from the url database.
URL moviesUrl = NetworkUtils.buildUrl(SEARCH_TERM);
try {
//The response we get is in the form of JSON.
String jsonMoviesResponse = NetworkUtils.getReponseFromHttpUrl(moviesUrl);
//A new JSON object created from the JSON response.
JSONObject moviesJson = new JSONObject(jsonMoviesResponse);
//Read the movie results array from the JSON object.
JSONArray moviesArray = moviesJson.getJSONArray(MOVIES_RESULTS);
//A loop is created to read the array and add the data we need to a list.
for (int i = 0; i < moviesArray.length(); i++) {
错误消息:
Process: com.example.thodzic.movies, PID: 8776
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:353)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
at java.util.concurrent.FutureTask.run(FutureTask.java:271)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.String.length()' on a null object reference
at org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116)
at org.json.JSONTokener.nextValue(JSONTokener.java:94)
at org.json.JSONObject.<init>(JSONObject.java:159)
at org.json.JSONObject.<init>(JSONObject.java:176)
at com.example.thodzic.movies.MainActivity$FetchMovieTask.doInBackground(MainActivity.java:115)
at com.example.thodzic.movies.MainActivity$FetchMovieTask.doInBackground(MainActivity.java:96)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
06-26 08:22:30.796 8776-8818/com.example.thodzic.movies D/EGL_emulation: eglMakeCurrent: 0xa8b05120: ver 3 0 (tinfo 0xa8b03380)
URL已成功构建并返回一堆JSON,但该错误使它似乎没有返回JSON。仓库可以在这里找到 https://github.com/tarik3001/Movies 这是我的应用程序的完整代码:
MainActivity:
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
//Declare some of the objects we are using.
String SEARCH_TERM = "popularity.desc";
private RecyclerView mRecyclerView;
//GridLayoutManage will allow us to load our items in a grid.
private GridLayoutManager gridLayoutManager;
//Custoj Adapter lets us bind out data from the web server with our recylerview
private MovieAdapter mMovieAdapter;
//Need a list to store the data from the server.
private List<Movie> movieData;
/*
API KEY
https://api.themoviedb.org/3/movie/550?api_key=1f5029b7d824dee72f4d4a156dac90ed
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Reference the RecyclerView
mRecyclerView = findViewById(R.id.recycler_view);
//Reference the list. This needs to be done before setting the adapter to the recycler
//view or the app will think there is an empty list.
movieData = new ArrayList<>();
//To update the list with items, we create a new method to do that.
loadMovieData();
//Create a new grid layout manager in order to display data to a grid.
gridLayoutManager = new GridLayoutManager(this, 3);
mRecyclerView.setLayoutManager(gridLayoutManager);
//Bind the data we receive from the web server to the recyclerview itself.
mMovieAdapter = new MovieAdapter(this, movieData);
//Apply the adapter to the recyclerview.
mRecyclerView.setAdapter(mMovieAdapter);
}
//Inflate the menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sort_by_most_popular:
SEARCH_TERM = "popularity.desc";
}
return super.onOptionsItemSelected(item);
}
//Tell the new method to get the dat abased on the search term within the url.
private void loadMovieData() {
new FetchMovieTask().execute(SEARCH_TERM);
}
//We need to use an AsyncTask to perform the request to get the data. The first argument
//we use a String because this will allow us to pass the url.
public class FetchMovieTask extends AsyncTask<String, Void, Void> {
@Override
protected Void doInBackground(String... strings) {
final String MOVIES_RESULTS = "results";
final String MOVIES_POSTER_IMAGE = "poster_path";
final String MOVIES_TITLE = "title";
final String RELEASE_DATE = "release_date";
final String VOTE_AVERAGE = "vote_average";
final String PLOT = "overview";
//Create the network request to download the JSON data from the url database.
URL moviesUrl = NetworkUtils.buildUrl(SEARCH_TERM);
try {
//The response we get is in the form of JSON.
String jsonMoviesResponse = NetworkUtils.getReponseFromHttpUrl(moviesUrl);
//A new JSON object created from the JSON response.
JSONObject moviesJson = new JSONObject(jsonMoviesResponse);
//Read the movie results array from the JSON object.
JSONArray moviesArray = moviesJson.getJSONArray(MOVIES_RESULTS);
//A loop is created to read the array and add the data we need to a list.
for (int i = 0; i < moviesArray.length(); i++) {
String moviePoster;
String movieTitle;
String movieReleaseDate;
String voteAverage;
String plot;
JSONObject movie = moviesArray.getJSONObject(i);
moviePoster = ("http://image.tmdb.org/t/p/w185/" + movie.getString(MOVIES_POSTER_IMAGE));
movieTitle = movie.getString(MOVIES_TITLE);
movieReleaseDate = movie.getString(RELEASE_DATE);
voteAverage = movie.getString(VOTE_AVERAGE);
plot = movie.getString(PLOT);
Movie data = new Movie(movieTitle, movieReleaseDate, moviePoster, voteAverage, plot);
//Add the data items to our movieData list.
movieData.add(data);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//This is called when the network request is done. We use this method to tell our
//custom adapter that there is a change in the data list so that it can load new cardview
//widgets in the list.
@Override
protected void onPostExecute(Void aVoid) {
mMovieAdapter.notifyDataSetChanged();
}
}
};
MovieAdapter:
public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieAdapterViewHolder>{
//Define two fields which are referenced from the main activity.
private List<Movie> mMovieData;
private Context context;
//Create the constructor for the class
public MovieAdapter(Context context, List<Movie> movieData) {
this.context = context;
this.mMovieData = movieData;
}
//onBindViewHolder is method where we can manipulate the views.
@Override
public void onBindViewHolder(@NonNull MovieAdapterViewHolder holder, final int position) {
holder.mTitleTextView.setText(mMovieData.get(position).getTitle());
holder.mReleaseDateTextView.setText(mMovieData.get(position).getDate());
//Use Picasso library to quickly convert image url to image.
Picasso.with(context).load(mMovieData.get(position).getMoviePoster()).into(holder.mImageView);
//Create the new onclick listener to handle click events.
holder.mCardView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Une intent to tell the app where to navigate too once clicking on item.
Intent intent = new Intent(context, DetailsActivity.class);
//Attach extras to the intent because we needDetailsActivity to know which
//item we clicked on.
intent.putExtra("movie_title", mMovieData.get(position).getTitle());
intent.putExtra("poster", mMovieData.get(position).getMoviePoster());
intent.putExtra("release_date", mMovieData.get(position).getDate());
intent.putExtra("vote_average", mMovieData.get(position).getVoteAverage());
intent.putExtra("plot", mMovieData.get(position).getPlot());
//Start the activity and send it over. We need to use context here because
//we are in an adapter class and it doesn't know what the context is.
context.startActivity(intent);
}
});
}
//The onCreateViewHolder class is used in order to reference the cardview layout inflater to
//inflate the layout.
@NonNull
@Override
public MovieAdapterViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//Create a new view and use the layout inflater to
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.cardview_item, parent, false);
//We need to return a new instance of the viewholder with the referenced view.
return new MovieAdapterViewHolder(view);
}
//One of the methods we must override for the custom adapter class. Need to return the size
//of the list that we provide.
@Override
public int getItemCount() {
if (null == mMovieData)
return 0;
return mMovieData.size();
}
//Create the MovieAdapterViewHolder class from where we can update the cardview with data.
public class MovieAdapterViewHolder extends RecyclerView.ViewHolder {
//Our cardview has two text views and an image view.
public final TextView mTitleTextView;
public final TextView mReleaseDateTextView;
public final ImageView mImageView;
public final CardView mCardView;
public MovieAdapterViewHolder(View itemView) {
super(itemView);
mTitleTextView = itemView.findViewById(R.id.title_text_view);
mReleaseDateTextView = itemView.findViewById(R.id.release_date_text_view);
mImageView = itemView.findViewById(R.id.image_data);
mCardView = itemView.findViewById(R.id.card_view);
}
}
NetworkUtils
public class NetworkUtils {
//These utilities will be used to communicate with the servers.
private static final String TAG = NetworkUtils.class.getSimpleName();
private static final String BASE_URL = "https://api.themoviedb.org/3/discover/movie/";
private static final String API_KEY = "1f5029b7d824dee72f4d4a156dac90ed";
//This builds the URL used to talk to movie database.
public static URL buildUrl(String SEARCH_TERM) {
Uri builtUri = Uri.parse(BASE_URL).buildUpon()
.appendQueryParameter("api_key",API_KEY)
.appendQueryParameter("sort_by", SEARCH_TERM)
.build();
URL url = null;
try {
url = new URL(builtUri.toString());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Log.v(TAG, "Built URL " + url);
return url;
}
//This method returns the entire result from the HTTP response.
public static String getReponseFromHttpUrl(URL url) throws IOException {
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream inputStream = urlConnection.getInputStream();
Scanner scanner = new Scanner(inputStream);
scanner.useDelimiter("\\A");
boolean hasInput = scanner.hasNext();
if (hasInput) {
return scanner.next();
} else {
return null;
}
} finally {
urlConnection.disconnect();
}
}
}
数据类
public class Movie {
//Create the fields we need for this class
private String Title;
private String Date;
private String MoviePoster;
private String VoteAverage;
private String Plot;
//Generate the class constructor (Command + N shortcut)
public Movie(String title, String date, String moviePoster, String voteAverage, String plot) {
Title = title;
Date = date;
MoviePoster = moviePoster;
VoteAverage = voteAverage;
Plot = plot;
}
//Getters allow us to get the information we want from the list.
public String getTitle() {
return Title;
}
public void setTitle(String title) {
Title = title;
}
public String getDate() {
return Date;
}
//Setters allow us to set the information from the list into the item
public void setDate(String date) {
Date = date;
}
public String getVoteAverage() {
return VoteAverage;
}
public void setVoteAverage(String voteAverage) {
VoteAverage = voteAverage;
}
public String getPlot() {
return Plot;
}
public void setPlot(String plot) {
Plot = plot;
}
public String getMoviePoster() {
return MoviePoster;
}
public void setMoviePoster(String moviePoster) {
MoviePoster = moviePoster;
}
}
答案 0 :(得分:1)
如果您在NetworkUtils中查看方法 getReponseFromHttpUrl ,则会返回null:
if (hasInput) {
return scanner.next();
} else {
return null;
}
这可能是空指针异常的原因。