android学习记录—将java中的多线程下载移植到android中③(代码片段)

非著名程序员 非著名程序员     2022-11-30     217

关键词:

在这一节中,我们就来讲多线程下载以及断点续传在android中怎么使用,前两节是为本节做准备的,没有看前两节的同学,最好看完前面的两篇文章再来看这篇。其实在android端的应用和java基本上是差不多的,只不过在android端我建议对于断点续传的记录的保存放在android的sqlite3的数据库中,这样是最好的,当然保存在sd卡中也行,我为了方便起见,我没有建立数据库,而是直接保存到了sd卡中。先看一下我在android中运行的效果图,如下:


我在这里的代码加上了进度条的显示和下载进度百分比的显示,为了让进度条和百分比不混乱,我为下载线程中计算下载进度的代码加上了锁。如下:

还有就是下边的一段代码我把记录下载进度的文件保存到了SD卡中,我建议最好用数据库。这段代码如下:

其它的我倒是没有什么好解释的了,跟上一篇文章的内容差不多,而且我在代码中已经写得很详细了,那么就请大家看代码吧!

MainActivity中的代码如下:

package net.loonggg.android.downloader;

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.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

@SuppressLint("HandlerLeak")
public class MainActivity extends Activity 
	public int currentProcess = 0;// 下载文件的当前进度
	// 开启的线程的个数
	public static final int THREAD_COUNT = 3;
	public static int runningThread = 3;// 记录正在运行的下载文件的线程数
	private EditText et;
	private Button btn;
	private TextView tv;
	private ProgressBar pb;// 下载进度条

	private Handler handler = new Handler() 
		public void handleMessage(android.os.Message msg) 
			switch (msg.arg1) 
			case 0:
				Toast.makeText(getApplicationContext(), "下载失败!",
						Toast.LENGTH_SHORT).show();
				break;
			case 1:
				Toast.makeText(getApplicationContext(), "下载完成!",
						Toast.LENGTH_SHORT).show();
				break;
			case 2:
				tv.setText("下载进度:" + pb.getProgress() * 100 / pb.getMax() + "%");
				break;
			default:
				break;
			
		;
	;

	@Override
	protected void onCreate(Bundle savedInstanceState) 
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		et = (EditText) findViewById(R.id.et);
		pb = (ProgressBar) findViewById(R.id.pb);
		btn = (Button) findViewById(R.id.btn);
		tv = (TextView) findViewById(R.id.tv_process);
		btn.setOnClickListener(new View.OnClickListener() 

			@Override
			public void onClick(View v) 
				downLoad();
			
		);
	

	/**
	 * 下载文件的方法
	 */
	private void downLoad() 
		final String path = et.getText().toString();
		new Thread() 
			public void run() 
				try 
					// 1、连接服务器,获取一个文件,获取文件的长度,在本地创建一个大小跟服务器文件大小一样的临时文件
					URL url = new URL(path);
					HttpURLConnection conn = (HttpURLConnection) url
							.openConnection();
					conn.setConnectTimeout(5000);
					conn.setRequestMethod("GET");
					int code = conn.getResponseCode();
					if (code == 200) 
						// 服务器返回的数据的长度,实际就是文件的长度
						int length = conn.getContentLength();
						pb.setMax(length);// 为进度条设置最大值
						System.out.println("----文件总长度----" + length);
						// 在客户端本地创建出来一个大小跟服务器端文件一样大小的临时文件
						RandomAccessFile raf = new RandomAccessFile(
								"/sdcard/temp.apk", "rwd");
						// 指定创建的这个文件的长度
						raf.setLength(length);
						// 关闭raf
						raf.close();
						// 假设是3个线程去下载资源
						// 平均每一个线程下载的文件的大小
						int blockSize = length / THREAD_COUNT;
						for (int threadId = 1; threadId <= THREAD_COUNT; threadId++) 
							// 第一个线程开始下载的位置
							int startIndex = (threadId - 1) * blockSize;
							int endIndex = threadId * blockSize - 1;
							if (threadId == THREAD_COUNT) 
								endIndex = length;
							
							System.out.println("----threadId---"
									+ "--startIndex--" + startIndex
									+ "--endIndex--" + endIndex);
							new DownloadThread(path, threadId, startIndex,
									endIndex).start();
						
					
				 catch (Exception e) 
					e.printStackTrace();
					Message msg = new Message();
					msg.arg1 = 0;
					handler.sendMessage(msg);
				
			;
		.start();
	

	/**
	 * 下载文件的子线程,每一个线程下载对应位置的文件
	 * 
	 * @author loonggg
	 * 
	 */
	public class DownloadThread extends Thread 
		private int threadId;
		private int startIndex;
		private int endIndex;
		private String path;

		/**
		 * @param path
		 *            下载文件在服务器上的路径
		 * @param threadId
		 *            线程id
		 * @param startIndex
		 *            线程下载的开始位置
		 * @param endIndex
		 *            线程下载的结束位置
		 */
		public DownloadThread(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("/sdcard/" + threadId + ".txt");
				if (tempFile.exists() && tempFile.length() > 0) 
					FileInputStream fis = new FileInputStream(tempFile);
					byte[] temp = new byte[1024 * 10];
					int leng = fis.read(temp);
					// 已经下载的长度
					String downloadLen = new String(temp, 0, leng);
					int downloadInt = Integer.parseInt(downloadLen);
					// ------------------这两行代码是关于断点续传时,设置进度条的起点时的关键代码-------------------
					int alreadyDownloadInt = downloadInt - startIndex;
					currentProcess += alreadyDownloadInt;// 计算每个线程上次断点已经下载的文件的长度
					// ---------------------------------------------------------------------------------
					startIndex = downloadInt;
					fis.close();
				
				// ---------------------------------------------------------------

				URL url = new URL(path);
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setRequestMethod("GET");
				// 重要:请求服务器下载部分的文件 指定文件的位置
				conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
						+ endIndex);
				conn.setConnectTimeout(5000);
				// 从服务器请求全部资源的状态码200 ok 如果从服务器请求部分资源的状态码206 ok
				int code = conn.getResponseCode();
				System.out.println("---code---" + code);
				InputStream is = conn.getInputStream();// 已经设置了请求的位置,返回的是当前位置对应的文件的输入流
				RandomAccessFile raf = new RandomAccessFile("/sdcard/temp.apk",
						"rwd");
				// 随机写文件的时候从哪个位置开始写
				raf.seek(startIndex);// 定位文件
				int len = 0;
				byte[] buffer = new byte[1024];
				int total = 0;// 记录已经下载的数据的长度
				while ((len = is.read(buffer)) != -1) 
					RandomAccessFile recordFile = new RandomAccessFile(
							"/sdcard/" + threadId + ".txt", "rwd");// 记录每个线程的下载进度,为断点续传做标记
					raf.write(buffer, 0, len);
					total += len;
					recordFile.write(String.valueOf(startIndex + total)
							.getBytes());
					recordFile.close();
					// 同步加锁,防止混乱
					synchronized (MainActivity.this) 
						currentProcess += len;// 获取当前的总进度
						// 特殊情况,ProgressBarH和ProgressDialog进度条和对话框可以在子线程里面更新UI,系统内部代码特殊处理
						pb.setProgress(currentProcess);// 更改界面上进度条的进度
						Message msg = Message.obtain();
						msg.arg1 = 2;
						// 发送更新消息,更新进度的百分比
						handler.sendMessage(msg);
					
				
				is.close();
				raf.close();
				System.out.println("线程:" + threadId + "下载完毕了!");
			 catch (Exception e) 
				e.printStackTrace();
				Message msg = handler.obtainMessage();
				msg.arg1 = 0;
				handler.sendMessage(msg);
			 finally 
				threadFinish();
			
		

		/**
		 * 我个人认为不锁定也可以,但是锁定可能更安全,如果谁有好的建议,到底用不用锁定,请给我留言
		 */
		private synchronized void threadFinish() 
			runningThread--;
			if (runningThread == 0) // 所有的线程已经执行完毕
				for (int i = 1; i <= THREAD_COUNT; i++) // 删除记录下载进度的文件
					File file = new File("/sdcard/" + i + ".txt");
					file.delete();
					Message msg = handler.obtainMessage();
					msg.arg1 = 1;
					handler.sendMessage(msg);
				
			
		
	


还有就是对应的Activity_main.xml的代码如下:

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

    <EditText
        android:id="@+id/et"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="http://dl1.byme001.com/phone_android.apk" />

    <ProgressBar
        android:id="@+id/pb"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_process"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下载进度"
        android:textSize="30sp" />

    <Button
        android:id="@+id/btn"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="下载" />

</LinearLayout>
当然大家别忘了在清单文件中设置网络权限和读写SD卡的权限:

 <uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
到这里就完了!大家如果有什么不明白的,可以在下面留言!

转载请注明出处:http://blog.csdn.net/loongggdroid/article/details/17846085

java中的多线程

多线程基础进程:进程就是运行中的程序,当被关闭的时候,这段进程也关闭。比如我们玩玩游戏,打开游戏操作系统会为该进程分配一个空间,当退出游戏是,进程也就结束了线程:线程是由进程创建的,是进程的实体。比如... 查看详情

Java中的多线程矩阵乘法

】Java中的多线程矩阵乘法【英文标题】:MultithreadingmatrixmultiplicationinJava【发布时间】:2018-07-1222:47:02【问题描述】:我正在尝试构建一个程序,用于使用a*d线程将两个矩阵(A[a,b],B[c,d])相乘(用于打印完成后的一个索引的总和)... 查看详情

java的多线程学习,第三记

一,Java内存模型Java内存模型规定了所有的内存变量都存储在主内存中。每条线程中还有自己的工作内存,线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。线程对变量的所有操作(读取,... 查看详情

java高级-解析java中的多线程机制

...称为线程体(ThreadBody)。按照线程体在计算机系统内存中的状态不同,可以将线程分为创建、就绪、运行、睡眠、挂起和死亡等类型。这些线程状态类型下线程的特征为:创建状态:当利用new关键字创建线程对象实例后,它仅... 查看详情

用于理解 Java 中的多线程的简单任务

】用于理解Java中的多线程的简单任务【英文标题】:SimpleTaskforunderstandingMultithreadinginJava【发布时间】:2020-05-0203:21:54【问题描述】:我有几个关于java线程的问题。为了更好地理解,我试图用线程解决一些简单的任务。我有一个... 查看详情

java中的多线程如何理解——精简(代码片段)

... Executors的工具类构建线程池对象 引言    通过前面的学习,我们已经学会了线程是如何创建的以及线程的常用方法,接下来呢,我们将要深入性了解 查看详情

java的多线程学习,第五记

 死锁问题;publicclassDeadLock{//锁的嵌套会出现死锁//1尽量不要去写锁嵌套//2privatestaticObjectlocka=newObject();privatestaticObjectlockb=newObject();publicstaticvoidmain(String[]args){newDeadLock().deadLock();}privatev 查看详情

java的多线程学习,第一记

实现多线程有三种方法1,继承THread类importcom.sun.org.apache.xpath.internal.SourceTree;publicclasstest{//继承Thread类并重写run方法publicstaticclassMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("Iamachild 查看详情

java示例代码_Java中的多线程

java示例代码_Java中的多线程 查看详情

qt学习笔记8.qt中的多线程

一、界面线程与工作线程GUI程序的主线程:GUI的用户输入主要来自鼠标、键盘,称为事件。处理事件的主循环,称为事件循环。这个用于处理各种界面的线程,称为界面线程GUI界面卡死所有的事件处理函数(slots)都应该迅速返回... 查看详情

Android 上 Unity 中的多线程

】Android上Unity中的多线程【英文标题】:MultithreadinginUnityonAndroid【发布时间】:2018-02-1319:49:14【问题描述】:在游戏过程中需要进行一些“长时间的工作”。显然,在执行这项工作时,游戏会在1-2秒内冻结。所以我把“长时间工... 查看详情

通俗易懂两种常用的多线程实现方式——java并发系列学习笔记(代码片段)

如果我们想执行多线程操作,通常有两种方法一.继承Thread类方式步骤:自定义类继承Thread覆盖Thread中的run()方法创建该类的对象,调用start()方法,开启线程并调用run方法classMyThreadextendsThread privateintticket=10; publi... 查看详情

java中的多线程

一个java程序实际上是一个JVM进程,JVM进程用一个主线程来执行main()方法,在main()方法内部,我们又可以启动多个线程。此外,JVM还有负责垃圾回收的其他工作线程等。publicclassMyThread{publicstaticvoidmain(String[]args){finalThreadt=newtestThrea... 查看详情

如何限制Java程序中的多线程?

】如何限制Java程序中的多线程?【英文标题】:HowtorestrictthemultithreadinginaJavaprogram?【发布时间】:2019-06-0706:50:45【问题描述】:我正在创建一个在线判断,为此我希望提交的java代码不应该使用CPU的并行处理。所以,我希望禁用J... 查看详情

为android编程时的多线程问题

】为android编程时的多线程问题【英文标题】:MultiThreadingissueswhileprogramingforandroid【发布时间】:2011-01-0410:44:10【问题描述】:我正在Android上开发,但这个问题在任何其他Java平台上可能同样有效。我开发了一个多线程应用程序。... 查看详情

java示例代码_我应该如何处理Java中的多线程

java示例代码_我应该如何处理Java中的多线程 查看详情

android学习笔记----jni中的c控制java

面向对象的底层实现          java作为面向对象高级语言,可对现实世界进行建模。和面向过程不同的是面向对象软件的编写不是流程的堆积,而是对业务逻辑的多视角分解和分类。其过程大... 查看详情

与记录器线程的多线程应用程序交互

】与记录器线程的多线程应用程序交互【英文标题】:Multi-threadedapplicationinteractionwithloggerthread【发布时间】:2010-12-0911:33:10【问题描述】:我再次提出关于多线程的问题和我的并发编程课的练习。我有一个多线程服务器-使用.NET... 查看详情