android实现多任务断点下载

转载请声明:http://write.blog.youkuaiyun.com/postedit

基本思路大概如下:

     1.如果需要支持断点,需要将下载的进度保存到数据库中,以便下次从该断点处向服务器请求下载文件的起点。

     2.既然要用到数据库,那就少不了要建立一个实体类。

     3.多任务下载当中,每一个任务为一个线程,它需要独立响应暂停、继续等按钮的单击事件,暂停时这个线程需要wait,便需要考虑到同步问题。

     4.下载时需要实时更新进度,这就需要使用到handler,在handler的handlerMessage方法中更新进度条。

 代码如下:

     1.创建一个Helper类DBOpenHelper.class 这个类只是创建数据库的帮助类。

   

  1. public class DBOpenHelper extends SQLiteOpenHelper{  
  2.       
  3.     public DBOpenHelper(Context context) {    
  4.             super(context, "download.db"null1);    
  5.         }  
  6.       
  7.     @Override  
  8.     public void onCreate(SQLiteDatabase db) {  
  9.         // TODO Auto-generated method stub  
  10.         db.execSQL("CREATE TABLE info(path varchar(1024),thid integer,done integer,primary key(path,thid))");  
  11.           
  12.     }  
  13.   
  14.   
  15.   
  16.     @Override  
  17.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  18.         // TODO Auto-generated method stub  
  19.           
  20.     }  
  21.   
  22. }  


2. 创建实体类Info

  1. public class Info {  
  2. private String path;  
  3. private int thid;  
  4. private Long done;  
  5.   
  6. public Info(String path, int thid, Long i) {  
  7. this.path = path;  
  8. this.thid = thid;  
  9. this.done = i;  
  10. }  
  11.   
  12. public String getPath() {  
  13. return path;  
  14. }  
  15.   
  16. public void setPath(String path) {  
  17. this.path = path;  
  18. }  
  19.   
  20. public int getThid() {  
  21. return thid;  
  22. }  
  23.   
  24. public void setThid(int thid) {  
  25. this.thid = thid;  
  26. }  
  27.   
  28. public Long getDone() {  
  29. return done;  
  30. }  
  31.   
  32. public void setDone(Long done) {  
  33. this.done = done;  
  34. }  
  35.   
  36. }  


3.创建操作数据库的DAO类

  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4. import android.content.Context;  
  5. import android.database.Cursor;  
  6. import android.database.sqlite.SQLiteDatabase;  
  7.   
  8. public class InfoDao {  
  9.       
  10.     private static DBOpenHelper helper;  
  11.     public InfoDao(Context context) {  
  12.         if(helper == null)  
  13.         helper = new DBOpenHelper(context);  
  14.     }  
  15.       
  16.     public void insert(Info info){  
  17.         SQLiteDatabase db = helper.getWritableDatabase();  
  18.         db.execSQL("insert into info(path,thid,done) values(?,?,?)",new Object[]{info.getPath(),info.getThid(),info.getDone()});  
  19.           
  20.     }  
  21.     public void delete(String path,int thid){  
  22.         SQLiteDatabase db = helper.getWritableDatabase();  
  23.         db.execSQL("delete from info where path=? and this=?"new Object[]{path,thid});  
  24.           
  25.     }  
  26.     public void update(Info info) {    
  27.         SQLiteDatabase db = helper.getWritableDatabase();    
  28.         db.execSQL("UPDATE info SET done=? WHERE path=? AND thid=?"new Object[] { info.getDone(), info.getPath(), info.getThid() });    
  29.           
  30.     }   
  31.     public Info query(String path, int thid) {    
  32.         SQLiteDatabase db = helper.getWritableDatabase();    
  33.         Cursor c = db.rawQuery("SELECT path, thid, done FROM info WHERE path=? AND thid=?"new String[] { path, String.valueOf(thid) });    
  34.         Info info = null;    
  35.         if (c.moveToNext())    
  36.             info = new Info(c.getString(0), c.getInt(1), c.getLong(2));    
  37.         c.close();    
  38.          
  39.         return info;    
  40.     }    
  41.     public void deleteAll(String path, Long fileLen) {    
  42.         SQLiteDatabase db = helper.getWritableDatabase();    
  43.        // Cursor c = db.rawQuery("SELECT SUM(done) FROM info WHERE path=?", new String[] { path });    
  44.         //if (c.moveToNext()) {    
  45.            // int result = c.getInt(0);    
  46.            // if (result == len)    
  47.                 db.execSQL("DELETE FROM info WHERE path=? "new Object[] { path });    
  48.        // }    
  49.           
  50.     }  
  51.     public List<String> queryUndone() {    
  52.         SQLiteDatabase db = helper.getWritableDatabase();    
  53.         Cursor c = db.rawQuery("SELECT DISTINCT path FROM info"null);    
  54.         List<String> pathList = new ArrayList<String>();    
  55.         while (c.moveToNext())    
  56.             pathList.add(c.getString(0));    
  57.         c.close();    
  58.          
  59.         return pathList;    
  60.     }    
  61.     public void deleteUndone(String path) {    
  62.         SQLiteDatabase db = helper.getWritableDatabase();    
  63.         db.execSQL("DELETE FROM info WHERE path=? "new Object[] { path });    
  64.          
  65.     }  
  66.   
  67. }  


    在上面的构造函数中:

  1. private static DBOpenHelper helper;  
  2.     public InfoDao(Context context) {  
  3.         if(helper == null)  
  4.         helper = new DBOpenHelper(context);  
  5.     }  

是为了在多线程操作数据库时容易出现死锁。

3.创建下载文件的类:

  1. public class Downloader {  
  2.     private Long done=0l;    
  3.     private InfoDao dao;    
  4.     private Long fileLen;    
  5.     private Handler handler;    
  6.     private boolean isPause = false;  
  7.     private Context context;  
  8.     public Downloader(Context context, Handler handler) {    
  9.         this.context = context;  
  10.         dao = new InfoDao(context);    
  11.         System.out.println(dao);  
  12.         this.handler = handler;    
  13.     }    
  14.       
  15.     public void download(String path, int thCount) throws Exception {    
  16.         URL url = new URL(path);  
  17.         HttpClient httpClient = new DefaultHttpClient();//下面几段代码是为了连接服务器  
  18.         HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), 20 * 1000);  
  19.         HttpPost post = new HttpPost(path);    
  20.         HttpResponse response = httpClient.execute(post);  
  21.         if(response.getStatusLine().getStatusCode()==200 ){//如果返回200表明连接成功  
  22.             fileLen = response.getEntity().getContentLength();  
  23.             String name = path.substring(path.lastIndexOf("/") + 1);  
  24.             File file = new File(Environment.getExternalStorageDirectory(), name);  
  25.             RandomAccessFile raf = new RandomAccessFile(file, "rws");  
  26.             raf.setLength(fileLen);  
  27.             raf.close();  
  28.               
  29.             Message msg = new Message();  
  30.             msg.what = 0;  
  31.             msg.getData().putLong("fileLen", fileLen);  
  32.             handler.sendMessage(msg);//发送一个空的消息,将文件的长度传递给进度条.  
  33.               
  34.             Long partLen = (fileLen + thCount-1) / thCount;  
  35.              for (int i = 0; i < thCount; i++)    
  36.                  new DownloadThread(url, file, partLen, i).start();  //这里是为了实现一个任务多线程下载,但是存在死锁问题,这里就只创建一个线程。  
  37.          } else {    
  38.              throw new IllegalArgumentException("404 path: " + path);    
  39.          }    
  40.         }  
  41.     private final class DownloadThread extends Thread {    
  42.         private URL url;    
  43.         private File file;    
  44.         private Long partLen;    
  45.         private int id;    
  46.     
  47.         public DownloadThread(URL url, File file, Long partLen, int id) {    
  48.             this.url = url;    
  49.             this.file = file;    
  50.             this.partLen = partLen;    
  51.             this.id = id;    
  52.         }    
  53.           
  54.         @Override  
  55.         public void run() {  
  56.             // TODO Auto-generated method stub  
  57.             Info info = dao.query(url.toString(), id);//查询记录当中没有下完的任务  
  58.             if(info!=null){  
  59.                 done = info.getDone();  
  60.             }else{  
  61.                 info = new Info(url.toString(),id,0l);  
  62.                 dao.insert(info);  
  63.             }  
  64.             Long start =  info.getDone();// 开始位置 = 已下载量    
  65.             Long end =  partLen-1;    
  66.             try{  
  67.                 IAddressTask task = new IAddressTask(context);  
  68.                 HttpClient httpClient = new DefaultHttpClient();  
  69.                 HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), 20 * 1000);  
  70.                 HttpPost post = new HttpPost(url.toString());    
  71.                 post.setHeader("Range""bytes=" + start + "-" + end);  //为上次没有下载完成的任务请求下载的起始位置  
  72.                 HttpResponse response = httpClient.execute(post);  
  73.                   
  74.                 RandomAccessFile raf = new RandomAccessFile(file, "rws");    
  75.                 raf.seek(start);   
  76.                  InputStream in = response.getEntity().getContent();//获取输入流,写入文件  
  77.                  byte[] buf = new byte[1024*10];  
  78.                  int len;  
  79.                  while((len = in.read(buf))!=-1){  
  80.                      raf.write(buf,0,len);  
  81.                      done+=len;  
  82.                      info.setDone(done);  
  83.                      dao.update(info);  
  84.                      Message msg = new Message();    
  85.                      msg.what = 1;    
  86.                      msg.getData().putLong("done", done);    
  87.                      handler.sendMessage(msg);  //每次读取一定长度的文件内容后,更新进度条的进度  
  88.                      if(isPause){  
  89.                          synchronized (dao) {  
  90.                              try{  
  91.                                 dao.wait();//暂停时该线程进入等待状态,并释放dao的锁  
  92.                                 httpClient = new DefaultHttpClient();//重新连接服务器,在wait时可能丢失了连接,如果不加这一段代码会出现connection。。。。。peer的错误  
  93.                                 HttpConnectionParams.setConnectionTimeout(httpClient.getParams(), 20 * 1000);  
  94.                                 post = new HttpPost(url.toString());    
  95.                                 post.setHeader("Range""bytes=" + done + "-" + end);    
  96.                                 response = httpClient.execute(post);    
  97.                                 raf.seek(done);   
  98.                                 in = response.getEntity().getContent();  
  99.                              }catch (Exception e) {  
  100.                           
  101.                                  e.printStackTrace();  
  102.                                    
  103.                             }  
  104.                               
  105.                         }  
  106.                      }  
  107.                  }  
  108.                  in.close();    
  109.                  raf.close();   
  110.                  // 删除下载记录     
  111.                  dao.deleteAll(info.getPath(), fileLen);  
  112.              } catch (IOException e) {    
  113.                  e.printStackTrace();    
  114.              }    
  115.         }  
  116.     }     
  117.       //暂停下载     
  118.         public void pause() {    
  119.             isPause = true;    
  120.         }    
  121.         //继续下载     
  122.         public void resumeDownload() {    
  123.             isPause = false;    
  124.             //恢复所有线程     
  125.             synchronized (dao) {    
  126.                 dao.notifyAll();    
  127.             }    
  128.         }    
  129.         //删除下载     
  130.         public void delete(String path) {   
  131.                isPause = true;    
  132.             synchronized (dao) {    
  133.                 dao.deleteUndone(path) ;  
  134.             }  
  135.         }    
  136.      
  137. }  


5.创建activity:

 

  1. public class DownloadActivity extends Activity{  
  2.        
  3.       private LinearLayout rootLinearLayout;  
  4.       private EditText pathEditText;  
  5.   
  6.     @Override  
  7.     protected void onCreate(Bundle savedInstanceState) {  
  8.         // TODO Auto-generated method stub  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.downloadmain);  
  11.           
  12.       
  13.         rootLinearLayout = (LinearLayout) findViewById(R.id.root);  
  14.         pathEditText = (EditText) findViewById(R.id.path);  
  15.           
  16.         List<String> list = new InfoDao(this).queryUndone();//判断上次没有下载完的任务  
  17.         for(String path:list){  
  18.             createDownload(path);  
  19.         }  
  20.     }  
  21.      public void download(View view) {  //系在按钮的单击事件  
  22.             final String path = pathEditText.getText().toString();    
  23.             final File file = new File(Environment.getExternalStorageDirectory(),path.substring(path.lastIndexOf("/") + 1));   
  24.             if(file.exists()){  
  25.                 new AlertDialog.Builder(DownloadActivity.this)  
  26.                 .setTitle("文件下载")  
  27.                 .setPositiveButton("确定"new DialogInterface.OnClickListener() {  
  28.                     @Override  
  29.                     public void onClick(DialogInterface dialog, int which) {  
  30.                         file.delete();  
  31.                         createDownload(path);    
  32.                     }  
  33.                 })  
  34.                 .setNegativeButton("取消"new DialogInterface.OnClickListener() {  
  35.                     @Override  
  36.                     public void onClick(DialogInterface dialog, int which) {  
  37.                         dialog.cancel();  
  38.                     }  
  39.                 })  
  40.                 .setMessage("文件已存在,确定要重新下载吗?")  
  41.                 .create()  
  42.                 .show();  
  43.                   
  44.             }else createDownload(path);   
  45.            
  46.         }    
  47.     private void createDownload(String path) { //为每一个任务创建一个下载线程,并分配进度条、按钮等  
  48.         LayoutInflater inflater = LayoutInflater.from(this);  
  49.         LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.download2, null);    
  50.           
  51.         LinearLayout childLinearLayout = (LinearLayout)linearLayout.findViewById(R.id.linearpro);  
  52.         ProgressBar progressBar = (ProgressBar) linearLayout.findViewById(R.id.downloadpro);  
  53.         TextView textView = (TextView) linearLayout. findViewById(R.id.downloadper);  
  54.         Button button = (Button)  linearLayout.findViewById(R.id.pause);  
  55.         Button delbutton = (Button)  linearLayout.findViewById(R.id.delete);  
  56.          
  57.         try {   
  58.             MyListener listener = new MyListener(progressBar, textView, path);//为每一个任务创建一个监听对象  
  59.             button.setOnClickListener(listener);   
  60.             delbutton.setOnClickListener(listener);   
  61.             //调用当前页面中某个容器的addView,将新创建的View添加进来     
  62.             rootLinearLayout.addView(linearLayout);    
  63.         } catch (Exception e) {    
  64.             e.printStackTrace();    
  65.         }     
  66.     }  
  67.    private final class MyListener implements OnClickListener {    
  68.         private ProgressBar progressBar;    
  69.         private TextView textView;    
  70.         private Long fileLen;    
  71.         private Downloader downloader;    
  72.         private String name;  
  73.         private String path;  
  74.         public MyListener(ProgressBar progressBar, TextView textView, String path) {    
  75.             this.progressBar = progressBar;    
  76.             this.textView = textView;    
  77.             this.path = path;  
  78.             name = path.substring(path.lastIndexOf("/") + 1);    
  79.             downloader = new Downloader(getApplicationContext(), handler);    
  80.             handler.sendEmptyMessage(-1);//这里发送消息是为了防止因为连接服务器引起界面无响应  
  81.         }    
  82.         //Handler传输数据     
  83.         private Handler handler = new Handler() {    
  84.             @Override    
  85.             public void handleMessage(Message msg) {    
  86.                 switch (msg.what) {    
  87.                 case -1:  
  88.                     try {    
  89.                         downloader.download(path, 1);  //开始下载文件  
  90.                     } catch (Exception e) {    
  91.                         e.printStackTrace();    
  92.                         Toast.makeText(getApplicationContext(), "下载过程中出现异常"0).show();    
  93.                     }    
  94.                     break;  
  95.                     case 0:    
  96.                         //获取文件的大小     
  97.                         fileLen = msg.getData().getLong("fileLen");    
  98.                         //设置进度条最大刻度:setMax()     
  99.                         progressBar.setMax(100);    
  100.                         break;    
  101.                     case 1:    
  102.                         //获取当前下载的总量     
  103.                         Long done = msg.getData().getLong("done");    
  104.                         //当前进度的百分比     
  105.                         textView.setText(name + "\t" + done * 100 / fileLen + "%");    
  106.                         //进度条设置当前进度:setProgress()     
  107.                         progressBar.setProgress((int)(done * 100 / fileLen));    
  108.                         if (done.equals(fileLen)) {    
  109.                             Toast.makeText(getApplicationContext(), name + " 下载完成"0).show();    
  110.                             //下载完成后退出进度条     
  111.                             rootLinearLayout.removeView((View) progressBar.getParent().getParent());    
  112.                               
  113.                         }    
  114.                         break;    
  115.                     case 3:rootLinearLayout.removeView((View) progressBar.getParent().getParent());    
  116.                         break;  
  117.                 }    
  118.             }    
  119.         };    
  120.     
  121.         /**  
  122.          * 暂停和继续下载  
  123.          */    
  124.         public void onClick(View v) {  
  125.             if(v.getId()==R.id.pause){  
  126.                 Button pauseButton = (Button) v;    
  127.                 if ("||".equals(pauseButton.getText())) {    
  128.                     downloader.pause();    
  129.                     pauseButton.setText("▶");    
  130.                 } else {    
  131.                     downloader.resumeDownload();    
  132.                     pauseButton.setText("||");    
  133.                 }    
  134.             }else if(v.getId()==R.id.delete){  
  135.                 File file = new File(Environment.getExternalStorageDirectory(),path.substring(path.lastIndexOf("/") + 1));  
  136.                 if(file.exists())file.delete();  
  137.                 downloader.delete(path);  
  138.                 handler.sendEmptyMessage(3);    
  139.             }  
  140.               
  141.         }    
  142.    }  
  143.      
  144.       
  145.   
  146. }  



6.布局文件:downloadmain.xml

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:orientation="vertical"  
  5.     android:layout_width="fill_parent"  
  6.     android:layout_height="fill_parent"  
  7.     android:id="@+id/root"  
  8.     >  
  9.         <TextView    
  10.             android:layout_width="fill_parent"   
  11.             android:layout_height="wrap_content"   
  12.             android:text="请输入下载路径"  
  13.             />  
  14.         <LinearLayout   
  15.             android:layout_width="fill_parent"  
  16.             android:layout_height="wrap_content"  
  17.             android:layout_marginBottom="30dp">  
  18.                 <EditText  
  19.                     android:id="@+id/path"  
  20.                     android:text="http://192.168.0.232:8080/dy/ThinkinJava.pdf"  
  21.                     android:layout_width="fill_parent"   
  22.                     android:layout_height="wrap_content"   
  23.                     android:singleLine="true"  
  24.                     android:layout_weight="1"/>  
  25.                 <Button android:layout_width="wrap_content"   
  26.                     android:layout_height="wrap_content"   
  27.                     android:text="下载"  
  28.                     android:onClick="download" />  
  29.               
  30.         </LinearLayout>  
  31.   
  32. </LinearLayout>  

download2.xml

    1. <?xml version="1.0" encoding="utf-8"?>  
    2. <LinearLayout   
    3.     xmlns:android="http://schemas.android.com/apk/res/android"  
    4.     android:layout_width="fill_parent"  
    5.     android:layout_height="wrap_content">  
    6.         <LinearLayout android:id="@+id/linearpro"  
    7.             android:orientation="vertical"  
    8.             android:layout_width="fill_parent"  
    9.             android:layout_height="wrap_content"  
    10.             android:layout_weight="1">  
    11.                 <ProgressBar android:id="@+id/downloadpro"  
    12.                     android:layout_width="fill_parent"   
    13.                     android:layout_height="20dp"  
    14.                     style="?android:attr/progressBarStyleHorizontal"/>  
    15.                 <TextView android:id="@+id/downloadper"  
    16.                         android:layout_width="wrap_content"   
    17.                         android:layout_height="wrap_content"  
    18.                         android:layout_gravity="center"  
    19.                         android:text="0%"/>  
    20.         </LinearLayout>  
    21.         <Button android:id="@+id/pause"  
    22.             android:layout_width="40dip"  
    23.             android:layout_height="40dip"  
    24.             android:text="||"/>  
    25.         <Button android:id="@+id/delete"  
    26.             android:layout_width="40dip"   
    27.             android:layout_height="40dip"   
    28.             android:text="X" />  
    29. </LinearLayout>  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值