Android Рисование Bitmap на Canvas

Нужно загрузить изображение по URL и отобразить отрисовать его в Canvas. Загрузка изображения в AsyncTask никак не меняет ситуацию. Делаю следующим образом:

class Lesson extends View {

    public Lesson(Context context) {
        super(context);
    }

    public InputStream downloadImageFromUrl() throws Exception {
        
        URL url = new URL("https://mysite.ru/image.png");
        HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();

        httpsURLConnection.setDoInput(true);
        httpsURLConnection.connect();

        return httpsURLConnection.getInputStream();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        try {
            Bitmap bitmap = BitmapFactory.decodeStream(downloadImageFromUrl());
        } catch (Exception e) {
            e.printStackTrace();
        }

        canvas.drawBitmap(bitmap, 0, 0, null);
    }
}

Возникает ошибка: : Attempt to invoke virtual method 'boolean android.graphics.Bitmap.isRecycled()' on a null object reference. Почему возникает эта ошибка и как можно её решить без использования SurfaceView?


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

Автор решения: woesss
  1. Андроид запрещает выполнять сетевые операции в главном потоке. И собственно любые потенциально длительные операции выполнять в главном (он же UI) потоке - очень плохая практика. Это блокирует "отзывчивость" приложения, а то и всей системы. Поэтому подобные задачи следует выполнять в фоновых потоках (асинхронно). Сделать это можно множеством навороченных и не очень способов, в основе которых лежит класс потока Thread.
  2. метод onDraw() является частью подготовки кадра и в нём делать что-то длительное и тем более каждый раз - тоже очень плохая идея. Нужно сократить логику этого метода до минимально необходимой для вывода кадра - в идеале, ничего кроме вызовов canvas.drawXXX() там быть не должно

Пример "на коленке", надеюсь поймёте принцип

class Lesson extends View {

    // объявляем поле, в котором будем хранить ссылку на загруженный битмап
    private Bitmap bitmap;

    public Lesson(Context context) {
        super(context);
        // сразу при создании класса начинаем загрузку
        new Thread(() -> {
            try(InputStream stream = downloadImageFromUrl()) {
                bitmap = BitmapFactory.decodeStream(stream);
                if (bitmap == null) {
                     Log.e("Lesson", "что-то пошло не так");
                } else {
                    // вызываем перерисовку
                    postInvalidate();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }

    public InputStream downloadImageFromUrl() throws Exception {
        
        URL url = new URL("https://mysite.ru/image.png");
        HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();

        httpsURLConnection.setDoInput(true);
        httpsURLConnection.connect();

        return httpsURLConnection.getInputStream();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // проверяем есть ли картинка (возможно она ещё не загружена)
        if (bitmap != null) {
            canvas.drawBitmap(bitmap, 0, 0, null);
        }
    }
}
→ Ссылка