本应用实现的是输入文件的网络的地址,点击按钮开始下载,下载过程中有进度条和后面的文本提示进度,
下载过程中按钮不可点击,防止重复的下载,下载完毕后会进行Toast的提示显示,
并且回复按钮的可点击性,进度条也会清空,当然如果下载中途结束应用进程就会进行进度的保存,
下次下载同样的文件时就会从进度记录进行下载,节省流量和时间
应用需要的应用权限:
访问网络权限
<uses-permission android:name="android.permission.INTERNET"/>
外部储存的写入权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
布局文件代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <EditText android:id="@+id/et_path" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="请输入下载文件的地址" android:singleLine="true" android:text="http://172.22.64.193:8080/test.exe" > </EditText> <TableLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <TableRow android:layout_width="match_parent" android:layout_height="wrap_content" > <ProgressBar android:id="@+id/pb" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="8" /> <TextView android:id="@+id/tv_progressNumber" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="0%" /> </TableRow> </TableLayout> <Button android:id="@+id/bt_startDownlode" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="startDownlode" android:text="开始下载" /> </LinearLayout>
核心代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350package com.examp.mutildownloader; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; /** * Android下的多线程下载,断点下载 * * @author MartinDong * */ public class MainActivity extends Activity { // sd卡的路径 private static final File sd = Environment.getExternalStorageDirectory(); private static final int DOWNLODE_ERROR = 1; private static final int DOWNLODE_SUCCESS = 2; private static final int DOWNLODE_DELETE_TEMP = 3; public static final int PROGRESS_NUMBER_CHANGE = 4; // 定义线程个数 private static int threadCount = 3; // 定义当前存货的线程个数 private static int runningThread = 3; // 组件的获取 private EditText et_path; private ProgressBar pb; private Button bt_startDownlode; private TextView tv_progressNumber; // 定义存储的文件名称 private String filename; // 设置进度条的进度 // 进度条的数据 public int progressTemp = 0; // 定义消息处理 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case DOWNLODE_ERROR: Toast.makeText(getApplicationContext(), "下载失败....", Toast.LENGTH_SHORT).show(); break; case DOWNLODE_SUCCESS: // 设置按钮可用 bt_startDownlode.setEnabled(true); // 清空进度 progressTemp = 0; pb.setProgress(progressTemp); // 文本清0 tv_progressNumber.setText("0%"); Toast.makeText(getApplicationContext(), "下载成功....", Toast.LENGTH_SHORT).show(); break; case DOWNLODE_DELETE_TEMP: Toast.makeText(getApplicationContext(), "删除进度文件....", Toast.LENGTH_SHORT).show(); break; case PROGRESS_NUMBER_CHANGE: tv_progressNumber.setText(pb.getProgress() * 100 / pb.getMax() + "%"); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_path = (EditText) findViewById(R.id.et_path); // 获取组件 pb = (ProgressBar) findViewById(R.id.pb); bt_startDownlode = (Button) findViewById(R.id.bt_startDownlode); tv_progressNumber = (TextView) findViewById(R.id.tv_progressNumber); } public void startDownlode(View view) { // 设置按钮不可点击//防止重复提交 bt_startDownlode.setEnabled(false); // 从控件中获取下载的路径 final String path = et_path.getText().toString().trim(); // 获取输入地址的最后一个"/"出现的位置 int lastIndex = path.lastIndexOf("/"); // 获取文件名称及格式 filename = path.substring(lastIndex + 1, path.length()); System.out.println("文件名称为===============" + filename); // 判断路径是否有效 if (TextUtils.isEmpty(path)) { Toast.makeText(this, "请输入有效的下载路径......", Toast.LENGTH_SHORT).show(); return; } // 为了避免与主线程冲突,开启子线程完成,避免anr问题 new Thread() { public void run() { try { // 1,连接到服务器,获取一个文件,获取文件的大小跟服务器的文件一样的临时文件 // String path = "http://172.22.64.193:8080/tomcat.css"; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); // 设置超时 conn.setConnectTimeout(5000); // 设置请求方式 conn.setRequestMethod("GET"); // 获取服务器的返回码 int code = conn.getResponseCode(); // 判断返回码 if (code == 200) { // 获取返回的长度 int length = conn.getContentLength(); // 设置进度条的最大值 pb.setMax(length); System.out.println("文件总长度:" + length); // 在客户端创建出一个跟服务器大小一致的临时文件 RandomAccessFile raf = new RandomAccessFile(sd + "/" + filename, "rwd"); // 指定临时文件的大小 raf.setLength(length); // 释放资源 raf.close(); // 平均每一个线程的文件大小 int blockSize = length / threadCount; // 设置活跃的线程 runningThread = threadCount; for (int threadId = 1; threadId <= threadCount; threadId++) { // 线程开始的下载位置 int startIndex = (threadId - 1) * blockSize; // 线程的结束位置 int endIndex = threadId * blockSize - 1; // 判断是否是最后一个线程 if (threadId == threadCount) { // 设置结束的位置为到文件的最后 endIndex = length; } System.out.println("线程:" + threadId + "下载:开始位置>>>>>>>>" + startIndex + "结束>>>>>>>>>>" + endIndex); new DownlodeThread(path, threadId, startIndex, endIndex).start(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }; }.start(); } /** * 下载文件的子线程类,每一个线程下载对应位置文件数据 * * @author MartinDong * */ public class DownlodeThread extends Thread { private String path; private int threadId; private int startIndex; private int endIndex; /** * * @param path * 文件的下载路径 * @param threadId * 线程id * @param startIndex * 线程开始的位置 * @param endIndex * 线程结束的位置 */ public DownlodeThread(String path, int threadId, int startIndex, int endIndex) { this.path = path; this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run() { try { // 检查是否存在下载历史的文件 File tempFile = new File(sd + "/" + threadId + ".txt");// =========================断点记录操作=============================== if (tempFile.exists() && tempFile.length() > 0) { // 文件输入流 FileInputStream fis = new FileInputStream(tempFile); // 中间变量,缓存的作用 byte[] tempBuffer = new byte[1024]; // 获取进度文件的数据大小 int length = fis.read(tempBuffer); // 获取进度文件的数据 String historyData = new String(tempBuffer, 0, length); // 将进度数据装换为整型 int historyDataInt = Integer.parseInt(historyData); // 如果是断点传送,初始化进度条的进度 // 获取每个线程已经下载了的进度 int temp = historyDataInt - startIndex; // 为进度重新赋值 progressTemp += temp; // 修改真正的下载位置 startIndex = historyDataInt; fis.close(); }// =========================断点记录操作=============================== // 将地址转换为URL URL url = new URL(path); // 获取http连接 HttpURLConnection conn = (HttpURLConnection) url .openConnection(); // 设置连接的请求方式 conn.setRequestMethod("GET"); // 重要:请求服务器下载部分的文件,指定文件的位置 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); System.out .println("线程:" + threadId + "真实开始的下载进度:" + startIndex); // 设置超时时间 conn.setReadTimeout(5000); // 得到服务器的状态码,200表示请求的全部资源得到响应=== ok,206请求的部分资源得到响应=== ok int code = conn.getResponseCode(); System.out.println("code:" + code); if (code == 206) { // 返回的是指定位置的文件流 InputStream is = conn.getInputStream(); // 创建一个临时的文件 RandomAccessFile raf = new RandomAccessFile(sd + "/" + filename, "rwd"); // 移动指针,到指定的文件位置, raf.seek(startIndex); // 创建中间缓冲字节数组 byte[] buffer = new byte[1024]; // 读取文件的大小 int length = 0; // 定义已经下载的数据长度,用作断点下载的记录=========================断点记录操作=============================== int downlodeTotal = 0; // 循环写入 while ((length = is.read(buffer)) != -1) { // 定义一个记录线程的记录文件=========================断点记录操作=============================== RandomAccessFile historyFile = new RandomAccessFile(sd + "/" + threadId + ".txt", "rwd"); // 向文件中写入数据 raf.write(buffer, 0, length); // 记录已经下载的文件长度 downlodeTotal += length; // 将已经下载的文件长度和开始的读取位置相加,得到已经读取的文件位置 historyFile.write((downlodeTotal + startIndex + "") .getBytes()); historyFile.close();// =========================断点记录操作=============================== // 保持同步的更新 synchronized (MainActivity.this) { // 将每个线程的读取文件的大小累加到下载进度上 progressTemp += length; // 更新界面上的Progress的进度条的长度 pb.setProgress(progressTemp); // 特殊情况ProgressBar ProgressDialog // 是可以直接在子线程中跟新ui的,内部代码有过特殊的处理 // 使用Message.obtain();减少Message对象的创建 Message msg = Message.obtain(); msg.what = PROGRESS_NUMBER_CHANGE; handler.sendMessage(msg); } } is.close(); raf.close(); System.out.println("线程:" + threadId + "下载完毕............"); } else { System.out.println("线程:" + threadId + "下载失败请重新下载............"); // 向消息处理器发送信息 Message msg = new Message(); msg.what = DOWNLODE_ERROR; handler.sendMessage(msg); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { synchronized (MainActivity.this) { // 进行线程数量的变化操作 runningThread--; // 如果当前存活的线程为0,执行进度文件统一销毁的操作 if (runningThread == 0) { // 如果下载完毕,清除进度文件 for (int threadIndex = 1; threadIndex <= threadCount; threadIndex++) { // 这里创建的是与线程文件对应的文件,文件名可以自定义,方便起见是采用的是线程的ID表示 File temp = new File(sd + "/" + threadIndex + ".txt"); // 执行文件删除的操作 temp.delete(); } System.out.println("下载完毕,删除进度文件............."); // 向消息处理器发送信息 // 向消息处理器发送信息 Message msg = new Message(); msg.what = DOWNLODE_SUCCESS; handler.sendMessage(msg); } } } } } }
Demo下载地址:
http://download.csdn.net/detail/u011936142/7428611
最后
以上就是忧郁路人最近收集整理的关于Android多线程文件下载器的全部内容,更多相关Android多线程文件下载器内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复