2

I am trying to fetch data by using retrofit with MVVM Architecture. When I wanted to print the data on console, it works.

But when I wanna use them on the RecyclerView, I get this error:

E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.movieapp, PID: 4275 android.os.NetworkOnMainThreadException.

Here is my codes:

MovieRepository

public class MovieRepository {

private static MovieService movieService;
private final MutableLiveData<List<Movie>> listOfPopularMovie ;

private static MovieRepository newsRepository;


public static MovieRepository getInstance(){
    if(newsRepository == null){
        newsRepository=new MovieRepository();
    }
    return newsRepository;
}
public MovieRepository(){
    movieService= RetrofitService.getMovieService();
    listOfPopularMovie=new MutableLiveData<>();
}

public MutableLiveData<List<Movie>> getListOfPopularMovie(String key,int page) {
    Call<MovieResponse> popularMovies= movieService.getPopularMovie(key,page);
    popularMovies.enqueue(new Callback<MovieResponse>() {
        @Override
        public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
            listOfPopularMovie.setValue(response.body().getResults());
        }

        @Override
        public void onFailure(Call<MovieResponse> call, Throwable t) {
            listOfPopularMovie.postValue(null);
        }
    });
    return listOfPopularMovie;
}

MainViewModel

public class MainViewModel extends AndroidViewModel {
private MutableLiveData<List<Movie>> listOfPopularMovies = new MutableLiveData<>();
private MovieRepository movieRepository;


public MainViewModel(@NonNull Application application) {
    super(application);
    movieRepository = MovieRepository.getInstance();
}

public MutableLiveData<List<Movie>> getListOfPopularMovies(String key, int page) {
    listOfPopularMovies=movieRepository.getListOfPopularMovie(key,page);
    return listOfPopularMovies;
}

MovieAdapter

public class MovieAdapter extends RecyclerView.Adapter<MovieAdapter.MovieViewHolder> {
private List<Movie> movies;
private Context mContext;
public MovieAdapter(Context context){
    mContext=context;
}

@NonNull
@Override
public MovieViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view= LayoutInflater.from(mContext).inflate(R.layout.item_view,parent,false);

    return new MovieViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull MovieViewHolder holder, int position) {
    Movie movie=movies.get(position);
    String currentTitle=movie.getTitle();
    String imageUrl="https://image.tmdb.org/t/p/original";
    imageUrl +=movie.getBackdrop_path();
    try {
        URL url=new URL(imageUrl);
        Bitmap bmp=BitmapFactory.decodeStream(url.openConnection().getInputStream());
        Drawable drawable=new BitmapDrawable(mContext.getResources(),bmp);
        holder.image.setBackground(drawable);
        holder.title.setText(currentTitle);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}



@Override
public int getItemCount() {
    if(movies == null)
        return 0;
    return movies.size();
}
public void setMovie(List<Movie> taskEntries) {
    movies = taskEntries;
    notifyDataSetChanged();
}

class MovieViewHolder extends RecyclerView.ViewHolder {

    LinearLayout image;
    TextView title;
    public MovieViewHolder(@NonNull View itemView) {
        super(itemView);
        image=itemView.findViewById(R.id.movie_image);
        title=itemView.findViewById(R.id.movie_title);
    }
}

MainActivity

public class MainActivity extends AppCompatActivity {

MovieAdapter movieAdapter;
RecyclerView recyclerView;
MainViewModel mainViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    recyclerView= findViewById(R.id.recycler_view);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    movieAdapter= new MovieAdapter(this);
    recyclerView.setAdapter(movieAdapter);
    DividerItemDecoration decoration = new DividerItemDecoration(getApplicationContext(), VERTICAL);
    recyclerView.addItemDecoration(decoration);
    mainViewModel= new ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(MainViewModel.class);
    mainViewModel.getListOfPopularMovies("9e2629973011b0744ce3b589dff1fb32",1).observe(this, new Observer<List<Movie>>() {
        @Override
        public void onChanged(List<Movie> movies) {
           movieAdapter.setMovie(movies);
        }
    });


}
Daniel.Wang
  • 2,242
  • 2
  • 9
  • 27
  • Does this answer your question? [How to fix 'android.os.NetworkOnMainThreadException'?](https://stackoverflow.com/questions/6343166/how-to-fix-android-os-networkonmainthreadexception) – Ivan Wooll Mar 29 '21 at 15:00

1 Answers1

0

Your exception happened because you use network in UI thread.
I found the problem in your MovieAdapter code.

@Override
public void onBindViewHolder(@NonNull MovieViewHolder holder, int position) {
    ......................
    URL url = new URL(imageUrl);
    Bitmap bmp = BitmapFactory.decodeStream(url.openConnection().getInputStream());
    Drawable drawable = new BitmapDrawable(mContext.getResources(),bmp);
    holder.image.setBackground(drawable);
    .............................
}

So you used url.openConnection() code in onBindViewHolder method of MovieAdapter class. But onBindViewHolder is called in UI thread.

So, Answer is you should not call this function in onBindViewHolder method.

My suggestion is you can use image load library such as Glide or Picasso.

https://github.com/bumptech/glide
https://github.com/square/picasso

if you use Glide, your onBindViewHolder method like this.

Glide.with(holder.image.getContext()).load(imageUrl).into(holder.image);

I hope this will be helpful.

Daniel.Wang
  • 2,242
  • 2
  • 9
  • 27