斷點(diǎn)多線程下載的幾個(gè)關(guān)鍵點(diǎn):①:得到要下載的文件大小后,均分給幾個(gè)線程。②:使用RandomAccessFile類進(jìn)行讀寫,可以指定開始寫入的位置。③:數(shù)據(jù)庫保存下載信息,下一次繼續(xù)下載的時(shí)候從數(shù)據(jù)庫取出數(shù)據(jù),然后從上次下載結(jié)束的地方開始。
為石屏等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及石屏網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站設(shè)計(jì)制作、網(wǎng)站設(shè)計(jì)、石屏網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!
這里我使用了FinalDb的數(shù)據(jù)庫框架,同時(shí)在內(nèi)存中存儲(chǔ)了一份所有線程的下載信息,負(fù)責(zé)時(shí)時(shí)更新和查詢下載進(jìn)度。我測(cè)試用的是百度云網(wǎng)盤,文件大小50M左右。注意,線程中指定了開始結(jié)束下載位置的網(wǎng)絡(luò)請(qǐng)求成功的返回碼是206,并不是200。
效果圖:
線程類:線程類負(fù)責(zé)具體的下載,線程的下載信息被存儲(chǔ)到了數(shù)據(jù)庫。線程開始下載時(shí),根據(jù)線程ID查詢自己的存儲(chǔ)信息,然后開始從指定的位置下載和寫入文件。完畢后根據(jù)自己的當(dāng)前下載結(jié)果設(shè)置自己當(dāng)前的下載狀態(tài)。時(shí)時(shí)的下載進(jìn)度存儲(chǔ)只存儲(chǔ)到了內(nèi)存,只在本次下載結(jié)束才存儲(chǔ)到數(shù)據(jù)庫。
public class DownloadThread extends Thread { /** * 數(shù)據(jù)庫操作工具 */ private FinalDb finalDb; /** * 下載狀態(tài):未開始 */ public static final int STATE_READY = 1; /** * 下載狀態(tài):下載中 */ public static final int STATE_LOADING = 2; /** * 下載狀態(tài):下載暫停中 */ public static final int STATE_PAUSING = 3; /** * 下載狀態(tài):下載完成 */ public static final int STATE_FINISH = 4; /** * 下載狀態(tài) */ public int downloadState; /** * 線程ID */ private int threadID; /** * 要下載的URL路徑 */ private String url; /** * 本線程要下載的文件 */ public RandomAccessFile file; /** * 構(gòu)造器 */ 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() { //數(shù)據(jù)庫查詢本線程下載進(jìn)度 Listlist = finalDb.findAllByWhere(ThreadDownloadInfoBean.class, "threadID='" + threadID + "'"); //下載信息存放到內(nèi)存 if (list.get(0) != null) { MapUtil.map.put(threadID, list.get(0)); } //取出實(shí)體類 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"); //設(shè)置下載開始和結(jié)束的位置 conn.setRequestProperty("Range", "bytes=" + (bean.startDownloadPosition + bean.downloadedSize) + "-" + bean.endDownloadPosition); conn.connect(); if (conn.getResponseCode() == 206) { //更改下載狀態(tài) downloadState = STATE_LOADING; bean.downloadState = STATE_LOADING; Utils.Print("線程" + threadID + "連接成功"); is = conn.getInputStream(); // 1K的數(shù)據(jù)緩沖 byte[] bs = new byte[1024]; // 讀取到的數(shù)據(jù)長度 int len; //從指定的位置開始下載 file.seek(bean.startDownloadPosition); // 循環(huán)讀取,當(dāng)已經(jīng)下載的大小達(dá)到了指定的本線程負(fù)責(zé)的大小時(shí)跳出循環(huán),線程之間負(fù)責(zé)的文件首尾有重合的話沒有影響,因?yàn)閷懭氲膬?nèi)容時(shí)相同的 while ((len = is.read(bs)) != -1) { //不用在這個(gè)循環(huán)里面更新數(shù)據(jù)庫 file.write(bs, 0, len); //時(shí)時(shí)更新內(nèi)存中的已下載大小信息 bean.downloadedSize += len; //如果調(diào)用者暫停下載,則跳出結(jié)束方法 if (downloadState == STATE_PAUSING) { Utils.Print("線程" + threadID + "暫停下載"); break; } } is.close(); file.close(); } else { Utils.Print("線程" + threadID + "連接失敗"); } conn.disconnect(); //如果這個(gè)線程已經(jīng)下載完了自己負(fù)責(zé)的部分就修改下載狀態(tài) if (bean.downloadedSize >= bean.downloadTotalSize) { bean.downloadState = STATE_FINISH; } else { bean.downloadState = STATE_PAUSING; } //內(nèi)存中信息更新至數(shù)據(jù)庫 finalDb.update(bean, "threadID='" + bean.threadID + "'"); } catch (IOException e) { Utils.Print("線程" + threadID + "IO異常"); e.printStackTrace(); } } }
線程信息的封裝類:負(fù)責(zé)存儲(chǔ)每個(gè)線程的ID,開始下載的位置,結(jié)束下載的位置,已經(jīng)下載的大小,下載狀態(tài)等;這個(gè)類用FinalDb數(shù)據(jù)庫存儲(chǔ)到數(shù)據(jù)庫,一定要寫get,set方法和空參構(gòu)造器
@Table(name = "ThreadInfo") public class ThreadDownloadInfoBean { /** * id */ public int id; /** * 線程ID */ public int threadID; /** * 本線程時(shí)時(shí)下載開始的位置 */ public long startDownloadPosition; /** * 本線程時(shí)時(shí)下載結(jié)束的位置 */ public long endDownloadPosition; /** * 本線程負(fù)責(zé)下載的文件大小 */ public long downloadTotalSize; /** * 已經(jīng)下載了的文件大小 */ public long downloadedSize; /** * 本線程的下載狀態(tài) */ 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 + '}'; } }
下載工具類:這個(gè)類負(fù)責(zé)得到下載文件大小,分配線程下載大小,管理下載線程
public class DownUtil { /** * 數(shù)據(jù)庫操作工具 */ public FinalDb finalDb; /** * 下載狀態(tài):準(zhǔn)備好 */ public static final int STATE_READY = 1; /** * 下載狀態(tài):下載中 */ public static final int STATE_LOADING = 2; /** * 下載狀態(tài):暫停中 */ public static final int STATE_PAUSING = 3; /** * 下載狀態(tài):下載完成 */ public static final int STATE_FINISH = 4; /** * 下載狀態(tài) */ public int downloadState; /** * context */ private Context context; /** * 要下載文件的大小 */ public long fileSize; /** * 線程集合 */ private ArrayListthreadList = new ArrayList<>(); /** * 構(gòu)造器 */ public DownUtil(Context context) { this.context = context; finalDb = DBUtil.getFinalDb(context); judgeDownState(); } /** * 初始化時(shí)判斷下載狀態(tài) */ public void judgeDownState() { //取出數(shù)據(jù)庫中的下載信息,存儲(chǔ)到內(nèi)存中 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中是否存儲(chǔ)過要下載的文件大小 Long spFileSize = SPUtil.getInstance(context).getLong(DownloadActivity.fileName, 0L); long downloadedSize = getFinishedSize(); //SP中或者數(shù)據(jù)庫中沒有查詢到說明從沒有進(jìn)行過下載 if (spFileSize == 0 || downloadedSize == 0) { downloadState = STATE_READY; } else if (downloadedSize >= spFileSize) { downloadState = STATE_FINISH; } else { downloadState = STATE_PAUSING; fileSize = spFileSize; } } /** * 點(diǎn)擊了開始按鈕 */ public void clickDownloadBtn() { if (downloadState == STATE_READY) { startDownload(); } else if (downloadState == STATE_PAUSING) { continueDownload(); } } /** * 進(jìn)入應(yīng)用第一次開始下載 */ 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 = "下載測(cè)試" + contentDisposition.substring(contentDisposition.lastIndexOf("."), contentDisposition.lastIndexOf("\"")); //得到存儲(chǔ)路徑 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); /* * 計(jì)算一下每個(gè)線程需要分擔(dān)的下載文件大小 比如 總下載量為100 一共有三個(gè)線程 * 那么 線程1負(fù)責(zé)0-32,線程2負(fù)責(zé)33-65,線程3負(fù)責(zé)66-99和100, * 也就是說下載總量除以線程數(shù)如果有余數(shù),那么最后一個(gè)線程多下載一個(gè)余數(shù)部分 */ //每個(gè)線程均分的大小 long threadDownSize = fileSize / DownloadActivity.threadNum; //線程均分完畢剩余的大小 long leftDownSize = fileSize % DownloadActivity.threadNum; //創(chuàng)建要寫入的文件 RandomAccessFile file = new RandomAccessFile(DownloadActivity.fileName, "rw"); //設(shè)置文件大小 file.setLength(fileSize); //關(guān)閉 file.close(); for (int i = 0; i < DownloadActivity.threadNum; i++) { Utils.Print("開啟線程" + i); //指定每個(gè)線程開始下載的位置 long startPosition = i * threadDownSize; //指定每個(gè)線程負(fù)責(zé)下載的大小,當(dāng)現(xiàn)場(chǎng)是集合里面最后一個(gè)線程的時(shí)候,它要增加leftDownSize的大小 threadDownSize = i == DownloadActivity.threadNum - 1 ? threadDownSize + leftDownSize : threadDownSize; //存儲(chǔ)線程信息 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(); } /** * 繼續(xù)下載 */ private void continueDownload() { List list = finalDb.findAll(ThreadDownloadInfoBean.class); for (int i = 0; i < DownloadActivity.threadNum; i++) { //當(dāng)前線程已經(jīng)下載完了就不再開啟 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(); } /** * 點(diǎn)擊了暫停的按鈕 */ 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) { //如果該線程已經(jīng)下載的部分大于分配給它的部分多余部分不予計(jì)算 long addSize = bean.downloadedSize > bean.downloadTotalSize ? bean.downloadTotalSize : bean.downloadedSize; totalSize += addSize; } } return totalSize; } /** * 下載信息監(jiān)聽器 */ private DownloadInfoListener downloadInfoListener; /** * 下載信息監(jiān)聽器 */ public interface DownloadInfoListener { void connectSuccess(); void connectFail(); void IOException(); } /** * 設(shè)置下載信息監(jiān)聽器 */ public void setDownloadInfoListener(DownloadInfoListener downloadInfoListener) { this.downloadInfoListener = downloadInfoListener; } }
頁面Activity:負(fù)責(zé)展示下載進(jìn)度等信息,提供操作頁面
public class DownloadActivity extends BaseActivity { /** * 下載地址輸入框 */ private EditText et_download_url; /** * 確定輸入地址的按鈕 */ private Button btn_download_geturl; /** * 進(jìn)度條 */ 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; /** * 每隔一段時(shí)間刷新下載進(jìn)度顯示 */ 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; /** * 需要開啟的線程數(shù)量 */ public static final int threadNum = 5; /** * 存放文件路徑名稱的SP鍵名 */ public static final String FILE_NAME = "fileName"; /** * 要下載的文件的url地址 */ public static String url = ""; /** * 文件下載路徑和文件名稱 */ public static String fileName; /** * 上次統(tǒng)計(jì)已經(jīng)完成下載的文件大小 */ 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() { //當(dāng)前已經(jīng)完成下載的文件大小 long currentFinishedSize = downUtil.getFinishedSize(); //要顯示的下載信息的文字 StringBuilder downloadInfo = new StringBuilder(); tv_download_speed.setText("當(dāng)前下載速度:" + formateSize(currentFinishedSize - lastFinishedSize) + "/s"); //本次統(tǒng)計(jì)比上次統(tǒng)計(jì)增加了,更新視圖,本次統(tǒng)計(jì)較上次統(tǒng)計(jì)沒有變化,不做視圖更新 if (currentFinishedSize > lastFinishedSize) { lastFinishedSize = currentFinishedSize; downloadInfo.append("下載進(jìn)度:" + 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 + "下載進(jìn)度:" + 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; } /** * 強(qiáng)制子類實(shí)現(xiàn)該抽象方法,初始化各自的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); } /** * 強(qiáng)制子類實(shí)現(xiàn)該抽象方法,初始化各自數(shù)據(jù) */ @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); } } /** * 強(qiáng)制子類實(shí)現(xiàn)該抽象方法,設(shè)置自己的監(jiān)聽器 */ @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}; } /** * 子類在這個(gè)方法里面實(shí)現(xiàn)自己View的點(diǎn)擊監(jiān)聽事件的相應(yīng) * * @param v 父類傳遞到子類的點(diǎn)擊的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: //停止發(fā)送消息 handler.removeMessages(WHAT_INCREACE); //暫停下載 downUtil.clickPauseBtn(); //重置狀態(tài) downUtil.downloadState = DownUtil.STATE_READY; //統(tǒng)計(jì)下載的大小歸零 lastFinishedSize = 0; //重置文本信息 tv_download_speed.setText(""); tv_download_file_info.setText(""); tv_download_speed_info.setText(""); //進(jìn)度條歸零 np_download.setProgress(0); //清空內(nèi)存數(shù)據(jù) MapUtil.map.clear(); //sp清理 SPUtil.getInstance(this).removeAll(); //清空數(shù)據(jù)庫 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, "請(qǐng)輸入地址"); } else { url = editTextUrl; } break; } } @Override protected void onDestroy() { handler.removeCallbacksAndMessages(null); downUtil.clickPauseBtn(); super.onDestroy(); } /** * 格式化數(shù)據(jù)大小 */ private String formateSize(long size) { return Formatter.formatFileSize(this, size); } }
布局
<?xml version="1.0" encoding="utf-8"?>
下載Activity的父類
public abstract class BaseActivity extends Activity { /** * 點(diǎn)擊監(jiān)聽器,子類也可以使用 */ protected BaseOnClickListener onClickListener = new BaseOnClickListener(); /** * 在onCreate方法里面,找到視圖,初始化數(shù)據(jù),設(shè)置點(diǎn)擊監(jiān)聽器 */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(childView()); findView(); initData(); setListener(); } /** * 返回子類要顯示的布局 R.layout...... */ protected abstract int childView(); /** * 找到需要的視圖---網(wǎng)址:https://www.buzzingandroid.com/tools/android-layout-finder/ */ private void findView() { findChildView(); } /** * 初始化數(shù)據(jù) */ private void initData() { // 初始化子類的數(shù)據(jù) initChildData(); } /** * 對(duì)需要設(shè)置監(jiān)聽 的視圖設(shè)置監(jiān)聽 */ private void setListener() { // 子類實(shí)現(xiàn)setChildListene()方法,返回一個(gè)View數(shù)組,里面包含所有需要設(shè)置點(diǎn)擊監(jiān)聽的View,然后進(jìn)行For循環(huán),對(duì)里面所有View設(shè)置監(jiān)聽器 if (setChildListener() == null || setChildListener().length == 0) { return; } View[] viewArray = setChildListener(); for (View view : viewArray) { view.setOnClickListener(onClickListener); } } /** * 自定義的點(diǎn)擊監(jiān)聽類 */ protected class BaseOnClickListener implements View.OnClickListener { @Override public void onClick(View v) { clickChildView(v); } } /** * 強(qiáng)制子類實(shí)現(xiàn)該抽象方法,初始化各自的View */ protected abstract void findChildView(); /** * 強(qiáng)制子類實(shí)現(xiàn)該抽象方法,初始化各自數(shù)據(jù) */ protected abstract void initChildData(); /** * 強(qiáng)制子類實(shí)現(xiàn)該抽象方法,設(shè)置自己的監(jiān)聽器 */ protected abstract View[] setChildListener(); /** * 子類在這個(gè)方法里面實(shí)現(xiàn)自己View的點(diǎn)擊監(jiān)聽事件的相應(yīng) * * @param v 父類傳遞到子類的點(diǎn)擊的View */ protected abstract void clickChildView(View v); }
得到數(shù)據(jù)庫的操作類
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; } }
內(nèi)存中存儲(chǔ)下載信息的類
public class MapUtil { public static HashMapmap = new HashMap<>(); }
SP存儲(chǔ)的工具類
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(); } }
進(jìn)度條:這個(gè)可以忽略,用安卓原生的,代碼看我的另一篇博客:Android自定義View實(shí)現(xiàn)水平帶數(shù)字百分比進(jìn)度條
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。