Android okhttp 下載檔案到指定資料夾帶進度更新加7.0適配
阿新 • • 發佈:2019-01-04
專案需求:
從網路下載檔案,並且在手機上顯示,檔案型別目前有doc,docx,pdf,txt
當然小夥伴們可以自行新增,這裡用的是android Intent跳轉方式解決,主要通過intent找到可以開啟的軟體,進行閱讀。
此時,有人要問了,圖呢,圖呢,不好意思,並沒有做demo,專案中的東西就不方便展示了。
我說一下具體實現思路:
1.使用okhttp進行網路請求
2.將下載的檔案放在自己建立的資料夾中
3.在progressDialog中進行下載進度條的更新
4.下載完成後,進行intent跳轉
也就是說。給你一個按鈕,你點選後顯示progressDialog ,進度在100,點選檢視就ok了,頁面沒什麼東西,就不麻煩了。
這就是需要用的下載工具方法
/** * 下載檔案 * * @param fileUrl 檔案url * @param destFileDir 檔名:test.docx */ public <T> void downLoadFile(String fileUrl, final String destFileDir, final ReqProgressCallBack<T> callBack) { // File dir = new File(FileUtils.getAppDir()); final File file = new File(FileUtils.getAppDir(), destFileDir); if (file.exists() && file.length() > 0) { successCallBack((T) file, callBack); return; } if (!file.exists()) { try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } Request request = new Request.Builder().url(fileUrl).build(); final Call call = mOkHttpClient.newCall(request); call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Log.e(TAG, e.toString()); failedCallBack("下載失敗", callBack); } @Override public void onResponse(Call call, Response response) throws IOException { InputStream is = null; byte[] buf = new byte[2048]; int len = 0; FileOutputStream fos = null; try { long total = response.body().contentLength(); Log.e(TAG, "total------>" + total); long current = 0; is = response.body().byteStream(); fos = new FileOutputStream(file); while ((len = is.read(buf)) != -1) { current += len; fos.write(buf, 0, len); Log.e(TAG, "current------>" + current); progressCallBack(total, current, callBack); } fos.flush(); successCallBack((T) file, callBack); } catch (IOException e) { Log.e(TAG, e.toString()); failedCallBack("下載失敗", callBack); } finally { try { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } catch (IOException e) { Log.e(TAG, e.toString()); } } } }); }
回撥:
/** * 統一同意處理成功資訊 * * @param result * @param callBack * @param <T> */ private <T> void successCallBack(final T result, final ReqCallBack<T> callBack) { okHttpHandler.post(new Runnable() { @Override public void run() { if (callBack != null) { callBack.onReqSuccess(result); } } }); } /** * 統一處理失敗資訊 * * @param errorMsg * @param callBack * @param <T> */ private <T> void failedCallBack(final String errorMsg, final ReqCallBack<T> callBack) { okHttpHandler.post(new Runnable() { @Override public void run() { if (callBack != null) { callBack.onReqFailed(errorMsg); } } }); } /** * 統一處理進度資訊 * * @param total 總計大小 * @param current 當前進度 * @param callBack * @param <T> */ private <T> void progressCallBack(final long total, final long current, final ReqProgressCallBack<T> callBack) { okHttpHandler.post(new Runnable() { @Override public void run() { if (callBack != null) { callBack.onProgress(total, current); } } }); }
ReqProgressCallBack:
public interface ReqProgressCallBack<T> extends ReqCallBack<T>{
/**
* 響應進度更新
*/
void onProgress(long total, long current);
}
ReqCallBack
public interface ReqCallBack <T>{
/**
* 響應成功
*/
void onReqSuccess(T result);
/**
* 響應失敗
*/
void onReqFailed(String errorMsg);
}
接下來就是呼叫了:
首先我先生成一個ProgressDialog 用來顯示進度
大家可以在點選檔案時 生成
private void showProgress() {
progressDialog = new ProgressDialog(ContractDetailsActivity.this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 設定水平進度條
progressDialog.setCancelable(false);// 設定是否可以通過點選Back鍵取消
progressDialog.setCanceledOnTouchOutside(false);// 設定在點選Dialog外是否取消Dialog進度條
progressDialog.setTitle("正在下載");
progressDialog.setMax(100);
progressDialog.setButton(DialogInterface.BUTTON_POSITIVE, "點選檢視",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (progressDialog.getProgress() == 100) {
// 此file為全域性變數 主要為了 請求成功後賦值呼叫
if (mFile != null) {
// Log.e("mFile...", "不為空");
toOpenFile(mFile);
progressDialog.dismiss();
}
} else {
ToastUtils.showToast(ContractDetailsActivity.this, "請等待下載完成後檢視");
}
}
});
progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
});
progressDialog.show();
}
然後呼叫請求網路方法:
private void requestFile() {
RequestManager.getInstance(this).downLoadFile(SysConstant.baseFile + accessName, accessName, new ReqProgressCallBack<File>() {
@Override
public void onProgress(long total, long current) {
progressDialog.setProgress((int) (current/(double)total*100));
if (progressDialog.getProgress() == 100) {
progressDialog.setTitle("下載完成");
}
}
@Override
public void onReqSuccess(File result) {
Log.e("fileresult====", result.getName());
mFile = result;
progressDialog.setProgress(100);
progressDialog.setTitle("下載完成");
}
@Override
public void onReqFailed(String errorMsg) {
Log.e("onReqFailed", errorMsg + " ");
progressDialog.setTitle("下載失敗,請重新下載...");
}
});
}
嗯哼,去用意圖開啟 你所現在的檔案。
accessName是你從網路獲取的檔名,我們首先獲取檔名,在找到字尾
通過後綴進行判斷
private void toOpenFile(File file) {
Intent intent = null;
String end = accessName.substring(accessName.lastIndexOf(".") + 1, accessName.length()).toLowerCase();
Log.e("end", end);
if (end.equals("doc") || end.equals("docx")) {
// Log.e("intent,file", file.getAbsolutePath());
intent = FileUtils.getWordFileIntent(this, file);
} else if (end.equals("pdf")) {
intent = FileUtils.getPdfFileIntent(this, file);
} else if (end.equals("txt")) {
intent = FileUtils.getTextFileIntent(this, file);
} else {
ToastUtils.showToast(this, "檔案型別不支援...");
return;
}
if (intent != null) {
startActivity(intent);
} else {
ToastUtils.showToast(this, "未找到匹配的程式... ");
}
}
FileUtils類
這裡有寫好的 呼叫系統獲取檔案的方法。
getPath 主要傳的是onActivityResult返回的data。getData()
public class FileUtils {
/**
* 獲取對應檔案的Uri
*
* @param intent 相應的Intent
* @param file 檔案物件
* @return
*/
private static Uri getUri(Context mContext, Intent intent, File file) {
Uri uri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//判斷版本是否在7.0以上
// Log.e("packgame", mContext.getPackageName());
uri =
FileProvider.getUriForFile(mContext,
mContext.getPackageName() + ".fileprovider",
file);
//新增這一句表示對目標應用臨時授權該Uri所代表的檔案
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
public static String getAppDir() {
return Environment.getExternalStorageDirectory() + SysConstant.fileName;
}
public static String getLogDir() {
String dir = getAppDir() + "/Log/";
return mkdirs(dir);
}
private static String mkdirs(String dir) {
File file = new File(dir);
if (!file.exists()) {
file.mkdirs();
}
return dir;
}
//kb 轉換
public static String FormetFileSize(long fileS) {
DecimalFormat df = new DecimalFormat("#.00");
String fileSizeString = "";
String wrongSize = "0B";
if (fileS == 0) {
return wrongSize;
}
if (fileS < 1024) {
fileSizeString = df.format((double) fileS) + "B";
} else if (fileS < 1048576) {
fileSizeString = df.format((double) fileS / 1024) + "KB";
} else if (fileS < 1073741824) {
fileSizeString = df.format((double) fileS / 1048576) + "MB";
} else {
fileSizeString = df.format((double) fileS / 1073741824) + "GB";
}
return fileSizeString;
}
//android獲取一個用於開啟PDF檔案的intent
public static Intent getPdfFileIntent(Context context, File file) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = getUri(context, intent, file);
intent.setDataAndType(uri, "application/pdf");
if (isIntentAvailable(context, intent)) {
return intent;
} else {
return null;
}
}
//android獲取一個用於開啟文字檔案的intent
public static Intent getTextFileIntent(Context context, File file) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = getUri(context, intent, file);
intent.setDataAndType(uri, "text/plain");
if (isIntentAvailable(context, intent)) {
return intent;
} else {
return null;
}
}
//android獲取一個用於開啟Word檔案的intent
public static Intent getWordFileIntent(Context context, File file) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = getUri(context, intent, file);
intent.setDataAndType(uri, "application/msword");
if (isIntentAvailable(context, intent)) {
return intent;
} else {
return null;
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static String getPath(Context context, Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;//sdk版本是否大於4.4
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
} else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* 判斷Intent 是否存在 防止崩潰
*
* @param context
* @param intent
* @return
*/
private static boolean isIntentAvailable(Context context, Intent intent) {
PackageManager packageManager = context.getPackageManager();
if (intent.resolveActivity(packageManager) != null) {
return true;
}
return false;
}
}
然後,我們要進行android 7.0的適配了,當然 你們不適配也是可以的,
在我的fileUtil類中 把 判斷給去掉就可以了
主要是getUri這個方法
適配:
註冊檔案中:
新增provider
applicationId是build.gradle中的defaultConfig中的屬性值
主要是包名,你們還可以這樣寫
authorities="com.ceshi.simple.fileprovider"
這個屬性一定要和fileutils中的getUri方法中
uri=FileProvider.getUri(mContext,mContext.getPackName()+".fileprovider",file)
紅色標註一模一樣 否則會報錯的
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
在res包中 新建xml包 在xml包中新建file_paths 名字一致就可以了
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path path="" name="myFile"></external-path>
</paths>
</resources>
這個東西你們可以百度 詳情一下7.0適配問題,我記不住 就懶得註釋說明了
以上ok。