admin管理员组文章数量:1530518
2023年12月13日发(作者:)
Android 多线程、断点续传下载技术
1. 为什么使用该技术?
答:(1)之所以采用多线程下载是因为考虑到手机,及移动设备的cup处理能力,让下载任务多抢占cup资源,从而加快了下载的速度,提高了用户体验
(2)断点续传技术,就是在下载过程中如果网络出现问题,导致文件没有下载完,那么下次下载时,接着上次终端位置继续下载,从而减轻了服务器的负担。
2.下面我们就开始建一个多线程下载的项目,来体验多线程下载的优势,项目的结构如下
2.1设计UI
代码如下:
xmlns:android="/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/path" /> android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="/kcn/pc/Kanbox_" android:id="@+id/path" />
其中引用的如下:
SmartDownload!
3.数据库阶段:
3.1编写数据库工具类DBOpenHeler
package ;
import t;
import Database;
import OpenHelper;
public class DBOpenHelper extends SQLiteOpenHelper
{
private static final String DBNAME = "";
private static final int VERSION = 1;
public DBOpenHelper(Context context) {
super(context, DBNAME, null, VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) { L("CREATE TABLE IF NOT EXISTS
SmartFileDownlog (id integer primary key
autoincrement, downpath varchar(100), threadid
INTEGER, downlength INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int
oldVersion, int newVersion) {
L("DROP TABLE IF EXISTS
SmartFileDownlog");
onCreate(db);
}
}
3.2对各个线程的下载记录进行数据库的操作,编写Fileservice类
代码如下
package ;
import p; import ;
import t;
import ;
import Database;
/**
* 业务bean
*
*/
public class FileService {
private DBOpenHelper openHelper;
public FileService(Context context) {
openHelper = new DBOpenHelper(context);
}
/**
* 获取每条线程已经下载的文件长度
* @param path
* @return
*/
public Map
path){ SQLiteDatabase db =
dableDatabase();
Cursor cursor = ry("select threadid,
downlength from SmartFileDownlog where downpath=?",
new String[]{path});
Map
HashMap
while(Next()){
((0),
(1));
}
();
();
return data;
}
/**
* 保存每条线程已经下载的文件长度
* @param path
* @param map
*/
public void save(String path, Map Integer> map){//int threadid, int position SQLiteDatabase db = tableDatabase(); ransaction(); try{ for( et()){ L("insert into SmartFileDownlog(downpath, threadid, downlength) values(?,?,?)", new Object[]{path, (), ue()}); } nsactionSuccessful(); }finally{ nsaction(); } (); } /** * 实时更新每条线程已经下载的文件长度 * @param path * @param map */ public void update(String path, Map Integer> map){ SQLiteDatabase db = tableDatabase(); ransaction(); try{ for( et()){ L("update SmartFileDownlog set downlength=? where downpath=? and threadid=?", new Object[]{ue(), path, ()}); } nsactionSuccessful(); }finally{ nsaction(); } (); } /** * 当文件下载完成后,删除对应的下载记录 * @param path */ public void delete(String path){ SQLiteDatabase db = tableDatabase(); L("delete from SmartFileDownlog where downpath=?", new Object[]{path}); (); } } 4.实现文件下载阶段 4.1建立SmartFileDownloader类 用来实现文件的下载功能 代码如下 package ; import ; import AccessFile; import LConnection; import ; import HashMap; import ; import ; import rentHashMap; import r; import n; import t; import ; import rvice; public class SmartFileDownloader { private static final String TAG = "SmartFileDownloader"; private Context context; private FileService fileService; /* 已下载文件长度 */ private int downloadSize = 0; /* 原始文件长度 */ private int fileSize = 0; /* 线程数 */ private SmartDownloadThread[] threads; /* 本地保存文件 */ private File saveFile; /* 缓存各线程下载的长度*/ private Map ConcurrentHashMap /* 每条线程下载的长度 */ private int block; /* 下载路径 */ private String downloadUrl; /** * 获取线程数 */ public int getThreadSize() { return ; } /** * 获取文件大小 * @return */ public int getFileSize() { return fileSize; } /** * 累计已下载大小 * @param size */ protected synchronized void append(int size) { downloadSize += size; } /** * 更新指定线程最后下载的位置 * @param threadId 线程id * @param pos 最后下载的位置 */ protected void update(int threadId, int pos) { (threadId, pos); } /** * 保存记录文件 */ protected synchronized void saveLogFile() { (adUrl, ); } /** * 构建文件下载器 * @param downloadUrl 下载路径 * @param fileSaveDir 文件保存目录 * @param threadNum 下载线程数 */ public SmartFileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) { try { t = context; adUrl = downloadUrl; fileService = new FileService(t); URL url = new URL(adUrl); if(!()) (); s = new SmartDownloadThread[threadNum]; HttpURLConnection conn = (HttpURLConnection) nnection(); nectTimeout(5*1000); uestMethod("GET"); uestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/-excel, application/-powerpoint, application/msword, */*"); uestProperty("Accept-Language", "zh-CN"); uestProperty("Referer", downloadUrl); uestProperty("Charset", "UTF-8"); uestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); uestProperty("Connection", "Keep-Alive"); t(); printResponseHeader(conn); if (ponseCode()==200) { ze = tentLength();//根据响应获取文件大小 if (ze <= 0) throw new RuntimeException("Unkown file size "); String filename = getFileName(conn); le = new File(fileSaveDir, filename);/* 保存文件 */ Map a(downloadUrl); if(()>0){ for( et()) ((), ue()); } = (ze % )==0? ze / : ze / + 1; if(()==){ for (int i = 0; i < ; i++) { adSize += (i+1); } print("已经下载的长度"+ adSize); } }else{ throw new RuntimeException("server no response "); } } catch (Exception e) { print(ng()); throw new RuntimeException("don't connection this url"); } } /** * 获取文件名 */ private String getFileName(HttpURLConnection conn) { String filename = ing(dexOf('/') + 1); if(filename==null || "".equals(())){//如果获取不到文件名称 for (int i = 0;; i++) { String mine = derField(i); if (mine == null) break; if("content-disposition".equals(derFieldKey(i).toLowerCase())){ Matcher m = e(".*filename=(.*)").matcher(rCase()); if(()) return (1); } } filename = UUID()+ ".tmp";//默认取一个文件名 } return filename; } /** * 开始下载文件 * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null * @return 已下载文件大小 * @throws Exception */ public int download(SmartDownloadProgressListener listener) throws Exception{ try { RandomAccessFile randOut = new RandomAccessFile(le, "rw"); if(ze>0) gth(ze); (); URL url = new URL(adUrl); if(() != ){ ();//清除数据 for (int i = 0; i < ; i++) { (i+1, 0); } } for (int i = 0; i < ; i++) { int downLength = (i+1); if(downLength < && adSize s[i] = new SmartDownloadThread(this, url, le, , (i+1), i+1); s[i].setPriority(7); s[i].start(); }else{ s[i] = null; } } (adUrl, ); boolean notFinish = true;//下载未完成 while (notFinish) {// 循环判断是否下载完毕 (900); notFinish = false;//假定下载完成 for (int i = 0; i < ; i++){ if (s[i] != null && !s[i].isFinish()) { notFinish = true;//下载没有完成 if(s[i].getDownLength() == -1){//如果下载失败,再重新下载 s[i] = new SmartDownloadThread(this, url, le, , (i+1), i+1); s[i].setPriority(7); s[i].start(); } } } if(listener!=null) loadSize(adSize); } (adUrl); } catch (Exception e) { print(ng()); throw new Exception("file download fail"); } return adSize; } /** * 获取Http响应头字段 * @param http * @return */ public static Map getHttpResponseHeader(HttpURLConnection http) { Map LinkedHashMap for (int i = 0;; i++) { String mine = derField(i); if (mine == null) break; (derFieldKey(i), mine); } return header; } /** * 打印Http头字段 * @param http */ public static void printResponseHeader(HttpURLConnection http){ Map getHttpResponseHeader(http); for( et()){ String key = ()!=null ? ()+ ":" : ""; print(key+ ue()); } } //打印日志 private static void print(String msg){ Log.i(TAG, msg); } } 4.2下载过程中的线程实现 建立SmartDownloadThread类 具体代码如下: package ; import ; import tream; import AccessFile; import LConnection; import ; import ; public class SmartDownloadThread extends Thread { private static final String TAG = "SmartDownloadThread"; private File saveFile; private URL downUrl; private int block; /* *下载开始位置 */ private int threadId = -1; private int downLength; private boolean finish = false; private SmartFileDownloader downloader; public SmartDownloadThread(SmartFileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) { l = downUrl; le = saveFile; = block; ader = downloader; Id = threadId; ngth = downLength; } @Override public void run() { if(downLength < block){//未下载完成 try { HttpURLConnection http = (HttpURLConnection) nnection(); nectTimeout(5 * 1000); uestMethod("GET"); uestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/-excel, application/-powerpoint, application/msword, */*"); uestProperty("Accept-Language", "zh-CN"); uestProperty("Referer", ng()); uestProperty("Charset", "UTF-8"); int startPos = block * (threadId - 1) + downLength;//开始位置 int endPos = block * threadId -1;//结束位置 uestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围 uestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); uestProperty("Connection", "Keep-Alive"); InputStream inStream = utStream(); byte[] buffer = new byte[1024]; int offset = 0; print("Thread " + Id + " start download from position "+ startPos); RandomAccessFile threadfile = new RandomAccessFile(le, "rwd"); (startPos); while ((offset = (buffer, 0, 1024)) != -1) { (buffer, 0, offset); downLength += offset; (Id, downLength); gFile(); (offset); } (); (); print("Thread " + Id + " download finish"); = true; } catch (Exception e) { ngth = -1; print("Thread "+ Id+ ":"+ e); } } } private static void print(String msg){ Log.i(TAG, msg); } /** * 下载是否完成 * @return */ public boolean isFinish() { return finish; } /** * 已经下载的内容大小 * @return 如果返回值为-1,代表下载失败 */ public long getDownLength() { return downLength; } } 4.3 建立interface SmartDownloadProgressListener 侦听线程的下载进度 代码如下: package ; public interface SmartDownloadProgressListener { public void onDownloadSize(int size); } 5.建立activity 实现下载的触发,和界面的实时更新 具体代码如下: package ad; import ; import ty; import ; import nment; import r; import e; import ; import ; import xt; import ssBar; import ew; import ; import ownloadProgressListener; import ileDownloader; public class SmartDownloadActivity extends Activity { private ProgressBar downloadbar; private EditText pathText; private TextView resultView; private Handler handler = new Handler(){ @Override//信息 public void handleMessage(Message msg) { switch () { case 1: int size = a().getInt("size"); gress(size); float result = (float)gress()/ (float)(); int p = (int)(result*100); t(p+"%"); if(gress()==()) xt(, s, 1).show(); break; case -1: xt(, , 1).show(); break; } } }; @Override public void onCreate(Bundle savedInstanceState) { te(savedInstanceState); setContentView(); Button button = (Button)ewById(); downloadbar = (ProgressBar)ewById(adbar); pathText = (EditText)ewById(); resultView = (TextView)ewById(); lickListener(new kListener() { @Override public void onClick(View v) { String path = t().toString(); if(ernalStorageState().equals(_MOUNTED)){ File dir = ernalStorageDirectory();//文件保存目录 download(path, dir); }else{ xt(, error, 1).show(); } } }); } //对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错 private void download(final String path, final File dir){ new Thread(new Runnable() { @Override public void run() { try { SmartFileDownloader loader = new SmartFileDownloader(, path, dir, 3); int length = eSize();//获取文件的长度 (length); ad(new SmartDownloadProgressListener(){ @Override public void onDownloadSize(int size) {//可以实时得到文件下载的长度 Message msg = new Message(); = 1; a().putInt("size", size); ssage(msg); }}); } catch (Exception e) { Message msg = new Message();//信息提示 = -1; a().putString("error", "下载失败");//如果下载错误,显示提示失败! ssage(msg); } } }).start();//开始 } } 6.到此为止 运行会报错,因为没有向sdcard的协数据的权限,访问Internet的权限 这需要在配置 具体代码如下: xmlns:android="/apk/res/android" package="ad" android:versionCode="1" android:versionName="1.0"> android:label="@string/app_name"> android:label="@string/app_name"> android:name="" /> android:name="ER" /> android:name="ET"/> android:name="_UNMOUNT_FILESYSTEMS"/>
版权声明:本文标题:android多线程下载技术详解 内容由热心网友自发贡献,该文观点仅代表作者本人,
转载请联系作者并注明出处:https://m.elefans.com/xitong/1702457680a7938.html,
本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论