編輯:關於Android編程
文件下載在App應用中也用到很多,一般版本更新時多要用的文件下載來進行處理,以前也有看過很多大神有過該方面的博客,今天我也自己來實踐一下,寫的一般,還請大家多提意見,共同進步。主要思路:
1.多線程下載:
首先通過下載總線程數來劃分文件的下載區域:利用int range = fileSize / threadCount;得到每一段下載量;每一段的位置是i * range到(i + 1) * rang - 1,注意最後一段的位置是到filesize - 1;
通過Http協議的Range字段實現下載文件的分段;
通過Java類RandomAccessFile可以實現文件的隨機訪問,利用seek方法定位的文件的指定位置;
由HttpUrlConnection獲取流來進行流的讀寫,實現文件的存儲;
在下載過程中利用Handler來向外傳遞下載的信息。
2.斷點續傳:
對於每一個線程利用一個DownloadInfo類來保存下載的信息,每次在下載過程中向數據庫更新信息(我也有想過只在下載暫停時進行更新,但那樣的話我們的進程被殺掉時信息就無法保存下來)。在進行下載之前去訪問數據庫是否有記錄存在,如果沒有執行第一次下載的初始化,如果存在記錄但下載文件不存在時,刪掉數據庫中的記錄之後進行第一次下載的初始化,如果有記錄且文件存在,則從數據庫中取出信息。
實現的效果如圖,自己封裝的類提供了開始,暫停,刪除,以及重新下載的方法。還沒來得及將工程穿上CSDN,給大家一個百度雲盤的下載地址:http://pan.baidu.com/s/1dD1Xo8T

主要類的結構有:
1)DownloadHttpTool:利用Http協議進行網絡下載類<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+ICAgICAgICAgICAgICAgICAgICAgMqOpRG93bmxhb2RTcWxUb29so6wgRG93bkxvYWRIZWxwZXKjusr9vt2/4s/gudiy2df3wOA8L3A+CjxwPiAgICAgICAgICAgICAgICAgICAgIDOjqURvd25sb2FkSW5mb6O6z8LU2NDFz6Kxo7TmwOA8L3A+CjxwPiAgICAgICAgICAgICAgICAgICAgIDSjqURvd25sb2FkVXRpbKO6t+LXsM/C1Ni3vbeosqLM4bmpttTN4r3Tv9q1xMDgPGJyPgo8L3A+CjxwPiDM+dK7z8K0+sLro7pEb3dubG9hZEluZm/A4DxwcmUgY2xhc3M9"brush:java;">package com.example.test; public class DownloadInfo { /** * 保存每個下載線程下載信息類 * */ private int threadId;// 下載器id private int startPos;// 開始點 private int endPos;// 結束點 private int compeleteSize;// 完成度 private String url;// 下載文件的URL地址 public DownloadInfo(int threadId, int startPos, int endPos, int compeleteSize, String url) { this.threadId = threadId; this.startPos = startPos; this.endPos = endPos; this.compeleteSize = compeleteSize; this.url = url; } public DownloadInfo() { } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public int getThreadId() { return threadId; } public void setThreadId(int threadId) { this.threadId = threadId; } public int getStartPos() { return startPos; } public void setStartPos(int startPos) { this.startPos = startPos; } public int getEndPos() { return endPos; } public void setEndPos(int endPos) { this.endPos = endPos; } public int getCompeleteSize() { return compeleteSize; } public void setCompeleteSize(int compeleteSize) { this.compeleteSize = compeleteSize; } @Override public String toString() { return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos + ", endPos=" + endPos + ", compeleteSize=" + compeleteSize + "]"; } } DownLoadHelper類,創建我們的數據庫文件
package com.example.test;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* 利用數據庫來記錄下載信息
* @author acer
*/
public class DownLoadHelper extends SQLiteOpenHelper{
private static final String SQL_NAME = "download.db";
private static final int DOWNLOAD_VERSION=1;
public DownLoadHelper(Context context) {
super(context, SQL_NAME, null, DOWNLOAD_VERSION);
// TODO Auto-generated constructor stub
}
/**
* 在download.db數據庫下創建一個download_info表存儲下載信息
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "
+ "start_pos integer, end_pos integer, compelete_size integer,url char)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
DownlaodSqlTool進行數據的插入更新刪除等操作package com.example.test;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
/**
*
* 數據庫操作工具類
*/
public class DownlaodSqlTool {
private DownLoadHelper dbHelper;
public DownlaodSqlTool(Context context) {
dbHelper = new DownLoadHelper(context);
}
/**
* 創建下載的具體信息
*/
public void insertInfos(List infos) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
for (DownloadInfo info : infos) {
String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";
Object[] bindArgs = { info.getThreadId(), info.getStartPos(),
info.getEndPos(), info.getCompeleteSize(), info.getUrl() };
database.execSQL(sql, bindArgs);
}
}
/**
* 得到下載具體信息
*/
public List getInfos(String urlstr) {
List list = new ArrayList();
SQLiteDatabase database = dbHelper.getWritableDatabase();
String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
Cursor cursor = database.rawQuery(sql, new String[] { urlstr });
while (cursor.moveToNext()) {
DownloadInfo info = new DownloadInfo(cursor.getInt(0),
cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),
cursor.getString(4));
list.add(info);
}
return list;
}
/**
* 更新數據庫中的下載信息
*/
public void updataInfos(int threadId, int compeleteSize, String urlstr) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
String sql = "update download_info set compelete_size=? where thread_id=? and url=?";
Object[] bindArgs = { compeleteSize, threadId, urlstr };
database.execSQL(sql, bindArgs);
}
/**
* 關閉數據庫
*/
public void closeDb() {
dbHelper.close();
}
/**
* 下載完成後刪除數據庫中的數據
*/
public void delete(String url) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.delete("download_info", "url=?", new String[] { url });
}
} DownloadHttpTool進行網絡下載的類package com.example.test;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
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 android.util.Log;
public class DownloadHttpTool {
/**
*
* 利用Http協議進行多線程下載具體實踐類
*/
private static final String TAG = DownloadHttpTool.class.getSimpleName();
private int threadCount;//線程數量
private String urlstr;//URL地址
private Context mContext;
private Handler mHandler;
private List downloadInfos;//保存下載信息的類
private String localPath;//目錄
private String fileName;//文件名
private int fileSize;
private DownlaodSqlTool sqlTool;//文件信息保存的數據庫操作類
private enum Download_State {
Downloading, Pause, Ready;//利用枚舉表示下載的三種狀態
}
private Download_State state = Download_State.Ready;//當前下載狀態
private int globalCompelete = 0;//所有線程下載的總數
public DownloadHttpTool(int threadCount, String urlString,
String localPath, String fileName, Context context, Handler handler) {
super();
this.threadCount = threadCount;
this.urlstr = urlString;
this.localPath = localPath;
this.mContext = context;
this.mHandler = handler;
this.fileName = fileName;
sqlTool = new DownlaodSqlTool(mContext);
}
//在開始下載之前需要調用ready方法進行配置
public void ready() {
Log.w(TAG, "ready");
globalCompelete = 0;
downloadInfos = sqlTool.getInfos(urlstr);
if (downloadInfos.size() == 0) {
initFirst();
} else {
File file = new File(localPath + "/" + fileName);
if (!file.exists()) {
sqlTool.delete(urlstr);
initFirst();
} else {
fileSize = downloadInfos.get(downloadInfos.size() - 1)
.getEndPos();
for (DownloadInfo info : downloadInfos) {
globalCompelete += info.getCompeleteSize();
}
Log.w(TAG, "globalCompelete:::" + globalCompelete);
}
}
}
public void start() {
Log.w(TAG, "start");
if (downloadInfos != null) {
if (state == Download_State.Downloading) {
return;
}
state = Download_State.Downloading;
for (DownloadInfo info : downloadInfos) {
Log.v(TAG, "startThread");
new DownloadThread(info.getThreadId(), info.getStartPos(),
info.getEndPos(), info.getCompeleteSize(),
info.getUrl()).start();
}
}
}
public void pause() {
state = Download_State.Pause;
sqlTool.closeDb();
}
public void delete(){
compelete();
File file = new File(localPath + "/" + fileName);
file.delete();
}
public void compelete() {
sqlTool.delete(urlstr);
sqlTool.closeDb();
}
public int getFileSize() {
return fileSize;
}
public int getCompeleteSize() {
return globalCompelete;
}
//第一次下載初始化
private void initFirst() {
Log.w(TAG, "initFirst");
try {
URL url = new URL(urlstr);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
fileSize = connection.getContentLength();
Log.w(TAG, "fileSize::" + fileSize);
File fileParent = new File(localPath);
if (!fileParent.exists()) {
fileParent.mkdir();
}
File file = new File(fileParent, fileName);
if (!file.exists()) {
file.createNewFile();
}
// 本地訪問文件
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.setLength(fileSize);
accessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
int range = fileSize / threadCount;
downloadInfos = new ArrayList();
for (int i = 0; i < threadCount - 1; i++) {
DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range
- 1, 0, urlstr);
downloadInfos.add(info);
}
DownloadInfo info = new DownloadInfo(threadCount - 1, (threadCount - 1)
* range, fileSize - 1, 0, urlstr);
downloadInfos.add(info);
sqlTool.insertInfos(downloadInfos);
}
//自定義下載線程
private class DownloadThread extends Thread {
private int threadId;
private int startPos;
private int endPos;
private int compeleteSize;
private String urlstr;
private int totalThreadSize;
public DownloadThread(int threadId, int startPos, int endPos,
int compeleteSize, String urlstr) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
totalThreadSize = endPos - startPos + 1;
this.urlstr = urlstr;
this.compeleteSize = compeleteSize;
}
@Override
public void run() {
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
try {
randomAccessFile = new RandomAccessFile(localPath + "/"
+ fileName, "rwd");
randomAccessFile.seek(startPos + compeleteSize);
URL url = new URL(urlstr);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Range", "bytes="
+ (startPos + compeleteSize) + "-" + endPos);
is = connection.getInputStream();
byte[] buffer = new byte[1024];
int length = -1;
while ((length = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, length);
compeleteSize += length;
Message message = Message.obtain();
message.what = threadId;
message.obj = urlstr;
message.arg1 = length;
mHandler.sendMessage(message);
sqlTool.updataInfos(threadId, compeleteSize, urlstr);
Log.w(TAG, "Threadid::" + threadId + " compelete::"
+ compeleteSize + " total::" + totalThreadSize);
if (compeleteSize >= totalThreadSize) {
break;
}
if (state != Download_State.Downloading) {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
randomAccessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} DownloadUtils提供下載向外接口方法類:package com.example.test;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* 將下載方法封裝在此類
* 提供下載,暫停,刪除,以及重置的方法
*/
public class DownloadUtil {
private DownloadHttpTool mDownloadHttpTool;
private OnDownloadListener onDownloadListener;
private int fileSize;
private int downloadedSize = 0;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
int length = msg.arg1;
synchronized (this) {//加鎖保證已下載的正確性
downloadedSize += length;
}
if (onDownloadListener != null) {
onDownloadListener.downloadProgress(downloadedSize);
}
if (downloadedSize >= fileSize) {
mDownloadHttpTool.compelete();
if (onDownloadListener != null) {
onDownloadListener.downloadEnd();
}
}
}
};
public DownloadUtil(int threadCount, String filePath, String filename,
String urlString, Context context) {
mDownloadHttpTool = new DownloadHttpTool(threadCount, urlString,
filePath, filename, context, mHandler);
}
//下載之前首先異步線程調用ready方法獲得文件大小信息,之後調用開始方法
public void start() {
new AsyncTask() {
@Override
protected Void doInBackground(Void... arg0) {
// TODO Auto-generated method stub
mDownloadHttpTool.ready();
return null;
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
fileSize = mDownloadHttpTool.getFileSize();
downloadedSize = mDownloadHttpTool.getCompeleteSize();
Log.w("Tag", "downloadedSize::" + downloadedSize);
if (onDownloadListener != null) {
onDownloadListener.downloadStart(fileSize);
}
mDownloadHttpTool.start();
}
}.execute();
}
public void pause() {
mDownloadHttpTool.pause();
}
public void delete(){
mDownloadHttpTool.delete();
}
public void reset(){
mDownloadHttpTool.delete();
start();
}
public void setOnDownloadListener(OnDownloadListener onDownloadListener) {
this.onDownloadListener = onDownloadListener;
}
//下載回調接口
public interface OnDownloadListener {
public void downloadStart(int fileSize);
public void downloadProgress(int downloadedSize);//記錄當前所有線程下總和
public void downloadEnd();
}
} 在MainActivitypackage com.example.test;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.example.test.DownloadUtil.OnDownloadListener;
public class MainActivity extends FragmentActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private ProgressBar mProgressBar;
private Button start;
private Button pause;
private Button delete;
private Button reset;
private TextView total;
private int max;
private DownloadUtil mDownloadUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
start = (Button) findViewById(R.id.button_start);
pause = (Button) findViewById(R.id.button_pause);
delete = (Button) findViewById(R.id.button_delete);
reset = (Button) findViewById(R.id.button_reset);
total = (TextView) findViewById(R.id.textView_total);
String urlString = "http://bbra.cn/Uploadfiles/imgs/20110303/fengjin/013.jpg";
String localPath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/local";
mDownloadUtil = new DownloadUtil(2, localPath, "abc.jpg", urlString,
this);
mDownloadUtil.setOnDownloadListener(new OnDownloadListener() {
@Override
public void downloadStart(int fileSize) {
// TODO Auto-generated method stub
Log.w(TAG, "fileSize::" + fileSize);
max = fileSize;
mProgressBar.setMax(fileSize);
}
@Override
public void downloadProgress(int downloadedSize) {
// TODO Auto-generated method stub
Log.w(TAG, "Compelete::" + downloadedSize);
mProgressBar.setProgress(downloadedSize);
total.setText((int) downloadedSize * 100 / max + "%");
}
@Override
public void downloadEnd() {
// TODO Auto-generated method stub
Log.w(TAG, "ENd");
}
});
start.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.start();
}
});
pause.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.pause();
}
});
delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.delete();
}
});
reset.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.reset();
}
});
}
}
Android白話啟動篇(Android booting process)
今天我們將深入Android的啟動流程,從用戶按下開機鍵到Android界面的過程。在谷歌上搜索“Android boot process”會得到
Android實現滑動到頂部懸停的效果
先來看下要實現效果圖:查閱資料後,發現網上大部分都是用這種方法實現的:多寫一個和需要懸浮的部分一模一樣的layout,先把浮動區域的可見性設置為gone。當浮動區域滑動到
Android中級第九講--相機調焦
相機調焦:原理,使用豎直seekbar,根據用戶拖拉來獲得距離,然後以Parameter的形式設置到Camera。 實現O
Android實習札記(10)---ImageView的src屬性 VS blackground屬性
問題分析 相信大家對於ImageView圖片組件並不陌生吧,見名知意,就是用來顯示圖片的咯! 而顯示圖片的話可以通過src屬性,又或者blac