• Android多线程文件下载器



    本应用实现的是输入文件的网络的地址,点击button開始下载,下载过程中有进度条和后面的文本提示进度,

    下载过程中button不可点击,防止反复的下载,完成下载后会进行Toast的提示显示,

    而且回复button的可点击性,进度条也会清空,当然假设下载中途结束应用进程就会进行进度的保存,

    下次下载相同的文件时就会从进度记录进行下载,节省流量和时间

    应用须要的应用权限:

    訪问网络权限

      <uses-permission android:name="android.permission.INTERNET"/>

    外部储存的写入权限

      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    布局文件代码

    <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>


    核心代码

    package 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:
    				// 设置button可用
    				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) {
    		// 设置button不可点击//防止反复提交
    		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,连接到server,获取一个文件,获取文件的大小跟server的文件一样的暂时文件
    					// 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");
    					// 获取server的返回码
    					int code = conn.getResponseCode();
    					// 推断返回码
    					if (code == 200) {
    						// 获取返回的长度
    						int length = conn.getContentLength();
    
    						// 设置进度条的最大值
    						pb.setMax(length);
    
    						System.out.println("文件总长度:" + length);
    
    						// 在client创建出一个跟server大小一致的暂时文件
    						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");
    				// 重要:请求server下载部分的文件,指定文件的位置
    				conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
    						+ endIndex);
    
    				System.out
    						.println("线程:" + threadId + "真实開始的下载进度:" + startIndex);
    				// 设置超时时间
    				conn.setReadTimeout(5000);
    				// 得到server的状态码,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




  • 相关阅读:
    近期学习情况
    java连接数据库的两种方法总结
    近两个星期学习成果
    云笔记第一阶段总结
    圆面积
    C++计算器项目的初始部分
    C++视频课程
    A+B Format
    大一下学期的自我目标
    Kohana的请求流
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/6802646.html
Copyright © 2020-2023  润新知