編輯:關於Android編程
最近做的項目中需要實現斷點下載,即用戶一次下載可以分多次進行,下載過程可以中斷,在目前大多數的帶離線緩存的軟件都是需要實現這一功能。本文闡述了通過sqlite3簡單實現了一個具有斷點下載功能的demo。言歸正傳,開始正文。
設計
數據庫表存儲元數據
DBHelper.java
用於業務存儲的Dao
Dao.java
抽象下載信息的Bean
LoadInfo.java
呈現下載信息View
MainActivity.java
存儲下載信息Bean
DownloadInfo.java
封裝好的下載類
Downloader.java
代碼結構
具體實現
下載信息類:DownloadInfo.java
這裡的代碼很簡單,就是一個用來保存下載信息的類,很簡單,沒啥好說的,具體看注釋
package entity; public class DownloadInfo { private int threadId;//線程ID private int startPos;//下載起始位置 private int endPos;//下載結束位置 private int completeSize;//下載完成量 private String url;//資源URL public DownloadInfo(int tId,int sp, int ep,int cSize,String address){ threadId=tId; startPos=sp; endPos=ep; completeSize = cSize; url=address; } /** * @return the threadId */ public int getThreadId() { return threadId; } /** * @param threadId the threadId to set */ public void setThreadId(int threadId) { this.threadId = threadId; } /** * @return the startPos */ public int getStartPos() { return startPos; } /** * @param startPos the startPos to set */ public void setStartPos(int startPos) { this.startPos = startPos; } /** * @return the endPos */ public int getEndPos() { return endPos; } /** * @param endPos the endPos to set */ public void setEndPos(int endPos) { this.endPos = endPos; } /** * @return the completeSize */ public int getCompleteSize() { return completeSize; } /** * @param completeSize the completeSize to set */ public void setCompleteSize(int completeSize) { this.completeSize = completeSize; } /** * @return the url */ public String getUrl() { return url; } /** * @param url the url to set */ public void setUrl(String url) { this.url = url; } @Override public String toString() { // TODO Auto-generated method stub return "threadId:"+threadId+",startPos:"+startPos+",endPos:"+endPos+",completeSize:"+completeSize+",url:"+url; } }
數據庫 DBHelper.java
package db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DBHelper extends SQLiteOpenHelper { String sql ="create table download_info (id integer PRIMARY KEY AUTOINCREMENT," + "thread_id integer," + "start_pos integer," + "end_pos integer," + "complete_size integer," + "url char)"; public DBHelper(Context context) { // TODO Auto-generated constructor stub super(context, "download.db", null, 1); } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub } }
數據庫業務管理類 Dao.java
package db; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import entity.DownloadInfo; public class Dao { private DBHelper dbHelper; public Dao(Context context){ dbHelper = new DBHelper(context); } public boolean isNewTask(String url){ SQLiteDatabase db = dbHelper.getReadableDatabase(); String sql = "select count(*) from download_info where url=?"; Cursor cursor = db.rawQuery(sql, new String[]{url}); cursor.moveToFirst(); int count = cursor.getInt(0); cursor.close(); return count == 0; } public void saveInfo(List<DownloadInfo> infos){ SQLiteDatabase db = dbHelper.getWritableDatabase(); String sql = "insert into download_info(thread_id,start_pos,end_pos,complete_size,url) values(?,?,?,?,?)"; Object[] bindArgs= null; for(DownloadInfo info:infos){ bindArgs=new Object[]{info.getThreadId(),info.getStartPos(),info.getEndPos(),info.getCompleteSize(),info.getUrl()}; db.execSQL(sql, bindArgs); } } public List<DownloadInfo> getInfo(String url){ SQLiteDatabase db = dbHelper.getReadableDatabase(); List<DownloadInfo> infos = new ArrayList<DownloadInfo>(); String sql ="select thread_id,start_pos,end_pos,complete_size,url from download_info where url=?"; Cursor cursor=db.rawQuery(sql, new String[]{url}); while(cursor.moveToNext()){ DownloadInfo info = new DownloadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),cursor.getString(4)); infos.add(info); } cursor.close(); return infos; } public void deleteInfo(String url){ SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("download_info", "url=?", new String[]{url}); db.close(); } public void updateInfo(int completeSize,int threadId,String url){ SQLiteDatabase db = dbHelper.getWritableDatabase(); String sql ="update download_info set complete_size=? where thread_id=? and url=?"; db.execSQL(sql, new Object[]{completeSize,threadId,url}); } public void closeDB(){ dbHelper.close(); } }
當前狀態保存類 LoadInfo.java
package entity; public class LoadInfo { private int fileSize; private int completeSize; private String url; public LoadInfo(int fs,int cSize,String address){ fileSize=fs; completeSize = cSize; url=address; } /** * @return the fileSize */ public int getFileSize() { return fileSize; } /** * @param fileSize the fileSize to set */ public void setFileSize(int fileSize) { this.fileSize = fileSize; } /** * @return the completeSize */ public int getCompleteSize() { return completeSize; } /** * @param completeSize the completeSize to set */ public void setCompleteSize(int completeSize) { this.completeSize = completeSize; } /** * @return the url */ public String getUrl() { return url; } /** * @param url the url to set */ public void setUrl(String url) { this.url = url; } }
下載助手類:Downloader.java
package com.winton.downloadmanager; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.os.Handler; import android.os.Message; import db.Dao; import entity.DownloadInfo; import entity.LoadInfo; public class Downloader { private String url; private String localPath; private int threadCount; private Handler mHanler; private Dao dao; private int fileSize; private List<DownloadInfo> infos; private final static int INIT = 1; private final static int DOWNLOADING =2; private final static int PAUSE =3; private int state = INIT; public Downloader(String address,String lPath,int thCount,Context context,Handler handler){ url =address; localPath = lPath; threadCount = thCount; mHanler = handler; dao = new Dao(context); } public boolean isDownloading(){ return state == DOWNLOADING; } public LoadInfo getDownLoadInfos(){ if(isFirstDownload(url)){ init(); int range = fileSize/threadCount; infos = new ArrayList<DownloadInfo>(); for(int i=0;i<=threadCount-1;i++){ DownloadInfo info = new DownloadInfo(i, i*range, (i+1)*range-1, 0, url); infos.add(info); } dao.saveInfo(infos); return new LoadInfo(fileSize, 0, url); }else{ infos = dao.getInfo(url); int size = 0; int completeSize =0; for(DownloadInfo info:infos){ completeSize += info.getCompleteSize(); size += info.getEndPos()-info.getStartPos()+1; } return new LoadInfo(size, completeSize, url); } } public boolean isFirstDownload(String url){ return dao.isNewTask(url); } public void init(){ try { URL mUrl = new URL(this.url); HttpURLConnection connection = (HttpURLConnection)mUrl.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); fileSize = connection.getContentLength(); File file = new File(localPath); if(!file.exists()){ file.createNewFile(); } RandomAccessFile accessFile = new RandomAccessFile(file, "rwd"); accessFile.setLength(fileSize); accessFile.close(); connection.disconnect(); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void download(){ if(infos != null){ if(state ==DOWNLOADING){ return; } state = DOWNLOADING; for(DownloadInfo info:infos){ new DownloadThread(info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompleteSize(), info.getUrl()).start(); } } } class DownloadThread extends Thread{ private int threadId; private int startPos; private int endPos; private int completeSize; private String url; public DownloadThread(int tId,int sp,int ep,int cSize,String address) { // TODO Auto-generated constructor stub threadId=tId; startPos=sp; endPos = ep; completeSize = cSize; url = address; } @Override public void run() { // TODO Auto-generated method stub HttpURLConnection connection = null; RandomAccessFile randomAccessFile = null; InputStream is = null; try { URL mUrl = new URL(url); connection = (HttpURLConnection)mUrl.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); connection.setRequestProperty("Range", "bytes="+(startPos+completeSize)+"-"+endPos); randomAccessFile = new RandomAccessFile(localPath, "rwd"); randomAccessFile.seek(startPos+completeSize); is=connection.getInputStream(); byte[] buffer = new byte[4096]; int length =-1; while((length=is.read(buffer)) != -1){ randomAccessFile.write(buffer, 0, length); completeSize +=length; dao.updateInfo(threadId, completeSize, url); Message msg = Message.obtain(); msg.what=1; msg.obj=url; msg.arg1=length; mHanler.sendMessage(msg); if(state==PAUSE){ return; } } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { is.close(); randomAccessFile.close(); connection.disconnect(); dao.closeDB(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void delete(String url){ dao.deleteInfo(url); } public void reset(){ state=INIT; } public void pause(){ state=PAUSE; } }
View呈現類:MainActivity.java
package com.winton.downloadmanager; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import entity.LoadInfo; public class MainActivity extends Activity implements OnClickListener{ private TextView name; private ProgressBar process; private Button start,stop; private Downloader downloader; //處理下載進度UI的 Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { if(msg.what==1){ name.setText((String)msg.obj); int lenght = msg.arg1; process.incrementProgressBy(lenght); } if(msg.what==2){ int max =msg.arg1; process.setMax(max); Toast.makeText(getApplicationContext(), max+"", 1).show(); } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); name=(TextView)findViewById(R.id.tv_name); process=(ProgressBar)findViewById(R.id.pb_download); start = (Button)findViewById(R.id.bt_start); stop = (Button)findViewById(R.id.bt_stop); start.setOnClickListener(this); stop.setOnClickListener(this); downloader=new Downloader("http://img4.duitang.com/uploads/item/201206/11/20120611174542_5KRMj.jpeg", Environment.getExternalStorageDirectory().getPath()+"/test1.jpg", 1, getApplicationContext(), handler); } @Override public void onClick(View v) { // TODO Auto-generated method stub if(v==start){ new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub LoadInfo loadInfo = downloader.getDownLoadInfos(); Message msg =handler.obtainMessage(); msg.what=2; msg.arg1=loadInfo.getFileSize(); handler.sendMessage(msg); downloader.download(); } }).start(); return; } if(v==stop){ downloader.pause(); return; } } }
運行效果
總體比較簡單,基本實現了 斷點下載的功能,而且開啟了多個線程去實現分塊下載,對初學的同學有一定的幫助。
這些代碼我也是從網上各種搜集而來,自己親自動手敲了一遍,並做了一些小的改動,對整個斷點下載的過程有了一個深刻的認識。因此平時要多敲代碼,善於總結,必能有所收獲。
BaseActivity是項目中所有activity的基類,含有一些公共的屬性和方法,同時控制toolbar的顯示,以及其他一些功能。。。來看源碼:/** * BaseA
修改文件路徑 android4.4\packages\apps\Browser\src\com\android\browser\BrowserSettings.java
基本介紹現在的一些購物類App例如淘寶,京東等,在物品詳情頁,都采用了類似分層的模式,即上拉加載詳情的方式,節省了空間,使用戶的體驗更加的舒適。只要對於某個東西的介紹很多
相信大家都見到了微信圖標顏色漸變的過程,是不是感覺很牛逼?不得不說微信團隊確實是很厲害的團隊,不管是從設計還是開發人員。今天我帶大家來看看,微信 tab 欄圖標和字體顏色