1. 程式人生 > >從網路中下載檔案儲存到SD卡和顯示下載進度

從網路中下載檔案儲存到SD卡和顯示下載進度

任務:

1.從網路中下載檔案儲存到SD卡

2.顯示下載進度

人醜話不多,直接擼程式碼。

xml佈局檔案:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="點選我開始下載"
        android:id="@+id/btn_download"
        />


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下載進度:"
        android:layout_marginTop="40dp"/>


    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:progress="100"
        />

</LinearLayout>

java程式碼:

package com.example.jiaho.handleproject;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class DownLoadActivity extends AppCompatActivity {

    public static final int DOWNLOAD_MESSAGE_CODE = 10001;
    public static final int DOWNLOAD_MESSAGE_FAIL_CODE = 10002;
    public static final String AppURL = "http://192.168.31.138/mukewang.apk";
    private Button btn_download;
    private ProgressBar progressBar;
    private Handler mHandler;
    private static final int REQUEST_EXTERNAL_STORAGE=1;
    private static String[] PERMISSIONS_STORAGE={
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_download);
        btn_download=findViewById(R.id.btn_download);
        progressBar=findViewById(R.id.progressBar);
        /*
        * 主執行緒
        * 1.點選按鍵
        * 2.發起下載
        * 3.開啟子執行緒做下載
        * 4.下載過程中通知主執行緒    ------>   主執行緒更新進度條
        * */

        btn_download.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //在子執行緒中執行download方法
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        download(AppURL);
                    }
                }).start();
            }
        });

        //接收子執行緒的訊息
        mHandler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //處理訊息
                switch (msg.what){
                    case DOWNLOAD_MESSAGE_CODE:
                        progressBar.setProgress((int)msg.obj);
                        break;
                    case DOWNLOAD_MESSAGE_FAIL_CODE:
                        Toast.makeText(DownLoadActivity.this,"下載失敗",Toast.LENGTH_SHORT).show();
                }
            }
        };

    }

    public void download(String appUrl){
        try {
            //拿到Url
            URL url=new URL(appUrl);
            //開啟一個connect
            URLConnection urlConnection=url.openConnection();
            //獲取輸入流
            InputStream inputStream=urlConnection.getInputStream();
            //獲取檔案的總長度
            int contentLength=urlConnection.getContentLength();

            //建立一個下載路徑
            String downloadFolderName= Environment.getExternalStorageDirectory()+File.separator+"imooc"+File.separator;
            //建立一個檔案
            File file=new File(downloadFolderName);

            if(!file.exists()){
                //如果檔案不存在,就建立一個檔案
                file.mkdir();
            }

            //建立檔案
            String fileName=downloadFolderName+"mkapp.apk";
            File fileMove=new File(fileName);

            //如果檔案存在就把它刪掉,下次執行程式就不會報錯
            if (fileMove.exists()){
                fileMove.delete();
            }

            //記錄下載的大小
            int downloadSize=0;
            byte[] bytes=new byte[1024];
            int length=0;
            //建立一個輸出流
            //verifyStoragePermissions(DownLoadActivity.this);
            OutputStream outputStream=new FileOutputStream(fileName);
            while ((length=inputStream.read(bytes))!=-1){
                outputStream.write(bytes,0,length);
                downloadSize+=length;
                /*
                * 每完成一點下載,就及時更新UI
                * */
                Message message = Message.obtain();
                //提取出一個靜態變數
                message.what = DOWNLOAD_MESSAGE_CODE;
                message.obj=(downloadSize/contentLength)*100;
                //將訊息通過Handle發出去
                mHandler.sendMessage(message);
            }

        }catch (MalformedURLException e){
            //如果下載失敗了,也要傳送下載失敗的訊息
            NotifyDownLoadFail();
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
            NotifyDownLoadFail();
        }

    }

    //提取出一個方法
    private void NotifyDownLoadFail() {
        Message message=Message.obtain();
        message.what= DOWNLOAD_MESSAGE_FAIL_CODE;
        mHandler.sendMessage(message);
    }

    //檢查許可權的方法,android6.0才需要在java程式碼中實現許可權賦予
    public static void verifyStoragePermissions(Activity activity){
        //Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission!= PackageManager.PERMISSION_GRANTED){
            //沒有許可權,則通知使用者
            ActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
        }
    }
}

解釋一下程式碼段:

//檢查許可權的方法,android6.0才需要在java程式碼中實現許可權賦予
    public static void verifyStoragePermissions(Activity activity){
        //Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity,Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission!= PackageManager.PERMISSION_GRANTED){
            //沒有許可權,則通知使用者
            ActivityCompat.requestPermissions(activity,PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
        }
    }

這個程式碼在android6.0上才被需要,因為當時寫程式碼時,老是提示許可權不夠,就在網上查各種資料,所以各種變態許可權都試了一遍,足足嘗試了2個多小時,發現還是不行。後來我就納悶了,我的android才4.4啊,根本不需要這些才對啊。於是我重新認真檢查程式碼,發現了一個隱藏至深的bug,真是痛心疾首,老夫捶胸頓足,差點一口老血噴了出來。原來是我寫檔案路徑的時候,將程式碼File.separator寫成了File.pathSeparator。

簡單講下File.separator和File.pathSeparator的區別:

File.pathSeparator指的是分隔連續多個路徑字串的分隔符,例如:

java -cp test.jar;hello.jar

就是指";"

 File.separator才是用來分隔同一個路徑字串中的目錄,例如:

E:\Android\androidProject

就是指"\"