Android模拟多线程的断点下载
2015-06-23 21:31
465 查看
在本代码当中,是模拟多线程的断点下载,在代码中只是使用了简单的使用一个txt文件来保存每个线程的下载进度
(其实最好是将进度保存到我们Android自带的sqlite数据库当中)。
3.主程序MainActivity:
(其实最好是将进度保存到我们Android自带的sqlite数据库当中)。
ok,不多说,看看模拟的多线程断点下载的代码:
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">1.Android应用的主配置文件manifest:</span>pan>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.mutildownload" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" /> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.mutildownload.MainActivity" android:label="@string/app_name"> <intent-filter > <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>2.XML配置文件activity_main:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <EditText android:id="@+id/et" android:hint="请输入下载地址" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="http://dl.360safe.com/360NmGameAccSetup.exe" android:ems="10" > </EditText> <ProgressBar android:id="@+id/pb" style="?android:attr/progressBarStyleHorizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <Button android:onClick="download" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下载" /> <TextView android:layout_height="wrap_content" android:layout_width="wrap_content" android:id="@+id/tv_progress"/> </LinearLayout>
3.主程序MainActivity:
package com.example.mutildownload; 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.Handler; import android.os.Message; import android.text.TextUtils; import android.view.View; 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 ProgressBar pb; private TextView tv_progress; private static final int ThreadCount = 3; private static final int PATH_ERROR = 1; private static final int SERVER_ERROR = 2; private static final int DOWNLOAD_FINSH = 4; private static final int PROGRESS_UPDATE = 5; public static final int REDOWNLOAD = 6; private static int RunningThread = ThreadCount; private static int CurrentProgress = 0; Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case PATH_ERROR: Toast.makeText(getApplicationContext(), "下载路径错误", 0).show(); break; case SERVER_ERROR: Toast.makeText(getApplicationContext(), "服务器错误", 0).show(); break; case DOWNLOAD_FINSH: Toast.makeText(getApplicationContext(), "下载完成", 0).show(); break; case PROGRESS_UPDATE: tv_progress.setText("下载的进度:"+pb.getProgress()*100 / pb.getMax()+"%"); break; case REDOWNLOAD: Toast.makeText(getApplicationContext(), "下载失败,再次重新下载", 0).show(); break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et = (EditText) this.findViewById(R.id.et); pb = (ProgressBar) this.findViewById(R.id.pb); tv_progress = (TextView) this.findViewById(R.id.tv_progress); } public void download(View view) { // 连接服务器,获取一个文件,知道文件的长度,并在本地创建一个文件和服务器文件大小一样的临时文件 // String path = "http://dl.360safe.com/360DrvMgrInstaller_beta.exe" final String path = et.getText().toString().trim(); if (TextUtils.isEmpty(path)) { Toast.makeText(getApplicationContext(), "下载路径不能为空", 0).show(); return; } new Thread() { public void run() { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); conn.setRequestMethod("GET");// 设置get请求 conn.setReadTimeout(5000);// 设置超时时间 int code = conn.getResponseCode();// 得到响应码 if (code == 200) { // 读取到的文件的长度实,际上就是文件的长度 int length = conn.getContentLength(); // System.out.println("文件的长度:"+length); //设置进度条的最大值 pb.setMax(length); // 读取到服务器文件的大小,就利用RandomAccessFile随机文件访问类在本地客户端创建一个和服务器相同大小的文件 RandomAccessFile ras = new RandomAccessFile( "/sdcard/360.exe", "rwd"); // 设置要创建文件的长度 ras.setLength(length); ras.close(); int blockSize = length / ThreadCount; // 假设有3个线程去下载服务器中的文件 // 就要计算每个线程下载文件的哪一个部分,也就是每个线程应该下载的文件的大小 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 DownLoadThread(path, threadId, startIndex, endIndex).start(); } } else { System.out.println("读取失败"); Message msg = new Message(); msg.what = SERVER_ERROR; handler.sendMessage(msg); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println("路径有问题,下载失败"); Message msg = new Message(); msg.what = PATH_ERROR; handler.sendMessage(msg); } }; }.start(); } public class DownLoadThread extends Thread { private String path; private int threadId; private int startIndex; private int endIndex; /** * @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[] buffer = new byte[1024]; int leng = 0; leng = fis.read(buffer); String downloadlen = new String(buffer, 0, leng); int downloadint = Integer.parseInt(downloadlen); //如果当前线程下载完成,而文件还没删除,该线程被断后,再次执行就让他结束 if(downloadint == endIndex){ CurrentProgress --; return; } synchronized (MainActivity.this) { int CurrentThreadProgressint = downloadint - startIndex; CurrentProgress += CurrentThreadProgressint;//获取到总的进度 } startIndex = downloadint;// 重置开始读取数据的位置,保证从断点的位置开始下载 System.out.println("tempfile:" + threadId + "downloadin" + downloadint); } URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); // 设置下载服务器资源文件的部分内容,指定文件的位置 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); conn.setRequestMethod("GET"); conn.setReadTimeout(10000); int code = conn.getResponseCode();// 响应码,从服务器请求全部资源:200 // ok,请求部分资源:206 ok // System.out.println("responseCode:"+code); InputStream is = conn.getInputStream();// 已经设置了文件的读取位置,返回的是当前位置文件对应的输入流 RandomAccessFile ras = new RandomAccessFile("/sdcard/360.exe", "rwd"); ras.seek(startIndex);// 设置文件的定位 int len = 0; int total = 0;// 记录已经读取了多少数据 byte[] buffer = new byte[1024]; // 在这里可以使用File,RandomAccessFile不容易损伤硬盘 while ((len = is.read(buffer)) != -1) { RandomAccessFile raf = new RandomAccessFile("/sdcard/" + threadId + ".txt", "rwd");// 用来记录当前线程下载的位置 ras.write(buffer, 0, len); total += len; //加锁,同步执行 synchronized (MainActivity.this) { CurrentProgress += len;//获取下载的进度及每个线程的下载量之和 pb.setProgress(CurrentProgress);//修改进度条 //特殊情况,progressBar和progressDialog进度条对话框可以在子线程当中修改UI,内部代码做过特殊的处理 //Message msg = new Message();//每次跟新界面都创建一个Message对象,显然效率会受到影响 Message msg = Message.obtain();//会在从消息池当中返回一个Message对象,避免重复创建 msg.what = PROGRESS_UPDATE; handler.sendMessage(msg); } // 存储值: // 1.采用已读数据(total)来记录下载了多少数据,这是不可行的,因为total每次接着断点下载时,被初始化为0 // 2.采用下载位置(startIndex+total)来记录当前线程下载的位置 raf.write((startIndex + total + "").getBytes());// 以字符串的方式将读取数据存放到文件当中,记录的就是下载的位置 raf.close(); } is.close(); ras.close(); System.out.println("线程" + threadId + "下载完成"); } catch (Exception e) { e.printStackTrace(); System.out.println("ThreadId"+threadId+"下载失败"); Message msg = new Message(); msg.what = REDOWNLOAD; handler.sendMessage(msg); //如果线程下载失败,再次重新下载 new DownLoadThread(path, threadId, startIndex, endIndex).start(); RunningThread++;//finally必须要执行,但线程并没有下载完成,所以需要加回来 } finally { threadFinsh();//线程执行完毕 } } //同步线程,保证将断点文件删除 private synchronized void threadFinsh() { // 下载完成,将保存数据的文件删除 RunningThread--;// 记录了正在执行的线程,线程执行完毕,就将所有的记录文件删除 if (RunningThread == 0) { for (int i = 1; i <= ThreadCount; i++) { File deletefile = new File("/sdcard/"+i + ".txt"); deletefile.delete(); } System.out.println("文件删除完"); Message msg = new Message(); msg.what = DOWNLOAD_FINSH; handler.sendMessage(msg); } } } }
相关文章推荐
- Android通讯录数据库介绍与基本操作(增删改查)
- android的布局xml文件如何添加注释?
- Android开发:GridView与ListView相互切换(动画效果)
- Android中Fragment的知识
- Android OpenGL ES(三)----编程框架
- 安卓xml布局中 android:paddingBottom="@dimen/activity_vertical_margin"是什么意思?
- Android 图片裁剪 超出内存
- Android实战简易教程-第二十枪(GridView长按显示删除)
- android 伸缩控件ExpandableListView 展开失败的可能原因。
- android布局之 GridLayout的使用
- Activity的七大生命周期
- android-textview
- 通过Intent传输图片导致广播消息异常的问题根因分析(通过分析Android源码反向推理)
- Android 通知栏ANR原因
- Android入门之GridView(九宫图)
- android-RecyclerView
- Android深入浅出之Zygote
- Android CTS(兼容性测试)整个流程
- android:android Intent and IntentFilter
- Ubuntu14.04下搭建android5.1编译环境