赵雅智_java的多线程下载移植到android客户端
来源:互联网 发布:网络系统工程师 编辑:程序博客网 时间:2024/05/02 06:29
根据java的多线程下载,我们将进行对java的多线程下载移植到android客户端。
java多线程在客户端的移植步骤
1.添加用户权限:发送网络请求,sd卡的读写
2.点击按钮触发访问网络请求的时间,必须开启一个线程,在线程中去启动主线程中不能发送网络请求
new Thread(){@Overridepublic void run() {}}.start();
3.path路径需要指定为sd卡路径
a) 获取sd卡根路径:Environment.getExternalStorageDirectory();
b) 创建临时文件:new File(sd卡目录,创建的文件名);
4.更改所创建的sd目录下的临时文件名称
涉及知识点:
1.杀死程序
在Devices下 找到所开启的模拟器,点击你所创建的项目,右上角有红色 的stop,点击便可杀死程序。如下图
2.Android 更新UI的两种方法——handler和runOnUiThread()
在Android开发过程中,常需要更新界面的UI。而更新UI是要主线程来更新的,即UI线程更新。如果在主线线程之外的线程中直接更新页面显示常会报错。抛出异常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
只有原始创建这个视图层次(view hierachy)的线程才能修改它的视图(view)
方法一:
在Activity.onCreate(Bundle savedInstanceState)中创建一个Handler类的实例, 在这个Handler实例的handleMessage回调函数中调用更新界面显示的函数。
方法二:
利用Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程
runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "服务器端返回错误", 1).show();}});
3.substring()
substring() 方法用于提取字符串中介于两个指定下标之间的字符。
1.语法:stringObject.substring(start,stop)
a) start:必须;一个非负的整数,规定要提取的子串的第一个字符在 stringObject 中的位置
b) stop:可选。一个非负的整数,比要提取的子串的最后一个字符在 stringObject 中的位置多1。如果省略该参数,那么返回的子串会一直到字符串的结尾。
2.返回值
一个新的字符串,该字符串值包含 stringObject 的一个子字符串,其内容是从 start 处到 stop-1 处的所有字符,其长度为 stop 减start。
3.说明
substring() 方法返回的子串包括 start 处的字符,但不包括 stop 处的字符。
如果参数 start 与 stop 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。如果 start 比 stop 大,那么该方法在提取子串之前会先交换这两个参数。
程序主要代码
1.添加权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
2.布局文件
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivityss" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请输入下载文件的路径" /> <EditText android:id="@+id/et_url" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/textView1" android:layout_marginTop="14dp" android:text="http://172.16.232.237:8080/viedo/DSC_1495.JPG"> </EditText> <Button android:id="@+id/btn_download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/et_url" android:onClick="downLoadFile" android:text="下载" /></RelativeLayout>
3.StreamTools工具
package com.example.util;import java.io.ByteArrayOutputStream;import java.io.InputStream;public class StreamTools {public static String streamToStr(InputStream is) {String value = null;try {ByteArrayOutputStream baos = new ByteArrayOutputStream();int len = 0;byte buffer[] = new byte[1024];while ((len = is.read(buffer)) != -1) {baos.write(buffer, 0, len);}baos.close();is.close();value = new String(baos.toByteArray());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return value;}}
4.Activity主要代码
package com.example.android_download;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;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.text.TextUtils;import android.view.View;import android.widget.EditText;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import com.example.util.StreamTools;public class MainActivity extends Activity {private int threadNum = 3;// 线程开启的数量private int threadRunning = 3;// 正在运行的线程private EditText et_url; @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et_url = (EditText) findViewById(R.id.et_url);} // 下载文件(得到服务器端的文件大小 )public void downLoadFile(View v) {// 获取下载路径final String spec = et_url.getText().toString();if (TextUtils.isEmpty(spec)) {Toast.makeText(this, "下载地址不能为空", 0).show();} else {new Thread() {@Overridepublic void run() {// 访问网络地址try {// 根据下载的地址构建url对象URL url = new URL(spec);// 通过URL对象的openConnection()方法打开连接,返回一个连接对象HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();// 设置请求头httpURLConnection.setRequestMethod("GET");httpURLConnection.setConnectTimeout(5000);httpURLConnection.setReadTimeout(5000);// 判断是否响应成功if (httpURLConnection.getResponseCode() == 200) {/** * 第一步:得到服务器下载文件的大小,然后在本地设置一个临时文件和服务器端文件大小一致 */// 获取文件长度int fileLength = httpURLConnection.getContentLength();//判断SD卡是否可用if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){// 外部存储设备的路径File sdFile = Environment.getExternalStorageDirectory();//获取文件名称String fileName = spec.substring(spec.lastIndexOf("/")+1);// 随机访问文件的读取与写入RandomAccessFile(file, mode)RandomAccessFile accessFile = new RandomAccessFile(new File(sdFile, fileName), "rwd");// 设置临时文件与服务器文件大小一致accessFile.setLength(fileLength);// 关闭临时文件accessFile.close();/** * 第二步:计算出每个线程下载的大小(开始位置,结束位置) */// 计算出每个线程下载的大小int threadSize = fileLength / threadNum;// for循环,计算出每个线程的开始和结束位置for (int threadId = 1; threadId <= 3; threadId++) {int startIndex = (threadId - 1) * threadSize;// 开始位置int endIndex = threadId * threadSize - 1;// 结束位置if (threadId == threadNum) {// 最后一个 线程endIndex = fileLength - 1;}System.out.println("当前线程--" + threadId+ "-----开始位置" + startIndex + "----结束位置"+ endIndex + "-----线程大小" + threadSize);/** * 第三步:每创建好一次就要开启线程下载 */new DownLoadThread(threadId, startIndex,endIndex, spec,fileName).start();}}else {runOnUiThread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubToast.makeText(MainActivity.this, "SD卡不存在", 1).show();}});}} else {runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "服务器端返回错误", 1).show();}});}} catch (Exception e) {e.printStackTrace();}}}.start();}}/** * 每创建好一次就要开启线程下载 * * @author zhaoyazhi * */class DownLoadThread extends Thread {// 成员变量private int threadId;private int startIndex;private int endIndex;private String path;private String fileName;File sdFile = Environment.getExternalStorageDirectory();/** * * @param threadId * 线程的序号 * @param startIndex * 线程下载开始位置 * @param endIndex * 线程下载结束位置 * @param path * 线程下载保存文件的路径 */public DownLoadThread(int threadId, int startIndex, int endIndex,String path,String fileName) {super();this.threadId = threadId;this.startIndex = startIndex;this.endIndex = endIndex;this.path = path;this.fileName = fileName;}@Overridepublic void run() {// 可以通过每个线程去下载文件try {/** * 第四步:从本地文件上读取已经下载文件的开始位置 */File recordFile = new File(sdFile, threadId + ".txt");if (recordFile.exists()) {// 读取文件InputStream is = new FileInputStream(recordFile);// 利用工具类转换String value = StreamTools.streamToStr(is);// 获取记录的位置int recordIndex = Integer.parseInt(value);// 把记录的位置付给开始位置startIndex = recordIndex;}// 通过path对象构造URL 对象URL url = new URL(path);// 通过URL对象openConnectionHttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();// 设置请求头httpURLConnection.setRequestMethod("GET");httpURLConnection.setConnectTimeout(5000);// 设置下载文件的开始位置和结束位置httpURLConnection.setRequestProperty("Range", "bytes="+ startIndex + "-" + endIndex);// 获取状态码int code = httpURLConnection.getResponseCode();System.out.println(code);// 判断是否成功 只要设置"Range"头,返回的状态码就是206if (code == 206) {// 获取每个线程返回的流对象InputStream is = httpURLConnection.getInputStream();// 创建随机访问的对象RandomAccessFile accessFile = new RandomAccessFile(new File(sdFile, fileName), "rwd");// 指定开始位置accessFile.seek(startIndex);// 定义读取的长度int len = 0;// 定义缓冲区byte buffer[] = new byte[1024];int total = 0;// 循环读取while ((len = is.read(buffer)) != -1) {System.out.println("当前线程--" + threadId+ "-----当前下载的位置是" + (startIndex + total));// 保存每个线程的下载位置RandomAccessFile threadFile = new RandomAccessFile(new File(sdFile, threadId + ".txt"), "rwd");// 记录每次下载位置threadFile.writeBytes((startIndex + total) + "");threadFile.close();accessFile.write(buffer, 0, len);total += len;// 已经下载大小}accessFile.close();is.close();runOnUiThread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubToast.makeText(MainActivity.this, "当前线程" + threadId + "---下载完毕", 1).show();}});/** * 第五步:当你的n个线程都下载完毕 的时候我才进行删除记录下载位置的缓存文件 */deleteRecordFile();} else {runOnUiThread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubToast.makeText(MainActivity.this, "服务器端返回错误", 1).show();}});}// 设置你下载文件} catch (Exception e) {e.printStackTrace();}}}/** * synchronized避免线程同步 下载完删除存储文件下载位置的临时文件 */public synchronized void deleteRecordFile() {// 外部存储设备的路径File sdFile = Environment.getExternalStorageDirectory();// 线程下载完就减去threadRunning--;// 当没有正在运行的线程if (threadRunning == 0) {for (int i = 1; i <= 3; i++) {File recordFile = (new File(sdFile, i + ".txt"));if (recordFile.exists()) {recordFile.delete();}}}}}
运行输出结果
1.输出线程开始位置,结束位置,线程大小,和当前下载位置
2.当文件进行下载时,sdcard会缓存文件信息和临时记录下载位置的存储文件
3.当杀死线程时,导出图片,图片显示不全
4.当我截止线程时,观察可以得出线程1,2,3的下载位置,当我们在启动时继续上次的下载
5.完全下载后,保存下载位置的临时txt被删除
6.当把下载成功的图片转出,图片显示成功
五、常见问题
内存溢出
有时候我们为了方便测试下载。将缓存区的值设置的很大,例如byte buffer[] = new byte[1024*1024*100];如果这样设置的话会内存溢出的错误,所以我们一般情况下定义缓冲区为byte buffer[] = new byte[1024];
源码下载地址:http://download.csdn.net/detail/zhaoyazhi2129/7406379
转发请 标明出处:http://blog.csdn.net/zhaoyazhi2129/article/details/27189465
- 赵雅智_java的多线程下载移植到android客户端
- 赵雅智_java多线程下载
- 多线程下载的移植
- 多线程下载JAVA代码移植到android 添加下载进度条跟踪
- Android学习记录(6)—将java中的多线程下载移植到Android中③
- 多线程文件下载的服务器端及客户端
- linux多线程下载客户端
- flash网站使用air移植到android 客户端项目总结
- ffmepg移植到android实现RTSP客户端-问题小记
- Java多线程断点下载功能(可移植Android)
- 移植ftp客户端到A1200
- 移植 NTP客户端 到ARM
- 移植 DHCP 客户端 到ARM
- MQTT客户端移植到STM32
- android多线程下载的实现
- android简单的多线程下载
- 简单的android多线程下载
- 【实战】android的多线程下载
- Python处理JSON
- JS中offsetTop、clientTop、scrollTop、offsetTop各属性介绍
- hello world
- SAS:如何比较不同组之间的回归系数
- Android调试获取Log 【转】
- 赵雅智_java的多线程下载移植到android客户端
- static
- OS1
- 负载测试、压力测试和性能测试的异同
- ASP.NET页面使用JQuery EasyUI生成Dialog后台取值为空
- mvn 常用命令
- UNP学习笔记(2)
- 【坑】IOS DLL热更新
- oracle 库文件解决办法 bad ELF interpreter: No such file or directory