【问题标题】:I need to download several images to directory so that content can be accessed offline我需要将几张图片下载到目录中,以便可以离线访问内容
【发布时间】:2015-11-09 13:23:55
【问题描述】:

我得到一些 JSON 数据,其中包含一些食物菜单项

请注意:这只是一个示例,有时会有超过 2 张图像,并且数组中有更多菜单项!

{
  "menu": [
    {
      "url": "/api/v1/menu/1",
      "name": "Best Food",
      "description": "really nice food",
      "opening_time": "every day from 9am to 6pm",
      "contact_email": "info@food.com",
      "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)不是一个好方法。

所以我的应用中有一个部分可以查看保存的菜单,如果用户选择,可以选择下载以供离线访问。

此时值得一提的是,我已经在数据库中拥有原始菜单信息,因为用户必须首先查看菜单才能在数据库中获取它。

但问题是图像。

这是我不知道如何进行的地方,但我列出了我正在考虑的解决方案和问题,并希望人们能就最佳行动方案向我提供建议。

  1. 使用服务下载图像。我觉得这是强制性的,因为我不知道会有多少图像,并且我希望即使用户退出应用程序也能继续下载

  2. Glide 有一个只下载图片的选项,当我阅读 herehere 时,您可以配置其缓存的位置(内部私有或外部公共)。问题是我对设置缓存大小感到不舒服,因为我不知道需要什么。我想设置无限。

  3. 我需要能够删除已保存的菜单数据,尤其是如果它保存在外部公共目录中,因为在删除应用等时不会删除这些数据,或者如果用户选择从内部删除已保存的菜单应用程序。我在想我可以为此将文件图像 URI 或整个保存菜单的位置存储在数据库中,但不确定这是否是一个好方法

  4. 我阅读了不同的来源和答案,在这个用例中只是将图像缓存到 SD 卡等,我应该专门使用网络库来避免将位图分配到堆内存。我目前在我的应用中使用 OK HTTP。

【问题讨论】:

  • 让我知道,您是否需要 http 服务来下载无限图像。其实我不清楚你的问题。
  • "...设置缓存大小,因为我不知道需要什么。我想设置无限制。"我相信您的用户不希望您在他们的手机上存储无限的文件:)

标签: java android json caching android-glide


【解决方案1】:

我也在使用 ormlite 来存储带有 url 的对象,在我的应用程序的“登录”屏幕之后我有一个同步,根据我的经验,我真的推荐这个库 https://github.com/thest1/LazyList

很简单:

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

这个库使用外部 sd 上的 url 保存图像,对内存问题进行基本和简单的配置,所以如果你实际上有两个或多个具有相同 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】:

    1:使用 IntentService 进行下载。

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

    2:使用 AlarmManager 设置您的 IntentService,以便它运行,即使 应用程序未运行。您在 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( "你的目录");

    4:正在下载

    您的选择在这里。您可以使用从当前 API 到 基本 URLConnection。

    你可能想看看:

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

    另外,请注意您需要添加的权限 和

    希望这能为您指明一个有用的方向。

    【讨论】:

      【解决方案3】:

      试试这个library 来管理图片加载。

      使用服务下载图像。我觉得这是强制性的,因为 我不知道会有多少张图片,我要下载 即使用户退出应用程序也要继续

      所有下载都在工作线程中完成,因此在应用程序进程处于活动状态时它处于活动状态。可能会出现问题:应用程序在加载过程中死机。要解决此问题,我建议将AlarmManagerService 结合使用。将其设置为按计时器启动,检查数据库或 UIL 缓存中是否有未加载的图像文件并重新开始加载。

      Glide 有一个只下载图片的选项,你可以配置 我的缓存所在的位置(内部私有或外部公共) 在这里和这里阅读。问题是我对设置感到不舒服 缓存大小,因为我不知道需要什么。我想设置 无限制。

      UIL 有多种开箱即用的磁盘缓存实现,包括无限制的。它还为您提供缓存接口,以便您可以实现自己的。

      我需要能够删除保存的菜单数据,尤其是如果它 保存在外部公共目录中,因为当 应用程序被删除等,或者如果用户选择从 在应用程序内。我在想我可以存储文件图像 URI 或 整个保存菜单在数据库中的位置,但不确定是否 这是个好方法

      UIL 使用提供的文件链接为每个加载的文件生成唯一的文件名。您可以使用 JSON 中的链接删除任何加载的图像或取消任何下载。

      我在这个用例中阅读了不同的来源和答案 将图像缓存到 SD 卡等,我应该专门使用 网络库这样做是为了避免将位图分配到堆 记忆。我目前在我的应用程序中使用 OK HTTP。

      UIL 可以。它非常准确地管理内存,还为您提供了多种内存管理配置选项。例如,您可以在几个开箱即用的内存缓存实现之间进行选择。

      最后,我建议您访问上面的链接并自己阅读库文档/说明。它非常灵活,包含许多有用的功能。

      【讨论】:

        【解决方案4】:

        如果您希望即使用户退出也能继续下载,使用服务可能是一个不错的选择。卸载应用程序时,会自动删除使用 getExternalStorageDirectory() 创建的目录中存储的图像。此外,您可以检查内部存储器是否足够大以存储图像。如果您使用此方法,这些图像将在应用程序卸载时被删除。

        【讨论】:

          【解决方案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

              也许我不明白你的问题,但这没什么大不了的,先生, 当操作系统广播onBootCompleted 时,您的应用程序以您的应用程序启动的方式工作,然后创建一个Thread,您将在其中执行大量代码 - 获取您的 json 文件(当您需要它时),因为它是一个数组,您可以获得jsonObject 图像,无论是一千个还是百万个,您只需迭代它并使用任何方法下载它,我会说使用下载图像的传统方式,以便您更好地控制它。 在File 类的帮助下,您可以将它与Context 一起保存,您可以获得应用程序的缓存目录,这是一个内部存储器,将其保存在那里并在数据库中创建一个列,您可以在其中保存数据库中文件的路径作为字符串。

              当您的应用在 onPrepareOptionsMenu() 启动时,检查您的应用的缓存目录是否为空 - 如果不是,则您有一些文件,现在由于您拥有每个文件及其各自的路径,您可以使用 File.exist() 检查它是否存在无需下载。

              如果您需要节奏,您可以随时创建新线程。 Reciever 是在您的设备启动时收到通知的人,如果其他情况用于大量逻辑检查,for 用于您的循环,服务能够进行长时间运行的工作并有办法在UI 和后台线程。

              抱歉最后一段我只是想买空间:)

              【讨论】:

                猜你喜欢
                • 2014-05-01
                • 1970-01-01
                • 2023-03-27
                • 2016-06-07
                • 1970-01-01
                • 2015-10-18
                • 2011-12-03
                • 1970-01-01
                相关资源
                最近更新 更多