android内存断点,Android实现断点多线程下载

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

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

效果图:

9608aec2c69ef08090e7dec1ecb336de.gif

线程类:线程类负责具体的下载,线程的下载信息被存储到了数据库。线程开始下载时,根据线程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 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 threadList = new ArrayList<>();

/**

* 构造器

*/

public DownUtil(Context context) {

this.context = context;

finalDb = DBUtil.getFinalDb(context);

judgeDownState();

}

/**

* 初始化时判断下载状态

*/

public void judgeDownState() {

//取出数据库中的下载信息,存储到内存中

List 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 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);

}

}

布局

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">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

android:id="@+id/et_download_url"

android:layout_width="0dp"

android:layout_height="wrap_content"

android:layout_weight="1"

android:hint="输入下载地址"/>

android:id="@+id/btn_download_geturl"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="确定"/>

android:id="@+id/np_download"

android:layout_width="match_parent"

android:layout_height="100dp">

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

android:id="@+id/btn_download_start"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="开始下载"/>

android:id="@+id/btn_download_pause"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="暂停下载"/>

android:id="@+id/btn_download_cancel"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="取消下载"/>

android:id="@+id/tv_download_file_info"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:id="@+id/tv_download_speed"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

android:id="@+id/tv_download_speed_info"

android:layout_width="match_parent"

android:layout_height="wrap_content"/>

下载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 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实现水平带数字百分比进度条

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值