斷點(diǎn)續(xù)傳,App內(nèi)更新版本

? ? ? app內(nèi)更新版本是每個(gè)app基本必備的技能,但一般來說實(shí)現(xiàn)更新下載就已經(jīng)很ok了,但是當(dāng)你下載更新的時(shí)候,突然之間網(wǎng)絡(luò)不太好,下載中斷怎么辦? 再次下載時(shí)還要重新開始下?

如果這個(gè)apk有點(diǎn)大的話,或者網(wǎng)絡(luò)環(huán)境不好的話,有可能很長(zhǎng)時(shí)間下載不下來,安裝不上,那是很坑的一件事;

所以為了防止這種情況,斷點(diǎn)續(xù)傳就能體現(xiàn)出它的價(jià)值了,網(wǎng)絡(luò)環(huán)境不太好,重新下載?不存在的;

上次下載到什么位置,這次就從什么位置開始繼續(xù)下載...?

下載完成之后用戶手抖了點(diǎn)擊個(gè)返回...涼涼...? ?是不是還要重新下載一次才能繼續(xù)安裝?

NO NO NO, 只要你不結(jié)束后臺(tái)程序,那么沒問題,只要文件還在就可以直接安裝。完全不需要你重新下載...

/**

* 從服務(wù)器下載最新更新文件

*

* @return

* @throws Exception

*/

private void downloadFile() {

? ? if (StringUtils.isEmpty(downURL)) {

? ? ? ? LogUtils.e("----url is null");

? ? ? ? return;

? ? }

? ? long contentLength = getContentLength();

? ? InputStream inputStream = null;

? ? HttpURLConnection connection;

? ? try {

? ? ? ? URL url = new URL(downURL);

? ? ? ? connection = (HttpURLConnection) url.openConnection();

? ? ? ? connection.setRequestMethod("GET");

? ? ? ? connection.setConnectTimeout(8000);

? ? ? ? connection.setReadTimeout(8000);

? ? ? ? totalSize = mSPUtils.getLong(TOTALSIZE);

? ? ? ? downSize = mSPUtils.getLong(DOWN_SIZE);

? ? ? ? /**

? ? ? ? * 下載進(jìn)度與總大小相同或小于1 說明下載已完成或者第一次下載

? ? ? ? */

? ? ? ? if (downSize == totalSize || downSize < 1) {

? ? ? ? ? ? downSize = 0;

? ? ? ? }

? ? ? ? /**

? ? ? ? * 判斷下載的文件大小跟上次下載的文件大小 是否 相同

? ? ? ? * 如果相同:則下載的是一個(gè)文件,可以直接從上次下載的地方繼續(xù)下載 (斷點(diǎn)續(xù)傳)

? ? ? ? * 不相同: 則下載的不是一個(gè)文件,需要從新開始下載

? ? ? ? */

? ? ? ? if (totalSize != contentLength || totalSize < 1) {

? ? ? ? ? ? downSize = 0;

? ? ? ? ? ? totalSize = contentLength;

? ? ? ? }

? ? ? ? mSPUtils.put(TOTALSIZE, contentLength);

? ? ? ? //設(shè)置文件請(qǐng)求的位置 請(qǐng)求頭格式 鍵值對(duì) key : value range: bytes = 開始的位置 - 結(jié)束的位置

? ? ? ? connection.setRequestProperty("Range", "bytes=" + downSize + "-" + totalSize);

? ? ? ? mDownloadDialog.getmProgressBar().setMax((int) totalSize);

? ? ? ? mDownloadDialog.getmProgressBar().setProgress((int) downSize);

? ? ? ? // 如果以地址最后的文件命名,那么刪除的時(shí)候就取這個(gè)名字刪除

? ? ? ? String appName = downURL.substring(downURL.lastIndexOf("/"));

? ? ? ? filePath = SD_FOLDER + appName;

? ? ? ? File file = new File(filePath);

? ? ? ? if (!file.getParentFile().exists()) {//判斷文件目錄是否存在 ?

? ? ? ? ? ? file.getParentFile().mkdirs();

? ? ? ? }

? ? ? ? mAccessFile = new RandomAccessFile(file, "rw");

? ? ? ? mAccessFile.seek(downSize);

? ? ? ? inputStream = connection.getInputStream();

? ? ? ? byte[] buffer = new byte[1024];

? ? ? ? int len;

? ? ? ? while ((len = inputStream.read(buffer)) != -1 && isDownLoder) {

? ? ? ? ? ? mAccessFile.write(buffer, 0, len);

? ? ? ? ? ? downSize += len;

? ? ? ? ? ? // 獲取當(dāng)前下載量

? ? ? ? ? ? mHandler.sendEmptyMessage(DOWNLOAD_PROGRESS);

? ? ? ? }

? ? ? ? mHandler.sendMessage(mHandler.obtainMessage(DOWNLOAD_SUCCESS, file));

? ? } catch (IOException e) {

? ? ? ? e.printStackTrace();

? ? ? ? if (isNetworkConnected(mContext)) {

? ? ? ? ? ? if (isConnect) {

? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(DOWNLOAD_CONNECT);

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? mHandler.sendEmptyMessage(DOWNLOAD_FAIL);

? ? ? ? ? ? }

? ? ? ? } else {

? ? ? ? ? ? mHandler.sendEmptyMessage(DOWNLOAD_FAIL);

? ? ? ? }

? ? } finally {

? ? ? ? try {

? ? ? ? ? ? if (mAccessFile != null) {

? ? ? ? ? ? ? ? mAccessFile.close();

? ? ? ? ? ? }

? ? ? ? ? ? if (inputStream != null) {

? ? ? ? ? ? ? ? inputStream.close();

? ? ? ? ? ? }

? ? ? ? } catch (IOException e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

}

??有個(gè)坑需要注意一下:

RandomAccessFile 的兩個(gè)參數(shù) 我用的是文件和“rw”,后面的這個(gè)參數(shù)可讀寫,rwd也可以

但第一個(gè)參數(shù)是文件,創(chuàng)建文件時(shí),不能只給路徑,不然實(shí)例RandomAccessFile 會(huì)報(bào)錯(cuò),當(dāng)初一直報(bào)錯(cuò),一直爽到不報(bào)錯(cuò)為止

不能在setRequestProperty方法前調(diào)用getContentLength,不然給報(bào)錯(cuò):連接不能設(shè)置頭信息

所以你還得先獲取大小,然后才能比較文件大小,需要判斷下載的文件是否跟上一次下載的文件大小相同,如果相同說明是同一個(gè)文件,可以斷點(diǎn)續(xù)傳,從上次的位置開始下載,如果文件大小不一樣,你就要從頭開始下載了,不然下載的都不是同一個(gè)文件,那文件損壞當(dāng)然安裝不了。

所以你得用到保存了, 我借鑒網(wǎng)上的例子大多數(shù)用的是數(shù)據(jù)庫(kù),我看的有點(diǎn)難受,然后我就用了一個(gè)工具類保存了上次下載的位置和文件的總大小

當(dāng)下載的時(shí)候在判斷下載的位置是否等于總大小,等于那就是下載完成了? 小于1 那說明第一次下載

應(yīng)該能看懂的? 就這么點(diǎn)代碼

還有提示一下后臺(tái)得配置一下

connection.setRequestProperty("Range", "bytes=" + downSize + "-" + totalSize);

后臺(tái)返回的網(wǎng)址得支持?jǐn)帱c(diǎn)續(xù)傳;

我當(dāng)初后臺(tái)沒有配置,坑死,調(diào)式半天這個(gè)位置

獲取文件的總大小

?

/**

* 文件的總大小

*

* @return 文件的總大小

*/

private int getContentLength() {

? ? HttpURLConnection connection = null;

? ? try {

? ? ? ? URL url = new URL(downURL);

? ? ? ? connection = (HttpURLConnection) url.openConnection();

? ? ? ? connection.setConnectTimeout(5000);

? ? ? ? connection.setReadTimeout(5000);

? ? ? ? if (connection.getResponseCode() == 200) {

? ? ? ? ? ? return connection.getContentLength();

? ? ? ? }

? ? } catch (IOException e) {

? ? ? ? e.printStackTrace();

? ? } finally {

? ? ? ? try { //釋放資源

? ? ? ? ? ? if (connection != null) {

? ? ? ? ? ? ? ? connection.disconnect();

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? }

? ? return 0;

}

?

開始與暫停

?

//控制開始與下載

public void start() {

? ? downSize = SPStaticUtils.getInt(DOWN_SIZE);

? ? totalSize = SPStaticUtils.getInt(TOTALSIZE);

? ? // 如果下載完文件返回之后不用重新下載 直接安裝(結(jié)束后臺(tái)除外)

? ? if (mFile != null && downSize == totalSize){

? ? ? ? installApk(mContext, mFile);

? ? ? ? return;

? ? }

? ? isDownLoder = true;

? ? mProgressDialog.show();

? ? if (mDownLoadThread == null) {

? ? ? ? mDownLoadThread = new DownLoadThread();

? ? }

? ? mThreadPool.execute(mDownLoadThread);

}

//控制暫停下載

public void stop() {

? ? isDownLoder = false;

? ? SPStaticUtils.put(DOWN_SIZE, downSize);

? ? mProgressDialog.dismiss();

? ? if (mDownLoadThread != null) {

? ? ? ? mThreadPool.remove(mDownLoadThread);

? ? ? ? mDownLoadThread = null;

? ? }

}

?

?

/**

* 注意:自己配置fileprovider

*

* 安裝apk

*/

public static void installApk(Context context, File file) {

? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

? ? ? ? if (!isHasInstallPermissionWithO(context)) {

? ? ? ? ? ? startInstallPermissionSettingActivity(context);

? ? ? ? ? ? return;

? ? ? ? }

? ? }

? ? Intent intent = new Intent(Intent.ACTION_VIEW);

? ? Uri data;

? ? // 判斷版本大于等于7.0

? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

? ? ? ? // "com.demo.download.fileprovider"即是在清單文件中配置的authorities

? ? ? ? data = FileProvider.getUriForFile(context, "com.demo.download.fileprovider", file);

? ? ? ? // 給目標(biāo)應(yīng)用一個(gè)臨時(shí)授權(quán)

? ? ? ? intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

? ? } else {

? ? ? ? data = Uri.fromFile(file);

? ? }

? ? intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 防止打不開應(yīng)用

? ? intent.setDataAndType(data, "application/vnd.android.package-archive");

? ? context.startActivity(intent);

}

@RequiresApi(api = Build.VERSION_CODES.O)

public static boolean isHasInstallPermissionWithO(Context context) {

? ? if (context == null) return false;

? ? return context.getPackageManager().canRequestPackageInstalls();

}

/**

* 開啟設(shè)置安裝未知來源應(yīng)用權(quán)限界面

*

* @param context

*/

@RequiresApi(api = Build.VERSION_CODES.O)

private static void startInstallPermissionSettingActivity(Context context) {

? ? if (context == null) return;

? ? Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);

? ? ((Activity) context).startActivityForResult(intent, REQUEST_CODE_APP_INSTALL);

}

/**

* byte(字節(jié))根據(jù)長(zhǎng)度轉(zhuǎn)成kb(千字節(jié))和mb(兆字節(jié))

*

* @param bytes

* @return

*/

private static String bytes2kb(long bytes) {

? ? BigDecimal filesize = new BigDecimal(bytes);

? ? BigDecimal megabyte = new BigDecimal(1024 * 1024);

? ? float returnValue = filesize.divide(megabyte, 2, BigDecimal.ROUND_UP).floatValue();

? ? if (returnValue > 1)

? ? ? ? return (returnValue + "MB");

? ? BigDecimal kilobyte = new BigDecimal(1024);

? ? returnValue = filesize.divide(kilobyte, 2, BigDecimal.ROUND_UP).floatValue();

? ? return (returnValue + "KB");

}

?


上面是下載類

下面是activity中安裝回調(diào)

當(dāng)然你下載時(shí)調(diào)用很方便只需要一行代碼即可:

AppInnerDownLoder.getInstance(this).setDownUrl(path).start();

只需要傳Context 上下文,下載的url

?

@Override

public void onClick(View v) {

? ? switch (v.getId()) {

? ? ? ? case R.id.start:

? ? ? ? ? ? AppInnerDownLoder.getInstance(this).setDownUrl(path).start();

? ? ? ? ? ? break;

? ? ? ? case R.id.stop:

? ? ? ? ? ? AppInnerDownLoder.getInstance(this).stop();

? ? ? ? ? ? break;

? ? }

}

?

@Override

? ? protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

? ? ? ? super.onActivityResult(requestCode, resultCode, data);

? ? ? ? // 8.0以上版本安裝apk 獲取未知來源為true才會(huì)繼續(xù)下載安裝

? ? ? ? if (requestCode == AppInnerDownLoder.REQUEST_CODE_APP_INSTALL) {

//? ? ? ? ? ? if (StringUtils.isEmpty(path)) return;

? ? ? ? ? ? if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

? ? ? ? ? ? ? ? if (AppInnerDownLoder.isHasInstallPermissionWithO(this)) {

? ? ? ? ? ? ? ? ? ? if (AppInnerDownLoder.getInstance(this).getmFile() != null) {

? ? ? ? ? ? ? ? ? ? ? ? AppInnerDownLoder.installApk(this, AppInnerDownLoder.getInstance(this).getmFile());

? ? ? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? ? ? AppInnerDownLoder.getInstance(this).setDownUrl(path).start();

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }


下載鏈接:https://github.com/xiaobinAndroid421726260/Android_DownloadFile_Demo2019_6_11

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容