Android 基于IntentService的文件下載

文件下載這種事情是很耗時(shí)的垃沦。之前使用AsyncTask這樣的異步類來做下載,然后切到后臺(tái)就被干掉刁愿。所以打算試試Service炒辉。(不過按目前那些系統(tǒng)的尿性豪墅,其實(shí)Service也分分鐘被干掉)
不過,這里并不是直接使用Service類黔寇,而是使用的是繼承自Service的IntentService偶器。
這個(gè)東西有三大好處:
1.他有個(gè)任務(wù)隊(duì)列;
2.任務(wù)隊(duì)列執(zhí)行完后會(huì)自動(dòng)停止缝裤;
3.他會(huì)起一個(gè)獨(dú)立的線程屏轰,耗時(shí)操作不會(huì)影響你app的主線程。
這么自動(dòng)化的東西簡直省心憋飞。
話不多說霎苗,開始擼代碼。

首先榛做,要建個(gè)應(yīng)用唁盏,主文件如下(布局什么的代碼就不貼了):

package net.codepig.servicedownloaderdemo;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {
    private String _url="http://www.boosj.com/apk/boosjDance.apk";
    private EditText urlText;
    private Button goBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        goBtn=(Button) findViewById(R.id.goBtn);
        urlText=(EditText) findViewById(R.id.urlText);
        urlText.setText(_url);
        goBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                _url=urlText.getText().toString();
                //start download
                start_service();
            }
        });
    }

    public void start_service(){
        //等會(huì)再填
    }
}

以上代碼不重要,嗯检眯。

接下來是重點(diǎn)厘擂。創(chuàng)建一個(gè)IntentService 類,然后重寫他的onHandleIntent 锰瘸。

需要執(zhí)行的任務(wù)就寫在onHandleIntent 里
這里先用Thread.sleep模擬一下耗時(shí)任務(wù)刽严,試跑一下就可以看到主app關(guān)閉后service還在跑,跑完后就自己Destroy了避凝。

package net.codepig.servicedownloaderdemo;

import android.app.IntentService;
import android.content.Intent;

/**
 * 下載服務(wù)
 * Created by QZD on 2017/9/20.
 */

public class DownLoadService extends IntentService {
    public DownLoadService() {
        super("DownLoadService");//這就是個(gè)name
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    protected void onHandleIntent(Intent intent) {
        Bundle bundle = intent.getExtras();
        String downloadUrl = bundle.getString("download_url");

        Log.d(TAG,"下載啟動(dòng):"+downloadUrl);
        Thread.sleep(1_000);
        int count=0;
        while(count<20){
            count++;
            Log.d(TAG,"下載運(yùn)行中--"+count);
            Thread.sleep(1000);
        }
        Log.d(TAG,"下載結(jié)束");
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }
}

通過Intent接收任務(wù)舞萄,這里在MainActivity中通過startService啟動(dòng)服務(wù)

public void start_service(){
        Intent intent=new Intent(this,DownLoadService.class);
        intent.putExtra("download_url",_url);
        startService(intent);
    }

當(dāng)然,AndroidManifest.xml里也得注冊上

<service android:name="net.codepig.servicedownloaderdemo.DownLoadService"></service>

接下來我們看看怎么下載文件

首先別忘了添加權(quán)限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

添加downloadFile方法管理下載管削。
下載相關(guān)說明都在注釋里倒脓。

    /**
     * 文件下載
     * @param downloadUrl
     * @param file
     */
    private void downloadFile(String downloadUrl, File file){
        FileOutputStream _outputStream;//文件輸出流
        try {
            _outputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "找不到目錄!");
            e.printStackTrace();
            return;
        }
        InputStream _inputStream = null;//文件輸入流
        try {
            URL url = new URL(downloadUrl);
            HttpURLConnection _downLoadCon = (HttpURLConnection) url.openConnection();
            _downLoadCon.setRequestMethod("GET");
            fileLength = Integer.valueOf(_downLoadCon.getHeaderField("Content-Length"));//文件大小
            _inputStream = _downLoadCon.getInputStream();
            int respondCode = _downLoadCon.getResponseCode();//服務(wù)器返回的響應(yīng)碼
            if (respondCode == 200) {
                byte[] buffer = new byte[1024*8];// 數(shù)據(jù)塊含思,等下把讀取到的數(shù)據(jù)儲(chǔ)存在這個(gè)數(shù)組把还,這個(gè)東西的大小看需要定,不要太小茸俭。
                int len;
                while ((len = _inputStream.read(buffer)) != -1) {
                    _outputStream.write(buffer, 0, len);
                    downloadLength = downloadLength + len;
                    Log.d(TAG, downloadLength + "/" + fileLength );
                }
            } else {
                Log.d(TAG, "respondCode:" + respondCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {//別忘了關(guān)閉流
                if (_outputStream != null) {
                    _outputStream.close();
                }
                if (_inputStream != null) {
                    _inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

放入onHandleIntent執(zhí)行:

protected void onHandleIntent(Intent intent) {
        try {
            Bundle bundle = intent.getExtras();
            String downloadUrl = bundle.getString("download_url");

            File dirs = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download");//文件保存地址
            if (!dirs.exists()) {// 檢查文件夾是否存在吊履,不存在則創(chuàng)建
                dirs.mkdir();
            }

            File file = new File(dirs, "boosj.apk");//輸出文件名
            Log.d(TAG,"下載啟動(dòng):"+downloadUrl+" --to-- "+ file.getPath());
            // 開始下載
            downloadFile(downloadUrl, file);
            // 下載結(jié)束
            Log.d(TAG,"下載結(jié)束");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

跑一下,嗯调鬓,默默的下載完了艇炎。

但是,作為一個(gè)負(fù)責(zé)的app腾窝,當(dāng)然要給用戶反饋缀踪,所以我們要顯示一下進(jìn)度居砖。

我們用Notification來顯示進(jìn)度。(需要注意的是驴娃,如果有必要調(diào)用主UI線程來顯示進(jìn)度的話奏候,要充分考慮到Service運(yùn)行過程中,你的app未必是一直活動(dòng)著的唇敞,可能早就destroy了蔗草。)(當(dāng)然用綁定來啟動(dòng)service的另說,那是另一種使用場景疆柔。)
下載前(也就是執(zhí)行downloadFile方法前)先創(chuàng)建并對通知進(jìn)行相關(guān)設(shè)置咒精。
這里使用了NotificationCompat.Builder()這個(gè)方法。如果不考慮舊版本的兼容旷档,可以使用Notification.Builder()方法模叙。

private NotificationCompat.Builder builder;
private NotificationManager manager;
public void initNotification(){
        builder = new NotificationCompat.Builder(this);
        builder.setSmallIcon(R.mipmap.ic_launcher).setContentTitle("下載文件").setContentText("下載中……");//圖標(biāo)、標(biāo)題鞋屈、內(nèi)容這三個(gè)設(shè)置是必須要有的范咨。
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    }

然后使用NotificationManager.notify()方法將通知發(fā)送給系統(tǒng)。需要更新的話再次notify()給同一個(gè)ID的通知厂庇,如果該通知已存在則會(huì)更新渠啊,不存在就新建。

private int _notificationID= 1024;//嗯宋列,這是一個(gè)十分紳士的ID
manager.notify(_notificationID,builder.build());

為了顯示進(jìn)度昭抒,使用handler和Runnable來定時(shí)刷新评也,并通過setProgress方法顯示進(jìn)度條炼杖。

private Handler handler = new Handler();
private Runnable run = new Runnable() {
        public void run() {
            int _pec=(int) (downloadLength*100 / fileLength);
            builder.setContentText("下載中……"+_pec+"%");
            builder.setProgress(100, _pec, false);//顯示進(jìn)度條,參數(shù)分別是最大值盗迟、當(dāng)前值坤邪、是否顯示具體進(jìn)度(false顯示具體進(jìn)度,true就只顯示一個(gè)滾動(dòng)色帶)
            manager.notify(_notificationID,builder.build());
            handler.postDelayed(run, 1000);
        }
    };

完事了以后如果需要清除通知可以使用manager.cancelAll();或者manager.cancel(int _id);

完整代碼再來一遍

package net.codepig.servicedownloaderdemo;

import android.app.IntentService;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 下載服務(wù)
 * Created by QZD on 2017/9/20.
 */

public class DownLoadService extends IntentService {
    private final String TAG="LOGCAT";
    private int fileLength, downloadLength;//文件大小
    private Handler handler = new Handler();
    private NotificationCompat.Builder builder;
    private NotificationManager manager;
    private int _notificationID = 1024;
    public DownLoadService() {
        super("DownLoadService");//這就是個(gè)name
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    protected void onHandleIntent(Intent intent) {
        try {
            initNotification();

            Bundle bundle = intent.getExtras();
            String downloadUrl = bundle.getString("download_url");

            File dirs = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download");//文件保存地址
            if (!dirs.exists()) {// 檢查文件夾是否存在罚缕,不存在則創(chuàng)建
                dirs.mkdir();
            }

            File file = new File(dirs, "boosj.apk");//輸出文件名
            Log.d(TAG,"下載啟動(dòng):"+downloadUrl+" --to-- "+ file.getPath());
            manager.notify(_notificationID,builder.build());
            // 開始下載
            downloadFile(downloadUrl, file);
            // 下載結(jié)束
            builder.setProgress(0,0,false);//移除進(jìn)度條
            builder.setContentText("下載結(jié)束");
            manager.notify(_notificationID,builder.build());
//            manager.cancelAll();
//            manager.cancel(_notificationID);

            // 廣播下載完成事件艇纺,通過廣播調(diào)起對文件的處理。(就不多說了邮弹,在實(shí)際需要的地方接收廣播就好了黔衡。)
            Intent sendIntent = new Intent("downloadComplete");
            sendIntent.putExtra("downloadFile", file.getPath());
            sendBroadcast(sendIntent);
            Log.d(TAG,"下載結(jié)束");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件下載
     * @param downloadUrl
     * @param file
     */
    private void downloadFile(String downloadUrl, File file){
        FileOutputStream _outputStream;//文件輸出流
        try {
            _outputStream = new FileOutputStream(file);
        } catch (FileNotFoundException e) {
            Log.e(TAG, "找不到目錄!");
            e.printStackTrace();
            return;
        }
        InputStream _inputStream = null;//文件輸入流
        try {
            URL url = new URL(downloadUrl);
            HttpURLConnection _downLoadCon = (HttpURLConnection) url.openConnection();
            _downLoadCon.setRequestMethod("GET");
            fileLength = Integer.valueOf(_downLoadCon.getHeaderField("Content-Length"));//文件大小
            _inputStream = _downLoadCon.getInputStream();
            int respondCode = _downLoadCon.getResponseCode();//服務(wù)器返回的響應(yīng)碼
            if (respondCode == 200) {
                handler.post(run);//更新下載進(jìn)度
                byte[] buffer = new byte[1024*8];// 數(shù)據(jù)塊腌乡,等下把讀取到的數(shù)據(jù)儲(chǔ)存在這個(gè)數(shù)組盟劫,這個(gè)東西的大小看需要定,不要太小与纽。
                int len;
                while ((len = _inputStream.read(buffer)) != -1) {
                    _outputStream.write(buffer, 0, len);
                    downloadLength = downloadLength + len;
//                    Log.d(TAG, downloadLength + "/" + fileLength );
                }
            } else {
                Log.d(TAG, "respondCode:" + respondCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {//別忘了關(guān)閉流
                if (_outputStream != null) {
                    _outputStream.close();
                }
                if (_inputStream != null) {
                    _inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private Runnable run = new Runnable() {
        public void run() {
            int _pec=(int) (downloadLength*100 / fileLength);
            builder.setContentText("下載中……"+_pec+"%");
            builder.setProgress(100, _pec, false);//顯示進(jìn)度條侣签,參數(shù)分別是最大值塘装、當(dāng)前值、是否顯示具體進(jìn)度(false顯示具體進(jìn)度影所,true就只顯示一個(gè)滾動(dòng)色帶)
            manager.notify(_notificationID,builder.build());
            handler.postDelayed(run, 1000);
        }
    };

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        handler.removeCallbacks(run);
        super.onDestroy();
    }

    public void initNotification(){
        builder = new NotificationCompat.Builder(this);
        builder.setSmallIcon(R.mipmap.ic_launcher).setContentTitle("下載文件").setContentText("下載中……");//圖標(biāo)蹦肴、標(biāo)題、內(nèi)容這三個(gè)設(shè)置是必須要有的猴娩。
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    }
}

相關(guān)github項(xiàng)目地址:serviceDownloaderDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末阴幌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胀溺,更是在濱河造成了極大的恐慌裂七,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仓坞,死亡現(xiàn)場離奇詭異背零,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)无埃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門徙瓶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嫉称,你說我怎么就攤上這事侦镇。” “怎么了织阅?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵壳繁,是天一觀的道長。 經(jīng)常有香客問我荔棉,道長闹炉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任润樱,我火速辦了婚禮渣触,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壹若。我一直安慰自己嗅钻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布店展。 她就那樣靜靜地躺著养篓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪赂蕴。 梳的紋絲不亂的頭發(fā)上柳弄,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天,我揣著相機(jī)與錄音睡腿,去河邊找鬼语御。 笑死峻贮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的应闯。 我是一名探鬼主播纤控,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼碉纺!你這毒婦竟也來了船万?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤骨田,失蹤者是張志新(化名)和其女友劉穎耿导,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體态贤,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舱呻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悠汽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箱吕。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖柿冲,靈堂內(nèi)的尸體忽然破棺而出茬高,到底是詐尸還是另有隱情,我是刑警寧澤假抄,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布怎栽,位于F島的核電站,受9級特大地震影響宿饱,放射性物質(zhì)發(fā)生泄漏熏瞄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一刑棵、第九天 我趴在偏房一處隱蔽的房頂上張望巴刻。 院中可真熱鬧愚铡,春花似錦蛉签、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邑雅,卻和暖如春片橡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背淮野。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工捧书, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吹泡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓经瓷,卻偏偏與公主長得像爆哑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子舆吮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容