- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- 如何使用Retrofit+RxJava實(shí)現帶進(jìn)度條的文件下載
這篇文章主要為大家展示了“如何使用Retrofit+RxJava實(shí)現帶進(jìn)度條的文件下載”,內容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習一下“如何使用Retrofit+RxJava實(shí)現帶進(jìn)度條的文件下載”這篇文章吧。
先說(shuō)一下版本控制吧,通用做法基本上是通過(guò)接口獲取存儲的app版本號,與應用的版本號進(jìn)行比較,版本較低就去更新,先看一下如何獲取應用版本號吧
PackageManager packageManager = mActivity.getPackageManager(); PackageInfo packageInfo = null; try { packageInfo = packageManager.getPackageInfo(mActivity.getPackageName(), 0); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } String versionName = packageInfo.versionName;
可以看到使用的是Context中的getPackageManager方法來(lái)獲取PackageManager 對象,該對象可用于獲取版本的一些信息。
上面的屬于附內容,接下來(lái)就是關(guān)于Retrofit+RxJava實(shí)現進(jìn)度條下載文件的功能,Retrofit本身不提供進(jìn)度條顯示的功能,但Retrofit默認使用Okhttp來(lái)進(jìn)行網(wǎng)絡(luò )請求,這里就可以自定義攔截器來(lái)進(jìn)行攔截,實(shí)現進(jìn)度。Okhttp的Demo中也為我們提供了一份代碼,需要的可以去參考一下Progress.javar,可以看到攔截器的設置:
public class ProgressResponseBody extends ResponseBody { private ResponseBody responseBody; private ProgressListener progressListener; private BufferedSource bufferedSource; public ProgressResponseBody(ResponseBody responseBody,ProgressListener progressListener){ this.responseBody=responseBody; this.progressListener=progressListener; } @Override public MediaType contentType() { return responseBody.contentType(); } @Override public long contentLength() { return responseBody.contentLength(); } @Override public BufferedSource source() { if(bufferedSource==null){ bufferedSource= Okio.buffer(source(responseBody.source())); } return bufferedSource; } private Source source(Source source) { return new ForwardingSource(source) { long totalBytesRead = 0L; @Override public long read(Buffer sink, long byteCount) throws IOException { //當前讀取字節數 long bytesRead = super.read(sink, byteCount); //增加當前讀取的字節數,如果讀取完成了bytesRead會(huì )返回-1 totalBytesRead += bytesRead != -1 ? bytesRead : 0; //回調,如果contentLength()不知道長(cháng)度,會(huì )返回-1 progressListener.onProgress(totalBytesRead,responseBody.contentLength(),bytesRead,bytesRead==-1); return bytesRead; } }; }}
ProgressListener 用來(lái)監聽(tīng)進(jìn)度變化,回調到ProgressInterceptor中,ProgressInterceptor是一個(gè)自定義的攔截器,可以看一下代碼
public class ProgressInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Response response=chain.proceed(chain.request()); return response.newBuilder().body(new ProgressResponseBody(response.body(),progressListener)).build(); } static final ProgressListener progressListener=new ProgressListener() { @Override public void onProgress(long progress, long total, long speed, boolean done) { Log.i("log","progress="+progress+"total="+total); } };}
為了便于獲取progress,可以通過(guò)OkHttpClient的addNetworkInterceptor方法直接添加一個(gè)自定義的攔截器,例如:
//為Okhttp設置攔截器 OkHttpClient client = new OkHttpClient.Builder() .addNetworkInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Response originalResponse = chain.proceed(chain.request()); return originalResponse.newBuilder() .body(new ProgressResponseBody(originalResponse.body(), progressListener)) .build(); } }) .build(); //進(jìn)度回調監聽(tīng) ProgressListener progressListener=new ProgressListener() { @Override public void onProgress(long progress, long total, long speed, boolean done) { Message message=new Message(); message.obj=new AmallLoadBean(progress,total); progressHandler.sendMessage(message); } };
這里通過(guò)一個(gè)創(chuàng )建一個(gè)繼承自Handler的ProgressHandler靜態(tài)內部類(lèi)用于在主線(xiàn)程中刷新進(jìn)度,順帶提一下,使用static修飾ProgressHandler是因為靜態(tài)內部類(lèi)默認不持有外部類(lèi)對象的引用,需要注意一下Handler的內存泄漏,使用一下寫(xiě)法:
//處理下載版本進(jìn)度 public class ProgressHandler extends Handler{ private WeakReference<Activity> mActivityWeakReference; public ProgressHandler(Activity activity){ mActivityWeakReference=new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { if(mActivityWeakReference.get()!=null){ AmallLoadBean amallLoadBean= (AmallLoadBean) msg.obj; long progress=amallLoadBean.getProgress(); long total=amallLoadBean.getTotal(); float cp=(float)progress/(float)total; } } }
繼續回到下載文件中,我才用的是Retrofit+RxJava的方法來(lái)實(shí)現,寫(xiě)之前也看了一下別人寫(xiě)的,好像不全,下滿(mǎn)也遇到了一些小坑,講一下吧:
observable.subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .doOnNext(new Action1<ResponseBody>() { @Override public void call(ResponseBody responseBody) { saveFiles(responseBody); } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<ResponseBody>() { @Override public void onCompleted() { installApk(); } @Override public void onError(Throwable e) { ToastUtils.getInstance().showToast("請到應用市場(chǎng)下載最新版本"); } @Override public void onNext(ResponseBody responseBody) { } }); }
通過(guò)RxJava的doOnNext在subscribe方法之前存儲文件,這里需要注意的是doOnNext方法需要在子線(xiàn)程中執行,調用.observeOn(Schedulers.io())方法,然后再切換到主線(xiàn)程,否則文件下載不下來(lái)。當文件下載完成時(shí),在onCompleted方法中執行installApk()方法安裝app。需要注意的是這里需要做權限的適配,因為我的是自己封裝的因為就不拿出來(lái)了,挺簡(jiǎn)單就自己寫(xiě)吧。保存文件的代碼給大家放出來(lái)了,通俗的語(yǔ)言:
/** * 保存文件 */ public void saveFiles(ResponseBody responseBody){ InputStream inputStream = null; FileOutputStream fileOutputStream = null; byte[] buffer=new byte[2048]; int len; File file=new File(saveFileName); if(!file.exists()){ file.mkdirs(); } try { inputStream=responseBody.byteStream(); fileOutputStream=new FileOutputStream(file); while ((len=inputStream.read(buffer))!=-1){ fileOutputStream.write(buffer,0,len); } inputStream.close(); fileOutputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
在安裝文件的時(shí)候,需要注意7.0以后的適配,代碼看看就好,和拍照適配的原理一直,都是Android對私密性文件的權限問(wèn)題
/** * 安裝apk * */ private void installApk() { File apkfile = new File(saveFileName); if (!apkfile.exists()) { return; } //判斷版本號 if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ Uri apkUri = FileProvider.getUriForFile(activity, "******.fileprovider", apkfile); Intent install = new Intent(Intent.ACTION_VIEW); install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //添加這一句表示對目標應用臨時(shí)授權該Uri所代表的文件 install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); install.setDataAndType(apkUri, "application/vnd.android.package-archive"); activity.startActivity(install); }else{ Intent i = new Intent(Intent.ACTION_VIEW); i.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); activity.startActivity(i); } }
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站