华域联盟 Andriod Android实现断点多线程下载

Android实现断点多线程下载

断点多线程下载的几个关键点:①:得到要下载的文件大小后,均分给几个线程。②:使用RandomAccessFile类进行读写,可以指定开始写入的位置。③:数据库保存下载信息,下一次继续下载的时候从数据库取出数据,然后从上次下载结束的地方开始。

这里我使用了FinalDb的数据库框架,同时在内存中存储了一份所有线程的下载信息,负责时时更新和查询下载进度。我测试用的是百度云网盘,文件大小50M左右。注意,线程中指定了开始结束下载位置的网络请求成功的返回码是206,并不是200。

效果图:

线程类:线程类负责具体的下载,线程的下载信息被存储到了数据库。线程开始下载时,根据线程ID查询自己的存储信息,然后开始从指定的位置下载和写入文件。完毕后根据自己的当前下载结果设置自己当前的下载状态。时时的下载进度存储只存储到了内存,只在本次下载结束才存储到数据库。

public class DownloadThread extends Thread {
 
  /**
   * 数据库操作工具
   */
  private FinalDb finalDb;
 
  /**
   * 下载状态:未开始
   */
  public static final int STATE_READY = 1;
 
  /**
   * 下载状态:下载中
   */
  public static final int STATE_LOADING = 2;
 
  /**
   * 下载状态:下载暂停中
   */
  public static final int STATE_PAUSING = 3;
 
  /**
   * 下载状态:下载完成
   */
  public static final int STATE_FINISH = 4;
 
  /**
   * 下载状态
   */
  public int downloadState;
 
  /**
   * 线程ID
   */
  private int threadID;
 
  /**
   * 要下载的URL路径
   */
  private String url;
 
  /**
   * 本线程要下载的文件
   */
  public RandomAccessFile file;
 
  /**
   * 构造器
   */
  public DownloadThread(Context context, int threadID, String downloadUrl, RandomAccessFile randomAccessFile) {
    this.threadID = threadID;
    this.url = downloadUrl;
    this.file = randomAccessFile;
    finalDb = DBUtil.getFinalDb(context);
  }
 
  @Override
  public void run() {
    //数据库查询本线程下载进度
    List<ThreadDownloadInfoBean> list = finalDb.findAllByWhere(ThreadDownloadInfoBean.class, "threadID='" + threadID + "'");
    //下载信息存放到内存
    if (list.get(0) != null) {
      MapUtil.map.put(threadID, list.get(0));
    }
    //取出实体类
    ThreadDownloadInfoBean bean = MapUtil.map.get(threadID);
    Utils.Print("bean:" + bean.toString());
    InputStream is;
    HttpURLConnection conn;
    try {
      Utils.Print("线程" + threadID + "开始连接");
      conn = (HttpURLConnection) new URL(url).openConnection();
      conn.setConnectTimeout(5000);
      conn.setReadTimeout(5000);
      conn.setRequestMethod("GET");
      //设置下载开始和结束的位置
      conn.setRequestProperty("Range", "bytes=" + (bean.startDownloadPosition + bean.downloadedSize) + "-" + bean.endDownloadPosition);
      conn.connect();
      if (conn.getResponseCode() == 206) {
        //更改下载状态
        downloadState = STATE_LOADING;
        bean.downloadState = STATE_LOADING;
        Utils.Print("线程" + threadID + "连接成功");
        is = conn.getInputStream();
        // 1K的数据缓冲
        byte[] bs = new byte[1024];
        // 读取到的数据长度
        int len;
        //从指定的位置开始下载
        file.seek(bean.startDownloadPosition);
        // 循环读取,当已经下载的大小达到了指定的本线程负责的大小时跳出循环,线程之间负责的文件首尾有重合的话没有影响,因为写入的内容时相同的
        while ((len = is.read(bs)) != -1) {
          //不用在这个循环里面更新数据库
          file.write(bs, 0, len);
          //时时更新内存中的已下载大小信息
          bean.downloadedSize += len;
          //如果调用者暂停下载,则跳出结束方法
          if (downloadState == STATE_PAUSING) {
            Utils.Print("线程" + threadID + "暂停下载");
            break;
          }
        }
        is.close();
        file.close();
      } else {
        Utils.Print("线程" + threadID + "连接失败");
      }
      conn.disconnect();
      //如果这个线程已经下载完了自己负责的部分就修改下载状态
      if (bean.downloadedSize >= bean.downloadTotalSize) {
        bean.downloadState = STATE_FINISH;
      } else {
        bean.downloadState = STATE_PAUSING;
      }
      //内存中信息更新至数据库
      finalDb.update(bean, "threadID='" + bean.threadID + "'");
    } catch (IOException e) {
      Utils.Print("线程" + threadID + "IO异常");
      e.printStackTrace();
    }
  }
}

线程信息的封装类:负责存储每个线程的ID,开始下载的位置,结束下载的位置,已经下载的大小,下载状态等;这个类用FinalDb数据库存储到数据库,一定要写get,set方法和空参构造器

@Table(name = "ThreadInfo")
public class ThreadDownloadInfoBean {
 
  /**
   * id
   */
  public int id;
 
  /**
   * 线程ID
   */
  public int threadID;
 
  /**
   * 本线程时时下载开始的位置
   */
  public long startDownloadPosition;
 
  /**
   * 本线程时时下载结束的位置
   */
  public long endDownloadPosition;
 
  /**
   * 本线程负责下载的文件大小
   */
  public long downloadTotalSize;
 
  /**
   * 已经下载了的文件大小
   */
  public long downloadedSize;
 
  /**
   * 本线程的下载状态
   */
  public int downloadState;
 
  public ThreadDownloadInfoBean() {
 
  }
 
  public ThreadDownloadInfoBean(int downloadState, long downloadedSize, long downloadTotalSize, long endDownloadPosition, long startDownloadPosition, int threadID) {
    this.downloadState = downloadState;
    this.downloadedSize = downloadedSize;
    this.downloadTotalSize = downloadTotalSize;
    this.endDownloadPosition = endDownloadPosition;
    this.startDownloadPosition = startDownloadPosition;
    this.threadID = threadID;
  }
 
  public int getId() {
    return id;
  }
 
  public void setId(int id) {
    this.id = id;
  }
 
  public int getThreadID() {
    return threadID;
  }
 
  public void setThreadID(int threadID) {
    this.threadID = threadID;
  }
 
  public long getStartDownloadPosition() {
    return startDownloadPosition;
  }
 
  public void setStartDownloadPosition(long startDownloadPosition) {
    this.startDownloadPosition = startDownloadPosition;
  }
 
  public long getEndDownloadPosition() {
    return endDownloadPosition;
  }
 
  public void setEndDownloadPosition(long endDownloadPosition) {
    this.endDownloadPosition = endDownloadPosition;
  }
 
  public long getDownloadTotalSize() {
    return downloadTotalSize;
  }
 
  public void setDownloadTotalSize(long downloadTotalSize) {
    this.downloadTotalSize = downloadTotalSize;
  }
 
  public long getDownloadedSize() {
    return downloadedSize;
  }
 
  public void setDownloadedSize(long downloadedSize) {
    this.downloadedSize = downloadedSize;
  }
 
  public int getDownloadState() {
    return downloadState;
  }
 
  public void setDownloadState(int downloadState) {
    this.downloadState = downloadState;
  }
 
  @Override
  public String toString() {
    return "ThreadDownloadInfoBean{" +
        "id=" + id +
        ", threadID=" + threadID +
        ", startDownloadPosition=" + startDownloadPosition +
        ", endDownloadPosition=" + endDownloadPosition +
        ", downloadTotalSize=" + downloadTotalSize +
        ", downloadedSize=" + downloadedSize +
        ", downloadState=" + downloadState +
        '}';
  }
}

下载工具类:这个类负责得到下载文件大小,分配线程下载大小,管理下载线程

public class DownUtil {
/**
* 数据库操作工具
*/
public FinalDb finalDb;
/**
* 下载状态:准备好
*/
public static final int STATE_READY = 1;
/**
* 下载状态:下载中
*/
public static final int STATE_LOADING = 2;
/**
* 下载状态:暂停中
*/
public static final int STATE_PAUSING = 3;
/**
* 下载状态:下载完成
*/
public static final int STATE_FINISH = 4;
/**
* 下载状态
*/
public int downloadState;
/**
* context
*/
private Context context;
/**
* 要下载文件的大小
*/
public long fileSize;
/**
* 线程集合
*/
private ArrayList<DownloadThread> threadList = new ArrayList<>();
/**
* 构造器
*/
public DownUtil(Context context) {
this.context = context;
finalDb = DBUtil.getFinalDb(context);
judgeDownState();
}
/**
* 初始化时判断下载状态
*/
public void judgeDownState() {
//取出数据库中的下载信息,存储到内存中
List<ThreadDownloadInfoBean> list = finalDb.findAll(ThreadDownloadInfoBean.class);
if (list != null && list.size() == DownloadActivity.threadNum) {
for (int i = 0; i < list.size(); i++) {
MapUtil.map.put(i, list.get(i));
}
}
//查询SP中是否存储过要下载的文件大小
Long spFileSize = SPUtil.getInstance(context).getLong(DownloadActivity.fileName, 0L);
long downloadedSize = getFinishedSize();
//SP中或者数据库中没有查询到说明从没有进行过下载
if (spFileSize == 0 || downloadedSize == 0) {
downloadState = STATE_READY;
} else if (downloadedSize >= spFileSize) {
downloadState = STATE_FINISH;
} else {
downloadState = STATE_PAUSING;
fileSize = spFileSize;
}
}
/**
* 点击了开始按钮
*/
public void clickDownloadBtn() {
if (downloadState == STATE_READY) {
startDownload();
} else if (downloadState == STATE_PAUSING) {
continueDownload();
}
}
/**
* 进入应用第一次开始下载
*/
private void startDownload() {
//开启新线程,得到要下载的文件大小
new Thread() {
@Override
public void run() {
try {
HttpURLConnection conn;
conn = (HttpURLConnection) new URL(DownloadActivity.url).openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
conn.connect();
if (conn.getResponseCode() == 200) {
Utils.Print("DownUtil连接成功");
//得到要下载的文件大小
fileSize = conn.getContentLength();
//得到文件名后缀名
String contentDisposition = new String(conn.getHeaderField("Content-Disposition").getBytes("ISO-8859-1"), "UTF-8");
String fileName = "下载测试" + contentDisposition.substring(contentDisposition.lastIndexOf("."), contentDisposition.lastIndexOf("\""));
//得到存储路径
String sdCardPath = context.getExternalFilesDir(null).getPath();
DownloadActivity.fileName = sdCardPath + "/" + fileName;
SPUtil.getInstance(context).saveString(DownloadActivity.FILE_NAME, DownloadActivity.fileName);
SPUtil.getInstance(context).saveLong(DownloadActivity.fileName, fileSize);
/*
* 计算一下每个线程需要分担的下载文件大小 比如 总下载量为100 一共有三个线程
* 那么 线程1负责0-32,线程2负责33-65,线程3负责66-99和100,
* 也就是说下载总量除以线程数如果有余数,那么最后一个线程多下载一个余数部分
*/
//每个线程均分的大小
long threadDownSize = fileSize / DownloadActivity.threadNum;
//线程均分完毕剩余的大小
long leftDownSize = fileSize % DownloadActivity.threadNum;
//创建要写入的文件
RandomAccessFile file = new RandomAccessFile(DownloadActivity.fileName, "rw");
//设置文件大小
file.setLength(fileSize);
//关闭
file.close();
for (int i = 0; i < DownloadActivity.threadNum; i++) {
Utils.Print("开启线程" + i);
//指定每个线程开始下载的位置
long startPosition = i * threadDownSize;
//指定每个线程负责下载的大小,当现场是集合里面最后一个线程的时候,它要增加leftDownSize的大小
threadDownSize = i == DownloadActivity.threadNum - 1 ? threadDownSize + leftDownSize : threadDownSize;
//存储線程信息
ThreadDownloadInfoBean bean = new ThreadDownloadInfoBean(DownloadThread.STATE_READY, 0, threadDownSize, startPosition + threadDownSize, startPosition, i);
finalDb.save(bean);
RandomAccessFile threadFile = new RandomAccessFile(DownloadActivity.fileName, "rw");
threadList.add(new DownloadThread(context, i, DownloadActivity.url, threadFile));
threadList.get(i).start();
}
downloadState = STATE_LOADING;
downloadInfoListener.connectSuccess();
} else {
Utils.Print("DownUtil-连接失败");
downloadInfoListener.connectFail();
}
conn.disconnect();
} catch (IOException e) {
Utils.Print("DownUtil-IO异常");
downloadInfoListener.IOException();
e.printStackTrace();
}
}
}.start();
}
/**
* 继续下载
*/
private void continueDownload() {
List<ThreadDownloadInfoBean> list = finalDb.findAll(ThreadDownloadInfoBean.class);
for (int i = 0; i < DownloadActivity.threadNum; i++) {
//当前线程已经下载完了就不再开启
if (list.get(i).downloadState != DownloadThread.STATE_FINISH) {
Utils.Print("重新开启线程" + i);
RandomAccessFile threadFile = null;
try {
threadFile = new RandomAccessFile(DownloadActivity.fileName, "rw");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
DownloadThread downloadThread = new DownloadThread(context, i, DownloadActivity.url, threadFile);
threadList.add(downloadThread);
downloadThread.start();
}
}
downloadState = STATE_LOADING;
downloadInfoListener.connectSuccess();
}
/**
* 点击了暂停的按钮
*/
public void clickPauseBtn() {
if (downloadState == STATE_LOADING) {
stopDownload();
}
}
/**
* 暂停下载
*/
private void stopDownload() {
for (int i = 0; i < threadList.size(); i++) {
if (threadList.get(i).downloadState == DownloadThread.STATE_LOADING) {
threadList.get(i).downloadState = DownloadThread.STATE_PAUSING;
}
}
downloadState = STATE_PAUSING;
threadList.clear();
}
/**
* 返回此刻所有线程完成的下载大小
*/
public long getFinishedSize() {
long totalSize = 0;
for (int i = 0; i < DownloadActivity.threadNum; i++) {
ThreadDownloadInfoBean bean = MapUtil.map.get(i);
if (bean != null) {
//如果该线程已经下载的部分大于分配给它的部分多余部分不予计算
long addSize = bean.downloadedSize > bean.downloadTotalSize ? bean.downloadTotalSize : bean.downloadedSize;
totalSize += addSize;
}
}
return totalSize;
}
/**
* 下载信息监听器
*/
private DownloadInfoListener downloadInfoListener;
/**
* 下载信息监听器
*/
public interface DownloadInfoListener {
void connectSuccess();
void connectFail();
void IOException();
}
/**
* 设置下载信息监听器
*/
public void setDownloadInfoListener(DownloadInfoListener downloadInfoListener) {
this.downloadInfoListener = downloadInfoListener;
}
}

页面Activity:负责展示下载进度等信息,提供操作页面

public class DownloadActivity extends BaseActivity {
/**
* 下载地址输入框
*/
private EditText et_download_url;
/**
* 确定输入地址的按钮
*/
private Button btn_download_geturl;
/**
* 进度条
*/
private NumberProgressView np_download;
/**
* 开始下载的按钮
*/
private Button btn_download_start;
/**
* 暂停下载的按钮
*/
private Button btn_download_pause;
/**
* 取消下载的按钮
*/
private Button btn_download_cancel;
/**
* 文件信息显示
*/
private TextView tv_download_file_info;
/**
* 下载速度显示
*/
private TextView tv_download_speed;
/**
* 显示下载信息
*/
private TextView tv_download_speed_info;
/**
* 每隔一段时间刷新下载进度显示
*/
private final static int WHAT_INCREACE = 1;
/**
* 得到了文件名称
*/
private final static int WHAT_GET_FILENAME = 2;
/**
* downUtil连接失败
*/
private final static int WHAI_CONNECT_FAIL = 3;
/**
* downUtilIO异常
*/
private final static int WHAT_IO_EXCEPTION = 4;
/**
* 下载工具
*/
private DownUtil downUtil;
/**
* 需要开启的线程数量
*/
public static final int threadNum = 5;
/**
* 存放文件路径名称的SP键名
*/
public static final String FILE_NAME = "fileName";
/**
* 要下载的文件的url地址
*/
public static String url = "";
/**
* 文件下载路径和文件名称
*/
public static String fileName;
/**
* 上次统计已经完成下载的文件大小
*/
private long lastFinishedSize;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case WHAT_INCREACE:
updateView();
break;
case WHAT_GET_FILENAME:
tv_download_file_info.setText("下载路径:" + fileName);
break;
case WHAI_CONNECT_FAIL:
tv_download_file_info.setText("连接失败");
break;
case WHAT_IO_EXCEPTION:
tv_download_file_info.setText("IO异常");
break;
}
}
};
/**
* 更新视图
*/
private void updateView() {
//当前已经完成下载的文件大小
long currentFinishedSize = downUtil.getFinishedSize();
//要显示的下载信息的文字
StringBuilder downloadInfo = new StringBuilder();
tv_download_speed.setText("当前下载速度:" + formateSize(currentFinishedSize - lastFinishedSize) + "/s");
//本次统计比上次统计增加了,更新视图,本次统计较上次统计没有变化,不做视图更新
if (currentFinishedSize > lastFinishedSize) {
lastFinishedSize = currentFinishedSize;
downloadInfo.append("下载进度:" + formateSize(currentFinishedSize) + "/" + formateSize(downUtil.fileSize) + "\n");
for (int i = 0; i < threadNum; i++) {
ThreadDownloadInfoBean bean = MapUtil.map.get(i);
if (bean.downloadTotalSize != 0) {
downloadInfo.append("线程" + i + "下载进度:" + bean.downloadedSize * 100 / bean.downloadTotalSize + "%  " + formateSize(bean.downloadedSize) + "/" + formateSize(bean.downloadTotalSize) + "\n");
}
}
np_download.setProgress((int) (currentFinishedSize * 100 / downUtil.fileSize));
tv_download_speed_info.setText(downloadInfo);
//下载完成后
if (currentFinishedSize >= downUtil.fileSize) {
tv_download_speed.setText("下载完毕");
handler.removeMessages(WHAT_INCREACE);
return;
}
}
handler.sendEmptyMessageDelayed(WHAT_INCREACE, 1000);
}
/**
* 返回子类要显示的布局 R.layout......
*/
@Override
protected int childView() {
return R.layout.activity_download;
}
/**
* 强制子类实现该抽象方法,初始化各自的View
*/
@Override
protected void findChildView() {
et_download_url = (EditText) findViewById(R.id.et_download_url);
btn_download_geturl = (Button) findViewById(R.id.btn_download_geturl);
np_download = (NumberProgressView) findViewById(R.id.np_download);
btn_download_start = (Button) findViewById(R.id.btn_download_start);
btn_download_pause = (Button) findViewById(R.id.btn_download_pause);
btn_download_cancel = (Button) findViewById(R.id.btn_download_cancel);
tv_download_file_info = (TextView) findViewById(R.id.tv_download_file_info);
tv_download_speed = (TextView) findViewById(R.id.tv_download_speed);
tv_download_speed_info = (TextView) findViewById(R.id.tv_download_speed_info);
}
/**
* 强制子类实现该抽象方法,初始化各自数据
*/
@Override
protected void initChildData() {
downUtil = new DownUtil(this);
lastFinishedSize = downUtil.getFinishedSize();
StringBuilder downloadInfo = new StringBuilder();
fileName = SPUtil.getInstance(this).getString(FILE_NAME, null);
if (fileName != null) {
downloadInfo.append("下载路径:" + fileName);
if (downUtil.downloadState == DownUtil.STATE_FINISH) {
np_download.setProgress(100);
} else if (downUtil.downloadState == DownUtil.STATE_PAUSING) {
np_download.setProgress((int) (downUtil.getFinishedSize() * 100 / downUtil.fileSize));
}
tv_download_file_info.setText(downloadInfo);
}
}
/**
* 强制子类实现该抽象方法,设置自己的监听器
*/
@Override
protected View[] setChildListener() {
downUtil.setDownloadInfoListener(new DownUtil.DownloadInfoListener() {
@Override
public void connectSuccess() {
//不能在此更新,需要到主线程刷新UI
handler.sendEmptyMessage(WHAT_GET_FILENAME);
handler.sendEmptyMessage(WHAT_INCREACE);
}
@Override
public void connectFail() {
handler.sendEmptyMessage(WHAI_CONNECT_FAIL);
}
@Override
public void IOException() {
handler.sendEmptyMessage(WHAT_IO_EXCEPTION);
}
});
return new View[]{btn_download_start, btn_download_pause, btn_download_geturl, btn_download_cancel};
}
/**
* 子类在这个方法里面实现自己View的点击监听事件的相应
*
* @param v 父类传递到子类的点击的View
*/
@Override
protected void clickChildView(View v) {
switch (v.getId()) {
case R.id.btn_download_start:
downUtil.clickDownloadBtn();
break;
case R.id.btn_download_pause:
downUtil.clickPauseBtn();
handler.removeMessages(WHAT_INCREACE);
break;
case R.id.btn_download_cancel:
//停止发送消息
handler.removeMessages(WHAT_INCREACE);
//暂停下载
downUtil.clickPauseBtn();
//重置状态
downUtil.downloadState = DownUtil.STATE_READY;
//统计下载的大小归零
lastFinishedSize = 0;
//重置文本信息
tv_download_speed.setText("");
tv_download_file_info.setText("");
tv_download_speed_info.setText("");
//进度条归零
np_download.setProgress(0);
//清空内存数据
MapUtil.map.clear();
//sp清理
SPUtil.getInstance(this).removeAll();
//清空数据库
downUtil.finalDb.deleteAll(ThreadDownloadInfoBean.class);
//删除文件
if (fileName != null) {
File file = new File(fileName);
if (file.exists()) {
boolean delete = file.delete();
if (delete) {
Utils.ToastS(this, "删除" + fileName + "成功");
} else {
Utils.ToastS(this, "删除失败");
}
} else {
Utils.ToastS(this, "文件不存在");
}
} else {
Utils.ToastS(this, "文件不存在");
}
break;
case R.id.btn_download_geturl:
String editTextUrl = et_download_url.getText().toString();
if (editTextUrl.trim().equals("")) {
Utils.ToastS(this, "请输入地址");
} else {
url = editTextUrl;
}
break;
}
}
@Override
protected void onDestroy() {
handler.removeCallbacksAndMessages(null);
downUtil.clickPauseBtn();
super.onDestroy();
}
/**
* 格式化数据大小
*/
private String formateSize(long size) {
return Formatter.formatFileSize(this, size);
}
}

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.testdemo.activity.DownloadActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/et_download_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="输入下载地址"/>
<Button
android:id="@+id/btn_download_geturl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="确定"/>
</LinearLayout>
<com.example.testdemo.view.NumberProgressView
android:id="@+id/np_download"
android:layout_width="match_parent"
android:layout_height="100dp">
</com.example.testdemo.view.NumberProgressView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_download_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始下载"/>
<Button
android:id="@+id/btn_download_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停下载"/>
<Button
android:id="@+id/btn_download_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消下载"/>
</LinearLayout>
<TextView
android:id="@+id/tv_download_file_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_download_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/tv_download_speed_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

下载Activity的父类

public abstract class BaseActivity extends Activity {
/**
* 点击监听器,子类也可以使用
*/
protected BaseOnClickListener onClickListener = new BaseOnClickListener();
/**
* 在onCreate方法里面,找到视图,初始化数据,设置点击监听器
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(childView());
findView();
initData();
setListener();
}
/**
* 返回子类要显示的布局 R.layout......
*/
protected abstract int childView();
/**
* 找到需要的视图---网址:https://www.buzzingandroid.com/tools/android-layout-finder/
*/
private void findView() {
findChildView();
}
/**
* 初始化数据
*/
private void initData() {
// 初始化子类的数据
initChildData();
}
/**
* 对需要设置监听 的视图设置监听
*/
private void setListener() {
// 子类实现setChildListene()方法,返回一个View数组,里面包含所有需要设置点击监听的View,然后进行For循环,对里面所有View设置监听器
if (setChildListener() == null || setChildListener().length == 0) {
return;
}
View[] viewArray = setChildListener();
for (View view : viewArray) {
view.setOnClickListener(onClickListener);
}
}
/**
* 自定义的点击监听类
*/
protected class BaseOnClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
clickChildView(v);
}
}
/**
* 强制子类实现该抽象方法,初始化各自的View
*/
protected abstract void findChildView();
/**
* 强制子类实现该抽象方法,初始化各自数据
*/
protected abstract void initChildData();
/**
* 强制子类实现该抽象方法,设置自己的监听器
*/
protected abstract View[] setChildListener();
/**
* 子类在这个方法里面实现自己View的点击监听事件的相应
*
* @param v 父类传递到子类的点击的View
*/
protected abstract void clickChildView(View v);
}

得到数据库的操作类

public class DBUtil {
public static FinalDb getFinalDb(final Context context) {
FinalDb finalDb = FinalDb.create(context, "REMUXING.db", false, 1, new FinalDb.DbUpdateListener() {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVirsion, int newVirsion) {
}
});
return finalDb;
}
}

内存中存储下载信息的类

public class MapUtil {
public static HashMap<Integer, ThreadDownloadInfoBean> map = new HashMap<>();
}

SP存储的工具类

public enum SPUtil {
SPUTIL;
public static final String NOTIFICATIONBAR_HEIGHT = "notificationbar_height";
private static SharedPreferences sp;
public static SPUtil getInstance(Context context) {
if (sp == null) {
sp = context.getSharedPreferences("REMUXING", Context.MODE_PRIVATE);
}
return SPUTIL;
}
public void saveLong(String key, Long value) {
sp.edit().putLong(key, value).apply();
}
public Long getLong(String key, Long defValue) {
return sp.getLong(key, defValue);
}
public void saveString(String key, String value) {
sp.edit().putString(key, value).apply();
}
public String getString(String key, String defValue) {
return sp.getString(key, defValue);
}
public void removeAll() {
sp.edit().clear().apply();
}
}

工具类

public class Utils {
/**
* 打印
*/
public static void Print(String message) {
Log.e("TAG", "-----   " + message + "   ------") ;
}
/**
* 短吐司
*/
public static void ToastS(Context context, String message) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
}

进度条:这个可以忽略,用安卓原生的,代码看我的另一篇博客:Android自定义View实现水平带数字百分比进度条

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持华域联盟。

本文由 华域联盟 原创撰写:华域联盟 » Android实现断点多线程下载

转载请保留出处和原文链接:https://www.cnhackhy.com/107258.htm

本文来自网络,不代表华域联盟立场,转载请注明出处。

作者: sterben

Android progressbar实现带底部指示器和文字的进度条

Android自定义View实现圆环带数字百分比进度条

发表回复

联系我们

联系我们

2551209778

在线咨询: QQ交谈

邮箱: [email protected]

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们