Android开发(24)---安卓中实现多线程下载(带进度条和百分比)

当我们学完java中多线程的下载后,可以将它移植到我们的安卓中来,下面是具体实现源码:

DownActivity.java

package com.example.downloads;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import com.example.downloads.utils.DownLoadThread;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class DownActivity extends Activity {
	// 声明控件
	// 路径与线程数量
	public EditText et_url, et_num;
	// 进度条
	public static ProgressBar pb_thread;
	// 显示进度的操作
	public TextView tv_pb;
	// 线程的数量
	public static int threadNum = 3;
	// 每个线程负责下载的大小
	public int blockSize;
	public static int threadCount;// 数量
	// 访问的path
	public String path;
	public static boolean flag = true;
	// 记录进度条的值
	public static int pb_count = 0;
	public static Handler handler;
	public static final int TEXTVALUE = 1;
	public static int pb_num = 0;
	public static int size = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_down);
		et_url = (EditText) findViewById(R.id.et_path);
		et_num = (EditText) findViewById(R.id.et_threadNum);
		pb_thread = (ProgressBar) findViewById(R.id.pb_down);
		tv_pb = (TextView) findViewById(R.id.tv_pb);
		handler = new Handler() {
			@SuppressLint("HandlerLeak")
			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				switch (msg.what) {
				case TEXTVALUE:
					System.out.println("-------" + DownActivity.pb_count
							+ "//" + DownActivity.size);
					// 改变TEXTView
					pb_num = (DownActivity.pb_count * 100) / DownActivity.size;
					tv_pb.setText("当前进度是+" + pb_num + "%");
					
					break;

				default:
					break;
				}
			}

		};

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	public void downLoad(View v) {
		DownActivity.flag = true;
		DownActivity.pb_count = 0;

		path = et_url.getText().toString();
		String threadNum_et = et_num.getText().toString();

		if (TextUtils.isEmpty(path) || TextUtils.isEmpty(threadNum_et)) {
			Toast.makeText(this, "不能为空", Toast.LENGTH_LONG).show();
			return;
		}
		Toast.makeText(this, "url:" + path + "--" + threadNum_et,
				Toast.LENGTH_LONG).show();
		// 转换成数字
		threadNum = Integer.valueOf(threadNum_et);
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					// 创建出URL对象
					URL url = new URL(path);
					// 创建出 HttpURLConnection对象
					HttpURLConnection httpURLConnection = (HttpURLConnection) url
							.openConnection();

					// 设置 发请求发送的方式
					httpURLConnection.setRequestMethod("GET");
					// 设置请求是否超时时间
					httpURLConnection.setConnectTimeout(5000);
					// 设置
					httpURLConnection
							.setRequestProperty("User-Agent",
									" Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)");

					// 是否响应成功
					if (httpURLConnection.getResponseCode() == 200) {
						// 获取文件的大小
						size = httpURLConnection.getContentLength();
						System.out.println("文件的大小" + size);
						// 设置进度条的最大值
						pb_thread.setMax(size);

						// 创建文件 //保存到SD卡上

						// 首先判断是否拥有sdcard
						if (Environment.getExternalStorageState().equals(
								Environment.MEDIA_MOUNTED)) {
							// 获取sdCard文件目录对象
							File sdFile = Environment
									.getExternalStorageDirectory();
							// 创建文件对象
							File file = new File(sdFile, "youdao.exe");

							RandomAccessFile accessFile = new RandomAccessFile(
									file, "rwd");
							// 设置文件的大小
							accessFile.setLength(size);
							// 每个线程下载的大小
							blockSize = size / threadNum;
							// 开三个线程 操作此文件
							for (int i = 1; i <= threadNum; i++) {
								// 1 2 3
								// 计算出每个线程开始的位置
								int startSize = (i - 1) * blockSize;
								// 结束位置
								int endSize = (i) * blockSize;
								// 当线程是最后一个线程的时候
								if (i == threadNum) {
									// 判断文件的大小是否大于计算出来的结束位置
									if (size > endSize) {
										// 结束位置 等于 文件的大小
										endSize = size;
									}
								}
								// 为每个线程创建一个随机的读取
								RandomAccessFile threadAccessFile = new RandomAccessFile(
										file, "rwd");
								new Thread(new DownLoadThread(i,
										threadAccessFile, startSize, endSize,
										path)).start();
							}

						}

					}

				} catch (MalformedURLException e) {
					
					e.printStackTrace();
				} catch (IOException e) {
					
					e.printStackTrace();
				}

			}

		}).start();
	}

	/**
	 * 暂停操作
	 * 
	 * @param v
	 */
	public void downPause(View v) {
		Toast.makeText(this, "暂停", Toast.LENGTH_LONG).show();

		this.flag = false;

	}

}


DownLoadThread.java

package com.example.downloads.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import com.example.downloads.DownActivity;

import android.os.Environment;



public class DownLoadThread implements Runnable {

	
	public RandomAccessFile accessFile; // 每个线程 都拥有一个accessFile的文件对象 线程1 线程2 线程3

	// 线程下载文件的起始位置
	public int startSize;
	public int endSize;

	// 文件下载的path路径
	public String path;

	public int threadId; // 线程的标识

	public DownLoadThread(int threadId, RandomAccessFile accessFile,
			int startSize, int endSize, String path) {

		this.threadId = threadId;
		this.accessFile = accessFile;
		this.startSize = startSize;
		this.endSize = endSize;
		this.path = path;
	}

	@Override
	public void run() {
		// 执行run方法
		try {

			// 创建文件到SD卡上去

			// 首先判断是否拥有sdcard
			if (Environment.getExternalStorageState().equals(
					Environment.MEDIA_MOUNTED)) {
				// 获取sdCard文件目录对象
				File sdFile = Environment.getExternalStorageDirectory();
				File threadFile = new File(sdFile, threadId + ".txt");

				if (threadFile.exists()) {

					// 读取该文件的内容
					// 创建文件的输入流对象
					FileInputStream fis = new FileInputStream(threadFile);
					// 采用工具类读取
					byte data[] = StreamTools.isToData(fis);
					// 转化成字符串
					String threadLen = new String(data);

					if ((threadLen != null) && (!"".equals(threadLen))) {
						startSize = Integer.valueOf(threadLen);

						// 解决 416bug的错误
						if (startSize > endSize) {
							startSize = endSize - 1;
						}
					}

				}

				// 创建文件

				// 创建URL对象
				URL url = new URL(path);
				// 创建HttpURLConnection对象
				HttpURLConnection httpURLConnection = (HttpURLConnection) url
						.openConnection();
				// 设置请求的头

				httpURLConnection.setRequestMethod("GET");
				// 设置请求是否超时时间
				httpURLConnection.setConnectTimeout(5000);
				// 设置
				httpURLConnection
						.setRequestProperty("User-Agent",
								" Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)");

				// 关键的设置
				httpURLConnection.setRequestProperty("Range", "bytes="
						+ startSize + "-" + endSize);

				// 输出当前线程
				System.out.println("当前线程" + threadId + " 下载开始位置:" + startSize
						+ " 下载结束位置:" + endSize);
				// 响应成功

				// 设置随机读取文件的 开始位置
				accessFile.seek(startSize);
				// 获取相应流对象
				InputStream is = httpURLConnection.getInputStream();
				// 创建输出流对象

				byte buffer[] = new byte[1024];
				int len = 0;
				int threadTotal = 0;// 每个线程下载后保存记录 /
				while ((len = is.read(buffer)) != -1) {
					accessFile.write(buffer, 0, len);
					threadTotal += len;// 记录你写入的长度 //xml文件

					//改变进度条:
					setProgressBar(len);
					// 通过文件记录文件下载的长度
					FileOutputStream fos = new FileOutputStream(threadFile);
					fos.write((threadTotal + "").getBytes());
					fos.flush();
					fos.close();
					//发送handler消息
					DownActivity.handler.sendEmptyMessage(DownActivity.TEXTVALUE);
					if(!DownActivity.flag){
						return;
					}

				}
				accessFile.close();
				is.close();
				System.out.println(threadId + "线程执行完毕");

				// 线程操作
				synchronized (DownActivity.class) {
					DownActivity.threadCount++;
					if (DownActivity.threadCount >= DownActivity.threadNum) {
						for (int i = 1; i <= DownActivity.threadNum; i++) {
							// 获取sdCard上的文件
							File deleteFile = new File(sdFile, i + ".txt");
							if (deleteFile.exists()) {
								// 文件删除
								deleteFile.delete();
							}
						}
					}
				}
			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	
	
	
	public synchronized void setProgressBar(int len){
		DownActivity.pb_count+=len;
		DownActivity.pb_thread.setProgress(DownActivity.pb_count);
	}
	
	
	
}


StreamTools.java

package com.example.downloads.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class StreamTools {

	
	public static byte[] isToData(InputStream is) throws IOException{
		// 字节输出流
		ByteArrayOutputStream bops = new ByteArrayOutputStream();
		// 读取数据的缓存区
		byte buffer[] = new byte[1024];
		// 读取长度的记录
		int len = 0;
		// 循环读取
		while ((len = is.read(buffer)) != -1) {
			bops.write(buffer, 0, len);
		}
		// 把读取的内容转换成byte数组
		byte data[] = bops.toByteArray();
		
		bops.flush();
		bops.close();
		is.close();
		return data;
	}
}


strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">downloads</string>
    <string name="action_settings">Settings</string>
    <string name="tv_down">文件下载的地址</string>
    <string name="tv_threadNum">线程数量</string>
    <string name="tv_num">0%</string>
    <string name="btn_text">下载</string>
    <string name="btn_pause">暂停</string>
    <string name="et_path">http://172.22.64.8:8080/doudou/youdao.exe</string>
    <string name="et_threadNum">3</string>

</resources>

布局文件:

<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=".DownActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="@string/tv_down" />

    <EditText
        android:id="@+id/et_path"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/textView1"
        android:ems="10"
        android:inputType="none"
        android:text="@string/et_path" >

        <requestFocus />
    </EditText>

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/et_path"
        android:text="@string/tv_threadNum" />

    <EditText
        android:id="@+id/et_threadNum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView2"
        android:layout_alignRight="@+id/et_path"
        android:layout_below="@+id/textView2"
        android:ems="10"
        android:inputType="number"
        android:text="@string/et_threadNum" />

    <ProgressBar
        android:id="@+id/pb_down"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/et_threadNum"
        android:layout_alignRight="@+id/et_threadNum"
        android:layout_below="@+id/et_threadNum"
        android:layout_marginTop="14dp" />

    <TextView
        android:id="@+id/tv_pb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/textView1"
        android:layout_below="@+id/pb_down"
        android:layout_marginTop="24dp"
        android:text="@string/tv_num" />

    <Button
        android:id="@+id/btn_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/pb_down"
        android:layout_below="@+id/tv_pb"
        android:layout_marginTop="32dp"
        android:onClick="downLoad"
        android:text="@string/btn_text" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/btn_down"
        android:layout_below="@+id/btn_down"
        android:layout_marginTop="16dp"
        android:onClick="downPause"
        android:text="@string/btn_pause" />

</RelativeLayout>

效果如下:


最后要注意的是别忘了在项目清单文件中加入权限:

<!-- SDCard权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- 访问网络的权限 -->
    <uses-permission android:name="android.permission.INTERNET" />


 

 

  • 9
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Android 实现多线程下载安装主要需要以下步骤: 1. 获取下载链接和文件名:在下载之前,需要先获取下载链接和文件名。 2. 创建多个线程:每个线程负责下载文件的一部分。这里可以使用 Java 的线程池技术,通过线程池管理多个线程。 3. 下载文件:每个线程下载文件的一部分,并将下载的数据写入本地文件。 4. 合并文件:所有线程下载完成后,将下载的文件片段合并为完整的文件。 5. 安装应用:下载完成后,通过系统的 Intent 启动安装程序,将下载的应用安装到设备上。 下面是一个简单的示例代码: ``` public class DownloadTask { private static final int THREAD_COUNT = 3; // 线程数量 private String downloadUrl; // 下载链接 private String fileName; // 文件名 private String savePath; // 保存路径 private long fileSize; // 文件大小 private Context context; public DownloadTask(Context context, String downloadUrl, String fileName, String savePath) { this.context = context; this.downloadUrl = downloadUrl; this.fileName = fileName; this.savePath = savePath; } public void startDownload() { try { URL url = new URL(downloadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); fileSize = connection.getContentLength(); connection.disconnect(); RandomAccessFile file = new RandomAccessFile(savePath + fileName, "rw"); file.setLength(fileSize); file.close(); ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); long blockSize = fileSize / THREAD_COUNT; for (int i = 0; i < THREAD_COUNT; i++) { long start = i * blockSize; long end = (i + 1) * blockSize - 1; if (i == THREAD_COUNT - 1) { end = fileSize - 1; } DownloadThread thread = new DownloadThread(context, downloadUrl, savePath, fileName, start, end); executor.execute(thread); } executor.shutdown(); while (!executor.isTerminated()) { Thread.sleep(100); } mergeFile(); installApk(); } catch (Exception e) { e.printStackTrace(); } } // 合并文件 private void mergeFile() throws IOException { File file = new File(savePath + fileName); FileOutputStream fos = new FileOutputStream(file); for (int i = 0; i < THREAD_COUNT; i++) { FileInputStream fis = new FileInputStream(savePath + fileName + ".part" + i); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } fis.close(); new File(savePath + fileName + ".part" + i).delete(); } fos.close(); } // 安装应用 private void installApk() { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(new File(savePath + fileName)), "application/vnd.android.package-archive"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } class DownloadThread extends Thread { private static final int BUFFER_SIZE = 1024; private Context context; private String downloadUrl; private String savePath; private String fileName; private long start; private long end; public DownloadThread(Context context, String downloadUrl, String savePath, String fileName, long start, long end) { this.context = context; this.downloadUrl = downloadUrl; this.savePath = savePath; this.fileName = fileName; this.start = start; this.end = end; } @Override public void run() { try { URL url = new URL(downloadUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); connection.setRequestProperty("Range", "bytes=" + start + "-" + end); File file = new File(savePath + fileName + ".part" + getId()); FileOutputStream fos = new FileOutputStream(file); InputStream is = connection.getInputStream(); byte[] buffer = new byte[BUFFER_SIZE]; int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.close(); is.close(); connection.disconnect(); } catch (Exception e) { e.printStackTrace(); } } } ``` 在这个示例,我们创建了一个 `DownloadTask` 类来管理下载任务。`startDownload()` 方法启动下载任务,首先获取文件大小,并创建一个与文件大小相同的空文件。然后使用线程池创建多个线程下载文件的不同部分。每个线程下载完成后,将文件片段写入以该线程 ID 命名的临时文件。所有线程结束后,将临时文件合并为完整的文件,并启动安装程序来安装应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值