1. 程式人生 > >android 利用java中的多執行緒和io流,最快速度的下載伺服器檔案,android 實現apk下載展現通知欄

android 利用java中的多執行緒和io流,最快速度的下載伺服器檔案,android 實現apk下載展現通知欄

首先,我們得來說下多執行緒下載實現的大致思路,以及在使用多執行緒下載過程應該需要注意的問題。
     多執行緒下載實現的大致思路:
     大致思路是這樣的,也就是把整個一個檔案資源分為若干個部分,然後開啟若干個執行緒,並且使得每個執行緒負責下載每個子部分的檔案,由於
     執行緒下載是非同步的,大大縮短了下載的時間,最後將所有的子部分寫入到同一個檔案中,最後重組得到一個完整的檔案。
     首先,我們得說下整個資源下載,我們通過網路請求然後可以得到一個檔案的整體輸入流,然後我們需要得到不是整個檔案的輸入流,而是
     得到每個執行緒負責下載的子部分檔案的輸入流。然後得到這些指定大小的輸入流,再次寫入到我們本地檔案中,寫入流的時候也需要注意,
     每個子部分輸入流必須寫入相對應的檔案位置上,否則會造成後一個寫入檔案中的輸入流會覆蓋上一部分寫入檔案的輸入流。
  再次整理一下思路我們需要注意哪些問題:
          問題一:如何獲得整個下載資源的大小
   問題二:獲得整個檔案資源的大小後,如何去拆分這個檔案,如何去分配每個子部分檔案,並且讓不同的子執行緒來下載,也就是如何
   確定每個子執行緒下載檔案的區間(即每個子執行緒負責下載的子部分檔案的開始下載和結束下載位置)
   問題三:如何去獲得我們需要的指定的大小的輸入流,而不是獲得由服務端一下把整個檔案的輸入流
          問題四:如何使得每個子執行緒寫入檔案合適的起始位置,並且系統預設就是每次往檔案中寫資料的時候都是從0位置開始寫,這樣會出現後面
   寫入的資料,會覆蓋前面寫入的資料。
3、逐個擊破解決上面幾個問題,待這些問題都解決了,那麼我們的多執行緒也就實現了
      問題一的解決辦法:
                              獲取整個下載資源大小,這個很簡單,可以直接通過HttpURLConnection網路請求而得到一個HttpURLConnection型別的連線物件
         中的getContentLength來得到我們需要下載資原始檔的大小。
         更重要的是:我們拿到這個檔案大小來幹什麼???其實說白了,就是仿造一個一樣大小的空白檔案來佔用本地儲存空間
         為什麼要這樣做呢??其實細心的人就發現,當我們在下載電影或者檔案的時候我們會發現在下載目錄中會出現一個臨時檔案
         而這個臨時檔案大小與我們要下載的檔案的大小一致,並且這個檔案此時是空白的。不信你可以右擊檢視屬性檔案大小,
         為什麼要佔用空間,我們可以去設想一下這個情景,假如電腦中的儲存空間只剩1G了,而你下載的電影正好1G,電影正在下載過程
         假如沒有提前佔好空間的話,在下載過程中你又下載一個首歌,此時空間明顯不足以裝下這部電影,那請問這部電影將怎麼辦?
         所以為了防止這種情況出現,也就出現所謂提前佔用儲存空間。
 問題二的解決辦法:
                         如何去拆分整個檔案,因為我們要讓每個執行緒去負責每個子部分檔案的下載任務,所以直接按照執行緒的數目來分吧,但是有個問題
    就是無法做到每個執行緒平均分配每個子部分檔案的長度,所以我們就採用一個辦法,假設有n個執行緒,就是讓前n-1大小一樣,最後一個
    就包括一些剩餘的零頭吧,也就是最後一個執行緒處理最後剩餘所有的子部分檔案長度。
    所以就有如下公式:
                           前n-1個執行緒的子部分檔案尺寸:  size=len/threadCount
      這樣也就很容易得到了每個執行緒負責子部分檔案的長度
      虛擬碼:
           int size=length/threadCount;
    for (int id = 0; id < threadCount; id++) {
     //1、確定每個執行緒的下載區間
     //2、開啟對應子執行緒下載
     int startIndex=id*size;//確定每個執行緒的子部分檔案的開始位置
     int endIndex=(id+1)*size-1;//確定每個執行緒的子部分檔案的結束位置
     if (id==threadCount-1) {//判斷如果是最後一個執行緒,直接讓它的子部分檔案結束位置延伸最後即可,也即檔案長度-1
      endIndex=length-1;
     }
     System.out.println("第"+id+"個執行緒的下載區間為"+startIndex+"--"+endIndex);
 問題三的解決辦法:
               如何去指定確定大小的輸入流呢?在HttpURLConnection物件中有個setRequestProperty方法設定頭部資訊可以拿到拿到指定大小的輸入流
           conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
   但是需要注意的一點是:你的請求的伺服器必須支援多執行緒下載,並且才能拿到指定大小輸入流
   為什麼要拿到指定大小的輸入流為的就是與劃分子部分檔案長度對應起來,得到的對應指定大小的輸入流通過輸出流寫入到相應大小的子部分檔案中
 問題四的解決辦法:
                防止預設設定(每次都從0位置開始寫)的影響使得後面寫入的資料會覆蓋前面寫入的資料,通過RandomAccessFile中的seek方法傳入每個子部分檔案開始的
         位置,也就間接更改了預設每次都從0開始寫,從每個子部分檔案起始位置寫,這樣就不會覆蓋前面的資料。
         RandomAccessFile mAccessFile=new RandomAccessFile(file, "rwd");//"rwd"可讀,可寫
    mAccessFile.seek(startIndex);//表示從不同的位置寫檔案
通過解決上面四個問題,把整個實現的思路梳理了一下,那麼我將實現過程大致總結為以下5點:
                                1、得到下載資原始檔的大小,產生相同大小的隨機RandomAccessFile空白檔案,來佔用空間
    2、並且把RandomAccessFile空白檔案分割成若干個部分,並且確定每個子部分檔案的下載空間
    3、開啟對應的子執行緒
    4、從網路伺服器拿到指定大小的部分輸入流

    5、從RandomAccessFile檔案的不同的開始位置開始往其中寫入我們得到對應的指定大小的輸入流

  1. package com.mikyou.multithread;

  2. import java.io.File;

  3. import java.io.RandomAccessFile;

  4. import java.net.HttpURLConnection;

  5. import java.net.MalformedURLException;

  6. import java.net.URL;

  7. /**

  8. * @author zhongqihong

  9. * 多執行緒下載

  10. * */

  11. public class Main {

  12. public static final String PATH="http://120.203.56.190:8088/upload/mobilelist.xml";

  13. public static int threadCount=3;//進行下載的執行緒數量

  14. public static void main(String[] args) {

  15. try {

  16. URL url=new URL(PATH);

  17. HttpURLConnection conn=(HttpURLConnection) url.openConnection();

  18. conn.setRequestMethod("GET");

  19. conn.setConnectTimeout(8000);

  20. conn.setReadTimeout(8000);

  21. conn.connect();

  22. if (conn.getResponseCode()==200) {

  23. int length=conn.getContentLength();//返回檔案大小

  24. //佔據檔案空間

  25. File file =new File("mobilelist.xml");

  26. RandomAccessFile mAccessFile=new RandomAccessFile(file, "rwd");//"rwd"可讀,可寫

  27. mAccessFile.setLength(length);//佔據檔案的空間

  28. int size=length/threadCount;

  29. for (int id = 0; id < threadCount; id++) {

  30. //1、確定每個執行緒的下載區間

  31. //2、開啟對應子執行緒下載

  32. int startIndex=id*size;

  33. int endIndex=(id+1)*size-1;

  34. if (id==threadCount-1) {

  35. endIndex=length-1;

  36. }

  37. System.out.println("第"+id+"個執行緒的下載區間為"+startIndex+"--"+endIndex);

  38. new DownLoadThread(startIndex, endIndex, PATH, id).start();

  39. }

  40. }

  41. } catch (Exception e) {

  42. e.printStackTrace();

  43. }

  44. }

  45. }

  1. package com.mikyou.multithread;

  2. import java.io.BufferedReader;

  3. import java.io.File;

  4. import java.io.FileInputStream;

  5. import java.io.IOException;

  6. import java.io.InputStream;

  7. import java.io.InputStreamReader;

  8. import java.io.RandomAccessFile;

  9. import java.net.HttpURLConnection;

  10. import java.net.MalformedURLException;

  11. import java.net.ProtocolException;

  12. import java.net.URL;

  13. public class DownLoadThread extends Thread{

  14. private int startIndex,endIndex,threadId;

  15. private String urlString;

  16. public DownLoadThread(int startIndex,int endIndex,String urlString,int threadId) {

  17. this.endIndex=endIndex;

  18. this.startIndex=startIndex;

  19. this.urlString=urlString;

  20. this.threadId=threadId;

  21. }

  22. @Override

  23. public void run() {

  24. try {

  25. URL url=new URL(urlString);

  26. HttpURLConnection conn=(HttpURLConnection) url.openConnection();

  27. conn.setRequestMethod("GET");

  28. conn.setConnectTimeout(8000);

  29. conn.setReadTimeout(8000);

  30. conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);//設定頭資訊屬性,拿到指定大小的輸入流

  31. if (conn.getResponseCode()==206) {//拿到指定大小位元組流,由於拿到的使部分的指定大小的流,所以請求的code為206

  32. InputStream is=conn.getInputStream();

  33. File file =new File("mobilelist.xml");

  34. RandomAccessFile mAccessFile=new RandomAccessFile(file, "rwd");//"rwd"可讀,可寫

  35. mAccessFile.seek(startIndex);//表示從不同的位置寫檔案

  36. byte[] bs=new byte[1024];

  37. int len=0;

  38. int current=0;

  39. while ((len=is.read(bs))!=-1) {

  40. mAccessFile.write(bs,0,len);

  41. current+=len;

  42. System.out.println("第"+threadId+"個執行緒下載了"+current);

  43. }

  44. mAccessFile.close();

  45. System.out.println("第"+threadId+"個執行緒下載完畢");

  46. }

  47. } catch (Exception e) {

  48. e.printStackTrace();

  49. }

  50. super.run();

  1. }

  2. }

實現android apk通知欄版本升級:

private static String savePath;
private static String saveFileName;
private static  int id =1;
private static NotificationManager mNotifyManager;
private static NotificationCompat.Builder mBuilder ;
    savePath =  Environment.getExternalStorageDirectory()  + "/HJXimg/";
    saveFileName = savePath + "huixueyun_app_pro.apk";
    mNotifyManager = (NotificationManager) homeActivity.getSystemService(Context.NOTIFICATION_SERVICE);
    mBuilder = new NotificationCompat.Builder(homeActivity);
    mBuilder.setContentTitle("版本升級").setContentText("下載中,請稍等……").setSmallIcon(R.mipmap.yh_logo);
    mBuilder.setProgress(100, 0,false);
    mNotifyManager.notify(id, mBuilder.build());
    HttpUtils utils = new HttpUtils();

    utils.download(fileurl, saveFileName, new RequestCallBack<File>() {
        @Override
        public void onFailure(HttpException arg0, String arg1) {
            // TODO Auto-generated method stub
Toast.makeText(homeActivity,"下載失敗!",Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onSuccess(ResponseInfo<File> arg0) {
            // TODO Auto-generated method stub
LLog.e("argo--");
            mBuilder.setProgress(0, 0,false).setContentTitle("下載完成").setContentText("");
            mNotifyManager.notify(id, mBuilder.build());
            installApk(saveFileName,homeActivity);
        }

        @Override
        public void onLoading(long total, long current, boolean isUploading) {

            int currentNum = (int) (100*current/total);
            mBuilder.setProgress(100, currentNum,false);
            mNotifyManager.notify(id, mBuilder.build());
        }
    });

}


private static void installApk(String saveFileName, HomeActivity homeActivity) {
    File apkfile = new File(saveFileName);
    if (!apkfile.exists()) {
        return;
    }
    Intent i = new Intent(Intent.ACTION_VIEW);
    i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive");
    i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    homeActivity.startActivity(i);
}

相關推薦

android 利用java執行io速度下載伺服器檔案,android 實現apk下載展現通知欄

首先,我們得來說下多執行緒下載實現的大致思路,以及在使用多執行緒下載過程應該需要注意的問題。      多執行緒下載實現的大致思路:      大致思路是這樣的,也就是把整個一個檔案資源分為若干個部分,然後開啟若干個執行緒,並且使得每個執行緒負責下載每個子部分的檔案,由於

java執行的建立啟動(1)

多執行緒概述 1.什麼是多執行緒 執行緒是程式執行的一條路徑,一個程序中可以包含多條執行緒;多執行緒併發執行可以提高程式的效率 2.程序和執行緒之間的關係 作業系統可以同時執行多個任務,每個任務就是程序;程序可以同時執行多個任務,每個任務就是執

java執行一定嗎?看完就知道!!!

理解上下文切換   即使是單核處理器也支援多執行緒執行程式碼,CPU通過每個執行緒分配CPU時間片來實現這個機制.時間片是CPU分配給多個執行緒的時間,因為時間片非常短,所以CPU通過不停的切換執行緒執行,讓我們感覺多個執行緒是同時執行的,時間片一般是幾十毫秒(ms).  

Java執行併發體系知識點彙總

一、多執行緒 1、作業系統有兩個容易混淆的概念,程序和執行緒。 程序:一個計算機程式的執行例項,包含了需要執行的指令;有自己的獨立地址空間,包含程式內容和資料;不同程序的地址空間是互相隔離的;程序擁有各種資源和狀態資訊,包括開啟的檔案、子程序和訊號處理。 執行緒:表示程

Java進階(四十二)Java執行使用匿名內部類的方式進行建立3種方式

Java中多執行緒使用匿名內部類的方式進行建立3種方式 package cn.edu.ujn.demo; // 匿名內部類的格式: public class ThreadDemo {

Java執行交替列印

一、兩執行緒交替列印數字和字母 要求控制檯中輸出的內容為 A1B2C3D4 這應該是多執行緒面試題中推簡單的筆試題了吧; 1.Object. notify()和Object.wait()實現; private void init (){ char

JAVA執行 程序

首先了解一些基本知識 執行緒               執行緒,有時被稱為輕量程序(Lightweight Process,LWP),是程式執行流的最小單元。一個標準的執行緒由執行緒ID,當前指令指標(PC

java 執行池 ExecutorService shutdonw及其執行執行完成判斷

1.線上程池 ExecutorService的使用過程中,主執行緒開啟了多個子執行緒,如果判斷所有子執行緒任務是否完成問題; 方法一: BusinessHandler b = new Busines

java 執行之死鎖淺析

出現死鎖的前提條件: 1.必須是至少2個以上的執行緒在執行; 2.同時要保證其中至少有兩個執行緒中的鎖(至少有兩個)是相同的,而且都有鎖的巢狀; 分析:首先要明確的是當兩個執行緒都擁有相同的鎖時候,誰先拿到鎖,誰就有執行權(比如執行緒①先拿到執行權),同時②執行緒就沒有執

利用執行TCP技術實現客戶端與服務端之間的通訊

server.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h>

java執行讀取同一個檔案的不同位置執行讀取檔案

今天遇到一個問題,需要多個執行緒讀取同一個檔案的不同位置,提高效率,寫程式碼驗證了一下,把結果記錄下來。 首先我們寫個檔案,然後多執行緒讀取,以下是我實驗的程式碼: package com.alibaba.middleware.race; im

java執行筆記(一)

java對多執行緒程式設計提供了內建支援。多執行緒程式包含同時執行的兩個或多個部分。這種程式的每一部分被稱為一個執行緒,並且每一個執行緒定義了單獨的執行路徑。因此,多執行緒是特殊形式的多工處理。 程序:正在進行中的程式,是重量級任務,需要自己的地址空間。程序間通訊開銷很大,

Java執行啟動為什麼呼叫的是start方法而不是run方法?

### 前言 大年初二,大家新年快樂,我又開始碼字了。寫這篇文章,源於在家和基友交流的時候,基友問到了,我猛然發現還真是這麼回事,多執行緒啟動呼叫的都是start,那麼為什麼沒人掉用run呢?於是開啟我的idea,翻一波程式碼,帶大家一探究竟。 ### 繼承thread類實現多執行緒 我們知道java有三種方

python執行的共享資料通過queue來實現內有生產者消費者經典模型的示例程式碼

queue:佇列,即先進先出,它有以下幾個方法: 1.判斷佇列的大小:size() 2.向佇列中新增:put() 3.向佇列中取出:get() 4.如果佇列規定了長度,用來判斷是否滿了:full() import threading,time import queu

一位10年Java程式設計師總結進階的你懂執行jvm優化嗎?

感謝朋友們的認可和指正。本文是有感而發,因為看過了太多坑人的部落格和書籍,感慨自己走過的彎路,不希望其他初學者被網上互相抄襲的部落格和東拼西湊的書籍浪費時間,想以一個相對巨集觀的視野來描述一個概念,力求通俗易懂,所以沒有深入太多細節,簡化了很多模型,給部分朋友造成了疑惑,說聲抱歉。也沒有配圖,都是抽

java執行Thread類Runnable介面使用方法

java提供了兩種執行緒方式,一種是繼承java.lang包下的Thread類,覆寫Thread類的run()方法,在run()方法中實現執行線上程上的程式碼!第二種是實現Runnable介面建立多執行

JAVA基礎複習(七)執行網路

1、建立執行緒和任務,如: //任務類必須實現Runnable介面 public class TaskClass implements Runnable{ ... public TaskClass(...){ ... } //想要在該執行緒執行的

JAVA執行併發面試問題

1. 程序和執行緒之間有什麼不同? 一個程序是一個獨立(self contained)的執行環境,它可以被看作一個程式或者一個應用。而執行緒是在程序中執行的一個任務。Java執行環境是一個包含了不同的類和程式的單一程序。執行緒可以被稱為輕量級程序。執行緒需要較少的資源來建立和駐留在程

【小家javaJava執行(父執行)與子執行的通訊聯絡

相關閱讀 【小家java】java5新特性(簡述十大新特性) 重要一躍 【小家java】java6新特性(簡述十大新特性) 雞肋升級 【小家java】java7新特性(簡述八大新特性) 不溫不火 【小家java】java8新特性(簡述十大新特性) 飽受讚譽 【小家java】java9

Java面試:投行的15個執行併發面試題

多執行緒和併發問題已成為各種 Java 面試中必不可少的一部分。如果你準備參加投行的 Java 開發崗位面試,比如巴克萊銀行(Barclays)、花旗銀行(Citibank)、摩根史坦利投資公司(Morgan Stanley),你會遇到很多有關多執行緒的面試題。多執行緒和併發