文件下載這種事情是很耗時(shí)的。之前使用AsyncTask這樣的異步類來(lái)做下載,然后切到后臺(tái)就被干掉。所以打算試試Service。(不過(guò)按目前那些系統(tǒng)的尿性,其實(shí)Service也分分鐘被干掉)
不過(guò),這里并不是直接使用Service類,而是使用的是繼承自Service的IntentService。
這個(gè)東西有三大好處:
1.他有個(gè)任務(wù)隊(duì)列;
2.任務(wù)隊(duì)列執(zhí)行完后會(huì)自動(dòng)停止;
3.他會(huì)起一個(gè)獨(dú)立的線程,耗時(shí)操作不會(huì)影響你app的主線程。
這么自動(dòng)化的東西簡(jiǎn)直省心。
話不多說(shuō),開(kāi)始擼代碼。
首先,要建個(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ì)再填 }}以上代碼不重要,嗯。
接下來(lái)是重點(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(); }}通過(guò)Intent接收任務(wù),這里在MainActivity中通過(guò)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里也得注冊(cè)上
接下來(lái)我們看看怎么下載文件
首先別忘了添加權(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)說(shuō)明都在注釋里。
/** * 文件下載 * @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()); // 開(kāi)始下載 downloadFile(downloadUrl, file); // 下載結(jié)束 Log.d(TAG,"下載結(jié)束"); } catch (Exception e) { e.printStackTrace(); } }跑一下,嗯,默默的下載完了。
但是,作為一個(gè)負(fù)責(zé)的app,當(dāng)然要給用戶反饋,所以我們要顯示一下進(jìn)度。
我們用Notification來(lái)顯示進(jìn)度。(需要注意的是,如果有必要調(diào)用主UI線程來(lái)顯示進(jìn)度的話,要充分考慮到Service運(yùn)行過(guò)程中,你的app未必是一直活動(dòng)著的,可能早就destroy了。)(當(dāng)然用綁定來(lái)啟動(dòng)service的另說(shuō),那是另一種使用場(chǎng)景。)
下載前(也就是執(zhí)行downloadFile方法前)先創(chuàng)建并對(duì)通知進(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è)十分紳士的IDmanager.notify(_notificationID,builder.build());
為了顯示進(jìn)度,使用handler和Runnable來(lái)定時(shí)刷新,并通過(guò)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 );
完整代碼再來(lái)一遍
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()); // 開(kāi)始下載 downloadFile(downloadUrl, file); // 下載結(jié)束 builder.setProgress(0,0,false);//移除進(jìn)度條 builder.setContentText("下載結(jié)束"); manager.notify(_notificationID,builder.build());// manager.cancelAll();// manager.cancel(_notificationID); // 廣播下載完成事件,通過(guò)廣播調(diào)起對(duì)文件的處理。(就不多說(shuō)了,在實(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
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VEVB武林網(wǎng)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注