题记:从百度百科上面我们知道,SharedPreferences是不支持多线程的,但是这次使用SharedPreferences实现了多线程断点下载。点解?


服务器端:

使用的是tomcat服务器,在C:\apache-tomcat-7.0.59\webapps\ROOT目录下存放pp.zip文件(这个文件随便,但是要跟代码中url的path后面的参数对应)
开启tomcat服务器,先用浏览器访问下,看浏览器能不能提示下载或者直接在浏览器界面显示。可以的话再执行下面的。

布局文件如下:

<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:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"android:orientation="vertical" ><TextViewandroid:id="@+id/tv_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="25dp"android:text="pp.zip文件,快来下载吧" /><EditTextandroid:id="@+id/et_threadtotal"android:layout_width="wrap_content"android:hint="请输入线程数量"android:layout_height="wrap_content" /><Button android:onClick="click"android:id="@+id/btn"android:layout_margin="10dp"android:text="点我下载"android:layout_width="match_parent"android:layout_height="wrap_content"/><ProgressBarandroid:id="@+id/pb"android:max="100"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="15dp"/><TextViewandroid:id="@+id/tv_number"android:layout_margin="5dp"android:text="显示进度"android:layout_width="match_parent"android:layout_height="wrap_content"/>
</LinearLayout >

MainActivity代码如下:

package com.example.downloader;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
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;public class MainActivity extends Activity {private EditText et;private static long length;private static File file;private static String path;private static ProgressBar pb;private static long reallength;private static SharedPreferences sp;private static TextView tv_number;private static long reallengthrecord=0;private static int number;private static Button btn;private static Handler handler=new Handler(){public void handleMessage(android.os.Message msg) {switch (msg.what) {case 0:
//reallengthrecord是从sp中获取的,让进度条续加,如果sp文件没能获取到这个值,默认就是0;long data=(Long) msg.obj;int result=(int)(data*100/length);
//进度条的最大值已经在布局文件中设置为100了
//这里有个坑,setProgress()和setText()里面的参数一定要匹配,要传字符串的却传int值,错误无提示的哦,只会运行时报Resources$NotFoundException: String resource ID #0x0pb.setProgress(result);if(pb.getProgress()==100){
//进度到了100%,按钮设置为不可点击btn.setEnabled(false);}tv_number.setText(result+"%");break;default:break;}};};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);et = (EditText)findViewById(R.id.et_threadtotal);btn = (Button)findViewById(R.id.btn);pb = (ProgressBar)findViewById(R.id.pb);tv_number = (TextView)findViewById(R.id.tv_number);
//这里路径写死,一访问服务器,就可以获得下载path = "http://192.168.1.109:8080/pp.zip";new Thread(){public void run() {try {URL url=new URL(path);HttpURLConnection conn=(HttpURLConnection) url.openConnection();conn.setRequestMethod("GET");length = conn.getContentLength();
//文件名可以从path中获得,访问网络获取文件的大小,第一次的话创建一个与目标文件大小名称一致的空文件String file_name=path.substring(path.lastIndexOf("/")+1);file = new File(Environment.getExternalStorageDirectory(),file_name);if(!file.exists()){RandomAccessFile rf=new RandomAccessFile(file, "rwd");rf.setLength((long) length);
//子线程中土司可以使用Looper.prepare()和Looper.loop();子线程土司的数量不要超过50Looper.prepare();Toast.makeText(MainActivity.this, "创建文件成功", 0).show();Looper.loop();}} catch (Exception e) {Looper.prepare();Toast.makeText(MainActivity.this, "创建文件失败", 0).show();Looper.loop();}};}.start();
//使用sp回显,线程数量,如果用户不输入,默认为空,吐司提示需要输入线程
//生成的sp文件名是跟下载的文件名是对应的String str=path.substring(path.lastIndexOf("/")+1,path.lastIndexOf("."));sp=getSharedPreferences(str, 0);String threadcount=sp.getString("number","");int progress_result=sp.getInt("progress_result", 0);reallengthrecord=sp.getLong("reallength", 0);tv_number.setText(progress_result+"%");pb.setProgress(progress_result);et.setText(threadcount);}public void click(View v){
//在做下载前,先判断SD卡是否处于挂载,然后再判断sd卡剩余容量,至少给用户留出100M的空间String status=Environment.getExternalStorageState();if(Environment.MEDIA_MOUNTED.equals(status)){long freespace=Environment.getExternalStorageDirectory().getFreeSpace();if(freespace>(file.length()+1024*1024*100)){String threadcount=et.getText().toString().trim();if(TextUtils.isEmpty(threadcount)){Toast.makeText(MainActivity.this, "需要输入线程数量呢", 0).show();return;}else{      number = Integer.parseInt(et.getText().toString().trim());}
//这里每个线程下载数量分配得很好(length/number)+1,这种情况见于(总大小15分4个线程来搞,
//每个大小如果按照length/number来算,3,3,3,6,显然不好,应该是4,4,4,3)long blockSize=length%number==0?length/number:((length/number)+1);System.out.println(blockSize);for (int threadId = 0; threadId < number; threadId++) {long startIndex = threadId*blockSize;long endIndex;if(threadId==number-1){endIndex = length-1;}else{endIndex = (threadId+1)*blockSize-1;}
//启动线程去下载,这样写的好处是让线程ID,起始结束位置跟线程捆绑,使用多线程断点下载必须这样,如果只是多线程下载,可以不用这样new DownloadThread(threadId, startIndex, endIndex).start();}}else{
//内存不足,会有提示Toast.makeText(getApplicationContext(), "sd卡的内存不足", 0).show();}}}private static class DownloadThread extends Thread{private int threadId;private long  startIndex;private long  endIndex;private long curlength;private File file2;public DownloadThread(int threadId, long startIndex, long endIndex) {this.threadId = threadId;this.startIndex = startIndex;this.endIndex = endIndex;
//this.curlength = startIndex;这行代码的关键作用不解释this.curlength = startIndex;}@Overridepublic void run() {System.out.println("第 " + threadId+" 号 线程开始下载了 ,  下载 : " + startIndex+"~"+endIndex);try{URL url = new URL(path);HttpURLConnection conn =(HttpURLConnection) url.openConnection();
//判断sp文件是否存在,存在就读取里面保存的每个线程跑了的进度,请求数据以该进度开始,以及设置RandomAccessFile开始写的位置String str2="/data/data/com.example.downloader/shared_prefs";   file2 = new File(str2,path.substring(path.lastIndexOf("/")+1,path.lastIndexOf("."))+".xml");RandomAccessFile rf=new RandomAccessFile(file, "rwd");if(file2!=null&&file2.length()>0){long recordlength=sp.getLong(threadId+"curlength",0);curlength=recordlength;conn.setRequestProperty("Range", "bytes="+curlength+"-"+endIndex);System.out.println("bytes="+curlength+"-"+endIndex+"aaaaaaaaa");rf.seek(curlength);}else{
//sp文件不存在,就从默认的开始位置请求数据conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);rf.seek(startIndex);}int code = conn.getResponseCode();System.out.println("code :" + code);
//请求的数据Range如果范围越界或者不存在,则请求码为416,206表示请求部分数据成功if(code==206){BufferedInputStream in = new BufferedInputStream(conn.getInputStream());int len=0;byte[] buf = new byte[1024*1024];while((len=in.read(buf))>0){rf.write(buf, 0, len);
//curlength是该内部类 的成员变量,reallength是MainActivity的成员变量
//curlength是该内部类 的成员变量可用于统计每个线程跑的进度
//reallength可以记录所有的线程跑得进度,但不能确定是否为实际下载的进度(如果不断点那就是实际下载的进度,断点就不是实际下载的进度)
//程序运行一次,reallength就会记录,如果关闭再打开,reallength又重新开始记录,所以需要我们自己给它续加进度,才是文件下载的最最真实的进度curlength+=len;reallength+=len;
//sp文件的生成也是很即时的,在while循环中只要本地文件一写入数据,这里sp文件就可以即时生成,保存数据,数据即时更新(有时生成的sp文件还有bak备份文件)
//reallengthrecord是MainActivity的成员变量,这是续加续加续加的问题int progress_result=(int) (((reallength+reallengthrecord)*100)/length);Editor edit=sp.edit();edit.putInt("progress_result",progress_result);edit.putLong(threadId+"curlength",curlength);edit.putString("number", number+"");edit.putLong("reallength",reallength+reallengthrecord);edit.commit();//handler需要传入reallength+reallengthrecord(续加,第一次没有这个,//  就是0,关掉程序再打开界面续传就要加上sp文件里面保存的之前的下载的真实进度)Message msg=Message.obtain();msg.obj=reallength+reallengthrecord;msg.what=0;handler.sendMessage(msg);}
//关流是必须的,关流是必须的,关流是必须的,还有rf的模式设置为rwd,不然下载到99%就不动了,那就悲剧了in.close();rf.close();}}catch(Exception e){System.out.println("下载失败");}
//线程下载完就该把sp文件给删了System.out.println("第 " + threadId+"线程下载完了 ...");file2.delete();}}}

界面:

问题1.:我下载1.2G文件的无压力,但是下载2.63G的文件,在创建文件的时候就失败了,难道是随机访问流在创建文件有大小限制?
问题2.:SharedPreferences不支持多线程如何理解,是在哪一种多线程环境下SharedPreferences不支持?

跪求大神指导

源码下载链接:
链接:http://pan.baidu.com/s/1qXSERLU 密码:rw73

安卓客户端的多线程断点下载(SharedPreferences版)相关推荐

  1. iOS开发网络篇—多线程断点下载

    iOS开发网络篇-多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始, ...

  2. Java多线程断点下载

    多线程下载已经提高了下载的效率,但是当一些特殊情况发生的时候,我们需要对程序进行处理,这样效率会更高.比如,断电断网等造成下载中断,那么我们下一次又要重新开始下载,这样效率底下,所以我们可以考虑使用断 ...

  3. android学习笔记---31_多线程断点下载器,下载原理实现

    1.1.31_多线程断点下载器 ----------------------- 1.软件界面:   文件下载路径              text框   button 下载   点击后,下面显示下载 ...

  4. android 多线程断点下载,listview 模式 开始 暂停等功能

    android 多线程断点下载,listview 模式 代码依次如下: 布局: <?xml version="1.0" encoding="utf-8"? ...

  5. 即拿即用-Android多线程断点下载

    线程下载只需要确定好下载一个文件需要多少个线程,一般来说最好为3条线程,因为线程过多会占用系统资源,而且线程间的相互竞争也会导致下载变慢. 其次下载的时候将文件分割为三份(假设用3条线程下载)下载,在 ...

  6. Android多线程断点下载

    到华为后,信息管理特别严格,文件不能外发.所以好久都没写博客了,今天周日,老婆非要我学习.就闲来无事,写一篇博客,呵呵-- 前段时间,项目中提到了断点下载apk并静默安装的需求.本打算用应用市场成熟的 ...

  7. Android(java)学习笔记158:多线程断点下载的原理(JavaSE实现)

    1. 为什么需要多线程下载?     服务器的资源有限,同时的平均地分配给每个客户端.开启的线程越多抢占的服务的资源就越多,下载的速度就越块. 2. 下载速度的限制条件? (1)你的电脑手机宽带的带宽 ...

  8. Android 多线程断点下载demo实现

    先来一张效果图: 主要实现思路: 每一个下载都是通过RandomAccessFile对下载资源的总长进行切割之后,根据我们设置的线程多少进行计算之后开启多线程下载的.而每一个任务都是一个AsyncTa ...

  9. java 下载暂停实现_Java实现多线程断点下载(下载过程中可以暂停)

    线程可以理解为下载的通道,一个线程就是一个文件的下载通道,多线程也就是同时开启好几个下载通道.当服务器提供下载服务时,使用下载者是共享带宽的,在优先级相同的情况下,总服务器会对总下载线程进行平均分配. ...

最新文章

  1. mysql乐观锁总结和实践
  2. 玲珑杯 1157 - 造物主的戒律 主席树+离散化
  3. 松下壁挂式新风系统推荐_壁挂式新风系统哪个好?
  4. python 链表 【测试题】
  5. Python安装第三方包(setup.py)
  6. mysql分页是物理分页_学习MySQL:什么是分页
  7. 获取应用名字、版本号
  8. 如何使用Visual Studio 2017建立一个C语言项目
  9. 用友u8cloud使用教程_用友财务软件还不会操作?看完这些操作,工作得心应手...
  10. 二维码软件如何扫描二维码打开网页
  11. suitecrm查询功能去掉统配符%
  12. (152)IES光源概述文件
  13. arcgis api for js绘制箭头图
  14. 基于Qt秒表设计(Qt绘图秒表示例)
  15. 海思hisi v350报错librecovery (native:platform) should not link to libhi_common (native:vendor)
  16. 高中数学40分怎么办_高中数学不好怎么办
  17. 坐标系转换--椭球面多项式拟合公式转换模型变换关系理解
  18. 坐标系旋转后的点坐标、坐标点旋转后的点坐标
  19. Python 3.6以后版本的格式化输出
  20. Windows系统中苹果ipa上传到App Store Connect

热门文章

  1. Linux 开机运行sh 脚本 三种方法
  2. 轮胎上java和jacr区别_SUV轮胎和轿车轮胎有什么区别?
  3. 现代数学概略(度量-拓扑-线性-赋范线性-巴纳赫-内积-希尔伯特
  4. MES在流程和离散制造企业的15个差别(下)
  5. 【vue】用WOW.js+animate.css实现页面滚动加载元素动画
  6. 我是培训出来的我怕谁
  7. 为题目为《心中的彩虹》的作文写一篇300字的结尾
  8. jQuery UI 下载 拖动组件
  9. 微语录(2011-02-07---2011-02-13)
  10. YX-AGV-A101系列AGV控制器