Мне нужно загрузить несколько изображений в каталог, чтобы содержимое можно было получить в автономном режиме

Я получаю некоторые данные JSON, содержащие некоторые пункты меню продуктов

Обратите внимание: это всего лишь образец, в нем больше двух изображений и много других элементов меню!

{
  "menu": [
    {
      "url": "/api/v1/menu/1",
      "name": "Best Food",
      "description": "really nice food",
      "opening_time": "every day from 9am to 6pm",
      "contact_email": "[email protected]",
      "tel_number": "+54 911 3429 5762",
      "website": "http://bestfood.com",
      "images": [
        {
          "url": "https://blahblah/image1.jpg"
        },
        {
          "url": "https://blahblah/image2.jpg"
        }
      ]
    },

  ]
}

Каждый элемент имеет некоторую информацию и массив URL-адресов изображений.

Я использую библиотеку изображений Glide для обработки этих изображений и Retrofit 2.0 для загрузки данных JSON с конечной точки. Все хорошо на данный момент.

Однако мне нужно сохранить эти загруженные данные для автономного доступа.

В настоящее время я использую ORM Lite для своих существующих моделей для хранения всех данных JSON в базе данных. Эта часть в порядке.

Однако в моей базе данных я сохраняю только URL-адреса изображений, так как мне сказали, что это неплохой подход для хранения изображений (как blob) в базе данных.

Итак, в моем приложении есть раздел для просмотра сохраненного меню с возможностью его загрузки для автономного доступа, если пользователь выбирает.

На данном этапе стоит упомянуть, что у меня уже есть необработанная информация меню в базе данных, потому что пользователю нужно будет сначала просмотреть меню, чтобы получить его в БД.

Но проблема заключается в изображениях.

Вот где я не знаю, как действовать, но я перечисляю решения и проблемы, о которых я думаю, и надеялся, что люди могут посоветовать мне, что является лучшим способом действий.

  • Используйте сервис для загрузки изображений. Это я считаю обязательным, потому что я не знаю, сколько будет изображений, и я хочу, чтобы загрузка продолжалась, даже если пользователь выходит из приложения

  • У Glide есть опция только для загрузки изображений, и вы можете настроить, где находится ее кеш (внутренняя частная или внешняя публикация), когда я читаю здесь и здесь. Проблема в том, что мне не очень удобно устанавливать размер кеша, поскольку я не знаю, что требуется. Я хотел бы установить неограниченное количество.

  • Мне нужно иметь возможность удалять сохраненные данные меню, особенно если он сохранен во внешнем общедоступном каталоге, поскольку это не удаляется при удалении приложения и т.д., или если пользователь хочет удалить сохраненное меню изнутри приложение. Я думал, что я могу хранить URI файла изображения или местоположение всего сохраненного меню в базе данных для этого, но не уверен, что это хороший способ

  • Я читал в разных источниках и ответах, что в этом случае для простого кэширования изображений на SD-карте и т.д. я должен специально использовать сетевую библиотеку для этого, чтобы избежать выделения растрового изображения в кучу памяти. В настоящее время я использую OK HTTP в своем приложении.

Ответ 1

Я использую ormlite для хранения объектов с помощью URL-адресов, у меня есть синхронизация после экрана входа в приложение, по моему опыту я действительно рекомендую эту библиотеку https://github.com/thest1/LazyList

Это очень просто:

ImageLoader imageLoader=new ImageLoader(context);
imageLoader.DisplayImage(url, imageView);

Эта библиотека сохраняет изображение с помощью URL-адреса внешнего sd с базовой и простой конфигурацией проблем памяти, поэтому, если у вас на самом деле есть два или более элемента с одинаковым URL-адресом, эта библиотека отлично работает, url и imageView являются параметрами, если изображение не на телефоне, начинает новую задачу и помещает изображение в вид, когда загрузка заканчивается, и, кстати, эта библиотека также сохраняет кодированные изображения, поэтому эти изображения не отображаются в галерее. На самом деле вам нужны только эти файлы для реализации библиотеки: https://github.com/thest1/LazyList/tree/master/src/com/fedorvlasov/lazylist

Если вы хотите манипулировать некоторыми файлами, вы можете изменить имя папки в классе FileCache:

 public FileCache(Context context){
    //Find the dir to save cached images
    ...
        cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
    ...
}

Где "LazyList" - это имя папки, и их можно удалить, переместить и т.д. Удалить образец:

 /**
 * This method delete a file if exist
 */
public static void deleteFile(File file){
    if(file!=null && file.exists()) {
        file.delete();
    }
}

Теперь я узнал больше о кеше памяти и распределении растрового изображения в кучу памяти, впервые манипулируя изображениями онлайн и офлайн, я рекомендую эту библиотеку, также когда вы узнаете больше об этом, вы можете реализовать и отредактировать библиотеку к вашим потребностям.

Ответ 2

Использование службы может быть хорошим вариантом, если вы хотите, чтобы загрузка продолжалась, даже если пользователь выходит. Изображения, которые хранятся в каталогах, созданных с помощью getExternalStorageDirectory(), автоматически удаляются, когда ваше приложение удаляется. Кроме того, вы можете проверить, достаточно ли объем внутренней памяти для хранения изображений. Если вы используете эти методы, эти изображения будут удалены при удалении приложения.

Ответ 3

1: используйте IntentService для выполнения ваших загрузок.

http://developer.android.com/reference/android/app/IntentService.html

2: настройте свой IntentService с помощью AlarmManager, чтобы он работал, даже если  приложение не работает. Вы регистрируетесь в AlarmManager

http://developer.android.com/reference/android/app/AlarmManager.html

Существует множество способов, с помощью которых AlarmManager запускает  намерение.

Например:  // Регистрируем первый запуск, а затем интервал для повторных циклов.                                                                               alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,               SystemClock.elapsedRealtime() + DEFAULT_INITIAL_RUN,               DEFAULT_RUN_INTERVAL, pi);

3: Хранение данных  Здесь есть несколько вариантов, в зависимости от того, насколько  изображения/данные.

http://developer.android.com/reference/android/os/Environment.html

Пример: внешнее публичное хранилище  Файл dirBackup = Environment.getExternalStoragePublicDirectory(                               "YourDirectory" );

4: Загрузка

Ваш вариант здесь. Вы можете использовать что-либо из вашего текущего API для  базовое URL-соединение.

Вы можете посмотреть:

http://developer.android.com/reference/android/app/DownloadManager.html

Также следите за своими разрешениями, которые вам нужно добавить а также  

Надеюсь, это указывает на полезное направление.

Ответ 4

Попробуйте эту библиотеку для управления загрузкой изображений.

Используйте сервис для загрузки изображений. Это я считаю обязательным, потому что Я не знаю, сколько будет изображений, и я хочу загрузить для продолжения, даже если пользователь выходит из приложения

Вся загрузка выполняется в рабочих потоках, чтобы она была жива, пока процесс приложения жив. Там может возникнуть проблема: приложение умирает во время загрузки. Чтобы обойти это, я предлагаю использовать AlarmManager в комбинации с Service. Установите его для запуска по таймеру, проверьте, что вы загружаете базу данных или кеш UIL, чтобы файлы изображений не были загружены и снова начали загрузку.

У Glide есть опция только для загрузки изображений, и вы можете настроить где находится его кеш (внутренняя частная или внешняя публика), поскольку я читайте здесь и здесь. Проблема в том, что я не чувствую себя комфортно с настройкой размер кеша, поскольку я не знаю, что требуется. Я хотел бы установить неограничен.

UIL имеет несколько реализаций кэш-памяти из коробки, включая неограниченное. Он также предоставляет интерфейс кэша, чтобы вы могли реализовать свои собственные.

Мне нужно иметь возможность удалять сохраненные данные меню, особенно если его сохранен во внешнем общедоступном каталоге, поскольку это не удаляется, если приложение удаляется и т.д., или если пользователь хочет удалить сохраненное меню из в приложении. Я думал, что могу хранить URI файлов изображений или расположение всего сохраненного меню в базе данных для этого, но не уверен, что это хороший способ

UIL генерирует уникальное имя файла для каждого загруженного файла, используя предоставленную ссылку на файл. Вы можете удалить любое загруженное изображение или отменить любую загрузку, используя ссылку из своего JSON.

Я читал в разных источниках и ответах, что в этом случае для просто кэширование изображений на SD-карту и т.д., что я должен специально использовать сетевую библиотеку, чтобы избежать выделения растрового изображения в кучу Память. В настоящее время я использую OK HTTP в своем приложении.

UIL делает все в порядке. Он очень точно управляет памятью и предоставляет вам несколько вариантов конфигурации управления памятью. Например, вы можете выбирать между несколькими реализациями кэша памяти из коробки.

В заключение я предлагаю вам посетить приведенную выше ссылку и прочитать документацию/описание библиотеки самостоятельно. Он очень гибкий и содержит множество полезных функций.

Ответ 5

Я использую этот класс при загрузке изображений, он кэширует изображения, в следующий раз, когда вы их загружаете, он просто загружается из внешней памяти, он управляет кешем для вас, так что вам не придется беспокоиться о том, или неограниченно, довольно эффективно и быстро.

public class ImageLoader {

MemoryCache memoryCache = new MemoryCache();
 FileCache fileCache;
private Map<ImageView, String> imageViews = Collections
        .synchronizedMap(new WeakHashMap<ImageView, String>());
ExecutorService executorService;
// Handler to display images in UI thread
Handler handler = new Handler();

public ImageLoader(Context context) {
    fileCache = new FileCache(context);
    executorService = Executors.newFixedThreadPool(5);
}

final int stub_id = R.drawable.placeholder;

public void DisplayImage(String url, ImageView imageView) {
    imageViews.put(imageView, url);



    Bitmap bitmap = memoryCache.get(url);
    if (bitmap != null)
        imageView.setImageBitmap(bitmap);
    else {
        queuePhoto(url, imageView);
        imageView.setImageResource(stub_id);
    }
}

private void queuePhoto(String url, ImageView imageView) {
    PhotoToLoad p = new PhotoToLoad(url, imageView);
    executorService.submit(new PhotosLoader(p));
}

private Bitmap getBitmap(String url) {
    File f = fileCache.getFile(url);

    Bitmap b = decodeFile(f);
    if (b != null)
        return b;

    // Download Images from the Internet
    try {
        Bitmap bitmap = null;
        URL imageUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) imageUrl
                .openConnection();
        conn.setConnectTimeout(30000);
        conn.setReadTimeout(30000);
        conn.setInstanceFollowRedirects(true);
        InputStream is = conn.getInputStream();
        OutputStream os = new FileOutputStream(f);
        Utils.CopyStream(is, os);
        os.close();
        conn.disconnect();
        bitmap = decodeFile(f);
        return bitmap;
    } catch (Throwable ex) {
        ex.printStackTrace();
        if (ex instanceof OutOfMemoryError)
            memoryCache.clear();
        return null;
    }
}

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        FileInputStream stream1 = new FileInputStream(f);
        BitmapFactory.decodeStream(stream1, null, o);
        stream1.close();

        // Find the correct scale value. It should be the power of 2.
        // Recommended Size 512
        final int REQUIRED_SIZE = 70;
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_SIZE
                    || height_tmp / 2 < REQUIRED_SIZE)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        FileInputStream stream2 = new FileInputStream(f);
        Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
        stream2.close();
        return bitmap;
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

// Task for the queue
private class PhotoToLoad {
    public String url;
    public ImageView imageView;

    public PhotoToLoad(String u, ImageView i) {
        url = u;
        imageView = i;
    }
}

class PhotosLoader implements Runnable {
    PhotoToLoad photoToLoad;

    PhotosLoader(PhotoToLoad photoToLoad) {
        this.photoToLoad = photoToLoad;
    }

    @Override
    public void run() {
        try {
            if (imageViewReused(photoToLoad))
                return;
            Bitmap bmp = getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);
            if (imageViewReused(photoToLoad))
                return;
            BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);
            handler.post(bd);
        } catch (Throwable th) {
            th.printStackTrace();
        }
    }
}

boolean imageViewReused(PhotoToLoad photoToLoad) {
    String tag = imageViews.get(photoToLoad.imageView);
    if (tag == null || !tag.equals(photoToLoad.url))
        return true;
    return false;
}

// Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable {
    Bitmap bitmap;
    PhotoToLoad photoToLoad;

    public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
        bitmap = b;
        photoToLoad = p;
    }

    public void run() {
        if (imageViewReused(photoToLoad))
            return;
        if (bitmap != null)
            photoToLoad.imageView.setImageBitmap(bitmap);
        else
            photoToLoad.imageView.setImageResource(stub_id);
    }
}

public void clearCache() {
    memoryCache.clear();
    fileCache.clear();
}

}

Чтобы использовать его Просто создайте экземпляр из него, например

 ImageLoader Imageloaer = new ImageLoader(getBaseContext());
    Imageloaer.DisplayImage(imageUrl, imageView);

Ответ 6

Вы должны попробовать скачать с этим,

  class DownloadFile extends AsyncTask<String,Integer,Long> {
    ProgressDialog mProgressDialog = new ProgressDialog(MainActivity.this);// Change Mainactivity.this with your activity name. 
    String strFolderName;
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mProgressDialog.setMessage("Downloading Image ...");
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setMax(100);
        mProgressDialog.setCancelable(false);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.show();
    }
    @Override
    protected Long doInBackground(String... aurl) {
        int count;
        try {
            URL url = new URL((String) aurl[0]);
            URLConnection conexion = url.openConnection();
            conexion.connect();
            String targetFileName="downloadedimage.jpg";//Change name and subname

            int lenghtOfFile = conexion.getContentLength();
            String PATH = Environment.getExternalStorageDirectory()+"/myImage/";
            File folder = new File(PATH);
            if(!folder.exists()){
                folder.mkdir();//If there is no folder it will be created.
            }
            InputStream input = new BufferedInputStream(url.openStream());
            OutputStream output = new FileOutputStream(PATH+targetFileName);
            byte data[] = new byte[1024];
            long total = 0;
            while ((count = input.read(data)) != -1) {
                total += count;
                       publishProgress ((int)(total*100/lenghtOfFile));
                output.write(data, 0, count);
            }
            output.flush();
            output.close();
            input.close();
        } catch (Exception e) {}
        return null;
    }
    protected void onProgressUpdate(Integer... progress) {
         mProgressDialog.setProgress(progress[0]);
         if(mProgressDialog.getProgress()==mProgressDialog.getMax()){
            mProgressDialog.dismiss();

            Toast.makeText(getApplicationContext(), "Download Completed !", Toast.LENGTH_LONG).show();

         }
    }
    protected void onPostExecute(String result) {
    }
}

Этот код позволит вам загрузить весь URL-адрес изображений,

  new DownloadFile().execute("https://i.stack.imgur.com/w4kCo.jpg");

.....

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

Измените имя папки по своему усмотрению и попробуйте установить это изображение в растровое изображение приложения, а также избегайте повернуть ошибку изображений, используя это.

Ответ 7

Важные вещи, о которых стоит подумать, -

Thread, Service, File, Json, Context, Receiver и if else и for

Возможно, я не понял ваш вопрос, но это не очень важно, сэр, ваша программа вашего приложения будет работать так, когда ваше приложение начнет работу с os broadcast onBootCompleted, а затем создайте Thread, где вы собираетесь делать много кода - получите свой json файл (если вам это нужно), поскольку его массив, который вы получаете ваши изображения jsonObject, будь то тысяча или миллион, вы просто перебираете его и используете любой способ его загрузки, я бы сказал, используя традиционный способ загрузки ваших изображений, чтобы вы лучше его контролировали. С помощью класса File вы сохраните его вместе с Context, вы можете получить каталог вашего кеша приложения, который хранит его внутреннюю память, и создать столбец в базе данных, где вы можете сохранить путь к файлу в своей базе данных как строка.

Когда ваше приложение запустится в onPrepareOptionsMenu(), проверьте, пуст ли ваш каталог кеша приложений, если у вас нет файлов, теперь, поскольку у вас есть каждый файл и его соответствующий путь, вы можете проверить, существует ли он с File.exist(), если он нет необходимости загружать.

Если вам нужен темп, вы всегда можете создавать новые темы. Reciever должен был быть парнем, который получает уведомление, когда ваше устройство загружается, если другое для большой логической проверки, for для ваших циклов, служба, чтобы иметь возможность выполнять длительную работу и иметь способ связи между UI и фоновый поток.

Извините за последний абзац, я просто пытался купить место:)