В ImageView не загружается картинка по URL адресу

У меня есть RecyclerView в котором я отображаю фото и текст под фото через CardView. Данные получаю через Json файл. Текст отображается корректно, только вот картинка почему-то не грузится.

NewsAdapterRV:

package com.example.istrcheese;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;

public class NewsAdapterRV extends RecyclerView.Adapter<NewsAdapterRV.MyViewClass> {

ArrayList<String> txt_news;
ArrayList<String> img_news;
Context context;

public NewsAdapterRV(ArrayList<String> txt_news, ArrayList<String> img_news, Context context) {
    this.txt_news = txt_news;
    this.img_news = img_news;
    this.context = context;
}


@NonNull
@Override
public NewsAdapterRV.MyViewClass onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_card_view, parent, false);
    return new MyViewClass(view);
}

@Override
public void onBindViewHolder(@NonNull MyViewClass holder, int position) {
    holder.txt_News.setText(txt_news.get(position));
    holder.img_News.setImageDrawable(LoadImageFromWebOperations(img_news.get(position)));

    holder.itemView.setOnClickListener(view ->
            Toast.makeText(context, R.string.text_itemClicked, Toast.LENGTH_SHORT).show());
}

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

public static class MyViewClass extends RecyclerView.ViewHolder {

    TextView txt_News;
    ImageView img_News;

    public MyViewClass(View itemView) {
        super(itemView);
        txt_News = itemView.findViewById(R.id.item_news_CV_text);
        img_News = itemView.findViewById(R.id.item_news_CV_image);
    }
}
// Получение изображения по URL-адресу
public static Drawable LoadImageFromWebOperations(String url) {
    try {
        InputStream is = (InputStream) new URL(url).getContent();
        return Drawable.createFromStream(is, "src name");
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
}

HomeFragment:

package com.example.istrcheese;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

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

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Objects;

public class HomeFragment extends Fragment {

View view;
RecyclerView recyclerView;

ArrayList<String> txt_ns = new ArrayList<>();
ArrayList<String> img_ns = new ArrayList<>();

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view = inflater.inflate(R.layout.fragment_home, container, false);
    recyclerView = view.findViewById(R.id.news_recyclerView);
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(container.getContext().getApplicationContext());
    linearLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
    recyclerView.setLayoutManager(linearLayoutManager);
    JSONObject jsonObject;
    try {
        jsonObject = new JSONObject(Objects.requireNonNull(JsonDataFromAsset("exampleNews.json")));
        JSONArray jsonArray = jsonObject.getJSONArray("exampleNews");
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject userData = jsonArray.getJSONObject(i);
            txt_ns.add(userData.getString("text_news"));
            img_ns.add(userData.getString("image_news"));
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    NewsAdapterRV newsAdapterRV = new NewsAdapterRV(txt_ns, img_ns, HomeFragment.this.getContext());
    recyclerView.setAdapter(newsAdapterRV);
    return view;
}

@Nullable
private String JsonDataFromAsset(String fileName) {
    String json;
    try {
        InputStream inputStream = requireContext().getAssets().open(fileName);
        int sizeOfFile = inputStream.available();
        byte[] bufferData = new byte[sizeOfFile];
        inputStream.read(bufferData);
        inputStream.close();
        json = new String(bufferData, StandardCharsets.UTF_8);
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
    return json;
}

}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.istrcheese">

<application
    android:allowBackup="true"
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:fullBackupContent="@xml/backup_rules"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.IstrCheese"
    tools:targetApi="31">

    <activity
        android:name=".ForgetPasswordActivity"
        android:exported="false" />
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:windowSoftInputMode="adjustResize" />
    <activity
        android:name=".AuthActivity"
        android:exported="true"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <meta-data
        android:name="preloaded_fonts"
        android:resource="@array/preloaded_fonts" />
</application>


<uses-permission
    android:name="android.permission.INTERNET" />

</manifest>

Измененный HomeFragment:

package com.example.istrcheese;

import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

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

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Objects;

public class HomeFragment extends Fragment {

View view;
RecyclerView recyclerView;

ArrayList<String> txt_ns = new ArrayList<>();
ArrayList<Drawable> img_ns = new ArrayList<>();
String image_str;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view = inflater.inflate(R.layout.fragment_home, container, false);
    recyclerView = view.findViewById(R.id.news_recyclerView);
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(container.getContext().getApplicationContext());
    linearLayoutManager.setOrientation(RecyclerView.HORIZONTAL);
    recyclerView.setLayoutManager(linearLayoutManager);
    JSONObject jsonObject;
    try {
        jsonObject = new JSONObject(Objects.requireNonNull(JsonDataFromAsset("exampleNews.json")));
        JSONArray jsonArray = jsonObject.getJSONArray("exampleNews");
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject userData = jsonArray.getJSONObject(i);
            txt_ns.add(userData.getString("text_news"));
            image_str = userData.getString("image_news");
            img_ns.add(LoadImageFromWebOperations(image_str));
        }
    } catch (JSONException e) {
        e.printStackTrace();
    }
    NewsAdapterRV newsAdapterRV = new NewsAdapterRV(txt_ns, img_ns, HomeFragment.this.getContext());
    recyclerView.setAdapter(newsAdapterRV);
    return view;
}

@Nullable
private String JsonDataFromAsset(String fileName) {
    String json;
    try {
        InputStream inputStream = requireContext().getAssets().open(fileName);
        int sizeOfFile = inputStream.available();
        byte[] bufferData = new byte[sizeOfFile];
        inputStream.read(bufferData);
        inputStream.close();
        json = new String(bufferData, StandardCharsets.UTF_8);
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
    return json;
}

public static Drawable LoadImageFromWebOperations(String url) {
    try {
        InputStream is = (InputStream) new URL(url).getContent();
        return Drawable.createFromStream(is, "src name");
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}
}

ОБНОВЛЕНИЕ 2:

Адаптер

package com.example.istrcheese;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.RequestBuilder;

import java.util.ArrayList;

public class NewsAdapterRV extends RecyclerView.Adapter<NewsAdapterRV.MyViewClass> {

ArrayList<String> txt_news;
ArrayList<RequestBuilder<Drawable>> img_news;
Context context;

public NewsAdapterRV(ArrayList<String> txt_news, ArrayList<RequestBuilder<Drawable>> img_news, Context context) {
    this.txt_news = txt_news;
    this.img_news = img_news;
    this.context = context;
}


@NonNull
@Override
public NewsAdapterRV.MyViewClass onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_news_card_view, parent, false);
    return new MyViewClass(view);
}

@Override
public void onBindViewHolder(@NonNull MyViewClass holder, int position) {
    holder.txt_News.setText(txt_news.get(position));
    holder.img_News.setImageURI(img_news.get(position)); // что здесь нужно писать?

    holder.itemView.setOnClickListener(view ->
            Toast.makeText(context, R.string.text_itemClicked, Toast.LENGTH_SHORT).show());
}

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

public static class MyViewClass extends RecyclerView.ViewHolder {

    TextView txt_News;
    ImageView img_News;

    public MyViewClass(View itemView) {
        super(itemView);
        txt_News = itemView.findViewById(R.id.item_news_CV_text);
        img_News = itemView.findViewById(R.id.item_news_CV_image);
    }
}
}

Ответы (1 шт):

Автор решения: woesss

Андроид запрещает выполнять сетевые операции в главном потоке.
Тема многопоточности сложная и в контексте андроида с жизненным циклом компонентов ещё сложнее.
Проще всего воспользоваться готовым решением: для работы с картинками наиболее популярны Glide, Picasso.
Минимальный пример для Picasso, мне она кажется попроще.
Добавить зависимость в gradle:

implementation 'com.squareup.picasso:picasso:2.8'

В адаптере:

@Override
public void onBindViewHolder(@NonNull MyViewClass holder, int position) {
    holder.txt_News.setText(txt_news.get(position));
    Picasso.get().load(img_news.get(position)).into(holder.img_News); // img_news - это список ссылок на картинки

    holder.itemView.setOnClickListener(view ->
            Toast.makeText(context, R.string.text_itemClicked, Toast.LENGTH_SHORT).show());
}
→ Ссылка