多线程下载-下(断点)

多线程断点下载思路

加入断点功能

要知道每条线程的下载记录

数据库  SqliteDatabase    SqliteOpenHepler

download.db

table

_id threadid path downloadlength

//使用ContentProvider

设计业务方法

DownloadService

插入数据 insert()

更新数据 update()

查询

   3.1 查询是否有下载记录boolean isExist()

   3.2 查询每条记录的下载记录int queryByThreadid(int threadid)

   3.3 查询总的下载记录 intqueryAll()

删除

  delete()

在哪里修改代码?

在下载的时候,需要判断是否有下载记录 DownloadManager

更新下载记录在哪里呢?DownloadThread

暂停

MainActivity  DownloadManager boolean flag =              DownloadThread

1.png

对比之前学的多线程下载,我们今天学的断点多线程下载,加入了断点功能,之所以加入了断点功能,我们就要加入SQLite数据库进行存储每天线程的下载数据量,便于第一次没下载完,第二次读取数据,继续下载。因为使用到了SQLite数据库,所以我们自己也给自己提供了contentProvider,这样更好的简化了我们对数据库的操作以及不需要处理数据库的开关,没有频繁的操作数据库,这样使得我们的操作效率更高。

代码MainAtivity

package com.cym.multidon.ui;

import com.cym.multidon.R;

importcom.cym.multidon.download.DownloadManager;

importcom.cym.multidon.inter.ProgressInter;

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.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

public class Mainctivity extends Activityimplements OnClickListener{

   private Button bt_start;

       privateButton bt_stop;

       privateEditText et_url_value;

       privateProgressBar pb_progress;

       privateTextView tv_progress;

       privateDownloadManager dm;

       privatefinal static int SET_MAX = 0;

       privatefinal static int SET_LENGTH = 1;

       privatefinal static int SET_DOWNLOAD_LENGTH = 2;

      

       privateHandler mHandler = new Handler(){

              public voidhandleMessage(android.os.Message msg) {

                            switch (msg.what) {

                            case SET_MAX:

                                   pb_progress.setMax((Integer)msg.obj);

                                   break;

                            case SET_LENGTH:

改变进度条进度,以及算出进度百分比

                                   int now = pb_progress.getProgress() +(Integer) msg.obj;

                                   pb_progress.setProgress(now);

                     tv_progress.setText((int)((float)now/(float)pb_progress.getMax()*100)+ "%");

                                   break;

                            case SET_DOWNLOAD_LENGTH:

                                   pb_progress.setProgress((Integer)msg.obj);

                                   break;

                            }

              };

       };

       privateProgressInter pi = new ProgressInter() {

              

              @Override

              public void setMax(int max) {

                     //TODO Auto-generated method stub

                     Messagemsg = new Message();

                     msg.what= SET_MAX;

                     msg.obj= max;

                     mHandler.sendMessage(msg);

              }

              

              @Override

              public void setLength(int length) {

                     //TODO Auto-generated method stub

                     Messagemsg = new Message();

                     msg.what= SET_LENGTH;

                     msg.obj= length;

                     mHandler.sendMessage(msg);

              }

              

              @Override

              public void setDownloadLength(int length){

                     //TODO Auto-generated method stub

                     Messagemsg = new Message();

                     msg.what= SET_DOWNLOAD_LENGTH;

                     msg.obj= length;

                     mHandler.sendMessage(msg);

              }

       };

       /**Called when the activity is first created. */

   @Override

   public void onCreate(Bundle savedInstanceState) {

       super.onCreate(savedInstanceState);

       setContentView(R.layout.main);

       bt_start = (Button) findViewById(R.id.bt_start);

       bt_stop = (Button) findViewById(R.id.bt_stop);

       et_url_value = (EditText) findViewById(R.id.et_url_value);

       pb_progress = (ProgressBar) findViewById(R.id.pb_progress);

              tv_progress = (TextView)findViewById(R.id.tv_progress);

              dm = new DownloadManager(this);

              bt_start.setOnClickListener(this);

              bt_stop.setOnClickListener(this);

    }

       @Override

       publicvoid onClick(View v) {

              // TODO Auto-generated method stub

              

               if(v == bt_start){

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

                      {

                             Toast.makeText(this, "SDCARD无法下载", 300).show();

                      }else

                      {

                             dm.setDwondlaod(true);

                             bt_start.setEnabled(false);

                             bt_stop.setEnabled(true);

                             new Thread(){

                                    public void run() {

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

                                           try {

                                                 dm.download(path,pi, Environment.getExternalStorageDirectory());

                                          } catch (Exception e) {

                                                 //TODO Auto-generated catch block

                                                 e.printStackTrace();

                                          }

                                    };

                             }.start();

                      }

               }else if(v == bt_stop){

                      dm.setDwondlaod(false);

                      bt_start.setEnabled(true);

                      bt_stop.setEnabled(false);

               }

        

       }

   

}

sqlite

public class DownloadDBHelper extends SQLiteOpenHelper {

    private final static String name = "download.db"// 数据库名

    private static SQLiteOpenHelper mOpenHelper// 连接

   

    // 单例模式,同步修饰符,保证数据的安全性

    public synchronized static SQLiteOpenHelpergetInstance(Context context){

        if(mOpenHelper == null){

            mOpenHelper = new DownloadDBHelper(context, namenull, 1);

        }

        return mOpenHelper;

    }

   

    public DownloadDBHelper(Contextcontext, String name,

            CursorFactory factory, int version) {

        super(context, name, factory, version);

        // TODO Auto-generated constructor stub

    }

    @Override

    public void onCreate(SQLiteDatabase db) {

         db.execSQL("create tabledownload(_id integer primary key autoincrement," +

                "threadid integer," +

                "path text," +

                "downloadlength integer)");

                 

    }

    @Override

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

}


contentprovider

public class DownloadProvider extends ContentProvider {

验证url

private static UriMatchermatcher = new UriMatcher(UriMatcher.NO_MATCH);

       private static String authority ="com.cym.multidon.provider.DownloadProvider";

       private final static int DOWNLOAD = 10;

       static {

              matcher.addURI(authority,"download", DOWNLOAD);

       }

    private SQLiteOpenHelper mOpenHelper;

    @Override

    public boolean onCreate() {

        mOpenHelper = DownloadDBHelper.getInstance(getContext());

        return false;

    }

    @Override

    public Cursor query(Uri uri,String[] projection, String selection,

            String[] selectionArgs, String sortOrder) {

        // TODO Auto-generated method stub

        SQLiteDatabase db = mOpenHelper.getReadableDatabase();

        Cursor ret = null;

        int code = matcher.match(uri);

        switch (code) {

        case DOWNLOAD:

            ret = db.query("download", projection, selection, selectionArgs,

                    nullnullnull);

            break;

        default:

            break;

        }

        return ret;

    }

    @Override

    public String getType(Uri uri) {

        // TODO Auto-generated method stub

        return null;

    }

    @Override

    public Uri insert(Uri uri,ContentValues values) {

        // TODO Auto-generated method stub

        SQLiteDatabase db = mOpenHelper.getWritableDatabase();

        int code = matcher.match(uri);

        switch (code) {

        case DOWNLOAD:

                db.insert("download""_id", values);

            break;

        default:

            break;

        }

        return null;

    }

    @Override

    public int delete(Uri uri, String selection, String[] selectionArgs) {

        // TODO Auto-generated method stub

        SQLiteDatabase db = mOpenHelper.getWritableDatabase();

        int code = matcher.match(uri);

        switch (code) {

        case DOWNLOAD:

                db.delete("download", selection,selectionArgs);

            break;

        default:

            break;

        }

        return 0;

    }

    @Override

    public int update(Uri uri, ContentValues values, String selection,

            String[] selectionArgs) {

        // TODO Auto-generated method stub

        SQLiteDatabase db = mOpenHelper.getWritableDatabase();

        int code = matcher.match(uri);

        switch (code) {

        case DOWNLOAD:

                db.update("download", values, selection,selectionArgs);

            break;

        default:

            break;

        }

        return 0;

    }

}

DowloadService

package com.cym.multidon.service;

import com.cym.multidon.domain.DownloadInfo;

import android.content.ContentResolver;

import android.content.ContentValues;

import android.content.Context;

import android.database.Cursor;

import android.net.Uri;

public class DownloadService {

      

       privateContext context;

       privateContentResolver cr;

Private Uri uri =Uri.parse("content://com.cym.multidon.provider.DownloadProvider/download");

      

       publicDownloadService(Context context){

              this.context = context;

              this.cr = context.getContentResolver();

       }

       /**

        * 插入记录

        * @param info

        */

       publicvoid insert(DownloadInfo info){

              ContentValues values = newContentValues();

              values.put("threadid",info.getThreadid());

              values.put("path",info.getPath());

              values.put("downloadlength",0);

                     

              cr.insert(uri, values);

       }

       /**

        * 更新数据

        * @param info

        */

       publicvoid update(DownloadInfo info){

               ContentValues values = new ContentValues();

               values.put("downloadlength",info.getDownloadlength());

               String where = "threadid = ? and path =?";

               String[] selectionArgs = new String[]{info.getThreadid()+"",info.getPath()};

               cr.update(uri, values, where, selectionArgs);

       }

       /**

        * 判断下载记录是否存在

        * @param path

        * @return

        */

       publicboolean isExist(String path){

              boolean isExist = false;

              String selection = "path = ?";

              String[] selectionArgs = newString[]{path};

              Cursor cursor = cr.query(uri, newString[]{"*"}, selection, selectionArgs, null);

              if(cursor!=null){

                     if(cursor.moveToFirst()){

                            isExist = true;

                     }

                     cursor.close();

              }

              return isExist;

       }

       /**

        * 查询单个线程的的下载长度

        * @param info

        * @return

        */

       publicint queryByThreadid(DownloadInfo info){

              int downlaodlength = 0;

              String selection = "threadid = ? andpath = ?";

              String[] selectionArgs = newString[]{info.getThreadid()+"",info.getPath()};

              Cursor cursor = cr.query(uri, newString[]{"*"}, selection, selectionArgs, null);

              if(cursor != null){

                     if(cursor.moveToFirst()){

                            downlaodlength=cursor.getInt(cursor.getColumnIndex("downloadlength")) ;

                     }

                     cursor.close();

              }

              return downlaodlength;

       }

       /**

        * 查询以一共的下载总量

        * @param info

        * @return

        */

       publicint queryAll(String path){

              int downlaodlength = 0;

              String selection = "path = ?";

              String[] selectionArgs = newString[]{path};

              Cursor cursor = cr.query(uri, newString[]{"*"}, selection, selectionArgs, null);

              if(cursor != null){

                     while(cursor.moveToNext()){

              downlaodlength +=cursor.getInt(cursor.getColumnIndex("downloadlength")) ;

                     }

                     cursor.close();

              }

              return downlaodlength;

       }

       publicvoid delete(String path){

              String where = "path = ?";

              String[] selectionArgs = newString[]{path};

              cr.delete(uri, where, selectionArgs);

       }

}

DownloadManager

package com.cym.multidon.download;

import java.io.File;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import android.content.Context;

import android.util.Log;

importcom.cym.multidon.domain.DownloadInfo;

importcom.cym.multidon.inter.ProgressInter;

importcom.cym.multidon.service.DownloadService;

public class DownloadManager {

       privatefinal static int threadSzie = 3; 线程数量

       private boolean isDwondlaod; 是否开启下载

       privateDownloadService ds;

       publicboolean isDwondlaod() {

              return isDwondlaod;

       }

       public void setDwondlaod(boolean isDwondlaod) {

              this.isDwondlaod =isDwondlaod;

       }

      

       public DownloadManager(Context context) {

              super();

              this.ds = newDownloadService(context);

       }

       publicvoid download(String path, ProgressInter pi, File dir)throws Exception{

                     URLurl = new URL(path);

                     HttpURLConnectionconn = (HttpURLConnection) url.openConnection();

                     conn.setRequestMethod("GET");

                     conn.setConnectTimeout(5000);

                     if(conn.getResponseCode()== 200){

                            int contentSize =conn.getContentLength();

                            File file = newFile(dir,getPathName(path));

                            RandomAccessFile raf = newRandomAccessFile(file, "rwd");

                            raf.setLength(contentSize);

                            raf.close();

                            pi.setMax(contentSize);

如果有下载的记录那么就读取数据修改成上次进度

                            if(ds.isExist(path)){

                                   int length = ds.queryAll(path);

                                   pi.setDownloadLength(length);

                            }else

                            {

如果的没有下载记录则创建记录

                                   for (int threadid = 0; threadid <threadSzie; threadid++) {

                                                 ds.insert(new DownloadInfo(threadid,path, 0));

                                   }

                            }

计算出每个线程下载量

int block =contentSize%threadSzie == 0 ? contentSize/threadSzie :contentSize/threadSzie+1;

                            for (int threadid = 0; threadid <threadSzie; threadid++) {

                                  启动三条线程开始下载

                                   new DownloadThread(threadid, path, dir, block, pi, this,ds).start();

                            }

                     }

       }

截取url后面的文件名

       publicString getPathName(String path){

              returnpath.substring(path.lastIndexOf("/")+1);

       }

}

DownloadThread

package com.cym.multidon.download;

import java.io.File;

import java.io.FileNotFoundException;

import java.io.InputStream;

import java.io.RandomAccessFile;

import java.net.HttpURLConnection;

import java.net.URL;

import com.cym.multidon.domain.DownloadInfo;

importcom.cym.multidon.inter.ProgressInter;

importcom.cym.multidon.service.DownloadService;

public class DownloadThread extends Thread{

       privateint threadid; // 线程id

       privateString path;

       privateFile dir;

       privateint block;

       privateProgressInter pi;

       privateDownloadManager dm;

       privateDownloadService ds;

       privateint startPosition;

       privateint endPosition;

       publicDownloadThread(int threadid, String path, File dir, int block,

                     ProgressInterpi, DownloadManager dm, DownloadService ds) {

              super();

              this.threadid = threadid;

              this.path = path;

              this.dir = dir;

              this.block = block;

              this.pi = pi;

              this.dm = dm;

              this.ds = ds;

算出开始位置和结束位置

              this.startPosition= threadid * block;

              this.endPosition =(threadid + 1) * block - 1;

       }

       @Override

       publicvoid run() {

              // TODO Auto-generated method stub

              super.run();

              DownloadInfo info= new DownloadInfo(threadid, path, 0);

一开始查询该线程的下载量

              int size =ds.queryByThreadid(info);

              File file = new File(dir,dm.getPathName(path));

从上次记录的下载进度开始下载(默认为0)

              startPosition +=size;

              try {

                     RandomAccessFileraf = new RandomAccessFile(file, "rwd");

设置文件读取位置

                     raf.seek(startPosition);

                     byte[]buffer = new byte[1024];

                     intlen = 0;

                     URLurl = new URL(path);

                     HttpURLConnectionconn = (HttpURLConnection) url.openConnection();

                     conn.setRequestMethod("GET");

                     conn.setConnectTimeout(5000);

设置请求内容位置

                     conn.setRequestProperty("Range", "bytes="+ startPosition + "-"

                                   + endPosition);

                     InputStreamis = conn.getInputStream();

                     while((len = is.read(buffer)) != -1) {

如果是暂停下载,boolean值则是false,则返回进入修改,及更改进度条

                            if(!dm.isDwondlaod()) {

                                   return;

                            }

如果是下载则修改,及更新进度条进度

                            raf.write(buffer, 0, len);

不断的修改,没一次下载就修改一次下载量数据及进度条进度

                            size += len;

                            info.setDownloadlength(size);

                            ds.update(info);

                            pi.setLength(len);

                     }

                     is.close();

                     raf.close();

              } catch (Exception e) {

                     //TODO Auto-generated catch block

                     e.printStackTrace();

              }

       }

}

课后问题

多线程下载是怎么实现的?

开启多个线程下载一个文件

在这里我们使用哪些知识点?

ThreadHandlerHttp协议、ProgressBarContentProviderSQLite

断点功能是怎么实现的。

把数据存储在SQLite里面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值