android多线程下载技术详解
黑马程序员安卓教程:Android下的多线程下载器
Android下的多线程下载器在文档“javase下的多线程断点下载”中,我们已经了解了多线程断点下载的核心逻辑和代码,下面我们将在Android中实现多线程断点下载,其实质就是将javase多线程断点下载的核心代码移植到Android工程下。
具体步骤如下:1.新建工程【File】→【new】→【Android Application Project】命名为:06_android多线程下载器。
2.编写布局根据设计需求,编写布局文件,效果如图1-1所示:图1-1所示图1-1对应的xml布局文件代码如例1-1:例1-1例1-1在布局文件中定义了关键控件的id,已经点击按钮的响应事件。
当用户点击按钮时,则读取请求路径和请求线程数并向服务端发送请求,多线程下载指定路径下的资源到客户端。
3.查找控件在Activity中查找相关控件,代码如例1-2:4.编写点击事件编写按钮的点击事件download,从服务端请求下载数据。
代码如例1-3所示:例1-3是点击事件的响应事件,当点击按钮时先要读取请求路径输入框和请求线程输入框的值,并判断其值是否为空。
若请求路径为空则弹出toast提示;若请求线程为空则使用默认的线程数3,不为空则需要将该请求线程值转化为int类型的数值。
在判断之后便是请求网络资源的方法,即例1-3处省略部分。
由于请求网络资源是一个耗时操作,因此我们应该新建一个子线程,在该子线程的run方法中添加断点下载资源的方法。
如例1-4所示:例1-4开辟多个线程开始下载网络资源,每一个线程下载指定区间的一部分数据,关于下载方法DownloadThread()我们在javase多线程断点下载中已经详细的阐述过,在此不再讲述。
需要指出的是:在javase多线程断点下载中,我们是在工程目录下新建文件用来保存当前线程已经下载的位置,由于Android系统的文件目录是只读的,不可修改。
所以我们在保存记录当前线程的下载位置时需要指定到具体的目录下,如指定到SD卡中,代码如例1-5:例1-5保存当前线程的下载位置到SD卡中,那么在读该文件数据时也需要在该SD卡指定的路径下载读取,如例1-6:运行程序,logcat打印信息如图1-2所示:图1-2所示打开DDMS的文件浏览视图,观察下载的exe文件,如图1-3所示:图1-3所示。
Android下使用Http协议实现多线程断点续传下载
0.使用多线程下载会提升文件下载的速度,那么多线程下载文件的过程是:(1)首先获得下载文件的长度,然后设置本地文件的长度HttpURLConnection.getContentLength();RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd");file.setLength(filesize);//设置本地文件的长度(2)根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
如:文件的长度为6M,线程数为3,那么,每条线程下载的数据长度为2M,每条线程开始下载的位置如下图所示。
例如10M大小,使用3个线程来下载,线程下载的数据长度 (10%3 == 0 ? 10/3:10/3+1) ,第1,2个线程下载长度是4M,第三个线程下载长度为2M下载开始位置:线程id*每条线程下载的数据长度 = ?下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?(3)使用Http的Range头字段指定每条线程从文件的什么位置开始下载,下载到什么位置为止,如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止代码如下:HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");(4)保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
RandomAccessFile threadfile = new RandomAccessFile("QQWubiSetup.exe ","rwd");threadfile.seek(2097152);//从文件的什么位置开始写入数据1.多线程下载的核心代码示例Java代码1.public class MulThreadDownload2.{3. /**4. * 多线程下载5. * @param args6. */7. public static void main(String[] args)8. {9. String path = "/QQWubiSetup.exe";10. try11. {12. new MulThreadDownload().download(path, 3);13. }14. catch (Exception e)15. {16. e.printStackTrace();17. }18. }19. /**20. * 从路径中获取文件名称21. * @param path 下载路径22. * @return23. */24. public static String getFilename(String path)25. {26. return path.substring(stIndexOf('/')+1);27. }28. /**29. * 下载文件30. * @param path 下载路径31. * @param threadsize 线程数32. */33. public void download(String path, int threadsize) throwsException34. {35. URL url = new URL(path);36. HttpURLConnection conn = (HttpURLConnection)url.openConnection();37. conn.setRequestMethod("GET");38. conn.setConnectTimeout(5 * 1000);39. //获取要下载的文件的长度40. int filelength = conn.getContentLength();41. //从路径中获取文件名称42. String filename = getFilename(path);43. File saveFile = new File(filename);44. RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");45. //设置本地文件的长度和下载文件相同46. accessFile.setLength(filelength);47. accessFile.close();48. //计算每条线程下载的数据长度49. int block = filelength%threadsize==0? filelength/threadsize : filelength/threadsize+1;50. for(int threadid=0 ; threadid < threadsize ; threadid++){51. new DownloadThread(url, saveFile, block, threadid).start();52. }53. }54.55. private final class DownloadThread extends Thread56. {57. private URL url;58. private File saveFile;59. private int block;//每条线程下载的数据长度60. private int threadid;//线程id61. public DownloadThread(URL url, File saveFile, int block, int threadid)62. {63. this.url = url;64. this.saveFile = saveFile;65. this.block = block;66. this.threadid = threadid;67. }68.@Override69. public void run()70. {71. //计算开始位置公式:线程id*每条线程下载的数据长度= ?72. //计算结束位置公式:(线程id +1)*每条线程下载的数据长度-1 =?73. int startposition = threadid * block;74. int endposition = (threadid + 1 ) * block - 1;75. try76. {77. RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");78. //设置从什么位置开始写入数据79. accessFile.seek(startposition);80. HttpURLConnection conn = (HttpURLConnection)url.openConnection();81. conn.setRequestMethod("GET");82. conn.setConnectTimeout(5 * 1000);83. conn.setRequestProperty("Range", "bytes="+ startposition+ "-"+ endposition);84. InputStream inStream = conn.getInputStream();85. byte[] buffer = new byte[1024];86. int len = 0;87. while( (len=inStream.read(buffer)) != -1 )88. {89. accessFile.write(buffer, 0, len);90. }91. inStream.close();92. accessFile.close();93. System.out.println("线程id:"+ threadid+ "下载完成");94. }95. catch (Exception e)96. {97. e.printStackTrace();98. }99. }100. }101.}2.多线程断点下载功能,这里把断点数据保存到数据库中:注意代码注释的理解(0)主Activity,关键点使用Handler更新进度条与开启线程下载避免ANR若不使用Handler却要立即更新进度条数据,可使用://resultView.invalidate(); UI线程中立即更新进度条方法//resultView.postInvalidate(); 非UI线程中立即更新进度条方法Java代码1./**2. * 注意这里的设计思路,UI线程与参数保存问题,避免ANR问题,UI控件的显示3. * @author kay4. *5. */6.public class DownloadActivity extends Activity7.{8. private EditText downloadpathText;9. private TextView resultView;10. private ProgressBar progressBar;11.12.@Override13. public void onCreate(Bundle savedInstanceState)14. {15. super.onCreate(savedInstanceState);16. setContentView(yout.main);17.18. downloadpathText = (EditText) this.findViewById(R.id.downloadpath);19. progressBar = (ProgressBar) this.findViewById(R.id.downloadbar);20. resultView = (TextView) this.findViewById(R.id.result);21. Button button = (Button) this.findViewById(R.id.button);22. button.setOnClickListener(new View.OnClickListener()23. {24.@Override25. public void onClick(View v)26. {27. //取得下载路径28. String path = downloadpathText.getText().toString();29. //判断SDCard是否存在30. if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))31. {32. //下载操作33. download(path, Environment.getExternalStorageDirectory());34. }35. else36. {37. Toast.makeText(DownloadActivity.this, R.string.sdcarderror, 1).show();38. }39. }40. });41. }42. //主线程(UI线程)43. //业务逻辑正确,但是该程序运行的时候有问题(可能出现ANR问题)44. //对于显示控件的界面更新只是由UI线程负责,如果是在非UI线程更新控件的属性值,更新后的显示界面不会反映到屏幕上45. /**46. * 参数类型:因为启动一个线程还需要使用到上面方法的参数,而主方法启动后很快就会销毁,47. * 那么使用Final可以解决参数丢失的问题48. * path 注意是Final类型49. * savedir 注意是Final类型50. */51. private void download(final String path, final File savedir)52. {53. //这里开启一个线程避免ANR错误54. new Thread(new Runnable()55. {56.@Override57. public void run()58. {59. FileDownloader loader = new FileDownloader(DownloadActivity.this, path, savedir, 3);60. //设置进度条的最大刻度为文件的长度61. progressBar.setMax(loader.getFileSize());62. try63. {64. loader.download(new DownloadProgressListener()65. {66. /**67. * 注意这里的设计,显示进度条数据需要使用Handler来处理68. * 因为非UI线程更新后的数据不能被刷新69. */70.@Override71. public void onDownloadSize(int size)72. {73. //实时获知文件已经下载的数据长度74. Message msg = new Message();75. //设置消息标签76. msg.what = 1;77. msg.getData().putInt("size", size);78. //使用Handler对象发送消息79. handler.sendMessage(msg);80. }81. });82. }83. catch (Exception e)84. {85. //发送一个空消息到消息队列86. handler.obtainMessage(-1).sendToTarget();87. /**88. * 或者使用下面的方法发送一个空消息89. * Message msg = new Message();90. * msg.what = 1;91. * handler.sendMessage(msg);92. */93. }94. }95. }).start();96. }97.98.99. /**Handler原理:当Handler被创建时会关联到创建它的当前线程的消息队列,该类用于往消息队列发送消息100. *101. * 消息队列中的消息由当前线程内部进行处理102. */103. private Handler handler = new Handler()104. {105. //重写Handler里面的handleMessage方法处理消息106.@Override107. public void handleMessage(Message msg)108. {109. switch (msg.what)110. {111. case 1:112. //进度条显示113. progressBar.setProgress(msg.getData().getInt("size"));114. float num = (float)progressBar.getProg ress()/(float)progressBar.getMax();115. int result = (int)(num*100);116. //resultView.invalidate(); UI线程中立即更新进度条方法117. //resultView.postInvalidate(); 非UI线程中立即更新进度条方法118. resultView.setText(result+ "%"); 119. //判断是否下载成功120. if(progressBar.getProgress()==progress Bar.getMax())121. {122. Toast.makeText(DownloadActivity.th is, R.string.success, 1).show();123. }124. break;125. case -1:126. Toast.makeText(DownloadActivity.this, R.string.error, 1).show();127. break;128. }129. }130. };131.132.}(1)下载类:注意计算每条线程的下载长度与下载起始位置的方法Java代码1.public class DownloadThread extends Thread2.{3. private static final String TAG = "DownloadThread";4. private File saveFile;5. private URL downUrl;6. private int block;7. //下载开始位置8. private int threadId = -1;9. //下载文件长度10. private int downLength;11. private boolean finish = false;12. private FileDownloader downloader;13. public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId)14. {15. this.downUrl = downUrl;16. this.saveFile = saveFile;17. this.block = block;18. this.downloader = downloader;19. this.threadId = threadId;20. this.downLength = downLength;21. }22.23.@Override24. public void run()25. {26. //未下载完成27. if(downLength < block)28. {29. try30. {31. HttpURLConnection http = (HttpURLConnection)downUrl.openConnection();32. http.setConnectTimeout(5 * 1000);33. http.setRequestMethod("GET");34. http.setRequestProperty("Accept", "image/gif,image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave -flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, applicati on/vnd.ms-excel, application/vnd.ms-powerpoint, application/ms word, */*");35. http.setRequestProperty("Accept-Language", "zh-CN");36. http.setRequestProperty("Referer", downUrl.toString());37. http.setRequestProperty("Charset", "UTF-8");38. //下载开始位置:线程id*每条线程下载的数据长度 = ?39. int startPos = block * (threadId - 1) + downLength;40. //下载结束位置:(线程id+1)*每条线程下载的数据长度-1=?41. int endPos = block * threadId -1;42. //设置获取实体数据的范围43. http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);44. http.setRequestProperty("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)");45. http.setRequestProperty("Connection", "Keep-Alive");46.47. InputStream inStream = http.getInputStream();48. byte[] buffer = new byte[1024];49. int offset = 0;50. print("Thread " + this.threadId + " start download from position "+ startPos);51. RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");52. threadfile.seek(startPos);53. while ((offset = inStream.read(buffer, 0, 1024)) != -1)54. {55. threadfile.write(buffer, 0, offset);56. downLength += offset;57. downloader.update(this.threadId, downLength);58. downloader.append(offset);59. }60. threadfile.close();61. inStream.close();62. print("Thread " + this.threadId + " downloadfinish");63. //标记是否完成64. this.finish = true;65. }66. catch (Exception e)67. {68. this.downLength = -1;69. print("Thread "+ this.threadId+ ":"+ e);70. }71. }72. }73.74. private static void print(String msg)75. {76. Log.i(TAG, msg);77. }78.79. /**80. * 下载是否完成81. * @return82. */83. public boolean isFinish()84. {85. return finish;86. }87.88. /**89. * 已经下载的内容大小90. * @return 如果返回值为-1,代表下载失败91. */92. public long getDownLength()93. {94. return downLength;95. }96.}文件下载器,使用Java代码1./**2. * 文件下载器,使用这个类的方法如下示例:3. * FileDownloader loader = new FileDownloader(context, "http:///ejb3/ActivePort.exe",4. * new File("D:\\androidsoft\\test"), 2);5. * loader.getFileSize();//得到文件总大小6. * try {7. * loader.download(new DownloadProgressListener(){8. * public void onDownloadSize(int size) {9. * print("已经下载:"+ size);10. * }11. * });12. * }13. * catch (Exception e)14. * {15. * e.printStackTrace();16. * }17. */18.public class FileDownloader19.{20. private static final String TAG = "FileDownloader";21. private Context context;22. private FileService fileService;23. //已下载文件长度24. private int downloadSize = 0;25. //原始文件长度26. private int fileSize = 0;27. ///线程数28. private DownloadThread[] threads;29. //本地保存文件30. private File saveFile;31. //缓存各线程下载的长度32. private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();33. //每条线程下载的长度34. private int block;35. //下载路径36. private String downloadUrl;37. //获取线程数38. public int getThreadSize()39. {40. return threads.length;41. }42. /**43. * 获取文件大小44. * @return45. */46. public int getFileSize()47. {48. return fileSize;49. }50. /**51. * 累计已下载大小52. * @param size53. */54. protected synchronized void append(int size)55. {56. downloadSize += size;57. }58. /**59. * 更新指定线程最后下载的位置60. * @param threadId 线程id61. * @param pos 最后下载的位置62. */63. protected synchronized void update(int threadId, int pos)64. {65. this.data.put(threadId, pos);66. this.fileService.update(this.downloadUrl, this.data);67. }68. /**69. * 文件下载构造器70. * @param downloadUrl 下载路径71. * @param fileSaveDir 文件保存目录72. * @param threadNum 下载线程数73. */74. public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum)75. {76. try77. {78. this.context = context;79. this.downloadUrl = downloadUrl;80. fileService = new FileService(this.context);81. URL url = new URL(this.downloadUrl);82. if(!fileSaveDir.exists()) fileSaveDir.mkdirs();83. this.threads = new DownloadThread[threadNum];84. HttpURLConnection conn = (HttpURLConnection) url.openConnection();85. conn.setConnectTimeout(5*1000);86. conn.setRequestMethod("GET");87. conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-fla sh, application/xaml+xml, application/vnd.ms-xpsdocument, appl ication/x-ms-xbap, application/x-ms-application, application/v nd.ms-excel, application/vnd.ms-powerpoint, application/msword , */*");88. conn.setRequestProperty("Accept-Language", "zh-CN");89. conn.setRequestProperty("Referer", downloadUrl);90. conn.setRequestProperty("Charset", "UTF-8");91. conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR1.1.4322; .NET CLR2.0.50727; .NET CLR3.0.04506.30; .NET CLR3.0.4506.2152; .NET CLR 3.5.30729)");92. conn.setRequestProperty("Connection", "Keep-Alive");93. conn.connect();94. printResponseHeader(conn);95. if (conn.getResponseCode()==200)96. {97. //根据响应获取文件大小98. this.fileSize = conn.getContentLength();99. if (this.fileSize <= 0) throw new RuntimeException("Unkown file size ");100. //获取文件名称101. String filename = getFileName(conn); 102. //构建保存文件103. this.saveFile = new File(fileSaveDir, file name);104. //获取下载记录105. Map<Integer, Integer> logdata = fileService.getData(downloadUrl);106. //如果存在下载记录107. if(logdata.size()>0)108. {109. //把各条线程已经下载的数据长度放入data 中110. for(Map.Entry<Integer, Integer> entry : logdata.entrySet())111. data.put(entry.getKey(), entry.get Value());112. }113. //下面计算所有线程已经下载的数据长度114. if(this.data.size()==this.threads.length)115. {116. for (int i = 0; i < this.threads.lengt h; i++)117. {118. this.downloadSize += this.data.get (i+1);119. }120. print("已经下载的长度"+ this.downloadSize);121. }122. //计算每条线程下载的数据长度123. this.block = (this.fileSize % this.threads .length)==0? this.fileSize / this.threads.length : this.fileSi ze / this.threads.length + 1;124. }125. else126. {127. throw new RuntimeException("server no resp onse ");128. }129. }130. catch (Exception e)131. {132. print(e.toString());133. throw new RuntimeException("don't connection t his url");134. }135. }136. /**137. * 获取文件名138. */139. private String getFileName(HttpURLConnection conn) 140. {141. String filename = this.downloadUrl.substring(this.stIndexOf('/') + 1);142. if(filename==null || "".equals(filename.trim())){/ /如果获取不到文件名称143. for (int i = 0;; i++) {144. String mine = conn.getHeaderField(i); 145. if (mine == null) break;146. if("content-disposition".equals(conn.getHe aderFieldKey(i).toLowerCase())){147. Matcher m = pile(".*filenam e=(.*)").matcher(mine.toLowerCase());148. if(m.find()) return m.group(1); 149. }150. }151. filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名152. }153. return filename;154. }155.156. /**157. * 开始下载文件158. * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null159. * @return 已下载文件大小160. * @throws Exception161. */162. public int download(DownloadProgressListener listener) throws Exception{163. try164. {165. //创建本地文件166. RandomAccessFile randOut = new RandomAccessFil e(this.saveFile, "rw");167. if(this.fileSize>0) randOut.setLength(this.fil eSize);168. randOut.close();169. URL url = new URL(this.downloadUrl);170. if(this.data.size() != this.threads.length) 171. {172. this.data.clear();173. for (int i = 0; i < this.threads.length; i ++)174. {175. //初始化每条线程已经下载的数据长度为176. this.data.put(i+1, 0);177. }178. }179. //开启线程进行下载180. for (int i = 0; i < this.threads.length; i++)181. {182. int downLength = this.data.get(i+1); 183. //判断线程是否已经完成下载,否则继续下载184. if(downLength < this.block && this.downloa dSize<this.fileSize)185. {186. this.threads[i] = new DownloadThread(t his, url, this.saveFile, this.block, this.data.get(i+1), i+1);187. this.threads[i].setPriority(7);//可删除这条188. this.threads[i].start();189. }190. else191. {192. this.threads[i] = null;193. }194. }195. this.fileService.save(this.downloadUrl, this.d ata);196. //下载未完成197. boolean notFinish = true;198. // 循环判断所有线程是否完成下载199. while (notFinish)200. {201. Thread.sleep(900);202. //假定全部线程下载完成203. notFinish = false;204. for (int i = 0; i < this.threads.length; i ++)205. {206. //如果发现线程未完成下载207. if (this.threads[i] != null && !this.t hreads[i].isFinish())208. {209. //设置标志为下载没有完成210. notFinish = true;211. //如果下载失败,再重新下载212. if(this.threads[i].getDownLength() == -1)213. {214. this.threads[i] = new Download Thread(this, url, this.saveFile, this.block, this.data.get(i+1 ), i+1);215. this.threads[i].setPriority(7) ;216. this.threads[i].start(); 217. }218. }219. }220. //通知目前已经下载完成的数据长度221. if(listener!=null) listener.onDownloadSize (this.downloadSize);222. }223. //删除数据库中下载信息224. fileService.delete(this.downloadUrl);225. }226. catch (Exception e)227. {228. print(e.toString());229. throw new Exception("file download fail"); 230. }231. return this.downloadSize;232. }233.234. /**235. * 获取Http响应头字段236. * @param http237. * @return238. */239. public static Map<String, String> getHttpResponseHeade r(HttpURLConnection http) {240. Map<String, String> header = new LinkedHashMap<Str ing, String>();241. for (int i = 0;; i++) {242. String mine = http.getHeaderField(i); 243. if (mine == null) break;244. header.put(http.getHeaderFieldKey(i), mine);245. }246. return header;247. }248. /**249. * 打印Http头字段250. * @param http251. */252. public static void printResponseHeader(HttpURLConnecti on http)253. {254. Map<String, String> header = getHttpResponseHeader (http);255. for(Map.Entry<String, String> entry : header.entry Set())256. {257. String key = entry.getKey()!=null ? entry.getK ey()+ ":" : "";258. print(key+ entry.getValue());259. }260. }261. private static void print(String msg)262. {263. Log.i(TAG, msg);264. }265.}Java代码1.public interface DownloadProgressListener2.{3. public void onDownloadSize(int size);4.}(2)文件操作,断点数据库存储Java代码1.public class DBOpenHelper extends SQLiteOpenHelper2.{3. private static final String DBNAME = "itcast.db";4. private static final int VERSION = 1;5.6. public DBOpenHelper(Context context)7. {8. super(context, DBNAME, null, VERSION);9. }10.11.@Override12. public void onCreate(SQLiteDatabase db)13. {14. db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)");15. }16.@Override17. public void onUpgrade(SQLiteDatabase db, int oldVersion,int newVersion)18. {19. db.execSQL("DROP TABLE IF EXISTS filedownlog");20. onCreate(db);21. }22.}Java代码1./**2. * 文件下载业务bean3. */4.public class FileService5.{6. private DBOpenHelper openHelper;7. public FileService(Context context)8. {9. openHelper = new DBOpenHelper(context);10. }11. /**12. * 获取每条线程已经下载的文件长度13. * @param path14. * @return15. */16. public Map<Integer, Integer> getData(String path)17. {18. SQLiteDatabase db = openHelper.getReadableDatabase();19. Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path});20. Map<Integer, Integer> data = new HashMap<Integer, Integer>();21. while(cursor.moveToNext())22. {23. data.put(cursor.getInt(0), cursor.getInt(1));24. }25. cursor.close();26. db.close();27. return data;28. }29. /**30. * 保存每条线程已经下载的文件长度31. * @param path32. * @param map33. */34. public void save(String path, Map<Integer, Integer> map)35. {//int threadid, int position36. SQLiteDatabase db = openHelper.getWritableDatabase();37. db.beginTransaction();38. try39. {40. for(Map.Entry<Integer, Integer> entry : map.entrySet())41. {42. db.execSQL("insert into filedownlog(downpath,threadid, downlength) values(?,?,?)",43. new Object[]{path, entry.getKey(), entry.getValue()});44. }45. db.setTransactionSuccessful();46. }47. finally48. {49. db.endTransaction();50. }51. db.close();52. }53. /**54. * 实时更新每条线程已经下载的文件长度55. * @param path56. * @param map57. */58. public void update(String path, Map<Integer, Integer> map)59. {60. SQLiteDatabase db = openHelper.getWritableDatabase();61. db.beginTransaction();62. try{63. for(Map.Entry<Integer, Integer> entry : map.entrySet()){64. db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?",65. new Object[]{entry.getValue(), path,entry.getKey()});66. }67. db.setTransactionSuccessful();68. }finally{69. db.endTransaction();70. }71. db.close();72. }73. /**74. * 当文件下载完成后,删除对应的下载记录75. * @param path76. */77. public void delete(String path)78. {79. SQLiteDatabase db = openHelper.getWritableDatabase();80. db.execSQL("delete from filedownlog where downpath=?", new Object[]{path});81. db.close();82. }83.}。
黑马程序员安卓教程:Javase下实现多线程下载编码
Javase下实现多线程下载编码多线程下载各个步骤的逻辑我们都已经讲述过,下面我们通过一个案例来实现多线程下载的具体实现。
由于android对标准的javase完全支持,我们可以先创建一个java项目,在该项目中实现多线程下载的功能,这样做也方便了测试。
1.部署服务端运行tomcat服务器,在tomcat安装路径的ROOT目录下放置下载资源(参考:当前计算机的D:\soft\tomcat\apache-tomcat-7.0.52\webapps\ROOT),如图1-1所示:图1-1所示从图1-1可知该下载资源是QQ6.6.exe。
启动tomcat,在浏览器中访问该下载资源(路径为:http://localhost:8080/QQ6.6.exe),效果如图1-2所示:图1-2所示从图1-2可知该下载资源已经完成部署。
2.编写客户端在eclipse中编写一个Java工程,实现多线程从tomcat下载.exe文件。
(1)创建工程【File】→【new】→【Java Project】命名为:多线程下载(2)使用默认包名,创建一个类MultiDownload,工程目录结构如图1-3所示:图1-3所示(3)在多线程下载类(即MultiDownload.class)中,编写下载方法,具体步骤如下:●等分服务端资源首先,发送http请求,得到服务端的返回数据,该返回数据中包含了多个响应报文头,调用getContentLength方法获取返回数据体的长度。
代码如例1-1:运行程序,控制台打印如图1-4所示信息:图1-4所示从图1-4可知:我们已经获取了下载文件的大小,即58694840字节。
对比tomcat中ROOT下的资源文件可以发现控制台打印出的文件大小和该文件的实际大小一致。
其次,假设将该下载资源等分为3份,每一份对应一个线程来下载,则每一个线程下载的数据长度如图1-5所示:每一个线程下载的长度:58694840/3=19564946.66666对应公式:总数/线程个数(threadcount)=每个区块大小(blocksize)第0个线程下载区段第1个线程下载区段第2个线程下载区段0*blocksize~1*blocksize-11*blocksize~2*blocksize-12*blocksize~3*blocksize-1第n个线程下载区段n*blocksize~(n+1)*blocksize-1最后一个线程m下载区段m*blocksize~文件总长度-1图1-5所示根据图1-5所示的计算公式,我们可以在程序中处理每一个线程下载的数据长度,具体代码如例1-2:运行程序,控制台打印如图1-6信息:图1-6所示创建与下载资源等大的空文创建一个RandomAccessFile的对象,调用该对象的setLength方法指定该文件的长度,具体代码如例1-3:运行程序,在工程目录下可以看到新建的空文件,如图1-7所示:图1-7所示此时,浏览该temp.exe文件,效果如图1-8所示:图1-8所示从图1-8得知:此时该文件没有任何数据,都是空字符。
Android实现多线程断点下载
Android实现多线程断点下载⽬录QDownload1、如何使⽤1.1、导⼊依赖1.2、初始化下载组件1.3、核⼼控制器DownloadManager1.4、监听下载进度1.5、下载相关的操作1.6、应⽤市场apk下载的⼀个场景总结QDownloadQDownload是基于Android平台实现的下载框架。
API简洁易上⼿,只需5分钟即可实现⼀个多任务、多线程、断点下载的功能⽀持功能如下:⽀持多个下载任务同时下载单个任务⽀持开多个线程下载⽀持断点下载,在断⽹、进程被划掉可恢复下载⾃动校验服务器⽂件服务器是否⽀持断点下载,如果不⽀持则会开启单线程任务下载⽀持应⽤全局监听下载进度回调⽀持下载速度显⽰⽀持添加下载任务,暂停下载,恢复下载,取消下载⽀持批量暂停,批量恢复下载先贴个效果图主页多任务多线程断点下载页⾯1、如何使⽤1.1、导⼊依赖implementation 'com.qw.download:download:1.0.0-alpha01' 1.2、初始化下载组件public class MyApplication extends Application{@Overridepublic void onCreate() {super.onCreate();//初始化下载组件(可在⼦线程中做)DownloadManager.init(this);}}1.3、核⼼控制器DownloadManager api如下public class DownloadManager {private static DownloadManager mInstance;private final Context context;private DownloadManager(Context context) {}//初始化组件public static void init(Context context) {}//开启下载public static void add(DownloadEntry entry) {}//暂停下载public static void pause(DownloadEntry entry) {}//暂停所有任务public static void pauseAll() {}//恢复下载public static void resume(DownloadEntry entry) {}//恢复所有任务public static void recoverAll() {}}1.4、监听下载进度需要监听下载进度可通过注册DownloadWatcher来监听下载信息的变化private DownloadWatcher watcher = new DownloadWatcher() {@Overrideprotected void onDataChanged(DownloadEntiry entry) {//这⾥监听下载的实时信息mDownloadInfoLabel.setText(entry.toString());Log.e("MainActivity", entry.toString());}};@Overrideprotected void onResume() {super.onResume();//注册观察者DownloadManager.addObserver(watcher);}@Overrideprotected void onPause() {super.onPause();//移除观察者DownloadManager.removeObserver(watcher);}其中第3⾏DownloadEntiry entry存储了下载相关信息public class DownloadEntry implements Serializable {public String id;//下载的唯⼀标识public String url;//下载⽂件的url地址public boolean isSupportRange;//是否⽀持断点续传public long contentLength;//⽂件长度public long currentLength;//已下载⽂件长度public State state;//任务状态public HashMap<Integer, Long> ranges;//存储每个线程下载开始区块public int speed;//下载速度单位s}其中state有如下⼏个值public enum State {IDLE,//空闲CONNECT,//连接中(⽤户预取⽂件信息)ING,//下载中PAUSED,//已暂停CANCELLED,//已取消ERROR,//错误DONE,//完成WAIT//等待}1.5、下载相关的操作添加⼀个下载通过DownloadEntry构建下载实体public void addDownload(String id,String url){//id为任务的⼀个唯⼀标识,url为⽂件地址DownloadEntry entry = DownloadEntry.obtain(id,url)//添加⼀个下载任务DownloadManager.add(entry)}暂停下载可通过下载任务id 然后调⽤DownloadManager.findById(id)函数获取DownloadEntry public void pauseDownload(DownloadEntry entry){//添加⼀个下载任务DownloadManager.pause(entry)}恢复下载public void resumeDownload(DownloadEntry entry){//添加⼀个下载任务DownloadManager.resume(entry)}暂停所有下载任务public void pauseAll(DownloadEntry entry){//添加⼀个下载任务DownloadManager.resume(entry)}恢复所有下载任务public void recoverAll(DownloadEntry entry){//添加⼀个下载任务DownloadManager.recoverAll(entry)}1.6、应⽤市场apk下载的⼀个场景ApkEntry实体数据⽤来描述apk的基本信息public class ApkEntry {public String id;//包idpublic String cover;//apk 图标public String name;//apk 名称public String url;//apk 下载地址public long length;//apk ⼤⼩public String id() {if (TextUtils.isEmpty(id)) {//如果服务端没有返回唯⼀标记则⽤url的md5值作为下载唯⼀标识id = FileUtilities.getMd5FileName(url);}return id;}}这是我要下载这个apk就可以这么做public void downloadApk(ApkEntry apkEntry){//1、先check当前apk是否在下载DownloadEntry entry = DownloadManager.findById(apkEntry.id())if(entry==null || entry.state==State.PAUSED || entry.state==State.ERROR || entry.state==State.CANCELLED){//这四种情况:没有下载任务 || 任务是暂停 || 下载失败 || 任务已被取消DownloadManager.add(entry)}}暂停apk下载public void pauseDownloadApk(ApkEntry apkEntry){//1、先check当前apk是否在下载任务中DownloadEntry entry = DownloadManager.findById(apkEntry.id())if(entry!=null){DownloadManager.pause(entry)}}总结到此这篇关于Android实现多线程断点下载的⽂章就介绍到这了,更多相关Android多线程断点下载内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
Android应用开发中的多线程编程技术详解
Android应用开发中的多线程编程技术详解在Android应用中,当我们需要执行一些耗时的操作,例如网络请求、磁盘读写等,如果在主线程中执行,会导致UI界面卡顿,用户体验下降。
而解决这个问题的办法就是使用多线程编程技术。
本文将详细介绍Android应用开发中的多线程编程技术。
1. 线程的基础知识在Java中,线程就是一个独立的执行单元,它可以独立运行,也可以和其它线程一起协作完成任务。
而在Android中,我们常用的线程有两种:主线程和子线程。
主线程是Android应用的UI线程,负责响应用户的交互事件,执行UI更新操作;而子线程则是我们在应用中创建的线程,用于执行耗时的操作。
在Java中,有两种方式来创建线程:继承Thread类和实现Runnable接口。
其中,实现Runnable接口是更加常用的做法,因为它能够避免单继承的限制。
下面是一个简单的示例代码,实现了一个通过实现Runnable接口创建子线程的例子:```public class MyThread implements Runnable {@Overridepublic void run() {// 子线程要执行的任务}}// 在主线程中创建子线程MyThread myThread = new MyThread();Thread thread = new Thread(myThread);thread.start();```当我们创建了一个子线程后,它就会独立地运行在一个新的线程中。
在子线程中,我们可以执行一些耗时的操作,并且不会卡住主线程。
2. Android中的主线程在Android应用中,主线程是非常重要的,它负责维护UI界面的显示和交互,处理用户的输入事件,并更新UI的状态。
因此,在主线程中执行一些耗时的操作,就会使得UI界面出现卡顿现象,用户体验大大降低。
为了避免这种情况,Android系统引入了一种机制,叫做ANR (Application Not Responding)。
黑马程序员安卓教程:Android多线程下载器UI实现
//在本地SD卡上创建文件raf用来保存下载的数据,同时设置每一个子线程写入文件raf数据的开始位置raf.seek(startindex)
..........
inttotal = 0;//代表当前线程下载的总大小
//while循环将返回的数据流写入到本地文件中
while(( len = is.read(buffer))!=-1){
raf.write(buffer, 0, len);
total+=len;
intcurrentposition = startindex+total;
//当前线程下载的位置保存到其对应的临时文件中
.......
//while循环中不断的更新了进度条
intcurrentsize = lastDownloadsize+total;//当前进度
conn.setRequestProperty("Range","bytes="+startindex+"-"+endindex);
}
intcode = conn.getResponseCode();
//3.根据返回码,判断数据请求是否成功,并读取服务端返回数据流
if(code == 206){
//读取服务端返回的数据流conn.getInputStream()
pbs.get(threadid).setMax(totalblocksize);
pbs.get(threadid).setProgress(currentsize);
}
is.close();
raf.close();
System.out.println("线程:"+threadid+"下载完毕了。");
Android 多线程详解
Thread类常用方法(1)public static Thread currentThread():返回当前调用的线程(2)public final String getName():返回当前线程名称(3)public long getId():返回当前线程标识id(4)public final void setName(String threadName):设置线程名称(5)public void start():执行一个新的线程(6)public final void stop():关闭一个线程,不推荐使用因为该方法可能会产生死锁,推荐使用在Thread中设置标识符结束运行的代码(7)public static void sleep(long time):等待一段时间后在运行线程,单位是毫秒进程概念在同一时间内执行多个程序的操作系统都有进程的概念。
一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间、一组系统资源。
在进程的概念中,每一个进程的内部数据和状态都是完全独立的。
在Windows操作系统下我们可以通过〈Ctrl+Alt+Del〉组合键查看进程,在UNIX和Linux操作系统下是通过PS命令查看进程的。
打开Windows 当前运行的进程,如图在Windows操作系统中一个进程就是一个exe或dll程序,它们相互独立,互相也可以通信,在Android操作系统中进程间的通信应用也是很多Android空进程线程概念多线程指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务。
多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行。
线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制。
但与进程不同的是,同类的多个线程共享一块内存空间和一组系统资源,所以系统在各个线程之间切换时,资源占用要比进程小得多,正因如此,线程也被称为轻量级进程。
一个进程中可以包含多个线程。
Android 多线程下载图片
很多时候我们需要在Android设备上下载远程服务器上的图片进行显示,今天eoeAndroid整理出两种比较好的方法来实现远程图片的下载。
方法一、直接通过Android提供的Http类访问远程服务器,这里AndroidHttpClient是SDK 2.2中新出的方法,API Level为8,大家需要注意下,静态访问可以直接调用,如果SDK版本较低可以考虑Apache的Http库,当然HttpURLConnection 或URLConnection也可以。
Java代码:1.static Bitmap downloadBitmapByCwj(String url) {2.final AndroidHttpClient client =AndroidHttpClient.newInstance("Android123");3.final HttpGet getRequest = new HttpGet(url);4.5.try {6.HttpResponse response = client.execute(getRequest);7.final int statusCode = response.getStatusLine().getStatusCode();8.if (statusCode != HttpStatus.SC_OK) {9.Log.e("cwjDebug", "Error " + statusCode + " while retrieving bitmapfrom " + url);10.return null;11.}12.13.final HttpEntity entity = response.getEntity();14.if (entity != null) {15.InputStream inputStream = null;16.try {17.inputStream = entity.getContent();18.final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);19.return bitmap;20.} finally {21.if (inputStream != null) {22.inputStream.close();23.}24.entity.consumeContent();25.}26.}27.} catch (Exception e) {28.getRequest.abort();29.Log.e("android123Debug", "Error while retrieving bitmap from " +url, e.toString());30.} finally {31.if (client != null) {32.client.close();33.}34.}35.return null;36.}复制代码这里eoeAndroid提醒大家,BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法获取完整的数据,这里我们通过继承FilterInputStream类的skip方法来强制实现flush流中的数据,主要原理就是检查是否到文件末端,告诉http类是否继续。
多线程技术在Android手机开发中的运用
多线程技术在 A n d r o i d手机 开发中的运用
文/ 谢光 刘 志 惠
在A n d r o i d 手机 开发过程 中, 般 情 况下程 序是 通 过一 个 线程 进 行 工作 的 ,因此 当一 个任 务 耗
2 多线程技术在A n d r o i d - 术 A n d r o i d手 机 进程 线程 2 . 1 A n d r o i d 开发 中多线程结构模块 在判 定 一个 系统 是 否成 熟 时,技 术人 员 要关注一下几 点:运行 的速 度是否快速,响应 速度的快慢 ,安全性能 的强 弱等等 。多线程的 应用,可 以在较大程度上提 高安卓系统的运行 与相应速度 。安卓 的多线程结 构模块 包含 以下 几个方面:用于活动 的结构模块 ,此模块 主要 是通过视 图来显示包含信 息现 实和用 户动 作的 反 应的图形界面 。二是用于服 务的结构模块 , 此部分在进行工作时 ,主要在程序 后台,在触 发事件 时可通知程序进行数据 的更新 。三 是内 容 、数据 的提供部分 ,此板块 可以通过数据 的 管理,实现数据的共 同使用 ,是一个 大体积 的 数据存储 板块。四是 ,此模块通过程序 的协 调 功 能,对 目标活动提供所需要 的信 息,且 具有 意 图说 明的功用 。 在 所有 的程 序模 块 中,用 于活 动 的结构 模块是 需要直接同用户进行交互的 ,这种功 能 主 要 是 通 过 用 户 界 面 得 以实 现 ,用 户 界 面 通 过 展示程序 运行情况实现与用户 的直接交流 。若 安卓 的使用程序 中, 由于单线程的使用造成程 序无法响应 、程序运 行速度过慢的 问题 ,工作 人员 需要把应用程序 加载到新的线程 中,以实 现刷新主线 的活动界面 内容,实现对整个活动 界面 的刷新 。具体来 说,用 户在下载文件时 , 若文件体积过大 需要较 多的下载时间 ,那么程 序需要经此任务 调离主 线程 ,并迅 速开启新的 线 程 完 成 下 载 。 即 :用 户 先 是 打 开 了 主 界 面 , 然后输入网址 ,在此之后系统 就开 始对文件下 载 ,为保证系统运行速度 ,程序开启一些子线 程进行下载工作 ,如下载完 成,子 线程也可 以 对 主线程进行消息报告 , 主线程在 得到报告后, 会再次刷新页面 。
Android文件下载功能实现代码
Android⽂件下载功能实现代码本⽂实例为⼤家分享了Android⽂件下载功能的具体代码,供⼤家参考,具体内容如下1.普通单线程下载⽂件:直接使⽤URLConnection.openStream()打开⽹络输⼊流,然后将流写⼊到⽂件中!public static void downLoad(String path,Context context)throws Exception{URL url = new URL(path);InputStream is = url.openStream();//截取最后的⽂件名String end = path.substring(stIndexOf("."));//打开⼿机对应的输出流,输出到⽂件中OutputStream os = context.openFileOutput("Cache_"+System.currentTimeMillis()+end, Context.MODE_PRIVATE); byte[] buffer = new byte[1024];int len = 0;//从输⼊六中读取数据,读到缓冲区中while((len = is.read(buffer)) > 0){os.write(buffer,0,len);}//关闭输⼊输出流is.close();os.close();}2.普通多线程下载:步骤:获取⽹络连接本地磁盘创建相同⼤⼩的空⽂件计算每条线程需从⽂件哪个部分开始下载,结束依次创建,启动多条线程来下载⽹络资源的指定部分public class Downloader {//添加@Test标记是表⽰该⽅法是Junit测试的⽅法,就可以直接运⾏该⽅法了@Testpublic void download() throws Exception{//设置URL的地址和下载后的⽂件名String filename = "meitu.exe";String path = "http://10.13.20.32:8080/Test/XiuXiu_Green.exe";URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod("GET");//获得需要下载的⽂件的长度(⼤⼩)int filelength = conn.getContentLength();System.out.println("要下载的⽂件长度"+filelength);//⽣成⼀个⼤⼩相同的本地⽂件RandomAccessFile file = new RandomAccessFile(filename, "rwd");file.setLength(filelength);file.close();conn.disconnect();//设置有多少条线程下载int threadsize = 3;//计算每个线程下载的量int threadlength = filelength % 3 == 0 ? filelength/3:filelength+1;for(int i = 0;i < threadsize;i++){//设置每条线程从哪个位置开始下载int startposition = i * threadlength;//从⽂件的什么位置开始写⼊数据RandomAccessFile threadfile = new RandomAccessFile(filename, "rwd");threadfile.seek(startposition);//启动三条线程分别从startposition位置开始下载⽂件new DownLoadThread(i,startposition,threadfile,threadlength,path).start();}int quit = System.in.read();while('q' != quit){Thread.sleep(2000);}}private class DownLoadThread extends Thread {private int threadid;private int startposition;private RandomAccessFile threadfile;private int threadlength;private String path;public DownLoadThread(int threadid, int startposition,RandomAccessFile threadfile, int threadlength, String path) {this.threadid = threadid;this.startposition = startposition;this.threadfile = threadfile;this.threadlength = threadlength;this.path = path;}public DownLoadThread() {}@Overridepublic void run() {try{URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5000);conn.setRequestMethod("GET");//指定从什么位置开始下载conn.setRequestProperty("Range", "bytes="+startposition+"-");//System.out.println(conn.getResponseCode());if(conn.getResponseCode() == 206){InputStream is = conn.getInputStream();byte[] buffer = new byte[1024];int len = -1;int length = 0;while(length < threadlength && (len = is.read(buffer)) != -1){threadfile.write(buffer,0,len);//计算累计下载的长度length += len;}threadfile.close();is.close();System.out.println("线程"+(threadid+1) + "已下载完成");}}catch(Exception ex){System.out.println("线程"+(threadid+1) + "下载出错"+ ex);}}}}以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
Android应用开发中的多线程编程技术详解
Android应用开发中的多线程编程技术详解随着移动设备不断普及,Android应用的用户数量愈加庞大,因此,开发高质量、高效率的Android应用已经成为了一种竞争优势。
而在Android应用开发中,多线程编程技术就是必不可少的一个重要领域。
本文将详细探讨Android应用开发中的多线程编程技术,包括线程的概念、线程池和Handler机制、异步任务和Loader、消息队列和消息循环机制等内容。
通过深入了解这些多线程编程技术,开发者们可以更好地开发出高效率、高响应程序与丝滑顺畅的用户体验。
一、线程的概念线程可以理解为是一种轻量级进程(Lightweight Process),它是计算机中已被分配了系统资源的基本单位,可以执行一系列指令序列,并将结果返回给主线程。
在Android中,每个应用程序都是单独的进程,而每个进程又可以包含多个线程,这些线程使用操作系统调度算法来分享CPU资源,从而提高程序的执行效率和响应速度。
在Android中启动一个线程很简单,只需要使用Thread类即可。
一般情况下,我们需要自己定义一个类,这个类必须继承自Thread类,并重写其中的run()方法,run()方法中包含了子线程的代码逻辑。
下面是一个简单的线程启动示例:```javapublic class MyThread extends Thread {@Overridepublic void run() {// 线程代码逻辑super.run();}}//在主线程中启动MyThread线程MyThread thread = new MyThread();thread.start();```二、线程池和Handler机制虽然线程可以提高程序的响应速度和执行效率,但同时也存在一些问题。
比如,线程开启和销毁需要不少的时间和开销,频繁地调用可能会对系统稳定性产生影响。
因此,Android提供了线程池和Handler机制来解决这些问题。
android多线程讲解 看图理解
本期的多线程主题与Android相关,侧重讲解在Android中如何用好多线程,需要你有Java 的多线程基础。
首先我们思考几个问题,在Android应用中为什么要用多线程?为了解决哪些问题?或者为了实现哪些功能?有哪些好处?请先思考一分钟,再继续往下看。
学习而不思考就像吃东西而不嚼,要么无法下咽,要么尝不出味道,同时都会影响消化吸收。
控制一下你那脱缰野马一样的好奇心吧,先思考再往下看。
————————————————飘过这条分隔线,我们继续——————————————————1.为什么要用多线程这里列出几个原因:a) 提高用户体验或者避免ANR在事件处理代码中需要使用多线程,否则会出现ANR(Application is not responding),或者因为响应较慢导致用户体验很差。
图1 ANR对话框b) 异步应用中有些情况下并不一定需要同步阻塞去等待返回结果,可以通过多线程来实现异步,例如:上一点中提到的,你的应用中的某个Activity需要从云端获取一些图片,加载图片比较耗时,这时需要使用异步加载,加载完成一个图片刷新一个,见下面图2、图3 。
c) 多任务例如多线程下载。
后两点与Java中的多线程应用没有太大区别,不细说。
下面重点说明第一点,即如何减少事件响应的时间从而提高用户体验,以及如何避免ANR。
2.为什么通过多线程可以提高用户体验、避免ANR大家还记得我在群里说过的移动开发的“三不要”原则吗?即:不要让我想、不要让我等、不要让我烦。
响应慢了用户需要等,等的次数多了就会烦,你的应用离被卸载不远了。
首先我们来了解一下Android应用程序的main线程,它负责处理UI的绘制,Android系统为了防止应用程序反应较慢导致系统无法正常运行做了一个处理,一种情况是当用户输入事件在5秒内无法得到响应,那么系统会弹出ANR对话框,由用户决定继续等待还是强制结束应用程序(另一种情况是BroadcastReciever 超过10秒没执行完也会弹出ANR对话框)。
黑马程序员安卓教程:Android 实现多线程下载
Android 实现多线程下载将上面JavaSE实现多线程下载的代码经过一定的改造,便可移植到Android上。
有所不同的是Android有界面可以跟用户进行良好的交互,在界面(如图1-4)上让用户输入原文件地址、线程个数,然后点击25确定开始下载。
为了让用户可以清晰的看到每个线程下载的进度根据线程个数动态的生成等量的进度条(ProgressBar )。
图 1-4 Android 实现多线程下载1.6.1 ProgressBar 的使用ProgressBar 是一个进度条控件,用于显示一项任务的完成进度。
其有两种样式,一种是圆形的,该种样式是系统默认的,由于无法显示具体的进度值,适合用于不确定要等待多久的情形下;另一种是长条形的,,此类进度条有两种颜色,高亮颜色代表任务完成的总进度。
对于我们下载任务来说,由于总任务(要下载的字节数)是明确的,当前已经完成的任务(已经下载的字节数)也是明确的,因此特别适合使用后者。
由于在我们的需求里 ProgressBar 是需要根据线程的个数动态添加的,而且要求是长条形的。
因此可以事先在布局文件中编写好 ProgressBar 的样式。
当需要用到的时候再将该布局(见【文件 1-11】)填充起来。
ProgressBar 的 max 属性代表其最大刻度值,progress 属性代表当前进度值。
使用方法如下: ProgressBar.setMax(int max);设置最大刻度值。
ProgressBar.setProgress(int progress);设置当前进度值。
给 ProgressBar 设置最大刻度值和修改进度值可以在子线程中操作的,其内部已经特殊处理过了,因此不需要再通过发送 Message 让主线程修改进度。
1.6.2 编写布局这里给出两个布局,【文件 1-10】是 MainActivity 的布局,其显示效果如图1-4。
【文件 1-11】是 ProgressBar布局。
【推荐下载】Android多线程理解
Android 多线程理解2017/03/29 0 Android 理解多线程安卓应用程序通常是应用在一个单独的线程里的,即为主线程,用于显示用户界面以及响应用户的操作。
如果一些耗时任务也同时在主线程里去完成,则会影响用户体验,会阻塞UI 主线程。
我们会创建一个单独的线程或者子线程,去处理这些耗时操作(如网络请求、数据库操作等)。
那么这些子线程就是AsyncTask、Thread、HandlerThread,它们叫做安卓的工作者线程。
- AsyncTask - Handler一、AsyncTask 异步任务AsyncTask 是Android 的一个轻量级异步类,可以自定义类并继承AsyncTask,实现异步任务处理操作。
并且AsyncTask 提供了接口返回异步操作的进度,最后会返回结果到UI 主线程中。
优点:简单快捷,过程可以控制缺点:在处理多个异步操作时比较复杂参数说明Params 启动任务时输入的执行参数,一般为一个网络地址Progress异步任务执行的进度Result 异步任务执行后的结果的类型代码实例这是一个利用AsyncTask 加载网络图片的代码:public class MainActivity extends ActionBarActivity { private Button button1; private ImageView imageview1; private ProgressDialog dialog;private String image_uri=”https://imgsa.baidu/baike/c0%3Dbaike272%2C5%2C5%2C272%2C90/sign=c4f298bb9 7ef76c6c4dff379fc7f969f/9358d109b3de9c82f077b3156b81800a19d8431d.jpg”;@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(yout.activity_main); dialog = new ProgressDialog(this); dialog.setTitle(“图片下载”);dialog.setMessage(“正在下载,请稍后…”);button1 = (Button) findViewById(R.id.button1); imageview1 = (ImageView) findViewById(R.id.imageView1); button1.setOnClickListener(new OnClickListener() {@Override public void onClick(View v) { // TODO Auto-generated method stub new。
android多线程下载
本文将介绍在android平台下如何实现多线程下载,大家都知道,android平台使用java做为开发语言,所以java中支持的多线程下载方式在android平台下都支持,其中主要有两种方式可以实现多线程下载。
一种方式是使用很多个线程分别下载文件的不同部分,最后把所有下载完的文件合并成一个文件。
另一种方式是使用java为我们提供的RandomAccessFile类实现多线程的下载。
从性能上分析,第二种方式的存取速度会慢一些,但开发起来较为容易,不需要进行合并文件等操作。
本文将使用第二种方式来实现多线程下载,最终效果如下图所示:第一步,我们先写一个线程类,来完成对指定区域的数据进行下载,如下所示:package com.ideasandroid.demo;import java.io.BufferedInputStream;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import .URL;import .URLConnection;import android.util.Log;/*** Copyright (C) 2010 ideasandroid* 演示android多线程下载* 欢迎访问* 让程序开发不再那么神秘** 单个下载线程*/public class FileDownloadThread extends Thread{ private static final int BUFFER_SIZE=1024;private URL url;private File file;private int startPosition;private int endPosition;private int curPosition;//用于标识当前线程是否下载完成private boolean finished=false;private int downloadSize=0;public FileDownloadThread(URL url,File file,int startPosition,int endPosition){this.url=url;this.file=file;this.startPosition=startPosition;this.curPosition=startPosition;this.endPosition=endPosition;}@Overridepublic void run(){BufferedInputStream bis =null;RandomAccessFile fos =null;byte[] buf =new byte[BUFFER_SIZE];URLConnection con =null;try{con = url.openConnection();con.setAllowUserInteraction(true);//设置当前线程下载的起点,终点con.setRequestProperty("Range", "bytes="+ startPosition +"-"+ endPosition);//使用java中的RandomAccessFile 对文件进行随机读写操作fos =new RandomAccessFile(file, "rw");//设置开始写文件的位置fos.seek(startPosition);bis =newBufferedInputStream(con.getInputStream());//开始循环以流的形式读写文件while(curPosition < endPosition){int len = bis.read(buf, 0, BUFFER_SIZE);if(len ==-1){break;}fos.write(buf, 0, len);curPosition = curPosition + len;if(curPosition > endPosition){downloadSize+=len -(curPosition -endPosition)+1;}else{downloadSize+=len;}}//下载完成设为truethis.finished=true;bis.close();fos.close();}catch(IOException e){Log.d(getName()+" Error:", e.getMessage());}}public boolean isFinished(){return finished;}public int getDownloadSize(){return downloadSize;}}接下来就是使用图形界面来获取需要下载的内容,并实时更新下载进度条,代码如下所示:package com.ideasandroid.demo;import java.io.File;import .URL;import .URLConnection;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.EditText;import android.widget.ProgressBar;import android.widget.TextView;/*** Copyright (C) 2010 ideasandroid* 演示android多线程下载* 欢迎访问* 让程序开发不再那么神秘*/public class FileDownloadDemo extends Activity {private EditText downloadUrl;private EditText downloadFileName;private EditText downloadThreadNum;private Button downloadBt;private ProgressBar downloadProgressBar;private TextView progressMessage;private int downloadedSize =0;private int fileSize =0;@Overridepublic void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState);setContentView(yout.main);downloadUrl =(EditText)findViewById(R.id.downloadUrl);downloadFileName =(EditText) findViewById(R.id.downloadFileName);downloadThreadNum =(EditText)findViewById(R.id.downloadThreadNum);progressMessage =(TextView)findViewById(R.id.progressMessage);downloadBt =(Button)findViewById(R.id.downloadBt);downloadProgressBar =(ProgressBar) findViewById(R.id.downloadProgressBar);downloadProgressBar.setVisibility(View.VISIBLE);downloadProgressBar.setMax(100);downloadProgressBar.setProgress(0);downloadBt.setOnClickListener(newOnClickListener(){public void onClick(View v){download();}});}private void download(){// 获取SD卡目录String dowloadDir =Environment.getExternalStorageDirectory()+"/ideasdownload/";File file =new File(dowloadDir);//创建下载目录if(!file.exists()){file.mkdirs();}//读取下载线程数,如果为空,则单线程下载int downloadTN =Integer.valueOf("".equals(downloadThreadNum.getText().toString())?"1": downloadThreadNum.getText().toString());//如果下载文件名为空则获取Url尾为文件名int fileNameStart = downloadUrl.getText().toString().lastIndexOf("/");String fileName ="".equals(downloadFileName.getText().toString())?downloadUrl.getText().toString().substring(fileNameStart):downloadFileName.getText().toString();//开始下载前把下载按钮设置为不可用downloadBt.setClickable(false);//进度条设为0downloadProgressBar.setProgress(0);//启动文件下载线程newdownloadTask(downloadUrl.getText().toString(), Integer.valueOf(downloadTN), dowloadDir+ fileName).start();}Handler handler =new Handler(){@Overridepublic void handleMessage(Message msg){//当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息int progress =(Double.valueOf((downloadedSize * 1.0/ fileSize *100))).intValue();if(progress ==100){downloadBt.setClickable(true);progressMessage.setText("下载完成!");}else{progressMessage.setText("当前进度:"+ progress +"%");}downloadProgressBar.setProgress(progress);}};/*** @author ideasandroid* 主下载线程*/public class downloadTask extends Thread{ private int blockSize, downloadSizeMore;private int threadNum =5;String urlStr, threadNo, fileName;public downloadTask(String urlStr, int threadNum, String fileName){this.urlStr= urlStr;this.threadNum= threadNum;this.fileName= fileName;}@Overridepublic void run(){FileDownloadThread[] fds =new FileDownloadThread[threadNum];try{URL url =new URL(urlStr);URLConnection conn =url.openConnection();//获取下载文件的总大小fileSize =conn.getContentLength();//计算每个线程要下载的数据量blockSize =fileSize /threadNum;// 解决整除后百分比计算误差downloadSizeMore =(fileSize %threadNum);File file =new File(fileName);for(int i =0;i <threadNum;i++){//启动线程,分别下载自己需要下载的部分FileDownloadThread fdt = new FileDownloadThread(url, file,i *blockSize,(i +1)* blockSize -1);fdt.setName("Thread"+ i);fdt.start();fds[i]= fdt;}boolean finished =false;while(!finished){// 先把整除的余数搞定downloadedSize =downloadSizeMore;finished =true;for(int i =0; i <fds.length; i++){downloadedSize += fds[i].getDownloadSize();if(!fds[i].isFinished()){finished =false;}}//通知handler去更新视图组件handler.sendEmptyMessage(0);//休息1秒后再读取下载进度sleep(1000);}}catch(Exception e){}}}}。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Android 多线程、断点续传下载技术1.为什么使用该技术?答:(1)之所以采用多线程下载是因为考虑到手机,及移动设备的cup处理能力,让下载任务多抢占cup资源,从而加快了下载的速度,提高了用户体验(2)断点续传技术,就是在下载过程中如果网络出现问题,导致文件没有下载完,那么下次下载时,接着上次终端位置继续下载,从而减轻了服务器的负担。
2.下面我们就开始建一个多线程下载的项目,来体验多线程下载的优势,项目的结构如下2.1设计UImain.xml代码如下:<?xml version="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="/apk/res /android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><TextViewandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:text="@string/path"/><EditTextandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:text="/kcn/pc/K anbox_10012.exe"android:id="@+id/path"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/button"android:id="@+id/button"/><ProgressBarandroid:layout_width="fill_parent"android:layout_height="20px"style="?android:attr/progressBarStyleHorizontal"android:id="@+id/downloadbar"/><TextViewandroid:layout_width="fill_parent"android:layout_height="wrap_content"android:gravity="center"android:id="@+id/result"/></LinearLayout>其中引用的string.xml如下:<?xml version="1.0"encoding="utf-8"?><resources><string name="hello">Hello World, SmartDownload!</string><string name="app_name">SMART多线程断点下载器</string><string name="path">下载路径</string><string name="button">下载</string><string name="success">下载完成</string><string name="error">下载失败</string><string name="sdcarderror">SDCard不存在或者写保护</string></resources>3.数据库阶段:3.1编写数据库工具类DBOpenHelerpackage com.smart.db;import android.content.Context;import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;public class DBOpenHelper extends SQLiteOpenHelper {private static final String DBNAME = "smart.db";private static final int VERSION = 1;public DBOpenHelper(Context context) {super(context, DBNAME, null, VERSION);}@Overridepublic voiddb.execSQL("CREATE TABLE IF NOT EXISTS SmartFileDownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS SmartFileDownlog");onCreate(db);}}3.2对各个线程的下载记录进行数据库的操作,编写Fileservice 类代码如下package com.smart.db;import java.util.HashMap;import java.util.Map;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase; /*** 业务bean**/public class FileService {private DBOpenHelper openHelper;public FileService(Context context) { openHelper = new DBOpenHelper(context);}/*** 获取每条线程已经下载的文件长度* @param path* @return*/public Map<Integer, Integer> getData(String path){openHelper.getReadableDatabase();Cursor cursor = db.rawQuery("select threadid, downlength from SmartFileDownlog where downpath=?", new String[]{path});Map<Integer, Integer> data = newHashMap<Integer, Integer>();while(cursor.moveToNext()){data.put(cursor.getInt(0),cursor.getInt(1));}cursor.close();db.close();return data;}/*** 保存每条线程已经下载的文件长度* @param path* @param map*/public void save(String path, Map<Integer, Integer> map){//int threadid, int positionopenHelper.getWritableDatabase();db.beginTransaction();try{for(Map.Entry<Integer, Integer> entry : map.entrySet()){db.execSQL("insert intoSmartFileDownlog(downpath, threadid, downlength) values(?,?,?)",new Object[]{path, entry.getKey(), entry.getValue()});}db.setTransactionSuccessful();}finally{db.endTransaction();}db.close();}/*** 实时更新每条线程已经下载的文件长度* @param path* @param map*/public void update(String path, Map<Integer, Integer> map){SQLiteDatabase db =openHelper.getWritableDatabase();db.beginTransaction();try{for(Map.Entry<Integer, Integer> entry : map.entrySet()){db.execSQL("update SmartFileDownlog set downlength=? where downpath=? and threadid=?",new Object[]{entry.getValue(), path, entry.getKey()});}db.setTransactionSuccessful();}finally{db.endTransaction();}db.close();}/*** 当文件下载完成后,删除对应的下载记录* @param path*/public void delete(String path){SQLiteDatabase db =openHelper.getWritableDatabase();db.execSQL("delete from SmartFileDownlog where downpath=?", new Object[]{path});db.close();}}4.实现文件下载阶段4.1建立SmartFileDownloader类用来实现文件的下载功能代码如下package com.smart.impl;import java.io.File;import java.io.RandomAccessFile;import .HttpURLConnection;import .URL;import java.util.LinkedHashMap;import java.util.Map;import java.util.UUID;import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher;import java.util.regex.Pattern;import android.content.Context;import android.util.Log;import com.smart.db.FileService;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<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>();/* 每条线程下载的长度 */private int block;/* 下载路径 */private String downloadUrl;/*** 获取线程数*/public int getThreadSize() {return threads.length;}/*** 获取文件大小* @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) { this.data.put(threadId, pos);}/*** 保存记录文件*/protected synchronized void saveLogFile() { this.fileService.update(this.downloadUrl, this.data);}* 构建文件下载器* @param downloadUrl 下载路径* @param fileSaveDir 文件保存目录* @param threadNum 下载线程数*/public SmartFileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) {try {this.context = context;this.downloadUrl = downloadUrl;fileService = newFileService(this.context);URL url = new URL(this.downloadUrl);if(!fileSaveDir.exists())fileSaveDir.mkdirs();this.threads = newSmartDownloadThread[threadNum];HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5*1000);conn.setRequestMethod("GET");conn.setRequestProperty("Accept","image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash,application/xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-xbap,application/x-ms-application,application/vnd.ms-excel,application/vnd.ms-powerpoint, application/msword, */*");conn.setRequestProperty("Accept-Language", "zh-CN");conn.setRequestProperty("Referer", downloadUrl);conn.setRequestProperty("Charset","UTF-8");conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR2.0.50727; .NET CLR3.0.04506.30; .NET CLR3.0.4506.2152; .NET CLR 3.5.30729)");conn.setRequestProperty("Connection", "Keep-Alive");conn.connect();printResponseHeader(conn);if (conn.getResponseCode()==200) {this.fileSize =conn.getContentLength();//根据响应获取文件大小if (this.fileSize <= 0) throw new RuntimeException("Unkown file size ");String filename = getFileName(conn);this.saveFile = new File(fileSaveDir, filename);/* 保存文件 */Map<Integer, Integer> logdata = fileService.getData(downloadUrl);if(logdata.size()>0){for(Map.Entry<Integer, Integer> entry : logdata.entrySet())data.put(entry.getKey(),entry.getValue());}this.block = (this.fileSize %this.threads.length)==0? this.fileSize /this.threads.length : this.fileSize /this.threads.length + 1;if(this.data.size()==this.threads.length){for(int i = 0; i < this.threads.length; i++) {this.downloadSize +=this.data.get(i+1);}print("已经下载的长度"+this.downloadSize);}}else{throw new RuntimeException("server no response ");}} catch (Exception e) {print(e.toString());throw new RuntimeException("don't connection this url");}}/*** 获取文件名*/private String getFileName(HttpURLConnection conn) {String filename =this.downloadUrl.substring(stI ndexOf('/') + 1);if(filename==null ||"".equals(filename.trim())){//如果获取不到文件名称for (int i = 0;; i++) {String mine = conn.getHeaderField(i);if (mine == null) break;if("content-disposition".equals(conn.getHeader FieldKey(i).toLowerCase())){Matcher m =pile(".*filename=(.*)").matcher(mine.t oLowerCase());if(m.find()) return m.group(1);}}filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名}return filename;}/*** 开始下载文件* @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null* @return已下载文件大小* @throws Exception*/public intdownload(SmartDownloadProgressListener listener) throws Exception{try {RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw");if(this.fileSize>0)randOut.setLength(this.fileSize);randOut.close();URL url = new URL(this.downloadUrl);if(this.data.size() !=this.threads.length){this.data.clear();//清除数据for (int i = 0; i < this.threads.length; i++) {this.data.put(i+1, 0);}}for(int i = 0; i < this.threads.length; i++) {int downLength = this.data.get(i+1);if(downLength < this.block &&this.downloadSize<this.fileSize){ //该线程未完成下载时,继续下载this.threads[i] = new SmartDownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);this.threads[i].setPriority(7);this.threads[i].start();}else{this.threads[i] = null;}}this.fileService.save(this.downloadUrl, this.data);boolean notFinish = true;//下载未完成while (notFinish) {// 循环判断是否下载完毕Thread.sleep(900);notFinish = false;//假定下载完成for (int i = 0; i < this.threads.length; i++){if (this.threads[i] != null&& !this.threads[i].isFinish()) {notFinish = true;//下载没有完成if(this.threads[i].getDownLength() == -1){//如果下载失败,再重新下载this.threads[i] = new SmartDownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1);this.threads[i].setPriority(7);this.threads[i].start();}}}if(listener!=null)listener.onDownloadSize(this.downloadSize);}fileService.delete(this.downloadUrl);} catch (Exception e) {print(e.toString());throw new Exception("file download fail");}return this.downloadSize;}/*** 获取Http响应头字段* @param http* @return*/public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) { Map<String, String> header = new LinkedHashMap<String, String>();for (int i = 0;; i++) {String mine = http.getHeaderField(i);if (mine == null) break;header.put(http.getHeaderFieldKey(i), mine);}return header;}/*** 打印Http头字段* @param http*/public static voidprintResponseHeader(HttpURLConnection http){ Map<String, String> header = getHttpResponseHeader(http);for(Map.Entry<String, String> entry : header.entrySet()){String key = entry.getKey()!=null ? entry.getKey()+ ":" : "";print(key+ entry.getValue());}}//打印日志private static void print(String msg){ Log.i(TAG, msg);}}4.2下载过程中的线程实现建立SmartDownloadThread类具体代码如下:package com.smart.impl;import java.io.File;import java.io.InputStream;import java.io.RandomAccessFile;import .HttpURLConnection;import .URL;import android.util.Log;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) {this.downUrl = downUrl;this.saveFile = saveFile;this.block = block;this.downloader = downloader;this.threadId = threadId;this.downLength = downLength;}@Overridepublic void run() {if(downLength < block){//未下载完成try {HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();http.setConnectTimeout(5 * 1000);http.setRequestMethod("GET");http.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash,application/xaml+xml,application/vnd.ms-xpsdocument,application/x-ms-xbap,application/x-ms-application,application/vnd.ms-excel,application/vnd.ms-powerpoint, application/msword, */*");http.setRequestProperty("Accept-Language","zh-CN");http.setRequestProperty("Referer", downUrl.toString());http.setRequestProperty("Charset", "UTF-8");int startPos = block * (threadId - 1) + downLength;//开始位置int endPos = block * threadId -1;//结束位置http.setRequestProperty("Range","bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR2.0.50727; .NET CLR3.0.04506.30; .NET CLR3.0.4506.2152; .NET CLR 3.5.30729)");http.setRequestProperty("Connection", "Keep-Alive");InputStream inStream =http.getInputStream();byte[] buffer = new byte[1024];int offset = 0;print("Thread "+ this.threadId+ " start download from position "+ startPos);RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");threadfile.seek(startPos);while((offset = inStream.read(buffer, 0, 1024)) != -1) {threadfile.write(buffer, 0, offset);downLength += offset;downloader.update(this.threadId, downLength);downloader.saveLogFile();downloader.append(offset);}threadfile.close();inStream.close();print("Thread " + this.threadId + " download finish");this.finish = true;} catch (Exception e) {this.downLength = -1;print("Thread "+ this.threadId+ ":"+ 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 com.smart.impl;public interface SmartDownloadProgressListener { public void onDownloadSize(int size);}5.建立activity 实现下载的触发,和界面的实时更新具体代码如下:package com.smart.activoty.download;import java.io.File;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.widget.Button;import android.widget.EditText;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;importcom.smart.impl.SmartDownloadProgressListener; import com.smart.impl.SmartFileDownloader;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 (msg.what) {case 1:int size = msg.getData().getInt("size");downloadbar.setProgress(size);float result =(float)downloadbar.getProgress()/(float)downloadbar.getMax();int p = (int)(result*100);resultView.setText(p+"%");if(downloadbar.getProgress()==downloadbar.getM ax())Toast.makeText(SmartDownloadActivity.this, R.string.success, 1).show();break;case -1:Toast.makeText(SmartDownloadActivity.this, R.string.error, 1).show();break;}}};@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(yout.main);Button button =(Button)this.findViewById(R.id.button);downloadbar =(ProgressBar)this.findViewById(R.id.downloadbar); pathText =(EditText)this.findViewById(R.id.path);resultView =(TextView)this.findViewById(R.id.result);button.setOnClickListener(newView.OnClickListener() {@Overridepublic void onClick(View v) {String path =pathText.getText().toString();if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){File dir =Environment.getExternalStorageDirectory();//文件保存目录download(path, dir);}else{Toast.makeText(SmartDownloadActivity.this,R.string.sdcarderror, 1).show();}}});}//对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI 线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错private void download(final String path, final File dir){new Thread(new Runnable() {@Overridepublic void run() {try {SmartFileDownloader loader = new SmartFileDownloader(SmartDownloadActivity.this, path, dir, 3);int length = loader.getFileSize();//获取文件的长度downloadbar.setMax(length);loader.download(new SmartDownloadProgressListener(){@Overridepublic void onDownloadSize(int size) {//可以实时得到文件下载的长度Message msg = new Message();msg.what = 1;msg.getData().putInt("size", size);handler.sendMessage(msg);}});} catch (Exception e) {Message msg = new Message();//信息提示msg.what = -1;msg.getData().putString("error", "下载失败");//如果下载错误,显示提示失败!handler.sendMessage(msg);}}}).start();//开始}}6.到此为止运行会报错,因为没有向sdcard的协数据的权限,访问Internet的权限这需要在AndroidManifest.xml配置具体代码如下:<?xml version="1.0"encoding="utf-8"?><manifestxmlns:android="/apk/res /android"package="com.smart.activoty.download"android:versionCode="1"android:versionName="1.0"><application android:icon="@drawable/icon" android:label="@string/app_name"><activityandroid:name=".SmartDownloadActivity"android:label="@string/app_name"> <intent-filter><actionandroid:name="android.intent.action.MAIN"/><categoryandroid:name="UNCHER"/> </intent-filter></activity></application><uses-sdk android:minSdkVersion="8"/><!-- 访问internet权限 --><uses-permissionandroid:name="android.permission.INTERNET"/><!-- 在SDCard中创建与删除文件权限 --><uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FI LESYSTEMS"/><!-- 往SDCard写入数据权限 --><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_S TORAGE"/></manifest>。