1. 程式人生 > >Android 下載模組,使用Xutils3 的下載功能,Activity和services資料互動,listview顯示下載進度

Android 下載模組,使用Xutils3 的下載功能,Activity和services資料互動,listview顯示下載進度

臨近過年,今天將花了兩天時間寫的下載模組貢獻出來,以前我也是天天需要什麼功能,就天天查百度,現在自己也能開源一點東西了,也是非常很開心的,hahaha。

這裡寫圖片描述

離職前寫了音樂播放器的音樂下載介面,自己封裝了下載執行緒,並使用多執行緒斷點續傳下載音樂,感覺多執行緒下載音樂也並不比單執行緒下載快多少,就一個4M的音樂來說,開3條執行緒下了46s,單執行緒下了60s,如果同時現在3個音樂,那麼多執行緒下載相當於開了9 個執行緒,速度盡然和單執行緒的速度持平,有時候還會慢於3條執行緒,於是我放棄了多執行緒斷點續傳,還是老老實實寫單執行緒的吧(- -!我覺得應該是我的下載執行緒的毛病,哈哈)。。。

於是今天就用了xutils3的網路訪問模組,實現的需求是。
1、APP關閉後(kill),重新開啟時,能記錄當前下載進度,斷點續傳。
2、音樂能增刪,暫停,顯示下載速度和下載進度。

其實都寫好了哈,主要說說編碼思路。

通過Acitivity的bindService,可以呼叫service的bind來傳遞需要下載的檔案資訊到service,service通過呼叫xutils的帶進度的下載模組,來回調下載資訊,同時service通過下載觀察者不斷觀察下載進度,將進度傳遞給Activity,之後setAdapter即可。

裡面最主要的東西就是DownloadService,所有的程式碼邏輯都是這個類處理的。

傳遞下載檔案資訊的類MainActivity

這個類有個問題
就是檔名的獲取是根據Url倒敘擷取/後面的檔案來命名的,如果你的url不符合規則,請自行定義。


    private String getfileName(String url) {//根據下載地址給下載檔案命名
        try{
            return url.substring(url.lastIndexOf("/") + 1);
        }catch (Exception ex){
            return mBinder.getCurrentListSize()+"";//返回一個數字
        }
    }

許可權問題
程式碼中加了6.0的動態許可權判斷,因為要操作SD卡,所以這裡也要注意。
因為我沒有6.0的測試手機,所以這裡雖然都寫了,但是隻是作為提示使用,需要你自己更改邏輯位置,如果是6.0以下的,並沒有問題的 。


Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 0:
                    Toast.makeText(getBaseContext(),"已獲取SD卡許可權",Toast.LENGTH_SHORT).show();
                    //6.0系統在這裡進行service的初始化
                    break;
                case 1:
                    Toast.makeText(getBaseContext(),"SD卡許可權未開啟",Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    //判斷許可權方法
    private void initCheckSelfPermission() {
        //判斷是否有記憶體卡許可權
        CheckSelfPermission.getSDCard(this);
    }
    //動態許可權回撥
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        doNext(requestCode,grantResults);
    }
    //動態許可權回撥,yes和no點選
    private void doNext(int requestCode, int[] grantResults) {
        if (requestCode == DownLoadConstant.WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                LogUtils.d("許可權獲取成功");
                handler.sendEmptyMessage(0);
            } else {
                handler.sendEmptyMessage(1);
            }
        }
    }

最關鍵的是DownloadService ,bind裡有對外提供的增刪改查方法
在下載的過程中,有快取GetFileSharePreance 不斷在儲存下載的urls下載地址列表和檔案下載進度,為APP下次重啟提供斷點,裡面有詳細註釋。



/**
 * 作者:朱亮 on 2017/1/17 15:36
 * 郵箱:171422696@qq.com
 * 下載服務類,執行下載任務,並將進度傳遞到Activity中(這裡用一句話描述這個方法的作用)
 */
public class DownloadService extends Service {

    public static final String ACTION_START = "ACTION_START";
    public static final String ACTION_STOP = "ACTION_STOP";
    public static final String ACTION_DELETE = "ACTION_DELETE";
    private List<OnDownLoadBackListener> loadBackListeners = new ArrayList<OnDownLoadBackListener>();//註冊一個下載觀察者
    private static final int DOWNLOADSERVICEID = 1;//下載監聽傳遞值
    private FileInfo fileInfo;//下載物件
    private MyBinder myBinder = new MyBinder();
    private ArrayList<FileInfo> mCurretList;//當前服務中的正在下載列表
    private ArrayList<FileInfo> mCurretListOut;//輸出的下載列表
    private GetFileSharePreance preance;
    private HashMap<Integer,DownloanThread> map;//建立動態物件
    @Override
    public IBinder onBind(Intent intent) {
        return myBinder;
    }

    @Override
    public void onCreate() {
        mCurretList = new ArrayList<>();
        mCurretListOut = new ArrayList<>();
        map = new HashMap<Integer,DownloanThread>();
        preance = BaseApplication.getFileSharePreance();//獲取快取例項,這裡的快取主要用在APP關閉後,重新進入下載列表後顯示使用
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            // 獲得Activity穿來的引數
            fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            if (ACTION_START.equals(intent.getAction())) {
                startDownLoad(fileInfo);//開始下載
            } else if (ACTION_STOP.equals(intent.getAction())) {
                stopDownLoad(fileInfo);//停止下載
            } else if (ACTION_DELETE.equals(intent.getAction())) {
                deleteDownLoad(fileInfo);//刪除下載
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }
    //開始下載邏輯處理方法
    private void startDownLoad(FileInfo fileInfo) {
        if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {
            preance.setUrlList(mCurretList);//從快取地址集合中移除這一條(儲存最新的地址集合)
            map.put(fileInfo.getId(),new DownloanThread());
            map.get(fileInfo.getId()).Download(fileInfo);//開始下載
            SPUtils.setLong(getApplicationContext(), SPKey.CURRENTLIST_SIZE, fileInfo.getId());//設定下載ID自增
            sendHandler();
        }
    }
    //停止下載邏輯處理方法
    private void stopDownLoad(FileInfo fileInfo) {
        if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {
            if (map.get(fileInfo.getId()) != null) {
                map.get(fileInfo.getId()).downLoadCancel();//下載執行緒停止
            }
            sendHandler();
        }
    }

    //刪除下載邏輯處理方法
    private void deleteDownLoad(FileInfo fileInfo) {
        if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {
            for (int x = 0; x < mCurretList.size(); x++) {//如果是正在下載的列表,先暫停下載。
                if (mCurretList.get(x).getUrl().equals(fileInfo.getUrl())) {//如果匹配到了
                    preance.delete(mCurretList.get(x).getUrl());//刪除下載快取
                    if(map.get(fileInfo.getId()) != null){
                        map.get(fileInfo.getId()).downLoadCancel();//從下載執行緒停止
                    }
                    mCurretList.remove(mCurretList.get(x));//從下載列表移除這條
                    preance.setUrlList(mCurretList);//從快取地址集合中移除這一條(儲存最新的地址集合)
                    preance.delete(fileInfo.getUrl());//刪除快取資料
                }
            }
            sendHandler();
        }
    }


    public class MyBinder extends Binder {
        //註冊一個下載觀察者
        public void registDownLoadListener(OnDownLoadBackListener loadBackListener) {
            loadBackListeners.add(loadBackListener);
        }
        //取消一個觀察者
        public void unregistDownLoadListener(OnDownLoadBackListener loadBackListener) {
            loadBackListeners.remove(loadBackListener);
        }
        //設定當前的下載列表(會清空當前的下載列表哦,慎用,如果需要請用  appendToCurrentList())
        public void setCurrentList(ArrayList<FileInfo> list) {
            mCurretList.clear();
            if (list != null) {
                mCurretList.addAll(list);
            }
        }
        //獲取當前的正在下載的所有下載資訊列表
        public ArrayList<FileInfo> getCurrentList() {
            return mCurretList;
        }
        //從快取中獲取下載資訊
        public ArrayList<FileInfo> getCurrentListFShareP(){
            ArrayList<FileInfo> infos = new ArrayList<>();
            ArrayList<String> list = preance.getUrlList();//先獲取下載url集合
            for(int x = 0 ; x < list.size() ; x++){//根據url地址查詢下載的快取資料
                infos.add(preance.getFilePre(list.get(x)));
            }
            setCurrentList(infos);
            return infos;
        }
        //下載次數自增
        public int getCurrentListSize() {
            long l = SPUtils.getLong(getApplicationContext(), SPKey.CURRENTLIST_SIZE);
            return (int) l + 1;
        }
        //添加當前的下載列表(相同下載地址檔案過濾)
        public void appendToCurrentList(FileInfo info) {
            if (info != null) {
                // 只添加當前下載列表中沒有的
                boolean existed = false;
                for (int j = 0; j < mCurretList.size(); j++) {
                    if ((mCurretList.get(j).getUrl()).equals(info.getUrl())) {
                        existed = true;
                    }
                }
                if (!existed) {
                    mCurretList.add(info);
                    startDownLoad(info);//開始下載
                }
            }
        }
        //停止下載
        public void stopDownLoad(FileInfo fileInfo) {
            if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {
                stopDownLoad(fileInfo);
            }
        }
        //刪除當前的某一個列表
        public void deleyeCurrentList(FileInfo fileInfo) {
            deleteDownLoad(fileInfo);
        }
        //刪除所有的下載列表資訊(未編寫)
        public void deleteAll(){

        }
    }


    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case DOWNLOADSERVICEID:
                    for (int x = 0; x < loadBackListeners.size(); x++) {//有多少個介面在觀察下載資料
                        mCurretListOut.clear();
                        for(int j = 0 ; j < mCurretList.size() ;j++){//正在下載列表中的資料
                            if(map.get(mCurretList.get(j).getId()) == null){//假如下載執行緒沒有開啟
                                mCurretListOut.add(mCurretList.get(j));
                            }else {//從下載執行緒中取資料
                                mCurretListOut.add(map.get(mCurretList.get(j).getId()).getFileInfo());
                            }
                        }
                        loadBackListeners.get(x).onDownloadSize(mCurretListOut);//傳遞給不同的觀察者
                    }
                    //當下載的時候,延遲不斷執行這句話,讓所有的觀察者不至於失去下載的各種資訊,這裡控制檢視更新頻率
                    handler.sendEmptyMessageDelayed(
                            DOWNLOADSERVICEID, 1000);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    //傳送hanlde
    private void sendHandler(){
        if (!handler.hasMessages(DOWNLOADSERVICEID)) {//如果hanlde的觀察者沒有了,重新發送一個
            handler.sendEmptyMessage(DOWNLOADSERVICEID);
        }
    }
}




下載的實際類DownloanThread,名字看起來是個執行緒,其實不是,只是將實際下載類分離出來,呼叫xutils的下載方法和取消下載方法,提供一個獲取下載檔案詳細資訊方法。

//獲取下載中的檔案資訊
    public FileInfo getFileInfo(){
        return new FileInfo(fileInfo.getId(),fileInfo.getUrl(),fileInfo.getFileName(),length,downlenth,downType);
    }

在這裡回撥檔案的下載資訊

  cancelable = x.http().get(params, new Callback.ProgressCallback<File>() {
            @Override
            public void onWaiting() {
                LogUtils.e("等待中...");
                downType = 0;
            }
            @Override
            public void onStarted() {
                LogUtils.e("開始下載。。。");
                downType = 1;
            }
            @Override
            public void onLoading(long total, long current, boolean isDownloading) {
                LogUtils.e("tital = "+total + "   current = "+current + "  isDownLoading = "+isDownloading);
                length = (int) total;
                downlenth = (int) current;
                downType = 2;
                preance.update(new FileInfo(info.getId(),info.getUrl(),info.getFileName(),(int)total,(int)current,downType));
            }

            @Override
            public void onSuccess(File result) {
                LogUtils.e("下載成功");//在這裡下載成功的資料寫進資料庫裡即可(已下載介面的資料從這裡來的,哈哈,那個介面自己寫)
                downType = 3;
            }

            @Override
            public void onError(Throwable ex, boolean isOnCallback) {
                LogUtils.e("下載失敗");
                downType = 4;
            }

            @Override
            public void onCancelled(CancelledException cex) {
                LogUtils.e("下載取消");
                downType = 5;
            }

            @Override
            public void onFinished() {
                LogUtils.e("下載完成");
            }
        });

最後在Adapter可以根據downType來顯示下載過程中的狀態了

這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

已下載的介面沒有寫程式碼,偷個懶,請自己寫哈

原始碼下載

相關推薦

Android 下載模組使用Xutils3下載功能Activityservices資料互動listview顯示下載進度

臨近過年,今天將花了兩天時間寫的下載模組貢獻出來,以前我也是天天需要什麼功能,就天天查百度,現在自己也能開源一點東西了,也是非常很開心的,hahaha。 離職前寫了音樂播放器的音樂下載介面,自己封裝了下載執行緒,並使用多執行緒斷點續傳下載音樂,感覺多執行緒

Android實現登入功能Android與伺服器資料互動使用tomcat、mysql實現登入的demo程式web端android均可實現登入

1.使用到的開發工具為:Eclipse(Java EE),Android Studio,MYSQL 5.7.21;2.首先在MYSQL資料庫建表,我這裡使用的資料庫視覺化操作軟體為:navicat premium:如圖:這裡你可以取自己喜歡的資料庫名字,但是為了方便起見,我建

java利用jcraft實現遠端伺服器互動實現上傳下載檔案

git地址:https://github.com/fusugongzi/upLoadAndDownloadFile 第一步:引入maven支援,新增maven依賴 <!-- https://mvnrepository.com/artifact/com.jcraft

Android Activity之間實現資料(物件物件集合)傳遞

Android Activity之間實現資料傳遞是一項非常重要的技術,今天我就來講一講如何實現資料傳遞: 1.基本資料傳遞 MainActivity.java Intent intent=new Intent(this,ThinkActivity.cla

android 在selector中同時設定button的圓角點選效果簡單顏色無需美工

1,如果我們沒有美工來設計我們的圓角圖片,可以採用shape的方式實現: <?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://schemas.andr

爬蟲的編譯器的安裝pycharm第三方庫的安裝pip的安裝爬蟲認知篇(5)

        python之所以強大並逐漸流行起來,一部分原因要歸功於的Python的強大的第三方庫。這樣使用者就不用瞭解底層的思想,用最少的程式碼寫出最多的功能。            

前後端資料互動axiosjquery ajax的區別

axios作為Vue生態系統中濃墨重彩的一筆,我學習這個東西也是花了一定的時間的。剛開始的時候,也是遇到了很多問題。 逐漸摸透了它的脾氣。 首先說說FormData和Payload兩種資料格式的區別: 先是提交一個FormData的請求試試看: 然後我們看後端: 然後

輸入兩個數輸出其最大公約數最小公倍數並輸出所有的公約數

輸入兩個數,求其最大公約數和最小公倍數,並輸出所有的公約數 以下分別用三種方法求最大公約數,詳細程式碼如下: #include <iostream> using namespace std; //求最大公約數:求差法 void div1(int m,int n){ i

FLEX與JS資料互動以及Google外掛IFrame的使用

       最近因為專案需要,用到的flex,同時需要與js做資料互動,同時還用到了Google的外掛IFrame,總結一點點自己的使用心得,其中很多資料都是在網上Google到的。     FLEX呼叫

[Django]在資料庫有表但是沒有model的情況下對資料查詢修改

文章目錄連線資料庫查詢資料庫更新資料庫 連線資料庫 from django.db import connection sql = "SELECT id,record_time,lose_time

你必須知道的React的知識點:單向資料高效能虛擬DOM元件間的資料互動事件與資料的雙向繫結生命週期鉤子fetch:資料請求等

1、React除錯工具:React Developer Tools 2、React開發工具:Atom 3、React UI庫:Material-UI / Ant Deaign 4、React適用場景:資料不斷變化的大型應用程式 5、前端UI構建方式:資料模型、UI介面

恆生電子公司的一道筆試題有一個字串由*號其他字母組成請提供函式將字串頭部的*號全部移到字串的尾部(***aqer*f轉換成aqer*f***)

  現場筆試的時候沒有寫出來,筆試之後想了一下,就編寫了一下,用的方法很簡單,主要用vector實現的。以下貼上自己編的原始碼: #include<iostream> #include<string> #include<vector> u

安卓實現任意控制元件view可拖拽並監聽拖拽點選事件可自動拉回螢幕邊緣

因為專案中有需要實現控制元件可任意拖拽的需求,所以簡單寫了個自定義OnTouchListener,以作拋磚引玉,歡迎大家提議反饋。 完整實現類如下,程式碼中有詳細註釋: 使用者可以決定是否開啟自動拖拽邊緣功能,可以監聽控制元件的拖拽和點選事件 public cl

爬蟲入門從第一個爬蟲建立起做蟲師的心爬蟲的編譯器的安裝pycharm第三方庫的安裝pip的安裝爬蟲的認知篇(5)

          Python之所以強大並逐漸流行起來,一部分原因要歸功於Python強大的第三方庫。這樣使用者就不用瞭解底層的思想,用最少的程式碼寫出最多的功能。 在PyCharm中安裝

《ServerSuperIO Designer IDE使用教程》-2.與硬體閘道器資料互動並進行資料級聯轉發直到雲端。釋出:v4.2.1版本

v4.2.1 更新內容:1.重新定義資料轉發文字協議,使閘道器與ServerSuperIO以及之間能夠相關互動資料。2.擴充套件ServerSuperIO動態資料類的方法,更靈活。3.修復Designer增加轉發任務的一個BUG。4.修改資料轉發客戶端和服務端。5.增加硬體閘道器驅動。 v4.2.1 下

【運維專家大講堂】雲端計算資料時代資料庫運維工程師發展方向在哪?

本次運維專家大講堂精選2014年6月份ITPUB的採訪,訪談物件是楊志洪,網名boypoo,Oracle ACE成員,現擔任上海新炬網路技術有限公司的技術總監。 開場: 一直以來運維工程師的角色被蒙上了各種神祕面紗,平時他們是默默無聞的幕後工作者,很少被人關注。而一旦企業出現技術故障,大家就會立刻呼

製作一個安卓介面可以進行密碼賬號的判斷有用來儲存輸入賬號密碼的類用來在其他類中呼叫

首先附上安卓介面圖和完整版下載地址: 下載地址:https://download.csdn.net/download/qq_39343904/10881646                   &nb

【問題記錄】JAVA程序啟動大概率卡住6分鐘左右應用日誌沒有任何WARN ERROR系統日誌也沒有發現程序相關日誌最後定位TOMCAT SHA1PRNG耗時太長

系統是基於springboot開發的系統,java -jar啟動過程中發現經常會卡住6分鐘左右,才能啟動完成,全程沒有發現任何WANR和ERROR級別的日誌(其實早看看DEBUG和INFO日誌,可能問題早就解決了,慣性思維害人啊),再去檢視/var/log/message系統日誌,也沒發現任何和

為什麼執行spark任務會在hadoop歷史伺服器上看到而在spark8080埠頁面work資料夾下面看不到?

通過oozie任務排程工具執行一個spark任務:spark程式:oozie任務:程式成功執行了,但在8080埠頁面看不到,在hadoop 19888歷史伺服器頁面可以看到這是怎麼回事?將上面那個spark程式打成jar包使用spark-sumbit --class 主類 .

Android 實現簡單的登陸註冊功能(SharedPreferencesSQLite)

最近剛好做了一個Android的登入註冊介面,將資料利用SharedPreferences或者SQLite繫結到Android程式中,實現簡單的登陸註冊功能,本文未涉及到與伺服器的交流。 首先,對於登陸註冊要有一個明確的邏輯順序,不管是對於SharedPreferences