Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 69688f4

Browse files
committed
Merge pull request giantray#15 from troyliu0105/master
翻译完成(Download_a_file_with_Android_and_showing_the_progress_in_a_ProgressDialog.md)
2 parents 9135324 + faae46d commit 69688f4

File tree

2 files changed

+377
-1
lines changed

2 files changed

+377
-1
lines changed

‎README.md‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ stackoverflow-Java-top-qa
5353
5454
* [如何测试 private 方法,变量或者内部类](https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/How_to_test_a_class_that_has_private_methods,_fields_or_inner_classes.md)
5555

56+
> Android
57+
58+
* [在Android里面下载文件,并在ProgressDialog显示进度](https://github.com/troyliu0105/stackoverflow-java-top-qa/blob/master/contents/Download_a_file_with_Android_and_showing_the_progress_in_a_ProgressDialog.md)
59+
5660
### 待翻译问题链接(还剩x问题)
5761
- [Why is subtracting these two times (in 1927) giving a strange result?](http://stackoverflow.com/questions/6841333/why-is-subtracting-these-two-times-in-1927-giving-a-strange-result)
5862
- [Proper use cases for Android UserManager.isUserAGoat()?](http://stackoverflow.com/questions/13375357/proper-use-cases-for-android-usermanager-isuseragoat)
@@ -90,7 +94,6 @@ stackoverflow-Java-top-qa
9094
- [How can I Initialize a static Map?](http://stackoverflow.com/questions/507602/how-can-i-initialize-a-static-map)
9195
- [How do I discover memory usage of my application in Android?](http://stackoverflow.com/questions/2298208/how-do-i-discover-memory-usage-of-my-application-in-android)
9296
- [How can I generate an MD5 hash?](http://stackoverflow.com/questions/415953/how-can-i-generate-an-md5-hash)
93-
- [Download a file with Android, and showing the progress in a ProgressDialog](http://stackoverflow.com/questions/3028306/download-a-file-with-android-and-showing-the-progress-in-a-progressdialog)
9497
- [Uncatchable ChuckNorrisException](http://stackoverflow.com/questions/13883166/uncatchable-chucknorrisexception)
9598
- [Can I add jars to maven 2 build classpath without installing them?](http://stackoverflow.com/questions/364114/can-i-add-jars-to-maven-2-build-classpath-without-installing-them)
9699
- [Update Eclipse with Android development tools v. 23](http://stackoverflow.com/questions/24437564/update-eclipse-with-android-development-tools-v-23)
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
## 在Android里面下载文件,并在ProgressDialog显示进度
2+
3+
### 问题
4+
尝试写一个可以获得更新的应用程序。 为了达到这个效果,我写了一个可以下载文件并且在一个`ProgressDialog`里面显示进度的简单方法。我知道怎么使用`ProgressDialog`,但是我不太确定怎么显示当前进度和下载文件。
5+
6+
### 回答
7+
8+
有很多方式去下载文件。我给出一些最常用的方法;由你来选择选择哪一个最适合你的应用。
9+
10+
#### 1. 使用AsyncTask,并且在一个dialog里面显示进度
11+
这种方法允许你执行一些后台任务,并且同时更新UI(在这里,我们是更新进度条progress bar)。
12+
13+
首先是实例代码
14+
```java
15+
// 定义一个dialog为Activity的成员变量
16+
ProgressDialog mProgressDialog;
17+
18+
// 在OnCreate()方法里面初始化
19+
mProgressDialog = new ProgressDialog(YourActivity.this);
20+
mProgressDialog.setMessage("A message");
21+
mProgressDialog.setIndeterminate(true);
22+
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
23+
mProgressDialog.setCancelable(true);
24+
25+
// 执行下载器
26+
final DownloadTask downloadTask = new DownloadTask(YourActivity.this);
27+
downloadTask.execute("你要下载文件的Url");
28+
29+
mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
30+
@Override
31+
public void onCancel(DialogInterface dialog) {
32+
downloadTask.cancel(true);
33+
}
34+
});
35+
```
36+
37+
`AsyncTask`看起来像这样
38+
```java
39+
// 一般我们把AsyncTask的子类定义在Activity的内部
40+
// 通过这种方式,我们就可以轻松地在这里更改UI线程
41+
private class DownloadTask extends AsyncTask<String, Integer, String> {
42+
43+
private Context context;
44+
private PowerManager.WakeLock mWakeLock;
45+
46+
public DownloadTask(Context context) {
47+
this.context = context;
48+
}
49+
50+
@Override
51+
protected String doInBackground(String... sUrl) {
52+
InputStream input = null;
53+
OutputStream output = null;
54+
HttpURLConnection connection = null;
55+
try {
56+
URL url = new URL(sUrl[0]);
57+
connection = (HttpURLConnection) url.openConnection();
58+
connection.connect();
59+
60+
// 避免因为接收到非HTTP 200 OK状态,而导致只或者错误代码,而不是要下载的文件
61+
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
62+
return "Server returned HTTP " + connection.getResponseCode()
63+
+ " " + connection.getResponseMessage();
64+
}
65+
66+
// 这对显示下载百分比有帮助
67+
// 当服务器没有返回文件的大小时,数字可能为-1
68+
int fileLength = connection.getContentLength();
69+
70+
// 下载文件
71+
input = connection.getInputStream();
72+
output = new FileOutputStream("/sdcard/file_name.extension");
73+
74+
byte data[] = new byte[4096];
75+
long total = 0;
76+
int count;
77+
while ((count = input.read(data)) != -1) {
78+
// 允许用返回键取消下载
79+
if (isCancelled()) {
80+
input.close();
81+
return null;
82+
}
83+
total += count;
84+
// 更新下载进度
85+
if (fileLength > 0) // 只有当 fileLength>0 的时候才会调用
86+
publishProgress((int) (total * 100 / fileLength));
87+
output.write(data, 0, count);
88+
}
89+
} catch (Exception e) {
90+
return e.toString();
91+
} finally {
92+
try {
93+
if (output != null)
94+
output.close();
95+
if (input != null)
96+
input.close();
97+
} catch (IOException ignored) {
98+
}
99+
100+
if (connection != null)
101+
connection.disconnect();
102+
}
103+
return null;
104+
}
105+
```
106+
107+
上面的`doInBackground`方法总是在后台线程中运行。你不能在这里做任何UI线程相关的任务。另一方面,`onProgressUpdate`和`onPreExecute`是在UI线程里面运行的,所以你可以在这里更改进度条。
108+
109+
```java
110+
@Override
111+
protected void onPreExecute() {
112+
super.onPreExecute();
113+
// 取得CPU锁,避免因为用户在下载过程中按了电源键而导致的失效
114+
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
115+
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
116+
getClass().getName());
117+
mWakeLock.acquire();
118+
mProgressDialog.show();
119+
}
120+
121+
@Override
122+
protected void onProgressUpdate(Integer... progress) {
123+
super.onProgressUpdate(progress);
124+
// 如果到了这里,文件长度是确定的,设置indeterminate为false
125+
mProgressDialog.setIndeterminate(false);
126+
mProgressDialog.setMax(100);
127+
mProgressDialog.setProgress(progress[0]);
128+
}
129+
130+
@Override
131+
protected void onPostExecute(String result) {
132+
mWakeLock.release();
133+
mProgressDialog.dismiss();
134+
if (result != null)
135+
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
136+
else
137+
Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
138+
}
139+
```
140+
141+
为了可正常运行,你还要取得WAKE_LOCK权限
142+
```
143+
<uses-permission android:name="android.permission.WAKE_LOCK" />
144+
```
145+
146+
#### 2. 从服务器上下载文件
147+
148+
这里有个最大的问题:*我怎么从service来更新我的activity?*
149+
在下一个例子当中我们会使用两个你可能不熟悉的类:`ResultReceiver`和`IntentService`。`ResultReceiver`是一个可以允许我们用Service来更新线程的类;`IntentService`是一个可以生成用来处理后台任务的线程的`Service`子类(你需要知道,`Service`实际上是和你的应用运行在同一个线程的;当你继承了`Service`之后,你必须手动生成一个新的线程来处理费时操作)。
150+
151+
一个提供下载功能的`Service`看起来像这样:
152+
153+
```java
154+
public class DownloadService extends IntentService {
155+
public static final int UPDATE_PROGRESS = 8344;
156+
public DownloadService() {
157+
super("DownloadService");
158+
}
159+
@Override
160+
protected void onHandleIntent(Intent intent) {
161+
String urlToDownload = intent.getStringExtra("url");
162+
ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
163+
try {
164+
URL url = new URL(urlToDownload);
165+
URLConnection connection = url.openConnection();
166+
connection.connect();
167+
// 这对你在进度条上面显示百分比很有用
168+
int fileLength = connection.getContentLength();
169+
170+
// download the file
171+
InputStream input = new BufferedInputStream(connection.getInputStream());
172+
OutputStream output = new FileOutputStream("/sdcard/BarcodeScanner-debug.apk");
173+
174+
byte data[] = new byte[1024];
175+
long total = 0;
176+
int count;
177+
while ((count = input.read(data)) != -1) {
178+
total += count;
179+
// 更新进度条....
180+
Bundle resultData = new Bundle();
181+
resultData.putInt("progress" ,(int) (total * 100 / fileLength));
182+
receiver.send(UPDATE_PROGRESS, resultData);
183+
output.write(data, 0, count);
184+
}
185+
186+
output.flush();
187+
output.close();
188+
input.close();
189+
} catch (IOException e) {
190+
e.printStackTrace();
191+
}
192+
193+
Bundle resultData = new Bundle();
194+
resultData.putInt("progress" ,100);
195+
receiver.send(UPDATE_PROGRESS, resultData);
196+
}
197+
}
198+
```
199+
200+
把这个`Service`添加到清单文件中:
201+
```
202+
<service android:name=".DownloadService"/>
203+
```
204+
205+
activity里面的代码:
206+
207+
```java
208+
// 像第一个例子里面一样初始化ProgressBar
209+
210+
// 在这里启动下载
211+
mProgressDialog.show();
212+
Intent intent = new Intent(this, DownloadService.class);
213+
intent.putExtra("url", "url of the file to download");
214+
intent.putExtra("receiver", new DownloadReceiver(new Handler()));
215+
startService(intent);
216+
```
217+
218+
然后像这样来使用`ResultReceiver`:
219+
220+
```java
221+
private class DownloadReceiver extends ResultReceiver{
222+
public DownloadReceiver(Handler handler) {
223+
super(handler);
224+
}
225+
226+
@Override
227+
protected void onReceiveResult(int resultCode, Bundle resultData) {
228+
super.onReceiveResult(resultCode, resultData);
229+
if (resultCode == DownloadService.UPDATE_PROGRESS) {
230+
int progress = resultData.getInt("progress");
231+
mProgressDialog.setProgress(progress);
232+
if (progress == 100) {
233+
mProgressDialog.dismiss();
234+
}
235+
}
236+
}
237+
}
238+
```
239+
240+
##### 2.1 使用Groundy
241+
[Groundy](http://casidiablo.github.com/groundy)是一个可以帮助你在后台服务中运行代码片段的库,它是基于`ResultReceiver`这一概念。但是这个库现在已经被标记为**过时**了(deprecated)。下面是**完整**代码的样子。
242+
243+
你要展示dialog的Activity:
244+
245+
```java
246+
public class MainActivity extends Activity {
247+
248+
private ProgressDialog mProgressDialog;
249+
250+
@Override
251+
public void onCreate(Bundle savedInstanceState) {
252+
super.onCreate(savedInstanceState);
253+
setContentView(R.layout.main);
254+
255+
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
256+
public void onClick(View view) {
257+
String url = ((EditText) findViewById(R.id.edit_url)).getText().toString().trim();
258+
Bundle extras = new Bundler().add(DownloadTask.PARAM_URL, url).build();
259+
Groundy.create(DownloadExample.this, DownloadTask.class)
260+
.receiver(mReceiver)
261+
.params(extras)
262+
.queue();
263+
264+
mProgressDialog = new ProgressDialog(MainActivity.this);
265+
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
266+
mProgressDialog.setCancelable(false);
267+
mProgressDialog.show();
268+
}
269+
});
270+
}
271+
272+
private ResultReceiver mReceiver = new ResultReceiver(new Handler()) {
273+
@Override
274+
protected void onReceiveResult(int resultCode, Bundle resultData) {
275+
super.onReceiveResult(resultCode, resultData);
276+
switch (resultCode) {
277+
case Groundy.STATUS_PROGRESS:
278+
mProgressDialog.setProgress(resultData.getInt(Groundy.KEY_PROGRESS));
279+
break;
280+
case Groundy.STATUS_FINISHED:
281+
Toast.makeText(DownloadExample.this, R.string.file_downloaded, Toast.LENGTH_LONG);
282+
mProgressDialog.dismiss();
283+
break;
284+
case Groundy.STATUS_ERROR:
285+
Toast.makeText(DownloadExample.this, resultData.getString(Groundy.KEY_ERROR), Toast.LENGTH_LONG).show();
286+
mProgressDialog.dismiss();
287+
break;
288+
}
289+
}
290+
};
291+
}
292+
```
293+
294+
**Groundy**使用一个`GroundyTask`的实现类来下载文件和显示进度:
295+
296+
```java
297+
public class DownloadTask extends GroundyTask {
298+
public static final String PARAM_URL = "com.groundy.sample.param.url";
299+
300+
@Override
301+
protected boolean doInBackground() {
302+
try {
303+
String url = getParameters().getString(PARAM_URL);
304+
File dest = new File(getContext().getFilesDir(), new File(url).getName());
305+
DownloadUtils.downloadFile(getContext(), url, dest, DownloadUtils.getDownloadListenerForTask(this));
306+
return true;
307+
} catch (Exception pokemon) {
308+
return false;
309+
}
310+
}
311+
}
312+
```
313+
314+
添加这行代码到清单文件中:
315+
```
316+
<service android:name="com.codeslap.groundy.GroundyService"/>
317+
```
318+
319+
这实在是太简单了!只需要从[Github](https://github.com/casidiablo/groundy/downloads)上下载最新的jar文件就可以开始了。但是要记住,Groundy的主要用途是在后台服务中调用外部的REST API,然后更简单地在UI上更新结果。如果你要在你的应用里面做类似的事情,这个库将非常有帮助。
320+
321+
##### 2.2 使用[ion](https://github.com/koush/ion)
322+
323+
#### 3. 使用`DownloadManager`类(只适用于GingerBread及其以上的系统)
324+
325+
这个方法很酷炫,你不需要担心手动下载文件、处理线程和流之类等乱七八糟的东西。GingerBread带来一项新功能:`DownloadManager`。`DownloadManager`允许你轻松地下载文件和把复杂计算的任务委托给系统。
326+
327+
首先,我们来看一下工具方法:
328+
329+
```java
330+
/**
331+
* @param 使用context来检查设备信息和 DownloadManager 的信息
332+
* @return 如果downloadmanager可用则返回 true
333+
*/
334+
public static boolean isDownloadManagerAvailable(Context context) {
335+
336+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
337+
return true;
338+
}
339+
return false;
340+
}
341+
```
342+
343+
方法的名字就已经告诉了我们一切,只有当你确保可以使用`DownloadManager`的时候,你才可以做下面的事情:
344+
345+
```java
346+
String url = "url you want to download";
347+
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
348+
request.setDescription("Some descrition");
349+
request.setTitle("Some title");
350+
// in order for this if to run, you must use the android 3.2 to compile your app
351+
// 为了保证这个if语句会运行,你必须使用android 3.2来编译 (译者注:应该是大于android 3.2的版本)
352+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
353+
request.allowScanningByMediaScanner();
354+
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
355+
}
356+
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "name-of-the-file.ext");
357+
358+
// 获得下载服务和队列文件
359+
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
360+
manager.enqueue(request);
361+
```
362+
363+
#### 最后的一些思考
364+
365+
第一个和第二个方法只是冰山一角。如果你想你的应用更加健壮,你得留意许多事情。这里是一些建议:
366+
367+
+ 你必须检查用户是否有Internet连接。
368+
+ 确保你有正确的权限(`Internet`和`WRITE_EXTERNAL_STORAGE`),如果要检查网络可用性,你还需要`ACCESS_NETWORK_STATE`权限。
369+
+ 确保你要保存下载文件的目录存在,并且有相应的写入权限。
370+
+ 如果下载的文件太大,你可能需要实现一种方法来确保上次的请求失败后,可以接着从来。
371+
+ 如果可以有暂停或者取消下载的选项,用户会很感激你的!
372+
373+
除非你想对下载过程有绝对的控制权,否则我强烈推荐你使用`DownloadManager`。因为他已经处理好了上面的大部分建议。

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /