栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 移动开发 > Android

Android编程开发实现多线程断点续传下载器实例

Android 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android编程开发实现多线程断点续传下载器实例

本文实例讲述了Android编程开发实现多线程断点续传下载器。分享给大家供大家参考,具体如下:

使用多线程断点续传下载器在下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度,在下载过程中记录每个线程已拷贝数据的数量,如果下载中断,比如无信号断线、电量不足等情况下,这就需要使用到断点续传功能,下次启动时从记录位置继续下载,可避免重复部分的下载。这里采用数据库来记录下载的进度。

效果图

 

断点续传

1.断点续传需要在下载过程中记录每条线程的下载进度
2.每次下载开始之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库
3.在每次向文件中写入数据之后,在数据库中更新下载进度
4.下载完成之后删除数据库中下载记录

Handler传输数据

这个主要用来记录百分比,每下载一部分数据就通知主线程来记录时间

1.主线程中创建的View只能在主线程中修改,其他线程只能通过和主线程通信,在主线程中改变View数据
2.我们使用Handler可以处理这种需求

主线程中创建Handler,重写handleMessage()方法

新线程中使用Handler发送消息,主线程即可收到消息,并且执行handleMessage()方法

动态生成新View

可实现多任务下载

1.创建XML文件,将要生成的View配置好
2.获取系统服务LayoutInflater,用来生成新的View
复制代码 代码如下:LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
3.使用inflate(int resource, ViewGroup root)方法生成新的View
4.调用当前页面中某个容器的addView,将新创建的View添加进来

示例

进度条样式 download.xml



  
    
    
    
  
  

顶部样式 main.xml



  
  
    
    

MainActivity.java

public class MainActivity extends Activity {
  private LayoutInflater inflater;
  private LinearLayout rootLinearLayout;
  private EditText pathEditText;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    //动态生成新View,获取系统服务LayoutInflater,用来生成新的View
    inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    rootLinearLayout = (LinearLayout) findViewById(R.id.root);
    pathEditText = (EditText) findViewById(R.id.path);
    // 窗体创建之后, 查询数据库是否有未完成任务, 如果有, 创建进度条等组件, 继续下载
    List list = new InfoDao(this).queryUndone();
    for (String path : list)
      createDownload(path);
  }
  
  public void download(View view) {
    String path = "http://192.168.1.199:8080/14_Web/" + pathEditText.getText().toString();
    createDownload(path);
  }
  
  private void createDownload(String path) {
    //获取系统服务LayoutInflater,用来生成新的View
    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
    LinearLayout linearLayout = (LinearLayout) inflater.inflate(R.layout.download, null);
    LinearLayout childLinearLayout = (LinearLayout) linearLayout.getChildAt(0);
    ProgressBar progressBar = (ProgressBar) childLinearLayout.getChildAt(0);
    TextView textView = (TextView) childLinearLayout.getChildAt(1);
    Button button = (Button) linearLayout.getChildAt(1);
    try {
      button.setonClickListener(new MyListener(progressBar, textView, path));
      //调用当前页面中某个容器的addView,将新创建的View添加进来
      rootLinearLayout.addView(linearLayout);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
  private final class MyListener implements onClickListener {
    private ProgressBar progressBar;
    private TextView textView;
    private int fileLen;
    private Downloader downloader;
    private String name;
    
    public MyListener(ProgressBar progressBar, TextView textView, String path) {
      this.progressBar = progressBar;
      this.textView = textView;
      name = path.substring(path.lastIndexOf("/") + 1);
      downloader = new Downloader(getApplicationContext(), handler);
      try {
 downloader.download(path, 3);
      } catch (Exception e) {
 e.printStackTrace();
 Toast.makeText(getApplicationContext(), "下载过程中出现异常", 0).show();
 throw new RuntimeException(e);
      }
    }
    //Handler传输数据
    private Handler handler = new Handler() {
      @Override
      public void handleMessage(Message msg) {
 switch (msg.what) {
   case 0:
     //获取文件的大小
     fileLen = msg.getData().getInt("fileLen");
     //设置进度条最大刻度:setMax()
     progressBar.setMax(fileLen);
     break;
   case 1:
     //获取当前下载的总量
     int done = msg.getData().getInt("done");
     //当前进度的百分比
     textView.setText(name + "t" + done * 100 / fileLen + "%");
     //进度条设置当前进度:setProgress()
     progressBar.setProgress(done);
     if (done == fileLen) {
Toast.makeText(getApplicationContext(), name + " 下载完成", 0).show();
//下载完成后退出进度条
rootLinearLayout.removeView((View) progressBar.getParent().getParent());
     }
     break;
 }
      }
    };
    
    public void onClick(View v) {
      Button pauseButton = (Button) v;
      if ("||".equals(pauseButton.getText())) {
 downloader.pause();
 pauseButton.setText("▶");
      } else {
 downloader.resume();
 pauseButton.setText("||");
      }
    }
  }
}

Downloader.java

public class Downloader {
  private int done;
  private InfoDao dao;
  private int fileLen;
  private Handler handler;
  private boolean isPause;
  public Downloader(Context context, Handler handler) {
    dao = new InfoDao(context);
    this.handler = handler;
  }
  
  public void download(String path, int thCount) throws Exception {
    URL url = new URL(path);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    //设置超时时间
    conn.setConnectTimeout(3000);
    if (conn.getResponseCode() == 200) {
      fileLen = conn.getContentLength();
      String name = path.substring(path.lastIndexOf("/") + 1);
      File file = new File(Environment.getExternalStorageDirectory(), name);
      RandomAccessFile raf = new RandomAccessFile(file, "rws");
      raf.setLength(fileLen);
      raf.close();
      //Handler发送消息,主线程接收消息,获取数据的长度
      Message msg = new Message();
      msg.what = 0;
      msg.getData().putInt("fileLen", fileLen);
      handler.sendMessage(msg);
      //计算每个线程下载的字节数
      int partLen = (fileLen + thCount - 1) / thCount;
      for (int i = 0; i < thCount; i++)
 new DownloadThread(url, file, partLen, i).start();
    } else {
      throw new IllegalArgumentException("404 path: " + path);
    }
  }
  private final class DownloadThread extends Thread {
    private URL url;
    private File file;
    private int partLen;
    private int id;
    public DownloadThread(URL url, File file, int partLen, int id) {
      this.url = url;
      this.file = file;
      this.partLen = partLen;
      this.id = id;
    }
    
    public void run() {
      // 判断上次是否有未完成任务
      Info info = dao.query(url.toString(), id);
      if (info != null) {
 // 如果有, 读取当前线程已下载量
 done += info.getDone();
      } else {
 // 如果没有, 则创建一个新记录存入
 info = new Info(url.toString(), id, 0);
 dao.insert(info);
      }
      int start = id * partLen + info.getDone(); // 开始位置 += 已下载量
      int end = (id + 1) * partLen - 1;
      try {
 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 conn.setReadTimeout(3000);
 //获取指定位置的数据,Range范围如果超出服务器上数据范围, 会以服务器数据末尾为准
 conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
 RandomAccessFile raf = new RandomAccessFile(file, "rws");
 raf.seek(start);
 //开始读写数据
 InputStream in = conn.getInputStream();
 byte[] buf = new byte[1024 * 10];
 int len;
 while ((len = in.read(buf)) != -1) {
   if (isPause) {
     //使用线程锁锁定该线程
     synchronized (dao) {
try {
  dao.wait();
} catch (InterruptedException e) {
  e.printStackTrace();
}
     }
   }
   raf.write(buf, 0, len);
   done += len;
   info.setDone(info.getDone() + len);
   // 记录每个线程已下载的数据量
   dao.update(info);
   //新线程中用Handler发送消息,主线程接收消息
   Message msg = new Message();
   msg.what = 1;
   msg.getData().putInt("done", done);
   handler.sendMessage(msg);
 }
 in.close();
 raf.close();
 // 删除下载记录
 dao.deleteAll(info.getPath(), fileLen);
      } catch (IOException e) {
 e.printStackTrace();
      }
    }
  }
  //暂停下载
  public void pause() {
    isPause = true;
  }
  //继续下载
  public void resume() {
    isPause = false;
    //恢复所有线程
    synchronized (dao) {
      dao.notifyAll();
    }
  }
}

Dao:

DBOpenHelper:

public class DBOpenHelper extends SQLiteOpenHelper {
  public DBOpenHelper(Context context) {
    super(context, "download.db", null, 1);
  }
  @Override
  public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE info(path VARCHAr(1024), thid INTEGER, done INTEGER, PRIMARY KEY(path, thid))");
  }
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  }
}

InfoDao:

public class InfoDao {
  private DBOpenHelper helper;
  public InfoDao(Context context) {
    helper = new DBOpenHelper(context);
  }
  public void insert(Info info) {
    SQLiteDatabase db = helper.getWritableDatabase();
    db.execSQL("INSERT INTO info(path, thid, done) VALUES(?, ?, ?)", new Object[] { info.getPath(), info.getThid(), info.getDone() });
  }
  public void delete(String path, int thid) {
    SQLiteDatabase db = helper.getWritableDatabase();
    db.execSQL("DELETE FROM info WHERe path=? AND thid=?", new Object[] { path, thid });
  }
  public void update(Info info) {
    SQLiteDatabase db = helper.getWritableDatabase();
    db.execSQL("UPDATE info SET done=? WHERe path=? AND thid=?", new Object[] { info.getDone(), info.getPath(), info.getThid() });
  }
  public Info query(String path, int thid) {
    SQLiteDatabase db = helper.getWritableDatabase();
    Cursor c = db.rawQuery("SELECT path, thid, done FROM info WHERe path=? AND thid=?", new String[] { path, String.valueOf(thid) });
    Info info = null;
    if (c.moveTonext())
      info = new Info(c.getString(0), c.getInt(1), c.getInt(2));
    c.close();
    return info;
  }
  public void deleteAll(String path, int len) {
    SQLiteDatabase db = helper.getWritableDatabase();
    Cursor c = db.rawQuery("SELECT SUM(done) FROM info WHERe path=?", new String[] { path });
    if (c.moveTonext()) {
      int result = c.getInt(0);
      if (result == len)
 db.execSQL("DELETe FROM info WHERe path=? ", new Object[] { path });
    }
  }
  public List queryUndone() {
    SQLiteDatabase db = helper.getWritableDatabase();
    Cursor c = db.rawQuery("SELECt DISTINCT path FROM info", null);
    List pathList = new ArrayList();
    while (c.moveTonext())
      pathList.add(c.getString(0));
    c.close();
    return pathList;
  }
}

希望本文所述对大家Android程序设计有所帮助。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/163662.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号