1. 程式人生 > >Android okhttp 下載檔案到指定資料夾帶進度更新加7.0適配

Android okhttp 下載檔案到指定資料夾帶進度更新加7.0適配

專案需求:

從網路下載檔案,並且在手機上顯示,檔案型別目前有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。