Implementation method of saving multiple pictures to local in Android

  • 2021-11-10 10:42:16
  • OfStack

01. Problems encountered in actually developing and saving pictures

Business requirements

In the 9-grid material of the material list page, show the pictures loaded by the network request. If the user clicks the Save button, several pictures are saved locally. To do this, use glide to load the picture, then set listener to listen, and save the picture resource resource to the collection after the picture request is successful onResourceReady. At this point, if you click Save Control, the collection of picture resources will be iterated over and saved to the local folder.

Specific practice code display

At this time, the picture of the requesting network is directly converted into bitmap, and then stored in the collection. Then when you click the Save button, multiple images from the collection will be saved to the local folder.


//bitmap Picture collection 
private ArrayList<Bitmap> bitmapArrayList = new ArrayList<>();


RequestOptions requestOptions = new RequestOptions()
 .transform(new GlideRoundTransform(mContext, radius, cornerType));
GlideApp.with(mIvImg.getContext())
 .asBitmap()
 .load(url)
 .listener(new RequestListener<Bitmap>() {
  @Override
  public boolean onLoadFailed(@Nullable GlideException e, Object model,
     Target<Bitmap> target, boolean isFirstResource) {
  return true;
  }

  @Override
  public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> target,
      DataSource dataSource, boolean isFirstResource) {
  bitmapArrayList.add(resource);
  return false;
  }
 })
 .apply(requestOptions)
 .placeholder(ImageUtils.getDefaultImage())
 .into(mIvImg);
 
 
 
// Loop through the collection of picture resources, and then start saving pictures to local folders 
mBitmap = bitmapArrayList.get(i);
savePath = FileSaveUtils.getLocalImgSavePath();
FileOutputStream fos = null;
try {
 File filePic = new File(savePath);
 if (!filePic.exists()) {
 filePic.getParentFile().mkdirs();
 filePic.createNewFile();
 }
 fos = new FileOutputStream(filePic);
 // 100  The picture quality is full 
 mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (IOException e) {
 e.printStackTrace();
 return null;
} finally {
 if (fos != null) {
 try {
  fos.flush();
  fos.close();
 } catch (IOException e) {
  e.printStackTrace();
 }
 }
 // Refresh album 
 if (isScanner) {
 scanner(context, savePath);
 }
}

Problems encountered

After saving the picture locally, it is found that the picture is not the original picture, but the cropped picture displayed on the view control, that is, the size picture of ImageView.

Why do you encounter such a problem

If you pass an ImageView as an argument to. into (), Glide will use the size of ImageView to limit the size of the picture. For example, if the picture to be loaded is 1000x1000 pixels, but the size of ImageView is only 250x250 pixels, Glide will reduce the picture to a small size to save processing time and memory.

After setting the into control, that is to say, the image resource resource returned in the onResourceReady method is not the original image you loaded, but the image with the size set by ImageView. So after saving, you will find that the picture has become smaller.

So how to solve the problem?

The first method: 9 grid picture control will load network resources when showing, and then after loading the picture successfully, save the resources to the collection, and click Save to store the resources in the collection cyclically. This approach will only request 1 network. Because of the beginning

The second method: When the 9-grid picture control is displayed, the network resources will be loaded. When clicking Save 9-grid pictures, the network picture resources will be requested in turn and then the pictures will be saved locally. This method will request the network twice.

02. Request pictures directly with http and save local

http Request Picture


/**
 *  Request Network Picture 
 * @param url   url
 */
private static long time = 0;
public static InputStream HttpImage(String url) {
 long l1 = System.currentTimeMillis();
 URL myFileUrl = null;
 Bitmap bitmap = null;
 HttpURLConnection conn = null;
 InputStream is = null;
 try {
 myFileUrl = new URL(url);
 } catch (MalformedURLException e) {
 e.printStackTrace();
 }
 try {
 conn = (HttpURLConnection) myFileUrl.openConnection();
 conn.setConnectTimeout(10000);
 conn.setReadTimeout(5000);
 conn.setDoInput(true);
 conn.connect();
 is = conn.getInputStream();
 } catch (IOException e) {
 e.printStackTrace();
 } finally {
 try {
  if (is != null) {
  is.close();
  conn.disconnect();
  }
 } catch (IOException e) {
  e.printStackTrace();
 }
 long l2 = System.currentTimeMillis();
 time = (l2-l1) + time;
 LogUtils.e(" Millisecond value "+time);
 // Save 
 }
 return is;
}
```

Save to Local


InputStream inputStream = HttpImage(
 "https://img1.haowmc.com/hwmc/material/2019061079934131.jpg");
String localImgSavePath = FileSaveUtils.getLocalImgSavePath();
File imageFile = new File(localImgSavePath);
if (!imageFile.exists()) {
 imageFile.getParentFile().mkdirs();
 try {
 imageFile.createNewFile();
 } catch (IOException e) {
 e.printStackTrace();
 }
}
FileOutputStream fos = null;
BufferedInputStream bis = null;
try {
 fos = new FileOutputStream(imageFile);
 bis = new BufferedInputStream(inputStream);
 byte[] buffer = new byte[1024];
 int len;
 while ((len = bis.read(buffer)) != -1) {
 fos.write(buffer, 0, len);
 }
} catch (Exception e) {
 e.printStackTrace();
} finally {
 try {
 if (bis != null) {
  bis.close();
 }
 if (fos != null) {
  fos.close();
 }
 } catch (IOException e) {
 e.printStackTrace();
 }
}

03. Download pictures with glide and save them locally

glide Download Picture


File file = Glide.with(ReflexActivity.this)
 .load(url.get(0))
 .downloadOnly(500, 500)
 .get();

Save to Local


String localImgSavePath = FileSaveUtils.getLocalImgSavePath();
File imageFile = new File(localImgSavePath);
if (!imageFile.exists()) {
 imageFile.getParentFile().mkdirs();
 imageFile.createNewFile();
}
copy(file,imageFile);

/**
 *
 * @param source  Input file 
 * @param target  Output file 
 */
public static void copy(File source, File target) {
 FileInputStream fileInputStream = null;
 FileOutputStream fileOutputStream = null;
 try {
 fileInputStream = new FileInputStream(source);
 fileOutputStream = new FileOutputStream(target);
 byte[] buffer = new byte[1024];
 while (fileInputStream.read(buffer) > 0) {
  fileOutputStream.write(buffer);
 }
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 try {
  if (fileInputStream != null) {
  fileInputStream.close();
  }
  if (fileOutputStream != null) {
  fileOutputStream.close();
  }
 } catch (IOException e) {
  e.printStackTrace();
 }
 }
}
```

04. How to save multiple pictures continuously

Idea: Loop subthreads

Feasible (not recommended), if I want to download 9 pictures, add sub-threads to the for loop and finally render them. There are serious defects, thread delay, and picture order cannot be guaranteed. If it is a threaded thread, the first subthread is finished, and the subthread nested in the or loop of the subthread f is not finished, so the main thread cannot get the pictures obtained in the subthread. There is also how to judge that all threads are finished, for example, after all pictures are downloaded, toast is downloaded.

An unrecommended scheme

Create a thread pool to manage threads. About the thread pool encapsulation library, you can see the simple encapsulation of thread pool

This scheme does not know whether all the requested pictures in all threads are completed, and there is no guarantee of order.


ArrayList<String> images = new ArrayList<>();
for (String image : images){
 // Use this thread pool, in time run Method does not crash 
 PoolThread executor = BaseApplication.getApplication().getExecutor();
 executor.setName("getImage");
 executor.execute(new Runnable() {
  @Override
  public void run() {
   // Request network pictures and save them to local operation 
  }
 });
}

Recommended solutions


ArrayList<String> images = new ArrayList<>();
ApiService apiService = RetrofitService.getInstance().getApiService();
// Note: Here is to save multiple pictures, and asynchronous threads can be used 
ArrayList<Observable<Boolean>> observables = new ArrayList<>();
final AtomicInteger count = new AtomicInteger();
for (String image : images){
 observables.add(apiService.downloadImage(image)
   .subscribeOn(Schedulers.io())
   .map(new Function<ResponseBody, Boolean>() {
    @Override
    public Boolean apply(ResponseBody responseBody) throws Exception {
     saveIo(responseBody.byteStream());
     return true;
    }
   }));
}
// observable Adj. merge  Will all the observable Synthesis 1 A Observable , all of them observable Simultaneous transmission of data 
Disposable subscribe = Observable.merge(observables).observeOn(AndroidSchedulers.mainThread())
  .subscribe(new Consumer<Boolean>() {
   @Override
   public void accept(Boolean b) throws Exception {
    if (b) {
     count.addAndGet(1);
     Log.e("yc", "download is succcess");

    }
   }
  }, new Consumer<Throwable>() {
   @Override
   public void accept(Throwable throwable) throws Exception {
    Log.e("yc", "download error");
   }
  }, new Action() {
   @Override
   public void run() throws Exception {
    Log.e("yc", "download complete");
    //  Number of successful downloads   And   Number of picture collections 1 To, it means that all downloads were successful 
    if (images.size() == count.get()) {
     ToastUtils.showRoundRectToast(" Save successfully ");
    } else {
     if (count.get() == 0) {
      ToastUtils.showRoundRectToast(" Save failed ");
     } else {
      ToastUtils.showRoundRectToast(" Due to network problems   Save successfully " + count + ", Save failed " + (images.size() - count.get()));
     }
    }
   }
  }, new Consumer<Disposable>() {
   @Override
   public void accept(Disposable disposable) throws Exception {
    Log.e("yc","disposable");
   }
  });
  
  
  
private void saveIo(InputStream inputStream){
 String localImgSavePath = FileSaveUtils.getLocalImgSavePath();
 File imageFile = new File(localImgSavePath);
 if (!imageFile.exists()) {
  imageFile.getParentFile().mkdirs();
  try {
   imageFile.createNewFile();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 FileOutputStream fos = null;
 BufferedInputStream bis = null;
 try {
  fos = new FileOutputStream(imageFile);
  bis = new BufferedInputStream(inputStream);
  byte[] buffer = new byte[1024];
  int len;
  while ((len = bis.read(buffer)) != -1) {
   fos.write(buffer, 0, len);
  }
 } catch (Exception e) {
  e.printStackTrace();
 } finally {
  try {
   if (bis != null) {
    bis.close();
   }
   if (fos != null) {
    fos.close();
   }
  } catch (IOException e) {
   e.printStackTrace();
  }
  // Refresh album code omitted … 
 }
}

Link address: https://github.com/yangchong2...

Summarize


Related articles: