android程序---->android多线程下载(二)
上篇我们讲到了android中下载的断点续传问题,今天我们开始学习下载的多线程问题。本次的多线程源码下载:androdi中多线程下载的实现代码。有关断点续传的问题,请参见博客:android程序---->android多线程下载(一)
目录导航
android中多线程下载的思路
一、 多线程下载的步骤说明:
第一步: 我们要获得下载资源的的长度,用http请求中HttpURLConnection的getContentLength()方法
第二步:在本地创建一个文件,设计其长度。File file = new File()
第三步:根据文件长度和线程数计算每条线程下载的数据长度和下载位置。
第四步:从下载的位置下载数据,通过connection.setRequestProperty("Range", "bytes=" + start + "-" + end)方法;
第五步:保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据。
二、 根据文件长度和线程数计算每条线程下载的数据长度和下载位置:
第一个线程从0~32字节处写入
第二个线程从33~65字节范围内写入
第三个线程从66~100字节范围内写入
android中多线程中的原理说明
对于多线程的下载,有两个需要学习的知识点就是
1. connection.setRequestProperty("Range", "bytes=" + start + "-" + end)方法,它用于请求指定范围内的数据。
2. RandomAccessFile类的seek方法从指定位置开始写入数据到文件:
一、 connection.setRequestProperty("Range", "bytes=" + start + "-" + end)方法:
我们写一个Java类进行测试,请求文件我本机上的android.txt文件是:http://192.168.250.232:8080/android.txt
My name is huhx, and my blog address is http://www.cnblogs.com/huhx.welcome to my blog.
测试类如下,Http请求2到10位置之间的数据,由于从0开始,所以请求的数据应该有12个字符为:name is huhx
package com.huhx.mutilthread; import java.net.HttpURLConnection; import java.net.URL; public class MultiThread { public static void main(String[] args) { HttpURLConnection connection = null; try { URL url = new URL("http://192.168.250.232:8080/android.txt"); connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Range", "bytes=" + 3 + "-" + 14); connection.setReadTimeout(5000); connection.setRequestMethod("GET"); int length = -1; if (connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) { length = connection.getContentLength(); } if (length < 0) { return; } byte[] buffer = new byte[length]; while ((length = connection.getInputStream().read(buffer)) != -1) { System.out.println(new String(buffer)); System.out.println("length: " + buffer.length); } } catch (Exception e) { e.printStackTrace(); } finally { connection.disconnect(); } } }
如我们所想打印结果如下:
二、 RandomAccessFile类的seek方法与多线程:
我们写一个线程Download,用于写入指定位置的数据到文件:
package com.huhx.randomfile; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; /** * * @author huhx * */ public class Download implements Runnable { private String content; private int start; public Download(String content, int start) { this.content = content; this.start = start; } @Override public void run() { System.out.println(Thread.currentThread().getName() + ", " + content); File file = new File("output.txt"); RandomAccessFile accessFile = null; try { accessFile = new RandomAccessFile(file, "rwd"); accessFile.seek(start); accessFile.write(content.getBytes()); } catch (Exception e) { e.printStackTrace(); } finally { try { accessFile.close(); } catch (IOException e) { e.printStackTrace(); } } } }
在MainTest类中,开启三个线程写入数据:
我们用一个string[]数组进行模拟,从服务器通过setRequestProperty方法设置Range参数得到的数据:
package com.huhx.randomfile; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * * @author huhx * */ public class MainTest { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); String[] contents = {"hello", "world", "linux"}; for(int i = 0; i < 3; i ++) { Download download = new Download(contents[i], i * contents[i].length()); executorService.execute(download); } executorService.shutdown(); } }
打印结果如下:
pool-1-thread-2, world pool-1-thread-1, hello pool-1-thread-3, linux
并且output.txt文件内容如下,三个线程需要读的顺序有先后,但是最终的结果还是我们预期要的:
helloworldlinux
android中多线程下载的实现
有了上述android中多线程中原理的测试的理解,相信我们对于android中使用多线程下载有了比较不错的印象。现在我们通过实例,来讲述整个实现过程。本次的案例,为也避免内容的冗余,没有加断点续传的功能。项目结构如下:
在手机的存储卡,得到下载完成的文件:
一、 在MainActivity中初始化开始下载按钮,绑定开始下载事件,传递参数文件的下载地址和文件的名称:
package com.example.linux.multithreaddownload; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.TextView; /** * writer: huhx */ public class MainActivity extends AppCompatActivity { private Button startButon; private String fileUrl = "http://f4.market.mi-img.com/download/AppStore/0548a945da1eb4bf830dac7e60a50aaf9883d03db/net.oschina.gitapp.apk"; private String fileName = "linux.apk"; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); textView.setText(fileName); startButon = (Button) findViewById(R.id.start); startButon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this, DownloadService.class); intent.setAction(DownloadService.DOWNLOAD_START); intent.putExtra("fileUrl", fileUrl); intent.putExtra("fileName", fileName); startService(intent); } }); } }
二、 在DownloadService的onStartCommand方法中,启动一个线程去请求下载文件的大小:
@Override public int onStartCommand(Intent intent, int flags, int startId) { if (DOWNLOAD_START.equals(intent.getAction())) { fileUrl = intent.getStringExtra("fileUrl"); fileName = intent.getStringExtra("fileName"); // 开启多线程下载 new InitThread(fileUrl, fileName).start(); } return super.onStartCommand(intent, flags, startId); }
在InitThread线程中,代码如下,获取下载文件的长度,并发送下载的消息:
@Override public IBinder onBind(Intent intent) { return null; } /** * 初始化子线程 */ class InitThread extends Thread { private String fileUrl; private String fileName; public InitThread(String fileUrl, String fileName) { this.fileUrl = fileUrl; this.fileName = fileName; } @Override public void run() { HttpURLConnection connection = null; try { URL url = new URL(fileUrl); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setReadTimeout(5000); int length = -1; if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { // 获取文件的长度 length = connection.getContentLength(); } if (length <= 0) { return; } handler.obtainMessage(DOWNLOAD, length).sendToTarget(); } catch (Exception e) { e.printStackTrace(); } finally { connection.disconnect(); } } }
定义一个handler,用于消息的捕获及处理,在handleMessage方法中调用Download的download方法去下载文件:
private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case DOWNLOAD: int fileLength = (int) msg.obj; Download download = new Download(); download.download(fileUrl, fileName, fileLength);break; } } };
二、 在Download的类中,download方法用于开启多个线程去下载文件:
package com.example.linux.multithreaddownload; import android.content.Intent; import android.os.Environment; import android.widget.Toast; import java.io.File; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by Linux on 2016/4/10. */ public class Download { final static int THREAD_NUMBER = 3; private ExecutorService executor = Executors.newFixedThreadPool(THREAD_NUMBER); public static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/download/"; public void download(String fileUrl, String fileName, int fileLength) { int block = fileLength / THREAD_NUMBER; File dir = new File(DOWNLOAD_PATH); if (!dir.exists()) { dir.mkdir(); } File file = new File(dir, fileName); for (int i = 0; i < THREAD_NUMBER; i++) { long start = i * block; long end = (i + 1) * block - 1; if (i == THREAD_NUMBER - 1) { end = fileLength; } DownloadThread downloadThread = new DownloadThread(fileUrl, file.getAbsolutePath(), start, end); executor.execute(downloadThread); } executor.shutdown(); } }
四、 在DownloadThread中具体去执行下载文件的任务:
package com.example.linux.multithreaddownload; import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; /** * Created by huhxon 2016/4/10. */ public class DownloadThread implements Runnable { private String fileUrl; private String filePath; private long start; private long end; public DownloadThread(String filUrl, String filePath, long start, long end) { this.fileUrl = filUrl; this.filePath = filePath; this.start = start; this.end = end; } @Override public void run() { Log.i("Main", "thread: " + Thread.currentThread().getName() + ", start: " + start + ", end: " + end); HttpURLConnection connection = null; RandomAccessFile raf = null; InputStream is = null; try { URL url = new URL(fileUrl); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); connection.setRequestProperty("Range", "bytes=" + start + "-" + end); raf = new RandomAccessFile(new File(filePath), "rwd"); raf.seek(start); if (connection.getResponseCode() == HttpURLConnection.HTTP_PARTIAL) { is = connection.getInputStream(); byte[] buffer = new byte[4 * 1024]; int len; while ((len = is.read(buffer)) != -1) { raf.write(buffer, 0, len); } } Log.i("Main", Thread.currentThread().getName() + "完成下载"); } catch (Exception e) { e.printStackTrace(); } finally { try { raf.close(); is.close(); connection.disconnect(); } catch (IOException e) { e.printStackTrace(); } } } }
五、 在AndroidManifest.xml文件中定义服务和声明权限:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<service android:name=".DownloadService" android:exported="true" />
六、 打印日志结果如下:
04-10 20:41:19.491 313-567/com.example.linux.multithreaddownload I/Main: thread: pool-1-thread-3, start: 2265146, end: 3397719 04-10 20:41:19.493 313-566/com.example.linux.multithreaddownload I/Main: thread: pool-1-thread-2, start: 1132573, end: 2265145 04-10 20:41:19.503 313-565/com.example.linux.multithreaddownload I/Main: thread: pool-1-thread-1, start: 0, end: 1132572 04-10 20:41:36.642 313-565/com.example.linux.multithreaddownload I/Main: pool-1-thread-1完成下载 04-10 20:41:37.119 313-567/com.example.linux.multithreaddownload I/Main: pool-1-thread-3完成下载 04-10 20:41:37.254 313-566/com.example.linux.multithreaddownload I/Main: pool-1-thread-2完成下载
友情链接
- 关于android中断点续传下载 android程序---->android多线程下载(一)
- androdi中多线程下载的实现代码
出处: www.cnblogs.com/huhx
格言:你尽力了,才有资格说自己的运气不好。
版权:本文版权归作者huhx和博客园共有,欢迎转载。未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。