斷點多線程下載的幾個關鍵點:①:得到要下載的文件大小後,均分給幾個線程。②:使用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();
}
}
進度條:這個可以忽略,用安卓原生的,代碼看我的另一篇博客:http://blog.csdn.net/qq_27102463/article/details/51612999。