1

我創建了一個電影應用程序,它查詢REST API處的themoviedb.org。在從網格中選擇電影時,用戶被帶到詳細畫面,在該畫面中顯示關於電影的更多細節,其中列出了預告片評論列表。問題是,有時我我的代碼填充評論和預告片列表命中NullPointerException。它不會一直髮生,它不是特定於某個特定的電影條目,而是完全隨機的。runOnUIThread中的NullPointerException()

準確地說,當我在成功的網絡響應之後使用runOnUIThread()中的數據刷新我的RecyclerView適配器時,我隨機得到NullPointerExceptions。

這裏的GitHub庫鏈路https://github.com/Hackertronix/Project-Motion/tree/Stage_2

以下是我的代碼與堆棧跟蹤

我已經添加了註釋顯示的行號

DetailsFragment.java

沿
package com.execube.genesis.views.fragments; 

import android.annotation.TargetApi; 
import android.content.Intent; 
import android.graphics.Color; 
import android.graphics.Typeface; 
import android.net.Uri; 
import android.os.Build; 
import android.os.Bundle; 
import android.support.annotation.Nullable; 
import android.support.design.widget.CoordinatorLayout; 
import android.support.design.widget.FloatingActionButton; 
import android.support.design.widget.Snackbar; 
import android.support.v4.app.Fragment; 
import android.support.v4.content.ContextCompat; 
import android.support.v7.widget.CardView; 
import android.support.v7.widget.LinearLayoutManager; 
import android.support.v7.widget.RecyclerView; 
import android.support.v7.widget.Toolbar; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ImageView; 
import android.widget.ProgressBar; 
import android.widget.RatingBar; 
import android.widget.TextView; 
import android.widget.Toast; 

import com.execube.genesis.R; 
import com.execube.genesis.model.Event; 
import com.execube.genesis.model.Movie; 
import com.execube.genesis.model.Review; 
import com.execube.genesis.model.Trailer; 
import com.execube.genesis.utils.API; 
import com.execube.genesis.utils.EventBus; 
import com.execube.genesis.utils.JSONParser; 
import com.execube.genesis.utils.OkHttpHandler; 
import com.orm.SugarRecord; 
import com.squareup.picasso.Picasso; 

import org.json.JSONException; 
import org.json.JSONObject; 

import java.io.IOException; 
import java.util.ArrayList; 
import java.util.List; 

import okhttp3.Call; 
import okhttp3.Response; 

import static com.execube.genesis.R.drawable.ic_favorite_black_24dp; 


/** 
* Created by Prateek Phoenix on 4/30/2016. 
*/ 
public class DetailsFragment extends Fragment { 
    private static final String TAG = "DETAILS"; 
    private static final int DEFAULT_NUM_COLORS = 5; 

    private Movie mMovie; 
    private Movie entry,tempMovie; 
    private List<Movie> movie; 
    public Intent intent; 

    private TextView mDetailTitle; 
    private TextView mReleaseDate; 
    private TextView mOverview; 
    private TextView mOverviewHeader; 
    private TextView mReviesHeader; 
    private TextView mTrailersHeader; 

    private ImageView mBackdrop; 
    private Toolbar mToolbar; 

    private RatingBar mRatingBar; 

    private ArrayList<Review> mReviews=new ArrayList<>(); 
    private ArrayList<Trailer> mTrailers=new ArrayList<>(); 
    private List<Movie> updatedFavsList= new ArrayList<>(); 

    public static final String MOVIE_REVIEWS_ARRAY ="movie_details"; 
    private static final String MOVIE_TRAILERS_ARRAY = "movie_reviews"; 
    private Typeface fontBold; 
    private Typeface fontMediumLight; 
    private Typeface fontMedium; 

    private RecyclerView mReviewRecyclerView; 
    private RecyclerView mTrailerRecyclerView; 

    private ProgressBar mReviewsProgressbar; 
    private ProgressBar mTrailersProgressbar; 
    private CoordinatorLayout mCoordinatorLayout; 
    private CardView mReviewsCardView; 
    private FloatingActionButton mFloatingActionButton; 

    private ReviewsAdapter mReviewAdapter; 
    private int NumOfReviews; 
    private TrailersAdapter mTrailerAdapter; 

    private String id; 
    private boolean isFav; 
    public DetailsFragment() { 

    } 

    @Override 
    public void onCreate(@Nullable Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     Log.v(TAG,"Saving state in onSaveInstanceState"); 
     outState.putParcelableArrayList(MOVIE_REVIEWS_ARRAY,mReviews); 
     outState.putParcelableArrayList(MOVIE_TRAILERS_ARRAY,mTrailers); 
     super.onSaveInstanceState(outState); 
    } 

    @TargetApi(Build.VERSION_CODES.LOLLIPOP) 
    @Nullable 
    @Override 
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.fragment_detail, container, false); 


     mBackdrop = (ImageView) view.findViewById(R.id.details_poster); 

     mDetailTitle = (TextView) view.findViewById(R.id.detail_title_text); 
     mReleaseDate = (TextView) view.findViewById(R.id.release_date); 
     mOverview = (TextView) view.findViewById(R.id.overview); 
     mOverviewHeader = (TextView) view.findViewById(R.id.overview_header); 
     mReviesHeader=(TextView)view.findViewById(R.id.review_header); 
     mTrailersHeader=(TextView)view.findViewById(R.id.trailer_header); 

     mRatingBar = (RatingBar) view.findViewById(R.id.movie_rating); 
     mCoordinatorLayout=(CoordinatorLayout)view.findViewById(R.id.coordinator_layout); 
     mReviewRecyclerView= (RecyclerView)view.findViewById(R.id.review_recycler_view); 
     mTrailerRecyclerView=(RecyclerView)view.findViewById(R.id.trailer_recycler_view); 

     mReviewsProgressbar=(ProgressBar)view.findViewById(R.id.reviews_progressbar); 
     mTrailersProgressbar=(ProgressBar)view.findViewById(R.id.trailers_progressbar); 
     mFloatingActionButton=(FloatingActionButton)view.findViewById(R.id.fab); 

     mReviewsCardView= (CardView) view.findViewById(R.id.reviews_card); 


     intent = getActivity().getIntent(); 
     Bundle bundle=getArguments(); 
     mMovie=bundle.getParcelable("PARCEL"); 
     tempMovie=mMovie; 
     id = String.valueOf(mMovie.getMovieId()); 


     checkFav(); 
     mFloatingActionButton.show(); 

     assert mMovie != null; 

     //PREPPING THE URL FOR QUERY 

     String reviewQueryUrl = API.MOVIES_BASE_URL + id + "/reviews" + API.API_KEY; 
     String trailerQueryUrl = API.MOVIES_BASE_URL + id + "/videos" + API.API_KEY; 



     mDetailTitle.setText(mMovie.getTitle()); 
     mReleaseDate.setText(mMovie.getReleaseDate()); 
     mRatingBar.setProgress((int) mMovie.getVoteAverage()); 
     mOverview.setText(mMovie.getOverview()); 


     if (Build.VERSION.SDK_INT != 21) { 
      fontBold = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Gotham-Rounded-Bold.ttf"); 
      fontMedium = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Gotham-Rounded-Medium.ttf"); 
      fontMediumLight = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Gotham-Rounded-Book_.ttf"); 


      mDetailTitle.setTypeface(fontBold); 
      mReleaseDate.setTypeface(fontMedium); 
      mOverview.setTypeface(fontMediumLight); 
      mOverviewHeader.setTypeface(fontBold); 
      mReviesHeader.setTypeface(fontBold); 
      mTrailersHeader.setTypeface(fontBold); 
     } 



     //FETCHING JSON HERE 
     if(savedInstanceState!=null&&savedInstanceState.containsKey(MOVIE_REVIEWS_ARRAY)) 
     { 
      Log.v(TAG,"Restoring from bundle"); 
      mReviews=savedInstanceState.getParcelableArrayList(MOVIE_REVIEWS_ARRAY); 
      mTrailers=savedInstanceState.getParcelableArrayList(MOVIE_TRAILERS_ARRAY); 

      mReviewsProgressbar.setVisibility(View.GONE); 
      mTrailersProgressbar.setVisibility(View.GONE); 

     } 

     else { 
      OkHttpHandler reviewsHandler = new OkHttpHandler(reviewQueryUrl, reviewsCallback); 
      reviewsHandler.fetchData(); 

      OkHttpHandler trailersHandler= new OkHttpHandler(trailerQueryUrl, trailersCallback); 
      trailersHandler.fetchData(); 

     } 

     Picasso.with(getActivity()).load(API.IMAGE_URL + API.IMAGE_SIZE_500 + mMovie.getPosterPath()) 
       .placeholder(R.drawable.placeholder) 
       .error(R.drawable.error) 
       .into(mBackdrop); 
     getActivity().startPostponedEnterTransition(); 





     mReviewRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 
     mReviewAdapter= new ReviewsAdapter(); 
     mReviewRecyclerView.setAdapter(mReviewAdapter); 

     LinearLayoutManager layoutmanager= new LinearLayoutManager(getActivity(),LinearLayoutManager.HORIZONTAL,false); 
     mTrailerRecyclerView.setLayoutManager(layoutmanager); 
     mTrailerAdapter= new TrailersAdapter(); 
     mTrailerRecyclerView.setAdapter(mTrailerAdapter); 

     return view; 
    } 

    private void checkFav() { 
     movie=new ArrayList<>(); 
     movie=SugarRecord.find(Movie.class,"m_id=?",id); 
     if(movie.size()==0) 
     { 
      Log.v(TAG,"Null"); 

      mFloatingActionButton.setImageResource(R.drawable.ic_favorite_border_black_24dp); 
     } 
     else { 
      Log.v(TAG,"NOT Null"); 

      mFloatingActionButton.setImageResource(R.drawable.ic_favorite_black_24dp); 
     } 

     mFloatingActionButton.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
      movie=SugarRecord.find(Movie.class,"m_id=?",id); 
       if(movie.size()>0) 
       { 
        entry = movie.get(0); 
        entry.delete(); 

        Event event = new Event("Database has been modified!!"); 
        EventBus.getBus().post(event); 

        mFloatingActionButton.setImageResource(R.drawable.ic_favorite_border_black_24dp); 
        Snackbar snackbar = Snackbar.make(mCoordinatorLayout,"Movie removed from Favourites!!",Snackbar.LENGTH_SHORT); 
        View view= snackbar.getView(); 
        TextView textView = (TextView)view.findViewById(android.support.design.R.id.snackbar_text); 
        textView.setTextColor(Color.YELLOW); 
        snackbar.show(); 
       } 
       else 
       { 
        entry = tempMovie; 
        entry.save(); 
        Event event = new Event("Database has been modified!!"); 
        EventBus.getBus().post(event); 
        mFloatingActionButton.setImageResource(R.drawable.ic_favorite_black_24dp); 
        Snackbar snackbar = Snackbar.make(mCoordinatorLayout,"Movie added to Favourites!!",Snackbar.LENGTH_SHORT); 
        View view= snackbar.getView(); 
        TextView textView = (TextView)view.findViewById(android.support.design.R.id.snackbar_text); 
        textView.setTextColor(Color.YELLOW); 
        snackbar.show(); 
       } 

      } 
     }); 
    } 


    //OKHTTP CALLBACK FOR NETWORK CALL 
    private okhttp3.Callback reviewsCallback = new okhttp3.Callback() { 
     @Override 
     public void onFailure(Call call, IOException e) { 
      //TODO handle failure on UI thread 
     } 

     @Override 
     public void onResponse(Call call, Response response) throws IOException { 

      try { 
       String JSONData= response.body().string(); 
       JSONObject jsonObject = new JSONObject(JSONData); 
       NumOfReviews = jsonObject.getInt("total_results"); 
       JSONParser parser = new JSONParser(); 
       mReviews=parser.parseReviews(JSONData); 



      } catch (JSONException e) {} 

      getActivity().runOnUiThread(new Runnable() { 
       @Override //THIS IS THE FIRST LINE WHERE I GET A NULL POINTER EXCEPTION 
       public void run() { 

        if(mReviewAdapter!=null) 
        { 
         mReviewsProgressbar.setVisibility(View.GONE); 
         mReviewAdapter.notifyDataSetChanged(); 
        } 
       } 
      }); 

      getActivity().runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        if (NumOfReviews==0) 
        { 
         mReviewsCardView.setVisibility(View.INVISIBLE); 
        } 
       } 
      }); 
     } 
    }; 

    private okhttp3.Callback trailersCallback = new okhttp3.Callback() { 
     @Override 
     public void onFailure(Call call, IOException e) { 
      //TODO handle failure on UI thread 
     } 

     @Override 
     public void onResponse(Call call, Response response) throws IOException { 

      try { 
       String json1= response.body().string(); 
       JSONParser parser= new JSONParser(); 
       mTrailers = parser.parseTrailers(json1); 

      } catch (JSONException e) {} 

      getActivity().runOnUiThread(new Runnable() { 
       @Override 
       public void run() { //THIS IS THE 2nd LINE WHERE I GET A NULL POINTER EXCEPTION 
        if(mTrailerAdapter!=null) 
        { 
         mTrailersProgressbar.setVisibility(View.GONE); 
         mTrailerAdapter.notifyDataSetChanged(); 
        } 
       } 
      }); 
     } 
    }; 

    private class ReviewViewHolder extends RecyclerView.ViewHolder{ 
     private TextView mAuthorText; 
     private TextView mReviewText; 
     private Review mReview; 

     public ReviewViewHolder(View itemView) { 
      super(itemView); 
      mAuthorText= (TextView) itemView.findViewById(R.id.author_textview); 
      mReviewText= (TextView) itemView.findViewById(R.id.review_textview); 

     } 

     public void bind(Review review) 
     { 
      mReview= review; 

      mAuthorText.setText(mReview.getAuthor()); 
      mReviewText.setText(mReview.getContent()); 

      if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) 
      { 
       mAuthorText.setTypeface(fontBold); 
       mReviewText.setTypeface(fontMediumLight); 
      } 


     } 
    } 

    private class ReviewsAdapter extends RecyclerView.Adapter<ReviewViewHolder>{ 

     @Override 
     public ReviewViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      View view= LayoutInflater.from(getActivity()).inflate(R.layout.review_item,parent,false); 
      return new ReviewViewHolder(view); 
     } 

     @Override 
     public void onBindViewHolder(ReviewViewHolder holder, int position) { 
      Review review= mReviews.get(position); 
      holder.bind(review); 
     } 

     @Override 
     public int getItemCount() { 
      if(mReviews==null) 
      { return 0;} 
      else 
      {return mReviews.size();} 
     } 
    } 

    private class TrailerViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 
     private ImageView mTrailerThumbnail; 
     private Trailer mTrailer; 

     public TrailerViewHolder(View itemView) { 
      super(itemView); 

      mTrailerThumbnail=(ImageView)itemView.findViewById(R.id.trailer_thumbnail); 
      itemView.setOnClickListener(this); 
     } 

     public void bind(Trailer trailer) 
     { 
      mTrailer=trailer; 

      Picasso picasso =Picasso.with(getActivity()); 
      picasso.setIndicatorsEnabled(true); 
      picasso.load(API.YOUTUBE_THUMBNAIL_URL+mTrailer.getKey()+API.THUMBNAIL_QUALITY) 
        .into(mTrailerThumbnail); 

     } 

     @Override 
     public void onClick(View v) { 

      Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(API.YOUTUBE_TRAILER_URL+mTrailer.getKey())); 
      startActivity(intent); 

     } 
    } 

    private class TrailersAdapter extends RecyclerView.Adapter<TrailerViewHolder> 
    { 

     @Override 
     public TrailerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 
      View view= LayoutInflater.from(getActivity()).inflate(R.layout.trailer_item,parent,false); 
      return new TrailerViewHolder(view); 
     } 

     @Override 
     public void onBindViewHolder(TrailerViewHolder holder, int position) { 
      Trailer trailer= mTrailers.get(position); 
      holder.bind(trailer); 
     } 

     @Override 
     public int getItemCount() { 
      return mTrailers.size(); 
     } 
    } 

} 

堆棧跟蹤

06-10 02:02:30.548 25094-27219/? E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher 
Process: com.execube.genesis, PID: 25094 
    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.FragmentActivity.runOnUiThread(java.lang.Runnable)' on a null object reference 
     at com.execube.genesis.views.fragments.DetailsFragment$3.onResponse(DetailsFragment.java:349) 
     at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133) 
     at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
     at java.lang.Thread.run(Thread.java:818) 
     06-10 02:02:30.548 25094-27218/? E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher 
Process: com.execube.genesis, PID: 25094 
    java.lang.NullPointerException: Attempt to invoke virtual method 'void android.support.v4.app.FragmentActivity.runOnUiThread(java.lang.Runnable)' on a null object reference 
     at com.execube.genesis.views.fragments.DetailsFragment$2.onResponse(DetailsFragment.java:308) 
     at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133) 
     at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
     at java.lang.Thread.run(Thread.java:818) 
+0

'getActivity()'可能返回null ..提防 – Vucko

+0

@Vucko所以將這項工作 如果(getActivity()== NULL) 回報 其他{// 做的東西在這裏 } –

+0

那麼它不會拋出NPE。 – Vucko

回答

2

當你調用getActivity,它在片段不依附於任何活動可能會返回null。爲了避免NullPointerException,請務必首先檢查它是否不是null,然後繼續您的邏輯。

if(getActivity!=null){ 
    //do stuff 
} 
+2

爲什麼選擇Android,爲什麼? –

+0

是什麼? ......... – Vucko