Android 文件下载
??? 来源:http://1679554191.iteye.com/admin/blogs/1751065
?
??? Android开发中经常会遇到文件的下载,而下载的时间与网络状态和被下载文件的大小等因素有关。本文会对单线程下载和多线程下载做简要说明。无论哪种操作,最终都是基于HTTP(HTTPS)的网络访问。
?
??? 先看看基本的一个操作流程,然后对于每一个操作步骤逐一实现就可以了。
??? 基本流程:
??? 说明:这个流程是相对的,每个人可以根据具体情况而定。:)
?
??? 1、在普通Java项目中下载文件:为了简化问题,我们先不考虑在Android中实现过程,仅仅实现一个下载并保存网络资源的操作。
??? 代码如下:
import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;public class FileDownload {/** * @param args */public static void main(String[] args) {downloadFile("http://image.tianjimedia.com/uploadImages/2012/153/2H4Y79KZF2X3.jpg", "image.jpg");downloadFile("http://www.google.com", "message.txt");}private static void downloadFile(String urlPath, String filePath) {InputStream inputStream = null;ByteArrayOutputStream byteArrayOutputStream = null;byte[] data = null;try {URL url = new URL(urlPath);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(6 * 1000);? ? ? ? ? ? ? ? ? ? ? ? conn.connect();? ? ? ? ? ? ? ? ? ? ? ? if (conn.getResponseCode() == 200) {inputStream = conn.getInputStream();byteArrayOutputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int length = -1;while ((length = inputStream.read(buffer)) != -1) {byteArrayOutputStream.write(buffer, 0, length);}data = byteArrayOutputStream.toByteArray();}} catch (Exception e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e1) {e1.printStackTrace();}}if (byteArrayOutputStream != null) {try {byteArrayOutputStream.close();} catch (IOException e1) {e1.printStackTrace();}}}if (data != null) {File file = new File(filePath);FileOutputStream outputStream = null;try {outputStream = new FileOutputStream(file);outputStream.write(data);} catch (Exception e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}}
???? 说明:
??????? 1、InputStream inputStream = null;和ByteArrayOutputStream byteArrayOutputStream = null;之所以在try-catch外定义,是为了可以在finally代码块中可以访问,并释放该资源。在回收资源时,最好把不同资源放在单独的try-catch块中做资源释放操作,否则可能造成资源泄漏。
InputStream inputStream = null;ByteArrayOutputStream byteArrayOutputStream = null;try {......} catch (Exception e) {e.printStackTrace();} finally {try { if (inputStream != null) { inputStream.close(); } if (byteArrayOutputStream != null) { byteArrayOutputStream.close(); } } catch (Exception e) { e.printStackTrace(); }}
???? 如果采用上述资源释放代码,当inputStream和byteArrayOutputStream都不为null时,如果inputStream.close();发生异常,将导致byteArrayOutputStream.close();不被执行,从而没有释放该资源。
??????? 2、conn.setRequestMethod("GET");和conn.setConnectTimeout(6 * 1000);是设置连接属性。还有其它属性,如果需要的话,可以进行设定。
??????? 3、conn.connect();建立连接。
??????? 4、conn.getInputStream();获得连接后的输入流,从中可以获得数据。
??????? 5、在finally代码块中对申请的资源进行释放。
??? 运行该项目,会在项目的根目录下看到两个文件,image.jpg和message.txt,如图所示:
??? 2、在Android项目中下载文件:
??? a、对UI的设计:
? ? 主Activity中有两个Button,分别启动另外的两个Activity,来实现图片的下载显示和网页内容的获得并显示。布局文件就补贴出来了,会在附件中提供源码。
??? b、主Activity没有什么好说的,下面对另外两个Activity做详细说明:
??? DownloadImageActivity:
package com.anhuioss.download;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import android.app.Activity;import android.graphics.drawable.Drawable;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.text.TextUtils;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.ImageView;public class DownloadImageActivity extends Activity implements OnClickListener {private EditText filePathEditText;private Button downloadButton;private ImageView imageDisplay;private Handler handler = new Handler();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_download_image);filePathEditText = (EditText) findViewById(R.id.adi_image_path);downloadButton = (Button) findViewById(R.id.adi_button_download);imageDisplay = (ImageView) findViewById(R.id.adi_image);downloadButton.setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v == downloadButton) {if (!Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {return;}String path = filePathEditText.getText().toString();if (!TextUtils.isEmpty(path)) {downloadButton.setEnabled(false);imageDisplay.setImageDrawable(null);downloadFile(path, Environment.getExternalStorageDirectory() + File.separator + "image.jpg");}}}private void downloadFile(final String urlPath, final String filePath) {new Thread(new Runnable() {@Overridepublic void run() {InputStream inputStream = null;ByteArrayOutputStream byteArrayOutputStream = null;byte[] data = null;try {URL url = new URL(urlPath);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");conn.setConnectTimeout(6 * 1000);conn.connect();if (conn.getResponseCode() == 200) {inputStream = conn.getInputStream();byteArrayOutputStream = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int length = -1;while ((length = inputStream.read(buffer)) != -1) {byteArrayOutputStream.write(buffer, 0, length);}data = byteArrayOutputStream.toByteArray();}} catch (Exception e) {e.printStackTrace();} finally {if (inputStream != null) {try {inputStream.close();} catch (IOException e1) {e1.printStackTrace();}}if (byteArrayOutputStream != null) {try {byteArrayOutputStream.close();} catch (IOException e1) {e1.printStackTrace();}}}if (data != null) {File file = new File(filePath);FileOutputStream outputStream = null;try {outputStream = new FileOutputStream(file);outputStream.write(data);handler.post(new Runnable() {@Overridepublic void run() {downloadButton.setEnabled(true);Drawable drawable = null;try {drawable = Drawable.createFromPath(filePath);} catch (Error e) {e.printStackTrace();}imageDisplay.setImageDrawable(drawable);}});} catch (Exception e) {e.printStackTrace();} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}}).start();}}
? ? 说明:
??????? 1、private Handler handler = new Handler();handler的作用是在下载线程中完成下载后在UI线程里面更新UI。
??????? 2、由于示例中需要用到SDCARD,所以在做下载操作时,如果没有挂载SDCARD时,不进行下载处理:
if (!Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) { return;}
??????? 3、
handler.post(new Runnable() {@Overridepublic void run() {downloadButton.setEnabled(true);Drawable drawable = null;try {drawable = Drawable.createFromPath(filePath);} catch (Error e) {e.printStackTrace();}imageDisplay.setImageDrawable(drawable);}});
???? 利用handler的post方法,在UI线程里面更新UI。首先使能按钮,其次创建drawable,最后显示图片。
?
??? 3、运行项目,查看效果:在运行前,不要忘记在Manifest文件中添加网络访问和访问SDCARD的权限哦!
<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
???? 下载图片的效果如下:
?
??? 获取网页内容的页面效果:
?
?
?
?
??? 4、多线程文件下载,直接看普通Java项目中相关操作的代码:
import java.io.IOException;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;public class FileDownloadMultiThread {public static void main(String[] args) {downloadFile();}private static void downloadFile() {String fileName = "mysql.chm";String urlPath = "http://192.168.1.106/mysql.chm";// 下载开始前,创建RandomAccessFile并设置文件大小RandomAccessFile file = null;try {// 创建URL对象URL url = new URL(urlPath);// 创建HttpURL连接HttpURLConnection conn = (HttpURLConnection) url.openConnection();// 设置请求属性conn.setConnectTimeout(6 * 1000);conn.setRequestMethod("GET");// 连接conn.connect();// 需要下载文件的长度int fileLength = conn.getContentLength();// 创建RandomAccessFile并设置文件大小file = new RandomAccessFile(fileName, "rw");file.setLength(fileLength);// 启动的线程数量int threadCount = 6;// 计算每个线程需要下载数据的长度int threadDownloadLength = fileLength % 3 == 0 ? fileLength / 3 : fileLength / 3 + 1;// 创建threadCount个下载线程for (int i = 0; i < threadCount; i++) {// 计算开始位置int startPosition = i * threadDownloadLength;// 创建匿名下载线程对象new MultiDownloadThread(fileName, urlPath, startPosition).start();}} catch (Exception e) {e.printStackTrace();} finally {// 资源释放if (file != null) { try {file.close();} catch (IOException e) {e.printStackTrace();}}}}static class MultiDownloadThread extends Thread {private String urlPath;private int startPosition;private RandomAccessFile accessFile;public MultiDownloadThread(String fileName, String urlPath, int startPosition) {this.urlPath = urlPath;this.startPosition = startPosition;try {accessFile = new RandomAccessFile(fileName, "rw");accessFile.seek(startPosition);} catch (Exception e) {e.printStackTrace();}}public void run () {InputStream inputStream = null;try {URL url = new URL(urlPath);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(6 * 1000);conn.setRequestMethod("GET");// 设置HTTP的Range字段,指定该线程从文件的什么位置开始下载conn.setRequestProperty("Range", "bytes=" + startPosition + "-");conn.connect();inputStream = conn.getInputStream();byte[] buffer = new byte[1024];int readLength = -1;int totalLength = 0;while ((readLength = inputStream.read(buffer)) != -1) {accessFile.write(buffer, 0, readLength);totalLength = totalLength + readLength;}} catch (Exception e) {e.printStackTrace();} finally {if (accessFile != null) {try {accessFile.close();} catch (IOException e) {e.printStackTrace();}}if (inputStream != null) {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}}}}
???? 说明:
??????? 1、RandomAccessFile类创建目标文件并设置文件大小;
??????? 2、HttpURLConnection.setRequestProperty("Range", "bytes=" + startPosition + "-");方法指定下载位置;
??????? 3、RandomAccessFile.seek();设置文件指针偏移量,保存下载内容到指定位置;
??????? 4、由于线程的创建和销毁过程,线程之间的上下文切换等都需要系统资源,所以threadCount不是越大越好,而是根据具体情况进行设定;
??????? 4、在Android上实现多线程下载的方法类似,注意Android环境的特性就可以了,再次不再赘述!:)
?
??????? 5、多说一句:HTTP(HTTPS)的连接到网络上的数据资源的方式和设置多种多样,本例仅仅是从一个角度进行说明,希望对你有所帮助!如果需要Android项目源码,请查看附件!:)
?
?