Android实现单线程异步多文件下载的代码

简介:

 现在有一个需求,需要下载的文件有很多个,放在一个开放端口(URL)的目录下,这个目录下的文件会变动,就是文件名不定。现在需要把这个目录下的文件下载到Android设备上。我找了很多资料,发现不能把一个URL目录下的全部文件以文件名的方式列出来(?)。那么,把需要下载的多个文件打包成一个.zip文件放到URL上,下载下来后再通过代码解压,解压时如果有中文名的文件,不能正常解压(这个在另一篇文章中记载)。走另一条道路:需要下载的文件关联到一个点菜系统的菜单的全部图片,文件名就是菜单的图片名。点菜系统的菜单可以从服务器的数据库中查询到。这样就把菜单全部遍历一遍,每个菜单的图片名(如果有的话)和URL目录拼成文件下载路径(经理提出的方法),使用循环遍历下载。

/**
  * 准备下载菜谱图片,检查SDcard是否可用,设置下载更新地址,下载后保存位置信息。
  */
 private void downloadMenuImageCheck() {
  String status = Environment.getExternalStorageState();
  if (!Environment.MEDIA_MOUNTED.equals(status)) {
   ToastUtils.showFailure(getApplicationContext(),
     "SDcard cannot use!");
   return;
  }

  RequestFileInfo requestFileInfo = new RequestFileInfo();
  requestFileInfo.fileParentUrl = "http://192.168.1.103:8181/menupic";
  requestFileInfo.saveFilePath = Environment
    .getExternalStorageDirectory().getAbsolutePath()
    + "/waitab/menupic";

  showDialog(DialogUtil.DIALOG_DOWNLAOD_IMAGE_PROGRESS);   
  new DownlaodUpdateTask().execute(requestFileInfo);
 }

 /**
  * 找出所有菜品数据
  * @return List<menux>
  */
 private List<menux> updateMenux() {
  return mdb.findAll();
 }

 /**
  * 找出所有菜品数据
  * 下载菜品图片类 
  * 遍历菜品数据List,根据每一个菜品图片名字拼出下载路径
  * 多文件循环遍历单线程下载
  * 
  * @author modify by ZX
  * 
  */
 private class DownlaodUpdateTask extends
   AsyncTask<requestfileinfo progressvalue="" basiccallresult=""> {

  @Override
  protected BasicCallResult doInBackground(RequestFileInfo... params) {
   Log.d(TAG, "start doInBackground!"); 
   final RequestFileInfo req = params[0];
   ProgressValue progressValue = new ProgressValue(0, getString(R.string.msg_fetching_menu));
   publishProgress(progressValue);
   
   List<menux> menuxs ;
   menuxs = updateMenux();
   if (menuxs == null) {
    dismissDialog(DialogUtil.DIALOG_DOWNLAOD_IMAGE_PROGRESS); 
    return new BasicCallResult(
      "Can not get menu data! ", false); 
   }
   
   // 根据文件路径创建目录
   File basePath = new File(req.saveFilePath);
   if (!basePath.exists())
    basePath.mkdirs();
  
   int needDownloadImageCount = 0;//记录需要下载的图片数
   int finishDownloadImageCount = 0;//记录完成下载的图片数
   long startTime;
   long endTime;
   int length = 0;
   double totalLength = 0;
   int count = 0;
   
   class ImageInfo{
    public String imageName;
    public String imageMenuTypeGroupx;
    public HttpURLConnection conn;
   }
   List<imageinfo> imageInfos = new ArrayList<imageinfo>();
   
   //遍历菜品,取得需要下载的图片文件的总数量和能连接上的图片文件的总大小和信息
   for (Menux menux : menuxs) {
    try {
     String imageName = menux.getPicname();// 菜品图片名
     if (StringUtils.isNotEmpty(imageName)) {
      needDownloadImageCount += 1;
      URL url = new URL(req.fileParentUrl+ File.separator  
          + java.net.URLEncoder.encode(imageName,
            "UTF-8")); // 如果菜品名是中文,为了解决乱码问题,改变编码方式
      HttpURLConnection conn = (HttpURLConnection) url
        .openConnection();// throws IOException
      Log.i(TAG, "response code:" + conn.getResponseCode());
      if (HttpURLConnection.HTTP_OK == conn.getResponseCode()) {
       length += conn.getContentLength();
       String imageMenuTypeGroupx = menux.getMenutype() + menux.getGroupx();// 菜品分类
       
       ImageInfo imageInfo = new ImageInfo();
       imageInfo.imageName = imageName;
       imageInfo.imageMenuTypeGroupx = imageMenuTypeGroupx;
       imageInfo.conn = conn;
       imageInfos.add(imageInfo);
      }
     }
    }catch(IOException e){
     e.printStackTrace();
    }
   }
   
   if(imageInfos.size() == 0){
    return new BasicCallResult("No image need download! ", false);
   }
   totalLength = StringUtils.bytes2M(length);
   progressValue = new ProgressValue(0, getString(R.string.msg_start_download_menuimage));
   publishProgress(progressValue); 
   
   startTime = System.currentTimeMillis();
   
   // 遍历能够连接上的图片信息下载
   for (ImageInfo imageInfo : imageInfos) {
    try {
     /*
     String imageName = menux.getPicname();// 菜品图片名
     if (StringUtils.isNotEmpty(imageName)) { 
      String imageMenuTypeGroupx = menux.getMenutype()
        + menux.getGroupx();// 菜品分类
      URL url = new URL(req.fileParentUrl+ File.separator  
          + java.net.URLEncoder.encode(imageName,
            "UTF-8")); // 如果菜品名是中文,为了解决乱码问题,改变编码方式
      HttpURLConnection conn = (HttpURLConnection) url
        .openConnection();// throws IOException
      Log.i(TAG, "response code:" + conn.getResponseCode());
      if (HttpURLConnection.HTTP_OK == conn.getResponseCode()) {
      */
     String imageMenuTypeGroupx = imageInfo.imageMenuTypeGroupx;
     String imageName = imageInfo.imageName;
     InputStream is = imageInfo.conn.getInputStream();
       
       /*
        * 用这几种方式打开 is 都可以,用上面一种方式测试是否连接上,若连接上了,再做下载处理
        * 
       InputStream is = conn.getURL().openStream();
       InputStream is = url.openStream();
                            */
       
       // 根据菜品分类创建下一级目录
       File path;
       if (StringUtils.isNotEmpty(imageMenuTypeGroupx)) {
        path = new File(req.saveFilePath
          + File.separator + imageMenuTypeGroupx);
        if (!path.exists())
         path.mkdir();
       } else {
        path = new File(req.saveFilePath);
       }

       File imageFile = new File(path, imageName);
       FileOutputStream fos = new FileOutputStream(
         imageFile);
       //progressValue = new ProgressValue(0, " downloading:");
       byte buffer[] = new byte[1024];
       Log.d(TAG, "preper buffer!");
       finishDownloadImageCount += 1;
       do {
        int numread = is.read(buffer);    
        if (numread <= 0) {
         // publish end
         break;
        }
        count += numread;
        fos.write(buffer, 0, numread);
        Log.d(TAG, "start fos.write!");  
        
        endTime = System.currentTimeMillis();
        double currentLength = StringUtils.bytes2M(count); 
        double kbPerSecond = count * 1f / (endTime - startTime);//即时下载速度因精确到毫秒级的时间,
                                                                      //时间单位太大,会出现 endTime - startTime = 0 的情况,使得
                                                                      // kbPerSecond无限大,改为计算平均下载速度。完整表达式应为
                                                                      //(count / 1000f) / ((endTime - startTime) / 1000f),
                                                                      // b/ms = Kb/S   (b=byte)   
        double downloadTotalTime = (endTime - startTime) / 1000f;
        progressValue.message = String.format(
          "%d/%d\t\t%.2f M/%.2f M\t\t%.2fKb/S\t\t\t%.1f S ", finishDownloadImageCount, needDownloadImageCount,
          currentLength, totalLength, kbPerSecond, downloadTotalTime);
        progressValue.progress = (int) ((((float) count) / length) * DialogUtil.LONG_PROGRESS_MAX);
        publishProgress(progressValue);  

       } while (true);
       fos.flush();
       fos.close();
       is.close();
      //}
     } catch (MalformedURLException e) {
     e.printStackTrace();
     return new BasicCallResult("Wrong url! ", false);
      } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
     return new BasicCallResult("Error: "
       + e.getLocalizedMessage(), false);
    }
   }

   BasicCallResult callResult = new BasicCallResult(
     "download finish!", true);
   callResult.data = needDownloadImageCount + "";//int转String
   callResult.data2 = String.valueOf(finishDownloadImageCount);
   return callResult;
  }

  @Override
  protected void onPostExecute(BasicCallResult result) {
   if (result.ok) {
    Log.d(TAG, "download menu image success!"); 
    mProgressDialog.setMessage(getString(R.string.msg_download_image_success));
    //ToastUtils.showSuccess(getApplicationContext(), "congratulation!  download success!");
    if(Integer.parseInt(result.data) > Integer.parseInt(result.data2)){
     ToastUtils.showLong(getApplicationContext(),"needDownload:" + result.data + "   "
       + "finishDownload:" + result.data2 + "   "
       + "you should put all menu images to server");
    } 
   } else {
    Log.d(TAG, "download menu image failed!");
    ToastUtils.showFailure(getApplicationContext(), "5"
      + result.message);
   }
   
   DiSettings.putBoolean(getApplicationContext(),
     DiSettings.KEY_DOWNLOAD_MENU_IMAGE, false);
   
   dismissDialog(DialogUtil.DIALOG_DOWNLAOD_IMAGE_PROGRESS);
  }
  
  @Override
  protected void onProgressUpdate(ProgressValue... values) {   
            Log.d(TAG,values[0].toString());   
            mProgressDialog.setProgress(values[0].progress);   
            mProgressDialog.setMessage(values[0].message);
       }
   
 }
</imageinfo></imageinfo></menux></requestfileinfo></menux></menux>


相关文章
|
9月前
|
数据采集 Java API
Jsoup库能处理多线程下载吗?
Jsoup库能处理多线程下载吗?
|
2月前
|
数据采集 存储 JSON
Python爬取知乎评论:多线程与异步爬虫的性能优化
Python爬取知乎评论:多线程与异步爬虫的性能优化
|
2月前
|
编解码 Java Android开发
安卓虚拟摄像头免root版,虚拟摄像头替换真实摄像头,jar代码开源分享
通过动态替换摄像头输入流的方式实现虚拟摄像头功能,代码经过简化展示核心逻辑。实际开发中还需要考虑视频编解码优化
|
2月前
|
数据采集 监控 调度
干货分享“用 多线程 爬取数据”:单线程 + 协程的效率反超 3 倍,这才是 Python 异步的正确打开方式
在 Python 爬虫中,多线程因 GIL 和切换开销效率低下,而协程通过用户态调度实现高并发,大幅提升爬取效率。本文详解协程原理、实战对比多线程性能,并提供最佳实践,助你掌握异步爬虫核心技术。
|
11月前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
641 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
7月前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
228 6
|
8月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
312 17
|
9月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
283 3
|
9月前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
10月前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
137 6