posts - 78, comments - 34, trackbacks - 0, articles - 1
  语源科技BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

       我们编写的是AndoridHTTP多线程断点下载应用程序。因为之间我们学习的学习积累,直接使用单线程下载HTTP文件对我们来说是一件非常简单的事。那么,多线程断点下载的难点在哪里?1.多线程下载,2.支持断点。

 

       多线程下载:
      

       如何才能从文件的指定位置处开始下载文件?(比如从50MB开始)这一点我们可以通过HTTP请求信息头来设置,还记得HTTP请求信息头的“Range”属性吗?

 

       断点:

       首要问题(多线程下载)已经被我们解决了,支持断点下载想必大家也已经想到了。就是将下载的进度保存到文件中,但在Android中却不能这么做。通过老黎的试验,在Android平台中,我们需要向文件中写出下载的文件数据,还需要向另一个文件中写出下载进度,这样会出错。这样会导致有一个文件的内容没有被写出。所以我们就不能以文件的方式来保存下载进度,但可以通过数据库的方式保存下载进度。

 

       这两大问题我们已经有了解决思路,那么就开始动手编写吧!

 

       1.创建Android工程

       Project name:MulThreadDownloader

       BuildTarget:Android2.1

       Application name:多线程断点下载

       Package name:com.changcheng.download

       Create Activity:MulThreadDownloader

       Min SDK Version:7

 

       2.AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

      package="com.changcheng.download"

      android:versionCode="1"

      android:versionName="1.0">

    <application android:icon="@drawable/icon" android:label="@string/app_name">

        <activity android:name=".MulThreadDownloader"

                  android:label="@string/app_name">

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

    <uses-sdk android:minSdkVersion="7" />

         <!-- SDCard中创建与删除文件权限 -->

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

         <!-- SDCard写入数据权限 -->

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

         <!-- 访问internet权限 -->

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

</manifest>

 

       3.strings.xml

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <string name="hello">Hello World, DownloadActivity!</string>

    <string name="app_name">多线程断点下载</string>

    <string name="path">下载路径</string>

    <string name="downloadbutton">下载</string>

    <string name="sdcarderror">SDCard不存在或者写保护</string>

</resources>

 

       4.main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="vertical"

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    >

    <!-- 下载路径 -->

         <TextView 

             android:layout_width="fill_parent"

             android:layout_height="wrap_content"

             android:text="@string/path"

             />

    <EditText

             android:layout_width="fill_parent"

             android:layout_height="wrap_content"

             android:text="http://www.winrar.com.cn/download/wrar380sc.exe"

             android:id="@+id/path"

             />

         <!-- 下载按钮 -->

         <Button

                   android:layout_width="wrap_content"

             android:layout_height="wrap_content"

             android:text="@string/downloadbutton"

             android:id="@+id/button"

             />

          <!-- 进度条 -->

          <ProgressBar

                   android:layout_width="fill_parent"

             android:layout_height="20dip"

             style="?android:attr/progressBarStyleHorizontal"

                 android:id="@+id/downloadbar"/>

       <!-- 进度% -->    

       <TextView 

             android:layout_width="fill_parent"

             android:layout_height="wrap_content"

             android:gravity="center"

             android:id="@+id/resultView"

             />

</LinearLayout>

 

       5.MulThreadDownloader

package com.changcheng.download;

 

import java.io.File;

import com.changcheng.net.download.DownloadProgressListener;

import com.changcheng.net.download.FileDownloader;

import com.changcheng.download.R;

import android.app.Activity;

import android.os.Bundle;

import android.os.Environment;

import android.os.Handler;

import android.os.Message;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

 

public class MulThreadDownloader extends Activity {

         private EditText pathText;

         private ProgressBar progressBar;

         private TextView resultView;

         private Handler handler = new Handler(){

                   @Override

                   public void handleMessage(Message msg) {

                            if(!Thread.currentThread().isInterrupted()){

                                     switch (msg.what) {

                                     case 1:

                                               // 获取当前文件下载的进度

                                               int size = msg.getData().getInt("size");

                                               progressBar.setProgress(size);

                                               int result = (int)(((float)size/(float)progressBar.getMax()) * 100);

                                               resultView.setText(result+ "%");

                                               if(progressBar.getMax() == size){

                                                        Toast.makeText(MulThreadDownloader.this, "文件下载完成", 1).show();

                                               }

                                               break;

        

                                     case -1:

                                               String error = msg.getData().getString("error");

                                               Toast.makeText(MulThreadDownloader.this, error, 1).show();

                                               break;

                                     }                          

                            }

                            super.handleMessage(msg);

                   }                 

         };

        

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        pathText = (EditText)this.findViewById(R.id.path);

        progressBar = (ProgressBar)this.findViewById(R.id.downloadbar);

        resultView = (TextView)this.findViewById(R.id.resultView);

        Button button = (Button)this.findViewById(R.id.button);

        button.setOnClickListener(new View.OnClickListener() {                           

                            @Override

                            public void onClick(View v) {

                                     String path = pathText.getText().toString();

                                     if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){

                                               //下载文件需要很长的时间,主线程是不能够长时间被阻塞,如果主线程被长时间阻塞, 那么Android被回收应用

                                               download(path, Environment.getExternalStorageDirectory());

                                     }else{

                                               Toast.makeText(MulThreadDownloader.this, R.string.sdcarderror, 1).show();

                                     }

                            }

                   });

    }

    /**

     * 下载文件

     * @param path 下载路径

     * @param saveDir 文件保存目录

     */

  //对于AndroidUI控件,只能由主线程负责显示界面的更新,其他线程不能直接更新UI控件的显示

    public void download(final String path, final File saveDir){

       new Thread(new Runnable() {                         

                            @Override

                            public void run() {

                                     FileDownloader downer = new FileDownloader(MulThreadDownloader.this, path, saveDir, 3);

                                     progressBar.setMax(downer.getFileSize());//设置进度条的最大刻度

                                     try {

                                               downer.download(new DownloadProgressListener(){

                                                        @Override

                                                        public void onDownloadSize(int size) {

                                                                 Message msg = new Message();

                                                                 msg.what = 1;

                                                                 msg.getData().putInt("size", size);

                                                                 handler.sendMessage(msg);//发送消息

                                                        }});

                                     } catch (Exception e) {

                                               Message msg = new Message();

                                               msg.what = -1;

                                               msg.getData().putString("error", "下载失败");

                                               handler.sendMessage(msg);

                                     }

                            }

                   }).start();

    }

}

 

       6.FileDownload

package com.changcheng.net.download;

 

import java.io.File;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import java.util.LinkedHashMap;

import java.util.Map;

import java.util.UUID;

import java.util.concurrent.ConcurrentHashMap;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

import com.changcheng.download.service.FileService;

import android.content.Context;

import android.util.Log;

 

/**

 * 文件下载器

 * @author lihuoming@sohu.com

 *

 */

public class FileDownloader {

         private Context context;

         private FileService fileService;

        

         private static final String TAG = "FileDownloader";

         /* 已下载文件大小 */

         private int downloadSize = 0;

         /* 原始文件大小 */

         private int fileSize = 0;

         /* 线程数 */

         private DownloadThread[] threads;

         /* 下载路径  */

         private URL url;

         /* 本地保存文件 */

         private File saveFile;

         /* 下载记录文件 */

         private File logFile;

         /* 缓存各线程最后下载的位置*/

         private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();

         /* 每条线程下载的大小 */

         private int block;

         private String downloadUrl;//下载路径

         /**

          * 获取线程数

          */

         public int getThreadSize() {

                   return threads.length;

         }

         /**

          * 获取文件大小

          * @return

          */

         public int getFileSize() {

                   return fileSize;

         }

         /**

          * 累计已下载大小

          * @param size

          */

         protected synchronized