Android多线程断点续传下载的实现

Android多线程续传下载是Android应用中常见的功能。今天我们就来实现这样一个实例。实现效果如图:

结构如下图
功能结构
1)Activity向service传参数:将文件名,文件路径等传入service。
2)service 启动新的线程下载网络文件并存到本地。
3)向本地数据库写入下载的进度,以便确定下次下载的起始点。
4)broadcast 回传进度到Activity。
网络下载的关键点如下:
1)获取网络文件的长度
2)在本地创建文件,设置其长度和网络文件长度相同。
3)从上次下载的位置下载数据,同时保存进度到数据库。
4)将下载进度回传给Activity
开发过程如下:
(一)编写UI和实体类
文件类

public class FileInfo implements Serializable{
  private int id;
  private String url;
  private String fileName;
  private int length;
  private int finished;
  }

线程类

public class ThreadInfo {
    private int id;
    private String url;
    private int start;
    private int end;
    private int finished;
    }

(二)编写service获得Activity传入的参数

public class DownloadService extends Service{
    public static final String ACTION_START ="ACTION_START";
    public static final String ACTION_STOP="ACTION_STOP";
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 获得Activity传来的参数
        if(ACTION_START.equals(intent.getAction()))
        {
            FileInfo fileInfo=(FileInfo)intent.getSerializableExtra("fileInfo"); 
            Log.i("test ","start:"+fileInfo.toString());
        }else if(ACTION_STOP.equals(intent.getAction())){
            FileInfo fileInfo=(FileInfo)intent.getSerializableExtra("fileInfo");
        }
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

}

(四)在Activity中设置点击传递文件参数

final FileInfo fileInfo=new FileInfo(0, "http://101.4.136.35:9999/fw1.dl.wdjcdn.com/files/third/WanDouJiaSetup_a9.exe", 
                "王纪坤大逗比", 0, 0);
mBtStart.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View view) {
                // 通过intent,传递参数给service
                Intent intent=new Intent(MainActivity.this,DownloadService.class);
                intent.setAction(DownloadService.ACTION_START);
                intent.putExtra("fileinfo", fileInfo);
                startService(intent);   
            }
        });

(五)在service中创建子线程,实现文件的下载
在子线程中,连接网络,获取网络文件长度。在本地目录创建相同长度的文件。并返回本地文件。

class InitThread extends Thread{
        private FileInfo mFileInfo=null;
        public InitThread(FileInfo mFileInfo)
        {
            this.mFileInfo=mFileInfo;       
        }
        public void run(){
            HttpURLConnection httpUrlConn = null;
            RandomAccessFile raf = null;
            try {
                //1连接网络文件
                URL url=new URL(mFileInfo.getUrl());
                httpUrlConn=(HttpURLConnection)url.openConnection();
                httpUrlConn.setConnectTimeout(3000);
                httpUrlConn.setRequestMethod("GET");
                int length=-1;      
                        if(httpUrlConn.getResponseCode()==HttpStatus.SC_OK)
                {
                    //2获得文件长度
                    length=httpUrlConn.getContentLength();
                }
                if(length<=0)
                {
                    return ;
                }else{
                    //3在本地创建文件
                    File dir=new File(DOWNLOAD_PATH);
                    if(!dir.exists()){
                        dir.mkdir();
                    }
                    File file=new File(dir,mFileInfo.getFileName());
                    raf=new RandomAccessFile(file,"rwd");
                    raf.setLength(length);
                    mFileInfo.setLength(length);
                    mHandler.obtainMessage(MSG_INIT,mFileInfo).sendToTarget();

                }
                //4设置本地文件长度
            } catch (Exception e) {
                // TODO: handle exception
            }finally{
                httpUrlConn.disconnect();
                try {
                    raf.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

(六)创建数据库的连接和数据库访问接口
在数据库中保持下载进度
数据库的字段和线程信息相对应

 private static final String SQL_CREATE="create table thread_info(_id integer primary key autoincreament," +
            "thread_id integer,url text, start integer, end integer ,finished integer)";

定义数据库接口

public interface ThreadDAO {
    /*
     *插入线程信息
     */
   public void insertThread(ThreadInfo threadInfo);
   /*
    * 删除线程信息
    */
   public void deleteThread(String url,int thread_id);
   /*
    * 更新线程的下载进度
    */
   public void updateThread(String url,int thread_id,int finished);
   /*
    * 查询文件的线程信息,以集合的形式返回
    */
   public List<Thread> getThreads(String url);
   /*
    * 判断线程的信息是否存在
    */
   public boolean isExists(String url,int thread_id);
}

实现数据库接口

public class ThreadDAOImpl implements ThreadDAO{
    private DBhelper dbHelper;
    public ThreadDAOImpl(Context context){
        dbHelper=new DBhelper(context);
    }
    @Override
    public void insertThread(ThreadInfo threadInfo) {
        // TODO Auto-generated method stub
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        db.execSQL("insert into thread_info(thread_id,url,start,end,finished)values(?,?,?,?,?)",
                new Object[]{threadInfo.getId(),threadInfo.getUrl(),threadInfo.getStart(),threadInfo.getEnd(),threadInfo.getFinished()});
        db.close();
    }

    @Override
    public void deleteThread(String url, int thread_id) {
        // TODO Auto-generated method stub
        SQLiteDatabase database=dbHelper.getWritableDatabase();
        database.execSQL("delete from thread_info where url="+url+"and thread_id="+thread_id);
        database.close();
    }

    @Override
    public void updateThread(String url, int thread_id, int finished) {
        // TODO Auto-generated method stub
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        db.execSQL("update thread_info set finished=? where url= ?and thread_id=?", new Object[]{finished,url,thread_id});
    }

    @Override
    public List<ThreadInfo> getThreads(String url) {
        // TODO Auto-generated method stub
        List<ThreadInfo> list=new ArrayList<ThreadInfo>();
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        Cursor cursor=db.rawQuery("select * from thread_info where url=?", new String[]{url});
        while(cursor.moveToNext()){
            ThreadInfo thread=new ThreadInfo();
            thread.setId(cursor.getInt(cursor.getColumnIndex("thread_id")));
            thread.setUrl(cursor.getString(cursor.getColumnIndex("url")));
            thread.setStart(cursor.getInt(cursor.getColumnIndex("start")));
            thread.setEnd(cursor.getInt(cursor.getColumnIndex("end")));
            thread.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));
            list.add(thread);
        }
        db.close();
        return list;
    }

    @Override
    public boolean isExists(String url,int thread_id) {
        // TODO Auto-generated method stub
        SQLiteDatabase db=dbHelper.getWritableDatabase();
        Cursor cursor=db.rawQuery("select * from thread_info where url=? and thread_id=?", new String[]{url,thread_id+""});
        boolean exists=cursor.moveToNext();
        cursor.close();
        db.close();
        return exists;
    }
}

(七)定义下载任务类

public class DownloadTask {
   private Context mContext=null;
   private FileInfo mFileInfo=null;
   private ThreadDAO mDao=null;
   private int mFinished=0;
   public boolean isPaused=false;
   HttpURLConnection conn=null;
   RandomAccessFile raf=null;
   InputStream input=null;
   public DownloadTask(Context context,FileInfo fileInfo)
   {
       this.mContext=context;
       this.mFileInfo=fileInfo;
       mDao=new ThreadDAOImpl(mContext);
   }
   public void download(){
       //读取数据库的线程信息
       List<ThreadInfo> threadInfos= mDao.getThreads(mFileInfo.getUrl());
       ThreadInfo threadInfo=null;
       if(threadInfos.size()==0){
           //初始化县城信息对象
           threadInfo=new ThreadInfo(0, mFileInfo.getUrl(), 0, mFileInfo.getLength(), 0);
       }else{
           threadInfo=threadInfos.get(0);
       }
       new DownloadThread(threadInfo).start();
   }
   /**
    * 下载线程
    */
   class DownloadThread extends Thread{
       private ThreadInfo mThreadInfo=null;

    public DownloadThread(ThreadInfo mThreadInfo) {
        super();
        this.mThreadInfo = mThreadInfo;
    }
    public void run(){
        //向数据库插入线程信息
        if(!mDao.isExists(mThreadInfo.getUrl(), mThreadInfo.getId())){
            mDao.insertThread(mThreadInfo);
        }
        try {
            URL url=new URL(mThreadInfo.getUrl());
            conn=(HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(3000);
            conn.setRequestMethod("GET");
            //设置下载位置
            int start=mThreadInfo.getStart()+mThreadInfo.getFinished();
            conn.setRequestProperty("Range", "bytes="+start+"-"+mThreadInfo.getEnd());
            //设置文件写入位置
            File file=new File(DownloadService.DOWNLOAD_PATH, mFileInfo.getFileName());
            raf=new RandomAccessFile(file, "rwd");
            raf.seek(start);
            Intent intent=new Intent(DownloadService.ACTION_UPDATE);
            mFinished+=mThreadInfo.getFinished();
            //开始下载
            if(conn.getResponseCode()==HttpStatus.SC_OK){
                //读取数据
                input=conn.getInputStream();
                byte[] buffer=new byte[1024*4];
                int len=-1;
                long time=System.currentTimeMillis();
                while((len=input.read(buffer))!=-1){
                    //写入文件
                    raf.write(buffer,0,len);
                    //把下载进度发送广播给Activity
                    mFinished+=len;
                    if(System.currentTimeMillis()-time>500){
                        time=System.currentTimeMillis();
                        intent.putExtra("finished", mFinished*100/mFileInfo.getLength());
                        mContext.sendBroadcast(intent);
                    }
                    //在下载暂停时,保存下载进度
                    if(isPaused){
                        mDao.updateThread(mThreadInfo.getUrl(), mThreadInfo.getId(), mThreadInfo.getFinished());
                        return;
                    }
                }
                //删除线程信息

                mDao.deleteThread(mThreadInfo.getUrl(), mThreadInfo.getId());
            }


        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            conn.disconnect();
            try {
                raf.close();
                input.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }
   }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值