• Android(java)学习笔记159:多线程断点下载的原理(Android实现)


    之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载

    1. 新建一个Android工程:

    (1)其中我们先实现布局文件activity_main.xml:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="match_parent"
     5     android:orientation="vertical"
     6     tools:context=".MainActivity" >
     7 
     8     <EditText
     9         android:id="@+id/et_path"
    10         android:layout_width="match_parent"
    11         android:layout_height="wrap_content"
    12         android:text="http://192.168.1.100:8080/" />
    13 
    14     <EditText
    15         android:inputType="number"
    16         android:id="@+id/et_count"
    17         android:layout_width="match_parent"
    18         android:layout_height="wrap_content"
    19         android:hint="请设置下载线程的数量,默认3" />
    20 
    21     <Button
    22         android:layout_width="match_parent"
    23         android:layout_height="wrap_content"
    24         android:onClick="download"
    25         android:text="下载" />
    26 
    27     <LinearLayout
    28         android:paddingLeft="5dip"
    29         android:paddingRight="5dip"
    30         android:id="@+id/ll_container"
    31         android:layout_width="match_parent"
    32         android:layout_height="match_parent"
    33         android:orientation="vertical" >
    34     </LinearLayout>
    35 
    36 </LinearLayout>

    布局效果如下:

    (2)其中的进度条样式pb.xml如下

    <?xml version="1.0" encoding="utf-8"?>
    <ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_marginLeft="5dip"
        android:layout_marginRight="5dip"
        android:layout_height="wrap_content" />

    2. MainActivity.java:

      1 package com.itheima.mutiledownloader;
      2 
      3 import java.io.BufferedReader;
      4 import java.io.File;
      5 import java.io.FileInputStream;
      6 import java.io.FileNotFoundException;
      7 import java.io.IOException;
      8 import java.io.InputStream;
      9 import java.io.InputStreamReader;
     10 import java.io.RandomAccessFile;
     11 import java.net.HttpURLConnection;
     12 import java.net.MalformedURLException;
     13 import java.net.ProtocolException;
     14 import java.net.URL;
     15 
     16 import android.app.Activity;
     17 import android.os.Bundle;
     18 import android.os.Environment;
     19 import android.text.TextUtils;
     20 import android.view.View;
     21 import android.widget.EditText;
     22 import android.widget.LinearLayout;
     23 import android.widget.ProgressBar;
     24 import android.widget.Toast;
     25 
     26 public class MainActivity extends Activity {
     27     private EditText et_path;
     28     private EditText et_count;
     29     private LinearLayout ll_container;
     30     private int threadCount = 3;
     31     private String path;
     32     protected int runningThreadCount;
     33     
     34     @Override
     35     protected void onCreate(Bundle savedInstanceState) {
     36         super.onCreate(savedInstanceState);
     37         setContentView(R.layout.activity_main);
     38         et_count = (EditText) findViewById(R.id.et_count);
     39         et_path = (EditText) findViewById(R.id.et_path);
     40         ll_container = (LinearLayout) findViewById(R.id.ll_container);
     41     }
     42 
     43     public void download(View view){
     44         String str_count = et_count.getText().toString().trim();
     45         path = et_path.getText().toString().trim();
     46         if(TextUtils.isEmpty(path)&&!path.startsWith("http://")){
     47             Toast.makeText(this, "下载路径不合法", 0).show();
     48             return;
     49         }
     50         if(!TextUtils.isEmpty(str_count)){
     51             threadCount = Integer.parseInt(str_count);
     52         }
     53         ll_container.removeAllViews();
     54         for(int i=0;i<threadCount;i++){
     55             ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb,null);
     56             ll_container.addView(pb);
     57         }
     58         new Thread(){
     59             public void run() {
     60                 try {
     61                     URL url = new URL(path);
     62                     HttpURLConnection conn = (HttpURLConnection) url.openConnection();
     63                     conn.setRequestMethod("GET");
     64                     int code = conn.getResponseCode();
     65                     if(code == 200){
     66                         int length = conn.getContentLength();
     67                         System.out.println("服务器文件的大小为:"+length);
     68                         //创建一个空白文件文件的大小和服务器资源一样
     69                         RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFileName(path), "rw");
     70                         raf.setLength(length);
     71                         raf.close();
     72                         //每个线程下载的平均区块大小
     73                         int blocksize = length / threadCount;
     74                         runningThreadCount = threadCount;
     75                         for(int threadId = 0;threadId<threadCount;threadId++){
     76                              int startIndex = threadId*blocksize;
     77                              int endIndex = (threadId+1)*blocksize-1;
     78                              if(threadId==(threadCount-1)){//最后一个线程的修正
     79                                  endIndex = length - 1;
     80                              }
     81                              new DownloadThread(startIndex, endIndex, threadId).start();
     82                         }
     83                     }
     84                 } catch (Exception e) {
     85                     e.printStackTrace();
     86                     showToast("下载失败");
     87                 }
     88                 
     89             };
     90         }.start();
     91         
     92     }
     93     class DownloadThread extends Thread{
     94         /**
     95          * 理论上第一次开始的位置
     96          */
     97         int firstStartIndex;
     98         int startIndex;
     99         int endIndex;
    100         /**
    101          * 当前线程下载到文件的位置
    102          */
    103         int filePosition;
    104         int threadId;
    105         /**
    106          * 当前线程需要下载的总文件大小
    107          */
    108         int threadTotal;
    109         ProgressBar pb;//当前线程对应的进度条
    110         /**
    111          * 
    112          * @param startIndex 开始位置
    113          * @param endIndex 结束位置
    114          * @param threadId 线程id
    115          */
    116         public DownloadThread(int startIndex, int endIndex, int threadId) {
    117             this.startIndex = startIndex;
    118             this.firstStartIndex = startIndex;
    119             this.endIndex = endIndex;
    120             threadTotal = endIndex - startIndex;
    121             this.threadId = threadId;
    122             pb = (ProgressBar) ll_container.getChildAt(threadId);
    123             pb.setMax(threadTotal);
    124             filePosition = startIndex;
    125         }
    126 
    127         @Override
    128         public void run() {
    129             //System.out.println("线程:"+threadId+"理论下载的位置:"+startIndex+"~~~"+endIndex);
    130             //读取,看看有没有下载的历史数据,读下载的进度
    131             try {
    132             File file = new File(Environment.getExternalStorageDirectory().getPath()+"/"+threadId+getFileName(path)+".txt");//用一个文本记录当前线程下载的进程
    133             if(file.exists()&&file.length()>0){
    134                 FileInputStream fis = new FileInputStream(file);
    135                 BufferedReader br = new BufferedReader(new InputStreamReader(fis));
    136                 filePosition = Integer.parseInt(br.readLine());//上一次下载到文件的哪个位子。
    137                 startIndex = filePosition;
    138                 fis.close();
    139             }
    140             System.out.println("线程:"+threadId+"实际上下载的位置:"+startIndex+"~~~"+endIndex);
    141                 URL url = new URL(path);
    142                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    143                 conn.setRequestMethod("GET");
    144                 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
    145                 int code = conn.getResponseCode();//2XX 成功 3XX重定向 4XX资源找不到 5XX服务器异常
    146                 if(code == 206){
    147                     InputStream is = conn.getInputStream();
    148                     RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getPath()+"/"+getFileName(path), "rwd");
    149                     //一定要记得定位文件写的位置
    150                     raf.seek(startIndex);
    151                     byte[] buffer = new byte[1024*4];//缓冲区的大小
    152                     int len = -1;
    153                     while((len = is.read(buffer))!=-1){
    154                         raf.write(buffer, 0, len);
    155                         filePosition+=len;//记录写入数据的进度
    156                         int process = filePosition - firstStartIndex;
    157                         pb.setProgress(process);
    158                         RandomAccessFile rafinfo = new RandomAccessFile(file, "rwd");
    159                         rafinfo.write(String.valueOf(filePosition).getBytes());
    160                         rafinfo.close();
    161                     }
    162                     raf.close();
    163                     is.close();
    164                     System.out.println("线程:"+threadId+"下载完毕了。");
    165                     synchronized (MainActivity.this) {//加锁,同步运行,保证下面一段代码在同一个时间片运行,原子性执行
    166                         runningThreadCount--;
    167                         if(runningThreadCount==0){
    168                             System.out.println("所有的线程都下载完毕了");
    169                             showToast("下载完毕了");
    170                             for(int i =0;i<threadCount;i++){
    171                                 File f = new File(Environment.getExternalStorageDirectory().getPath()+"/"+i+getFileName(path)+".txt");
    172                                 System.out.println(f.delete());
    173                             }
    174                         }
    175                     }
    176                     
    177                 }
    178             } catch (Exception e) {
    179                 e.printStackTrace();
    180             }
    181         }
    182     }
    183     /**
    184      * 获取路径对应的文件名
    185      * @param path
    186      * @return
    187      */
    188     private static String getFileName(String path){
    189         int beginIndex = path.lastIndexOf("/")+1;
    190         return path.substring(beginIndex);
    191     }
    192     
    193     
    194     private void showToast(final String text){
    195         runOnUiThread(new Runnable() {
    196             @Override
    197             public void run() {
    198                 Toast.makeText(MainActivity.this, text, 0).show();
    199             }
    200         });
    201     }
    202 }

    3. 其中AndroidMainfest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.itheima.mutiledownloader"
        android:versionCode="1"
        android:versionName="1.0" >
    
        <uses-sdk
            android:minSdkVersion="8"
            android:targetSdkVersion="17" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <uses-permission android:name="android.permission.INTERNET"/>
    
        <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name="com.itheima.mutiledownloader.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>

     

  • 相关阅读:
    Jmeter_远程启动 I
    jmeter(九)逻辑控制器
    Mysql innodb 间隙锁 (转)
    MySQL- InnoDB锁机制
    Innodb间隙锁,细节讲解(转)
    性能测试:压测中TPS上不去的几种原因分析(就是思路要说清楚)
    Redis性能调优
    Redis基础
    VMThread占CPU高基本上是JVM在频繁GC导致,原因基本上是冰法下短时间内创建了大量对象堆积造成频繁GC。
    关于Hibernate二级缓存第三方插件EHCache缓存
  • 原文地址:https://www.cnblogs.com/hebao0514/p/4793046.html
Copyright © 2020-2023  润新知