📜 ⬆️ ⬇️

Caching images on the SD card

Most recently, the user sly2m described his method of saving images from the ImageView to the phone’s SD card . Someone (for example, I personally) expected something different from this post, namely:

1 . Work with images from the Internet
2 Automatic loading and saving of such images
3 Advanced image caching

If interested - please take a look.

So, we will write a class that will perform all three tasks that we need.
')
To begin with, we will create a class framework and a couple of additional methods that will be useful to us further:
package com.habra.imagemanager; public class ImageManager { public String md5(String s) { try { MessageDigest digest = java.security.MessageDigest.getInstance("MD5"); digest.update(s.getBytes()); byte messageDigest[] = digest.digest(); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < messageDigest.length; i++) { hexString.append(Integer.toHexString(0xFF & messageDigest[i])); } return hexString.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return ""; } public static void fileSave(InputStream is, FileOutputStream outputStream) { int i; try { while ((i = is.read()) != -1) { outputStream.write(i); } } catch (IOException e) { e.printStackTrace(); } } } 


I think the purpose of the md5 function is clear, but the fileSave function will save any InputStream to FileOutputStream, that is, download our pictures from the network and save them to the SD card.

Next, create a vector downloaded to store already loaded images, and a method for working with such a vector.
This is necessary to eliminate such an unpleasant effect as the execution of several download threads at once, for example, for ImageView elements inside the ListView.

 private Vector<ImageView> downloaded = new Vector<ImageView>(); public boolean findObject(ImageView object) { for (int i = 0; i < downloaded.size(); i++) { if (downloaded.elementAt(i).equals(object)) { return true; } } return false; } 


The findObject method will search for queued images and return true if it is found.

And now there are two main methods in our class:

 private Bitmap downloadImage(Context context, int cacheTime, String iUrl, ImageView iView) { Bitmap bitmap = null; if (cacheTime != 0) { File file = new File(context.getExternalCacheDir(), md5(iUrl) + ".cache"); long time = new Date().getTime() / 1000; long timeLastModifed = file.lastModified() / 1000; try { if (file.exists()) { if (timeLastModifed + cacheTime < time) { file.delete(); file.createNewFile(); fileSave(new URL(iUrl).openStream(), new FileOutputStream(file)); } } else { file.createNewFile(); fileSave(new URL(iUrl).openStream(), new FileOutputStream( file)); } bitmap = BitmapFactory.decodeStream(new FileInputStream(file)); } catch (Exception e) { e.printStackTrace(); } if (bitmap == null) { file.delete(); } } else { try { bitmap = BitmapFactory.decodeStream(new URL(iUrl).openStream()); } catch (Exception e) { e.printStackTrace(); } } if (iView != null) { downloaded.remove(iView); } return bitmap; } public void fetchImage(final Context context, final int cacheTime, final String url, final ImageView iView) { if (iView != null) { if (findObject(iView)) { return; } downloaded.add(iView); } new AsyncTask<String, Void, Bitmap>() { protected Bitmap doInBackground(String... iUrl) { return downloadImage(context, cacheTime, iUrl[0], iView); } protected void onPostExecute(Bitmap result) { super.onPostExecute(result); if (iView != null) { iView.setImageBitmap(result); } } }.execute(new String[] { url }); } 


So, the downloadImage method (Activity activity, int cacheTime, String iUrl, ImageView iView) performs the image downloading and caching.
Its parameters are:


But we will not work with it (didn’t notice the private? Modifier), but with the fetchImage function, which accepts the same parameters as in downloadImage. The fetchImage method itself keeps track of the list of loading and setting of images in the ImageView.
In my humble opinion, the code is intuitive and does not need comments. At the end of my post I will give an example of using the class:

 ImageManager man = new ImageManager(); ImageView i1 = (ImageView) findViewById(R.id.i1); ImageView i2 = (ImageView) findViewById(R.id.i2); ImageView i3 = (ImageView) findViewById(R.id.i3); man.fetchImage(this, 3600, "http://habrastorage.org/storage1/51624865/5d7f2b56/333c3c3f/fa5cdc9b.png", i1); man.fetchImage(this, 3600, "http://habrastorage.org/storage1/9042dd3c/acc1f8b3/782ca380/c05ecaf3.png", i2); man.fetchImage(this, 3600, "http://habrastorage.org/storage1/39a0bbce/4f56b8c7/ca84d78f/8b7bf972.png", i3); 


UPDATE: Class Source Code
UPDATE 2: Rewrote method fetchImage using AsyncTask
UPDATE 3: Activity changed to Context

Source: https://habr.com/ru/post/133239/


All Articles