ANDROID笔记:多线程断点下载
//mainActivity
package com.example.multthreaddownload; import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import android.annotation.SuppressLint; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import com.example.multthreaddownload.DownLoadThread.DownLoadState; import com.example.multthreaddownload.util.Breakpoint; import com.example.multthreaddownload.util.MyPoint; /** * 主类 实现DownLoadState接口,用于控制下载线程的结束 * * @author Administrator * */ public class MainActivity extends Activity implements DownLoadState { private ProgressBar bar = null; private EditText text; private TextView textView; private int filelength = 0; // 下载状态 private boolean isRunning = false; // 下载路径 private File father = Environment.getExternalStorageDirectory(); private File file = new File(father, "file.mp3"); // 断点集合类 private Breakpoint breakpoint = null; // 开始/暂停 private boolean isPause = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { bar = (ProgressBar) findViewById(R.id.progressBar1); text = (EditText) findViewById(R.id.url); final Button button = (Button) findViewById(R.id.button1); textView = (TextView) findViewById(R.id.textView1); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (isRunning) { isRunning = false; isPause = true; button.setText("开始"); } else {// 开始/继续下载 isRunning = true; // 测试数据 text.setText("http://172.16.12.82:8080/AIAIAPP/file.mp3"); if (isPause) { conntineDown(); } else { startDown(); } isPause = false; button.setText("暂停"); } } }); } /* 消息处理 */ int len = 0; @SuppressLint("HandlerLeak") private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0x1) { len = len + Integer.valueOf(msg.obj.toString()); bar.setProgress(len); textView.setText(len + "/" + filelength); } else if (msg.what == 0x2) { if (breakpoint == null) {// 单例模式 breakpoint = new Breakpoint(text.getText().toString(), file.getAbsolutePath()); } // 得到各个线程的断点 MyPoint myPoint = (MyPoint) msg.obj; // 添加到断点集合类 breakpoint.add(myPoint); } } }; /** * 下载线程 */ private void startDown() { new Thread(new Runnable() { @Override public void run() { int threadNumber = 1;// 设置线程个数 URL url; try { url = new URL(text.getText().toString()); HttpURLConnection http = (HttpURLConnection) url .openConnection(); filelength = http.getContentLength();// 获取下载文件的长度 // 设置进度条的最大值 bar.setMax(filelength); System.out.println("文件长度:" + filelength); // 每个线程需要下载的大小 int length = filelength / threadNumber; for (int i = 0; i < threadNumber - 1; i++) { new DownLoadThread().startDownload(MainActivity.this, i, text.getText().toString(), file, i * length, (i + 1) * length, handler); } // 最后一个线程下载剩余的空间 new DownLoadThread().startDownload(MainActivity.this, 4, text.getText().toString(), file, (threadNumber - 1) * length, filelength, handler); System.out.println("设置完毕"); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } /** * 继续下载 */ private void conntineDown() { new Thread(new Runnable() { @Override public void run() { System.err.println("继续下载"); System.err.println(breakpoint.get().size()); // 根据断点集合类中集合的大小生成下载线程 for (int i = 0; i < breakpoint.get().size(); i++) { // 得到各个断点 MyPoint point = breakpoint.get().get(i); // 下载开始点和结束点:point.startPos--->point.endPos new DownLoadThread().startDownload(MainActivity.this, i, breakpoint.url, new File(breakpoint.filePath), point.startPos, point.endPos, handler); } // 清空断点记录 breakpoint.get().clear(); } }).start(); } /** * 实现接口,当返回的isRunning为false时,所有下载线程停止 */ @Override public boolean handle() { return isRunning; } }
//下载工具类DownLoadThread
package com.example.multthreaddownload; import java.io.File; 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 android.annotation.SuppressLint; import android.os.Handler; import android.os.Message; import com.example.multthreaddownload.util.MyPoint; /** * 下载线程 * * @author Administrator * */ public class DownLoadThread { private String urlString; private File file = null; private int startPos; private int endPos; // 用于将信息传递给主类 private Handler handler; private int id = 0; // 控制线程的结束 DownLoadState state = null; public void startDownload(DownLoadState state, int id, String urlString, File file, int startPos, int endPos, Handler handler) { this.state = state; this.id = id; this.urlString = urlString; this.file = file; this.startPos = startPos; this.endPos = endPos; this.handler = handler; // 启动下载线程 new Thread(new myRunnable()).start(); } private class myRunnable implements Runnable { @SuppressLint("SdCardPath") @Override public void run() { // { // Message msg = new Message(); // msg.obj = "线程启动"; // handler.sendMessage(msg); // } try { // System.err.println(startPos + "--->" + endPos); URL url = new URL(urlString); // System.out.println("file.exists():" + file.exists()); // System.out.println(file.getAbsolutePath()); if (!file.exists()) { file.createNewFile(); } // System.out.println("file.exists():" + file.exists()); HttpURLConnection http = (HttpURLConnection) url .openConnection(); // 请求时长 http.setConnectTimeout(5 * 1000); http.setRequestMethod("GET"); http.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);// 设置获取实体数据的范围 InputStream inStream = http.getInputStream(); byte[] buffer = new byte[1024]; int offset = 0; RandomAccessFile threadfile = new RandomAccessFile(file, "rwd"); // 设置距离文件开头的指针偏移量(将文件读写指针移动至startPos位置) threadfile.seek(startPos); int len = 0; // state.handle()控制循环(控制线程停止) while (state.handle() && (offset = inStream.read(buffer, 0, 1024)) != -1) { threadfile.write(buffer, 0, offset); len = len + offset; {// 将下载进度传递给主类,控制进度条 Message msg = new Message(); msg.what = 0x1; msg.obj = offset; handler.sendMessage(msg); } } // 当线程停止时,将断点信息船体给主类 if (!state.handle()) { // System.err.println(id + "--" + startPos + "--" // + (startPos + len) + "--" + endPos); // System.err.println(id + "pause"); { MyPoint point = new MyPoint((startPos + len), endPos); Message msg = new Message(); msg.what = 0x2; msg.obj = point; handler.sendMessage(msg); } } threadfile.close(); inStream.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } { Message msg = new Message(); msg.obj = "线程下载完毕"; handler.sendMessage(msg); } } } /** * 接口 * * @author Administrator * */ public interface DownLoadState { public boolean handle(); } }
//断点集合类
package com.example.multthreaddownload.util; import java.util.ArrayList; import java.util.List; public class Breakpoint { public String url; public String filePath; public Breakpoint(String url, String filePath) { this.url = url; this.filePath = filePath; } private List<MyPoint> points = new ArrayList<MyPoint>(); public void add(MyPoint point) { points.add(point); } public List<MyPoint> get() { return points; } }
//断点类
package com.example.multthreaddownload.util; public class MyPoint { public int startPos; public int endPos; public MyPoint(int startPos, int endPos) { this.startPos = startPos; this.endPos = endPos; } }
界面XML文件
<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=".MainActivity" > <Button android:id="@+id/button1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignLeft="@+id/url" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="156dp" android:text="下载" /> <EditText android:id="@+id/url" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="33dp" android:ems="10" android:singleLine="true" > <requestFocus /> </EditText> <ProgressBar android:id="@+id/progressBar1" style="?android:attr/progressBarStyleHorizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignLeft="@+id/url" android:layout_below="@+id/url" android:layout_marginTop="60dp" /> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignRight="@+id/progressBar1" android:layout_below="@+id/progressBar1" android:layout_marginTop="20dp" android:textAppearance="?android:attr/textAppearanceLarge" /> </RelativeLayout>
//需要下列权限(有wifi权限是因为在真机上用wifi调试了)
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 在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.READ_EXTERNAL_STORAGE" />