1. 程式人生 > >DownloadManager下載APK並安裝(適配7.0,免費下載)

DownloadManager下載APK並安裝(適配7.0,免費下載)

效果圖

這裡寫圖片描述

DownloadManager(主角)

作用:下載管理器是一個處理長時間執行的HTTP下載的系統服務。客戶端可能要求將URI下載到特定的目標檔案。下載管理器將在後臺進行下載。

過程:在MainActivity中開啟IntentService服務,在IntentService中進行下載操作,完成後發出廣播,在MainActivity中接收廣播並進行安裝apk

兩個內部類

class DownloadManager.Query 用來過濾下載管理器查詢
class DownloadManager.Request 這個類包含請求新下載所必需的所有資訊

佈局情況

只有一個按鈕
這裡寫圖片描述

開啟服務

當點選按鈕的時候,進行開啟服務

//DownloadService繼承於IntentService,一會說明
Intent serviceIntent = new Intent(MainActivity.this, DownloadService.class);
//寫入你的apk下載地址,下面這個地址只是模擬的
serviceIntent.setData(Uri.parse("http://..../vooloc.apk"));
//開啟服務,不要寫成了startActivity;
startService(serviceIntent);

新建DownloadService類繼承於IntentService

重寫方法onHandleIntent ( Intent intent )

1.Request設定

/*獲取下載地址*/
String url = intent.getDataString();
/*獲取DownloadManager物件*/
DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request
(Uri.parse(url)); /*指定APK快取路徑和應用名稱,比如我這個路徑就是/storage/emulated/0/Download/vooloc.apk*/ request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "vooloc.apk"); /*設定網路下載環境為wifi或者移動資料*/ request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE); /*設定顯示通知欄,下載完成後通知欄自動消失*/ request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE); /*設定通知欄標題*/ request.setTitle("下載"); /*設定這個下載的描述,顯示在通知中*/ request.setDescription("應用正在下載"); /*設定型別為.apk*/ request.setMimeType("application/vnd.android.package-archive"); /*獲得唯一下載id*/ long requestId = downloadManager.enqueue(request);

2.Query設定

//將id放進Intent
Intent localIntent = new Intent(BROADCAST_ACTION);
localIntent.putExtra(EXTENDED_DATA_STATUS, requestId);
//查詢下載資訊
DownloadManager.Query query = new DownloadManager.Query();
//只包括帶有給定id的下載。
query.setFilterById(requestId);

3.進行下載

try {
    boolean isGoging = true;
    while (isGoging) {
        Cursor cursor = downloadManager.query(query);
        if (cursor != null && cursor.moveToFirst()) {
        int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
        switch (status) {
            //如果下載狀態為成功
            case DownloadManager.STATUS_SUCCESSFUL:
                isGoging = false;
                //呼叫LocalBroadcastManager.sendBroadcast將intent傳遞回去
                mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
                mLocalBroadcastManager.sendBroadcast(localIntent);
                break;
            }
        }
        if (cursor != null) {
            cursor.close();
        }
    }
} catch (Exception e) {
    e.printStackTrace();
}

5.別忘了在清單檔案(AndroidManifest)註冊服務

<service
    android:name=".DownloadService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.android.threadsample.BROADCAST" />
    </intent-filter>
</service>

回到MainActivity,註冊廣播

private void regist() {
    IntentFilter intentFilter = new IntentFilter(DownloadService.BROADCAST_ACTION);
    intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter);
    }

新建類MyReceiver繼承於BroadcastReceiver

/*找到下載好的apk*/
File file=new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),"vooloc.apk");
/*獲取許可權*/
try {
    Runtime.getRuntime().exec("chmod 777"+file.getCanonicalPath());
} catch (IOException e) {
    e.printStackTrace();
}
/*由於沒有在Activity環境下啟動Activity,設定下面的標籤*/
intent = new Intent(Intent.ACTION_VIEW);
/*如果設定,這個活動將成為這個歷史堆疊上的新任務的開始*/
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
/*判讀版本是否在7.0以上*/
if(Build.VERSION.SDK_INT>=24){
    /*7.0及以上的版本,私有目錄被限制訪問*/
    Uri apkUri = FileProvider.getUriForFile(context, "com.example.yx.downloadapk.FileProvider", file);
    /*新增這一句表示對目標應用臨時授權該Uri所代表的檔案*/
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
}else{
    /*7.0以下的版本*/
    intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
startActivity(intent);

取消廣播

    @Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    }

申請許可權

Android6.0開始,危險許可權需要動態申請!!!

剛好這裡的第二個許可權是危險許可權…

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

動態申請許可權

    //動態申請許可權
    private void requestPermission() {
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
        }
    }

適配7.0

由於Android7.0以上私有目錄被限制訪問,還要做些其他工作

開啟清單檔案(AndroidManifest),加上這段

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.yx.downloadapk.FileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

第二行的格式 android:authorities=”包名.FileProvider”

接下來在res資料夾下新建xml資料夾

在XML資料夾中新建file_paths.xml

file_paths.xml寫入

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <external-path path="" name="download"/>
    </paths>
</resources>

完整程式碼

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    MyReceiver receiver = new MyReceiver();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //動態申請許可權
        requestPermission();
        //初始化資料
        initView();
        //註冊廣播
        regist();
    }

    //動態申請許可權
    private void requestPermission() {
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
        }
    }

    //初始化資料
    private void initView() {
        Button btn_download = (Button) findViewById(R.id.btn_download);
        btn_download.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        //DownloadService繼承IntentService,一會說明
        Intent serviceIntent = new Intent(MainActivity.this, DownloadService.class);
        //寫入你的apk下載地址,下面這個地址只是模擬的
        serviceIntent.setData(Uri.parse("http://..../vooloc.apk"));
        //開啟服務,不要寫成了startActivity(serviceIntent);
        startService(serviceIntent);
    }

    public class MyReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "vooloc.apk");
            //獲取許可權
            try {
                Runtime.getRuntime().exec("chmod 777" + file.getCanonicalPath());
            } catch (IOException e) {
                e.printStackTrace();
            }
            // 由於沒有在Activity環境下啟動Activity,設定下面的標籤
            intent = new Intent(Intent.ACTION_VIEW);
            //如果設定,這個活動將成為這個歷史堆疊上的新任務的開始
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            //判讀版本是否在7.0以上
            if (Build.VERSION.SDK_INT >= 24) {
                //7.0以上的版本
                Uri apkUri = FileProvider.getUriForFile(context, "com.example.yx.downloadapk.FileProvider", file);
                //新增這一句表示對目標應用臨時授權該Uri所代表的檔案
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            } else {
                //7.0以下的版本
                intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
            }
            startActivity(intent);
        }
    }

    //註冊廣播
    private void regist() {
        IntentFilter intentFilter = new IntentFilter(DownloadService.BROADCAST_ACTION);
        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    }
}

DownloadService

public class DownloadService extends IntentService {
    private static final String TAG = "DownloadService";
    public static final String BROADCAST_ACTION = "com.example.android.threadsample.BROADCAST";
    public static final String EXTENDED_DATA_STATUS = "com.example.android.threadsample.STATUS";
    private LocalBroadcastManager mLocalBroadcastManager;

    public DownloadService() {
        super("DownloadService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //獲取下載地址
        String url = intent.getDataString();
        Log.i(TAG, url);
        //獲取DownloadManager物件
        DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        //指定APK快取路徑和應用名稱,比如我這個路徑就是/storage/emulated/0/Download/vooloc.apk
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "vooloc.apk");
        //設定網路下載環境為wifi或者移動資料
        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
        //設定顯示通知欄,下載完成後通知欄自動消失
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
        //設定通知欄標題
        request.setTitle("下載");
        //設定這個下載的描述,顯示在通知中
        request.setDescription("應用正在下載");
        //設定型別為.apk
        request.setMimeType("application/vnd.android.package-archive");
        //獲得唯一下載id
        long requestId = downloadManager.enqueue(request);
        //將id放進Intent
        Intent localIntent = new Intent(BROADCAST_ACTION);
        localIntent.putExtra(EXTENDED_DATA_STATUS, requestId);
        //查詢下載資訊
        DownloadManager.Query query = new DownloadManager.Query();
        //只包括帶有給定id的下載。
        query.setFilterById(requestId);
        try {
            boolean isGoging = true;
            while (isGoging) {
                Cursor cursor = downloadManager.query(query);
                if (cursor != null && cursor.moveToFirst()) {
                    int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
                    switch (status) {
                        //如果下載狀態為成功
                        case DownloadManager.STATUS_SUCCESSFUL:
                            isGoging = false;
                            //呼叫LocalBroadcastManager.sendBroadcast將intent傳遞回去
                            mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
                            mLocalBroadcastManager.sendBroadcast(localIntent);
                            break;
                    }
                }
                if (cursor != null) {
                    cursor.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

樣例網盤下載

IDE環境不同,直接匯入我的專案可能會報紅,目的不是去直接使用我的程式碼,只是為了方便觀看,可以在自己的IDE環境下自己寫一遍執行看看