android 軟件更新 app安裝 更新

app更新有以下幾點需要注意

1. 安卓7.0以上需要動態申請文件讀寫權限

2.安卓8.0以上安裝第三方應用需要手動打開設置給予權限。

3.manifest需要註冊下載服務service

4.manifest需要配置provider,否則文件讀取失敗

--------------------------------------------------------------------------以下是代碼

1.res下新建文件夾xml,在xml文件夾下新建文件file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <root-path
        name="root_path"
        path="." />
</paths>

2.AndroidManifest文件的配置

 <!--   Manifest中也要聲明安裝permission  -->

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

  <!--   需要註冊服務 DownloadService文件是新建的下載管理  -->
        <service android:name=".DownloadService" />
        <!--  authorities 值需要和DownloadService裏面一個參數對應。 更新:已經被放到activity裏面了   -->
        <!--  resource 是res文件夾下xml文件夾下的一個資源文件,名字要對應   -->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.fgh.google.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

3.新建文件DownloadService.java

package com.openfood.rider.utils;


import android.app.DownloadManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;

import com.openfood.rider.DeliveryApplication;
import com.openfood.rider.R;
import com.openfood.rider.view.ToastCustom;

import java.io.File;

/**
 * 在DownloadFinishReceiver類中需要更改一個參數。。。。。。。。。。。。。。。。。。
 * <p>
 * 安卓8.0安裝外部應用需要手動打開權限
 *
 * @author fgh
 */

public class DownloadService extends Service {

    public DownloadFinishReceiver mReceiver;

    public DownloadService() {

    }

    /**
     * 下載的文件名
     */
    public static String appName = "";


    @Override
    public void onCreate() {
        super.onCreate();
        //註冊下載完成的廣播
        mReceiver = new DownloadFinishReceiver();
        registerReceiver(mReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new DownBinder();
    }

    public class DownBinder extends Binder {

        public void startDownload(String name, String downUrl, OnFinishListener listener) {

            mOnFinishListener = listener;
            appName = name;


            //初始化DownloadManager並開始下載
            try {
                DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downUrl));
                File file = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), appName);
                request.setDestinationUri(Uri.fromFile(file));
                mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);

                //判斷是否有任務:true是有任務下載中
                if (checkStatus22()) {
                    return;
                }
                //刪除已經存在的apk包
                File apkFile = new File(DownloadService.this.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), appName);
                if (apkFile.exists()) {
                    apkFile.delete();
                }
                downloadId = mDownloadManager.enqueue(request);

                checkStatus();

            } catch (Exception e) {
                ToastCustom.Companion.showToast(DeliveryApplication.Companion.getContext()
                        , DeliveryApplication.Companion.getContext().getString(R.string.error_down)
                        , R.drawable.shape_error_bg, R.color.colorWhite);

            }

        }
    }

    public OnFinishListener mOnFinishListener;
    DownloadManager mDownloadManager;

    public interface OnFinishListener {
        void onFinish();

        void onDownLoading();

        void onDownError();

        void onDownPause();
    }


    public class DownloadFinishReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            //廣播回調
            checkStatus();
        }
    }

    private void checkStatus() {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(downloadId);
        Cursor c = mDownloadManager.query(query);
        if (c.moveToFirst()) {
            int status = c.getInt(c.getColumnIndex((DownloadManager.COLUMN_STATUS)));
            switch (status) {
                case DownloadManager.STATUS_PAUSED:
                    mOnFinishListener.onDownPause();
                    break;
                case DownloadManager.STATUS_PENDING:
                    mOnFinishListener.onDownLoading();
                    break;
                case DownloadManager.STATUS_RUNNING:
                    mOnFinishListener.onDownLoading();
                    break;
                case DownloadManager.STATUS_SUCCESSFUL:
                    mOnFinishListener.onFinish();
                    break;
                case DownloadManager.STATUS_FAILED:
                    mOnFinishListener.onDownError();
                    break;
            }
        }
        c.close();
    }


    //返回true是有任務下載中
    private boolean checkStatus22() {
        DownloadManager.Query query = new DownloadManager.Query();
        query.setFilterById(downloadId);
        Cursor c = mDownloadManager.query(query);
        if (c.moveToFirst()) {
            int status = c.getInt(c.getColumnIndex((DownloadManager.COLUMN_STATUS)));
            switch (status) {
                case DownloadManager.STATUS_PENDING:
                    mOnFinishListener.onDownLoading();
                    c.close();
                    return true;
                case DownloadManager.STATUS_RUNNING:
                    mOnFinishListener.onDownLoading();
                    c.close();
                    return true;
                default:
                    c.close();
                    return false;
            }
        }
        c.close();
        return false;
    }

    private static long downloadId;

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver);
    }

}



 

4.activity中,新建文件AppUpdateActivity.kt

package com.example.fgh.google

import android.Manifest
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.IBinder
import android.provider.Settings
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v4.content.FileProvider
import android.support.v7.app.AppCompatActivity
import com.example.fgh.google.utils.LogUtils
import kotlinx.android.synthetic.main.activity_update.*
import java.io.File

/**
 *
 *   app更新  下載 安裝
 *   manifest需要註冊服務service
 *   manifest配置組建provider android7.0以上需要
 *   res文件夾下新建文件file_paths.xml
 *
 */
class AppUpdateActivity : AppCompatActivity() {

    private var binder: DownloadService.DownBinder? = null

    private val connection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {

        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder) {
            binder = service as DownloadService.DownBinder
        }
    }

    private val apkPermissionCode = 996
    private val filePermissionCode = 34

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_update)

        //請求外部文件讀寫權限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //請求權限
            LogUtils.e(">>>>>>>>請求權限")
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), filePermissionCode)
        }

        initDown()

        val listener = DownloadService.OnFinishListener {
            LogUtils.e(">>>>>>>>>>>>>>>下載完了。。。。。")
            //安卓8.0外部apk安裝權限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (!packageManager.canRequestPackageInstalls()) {
                    //去打開權限
                    val uri = Uri.parse("package:$packageName")
                    val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, uri)
                    startActivityForResult(intent, apkPermissionCode)
                } else {
                    //有權限了,直接安裝
                    initApkInstall()
                }
            } else {
                //小於安卓8.0直接安裝
                initApkInstall()
            }

        }

        button.setOnClickListener {
            binder?.startDownload(appName, "http://gdown.baidu.com/data/wisegame/3d4de3ae1d2dc7d5/weixin_1360.apk", listener)
//            binder?.startDownload(appName, "http://cdn.xiaoxiongyouhao.com/apps/androilas.apk", listener)
        }

    }


    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == apkPermissionCode) {
            //權限申請到了,全裝
            initApkInstall()
        }
    }

    private fun initDown() {

        val intent = Intent(this, DownloadService::class.java)
        startService(intent)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)

    }

    private val appName = "apkUpdate.apk"

    /**
     * 安裝apk
     */
    private fun initApkInstall() {
        //Android獲取一個用於打開APK文件的intent
        val intent1 = Intent(Intent.ACTION_VIEW)
        // 由於沒有在Activity環境下啓動Activity,設置下面的標籤
        intent1.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        if (Build.VERSION.SDK_INT >= 24) { //判讀版本是否在7.0以上
            /**
             * 下面的參數需要改
             */
            //參數1 上下文, 參數2 Provider主機地址 和AndroidManifest配置文件中保持一致   參數3  共享的文件
            val apkUri = FileProvider.getUriForFile(this, "com.example.fgh.google.fileprovider",
                    File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), appName))
            //添加這一句表示對目標應用臨時授權該Uri所代表的文件
            intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent1.setDataAndType(apkUri, "application/vnd.android.package-archive")
        } else {
            intent1.setDataAndType(Uri.fromFile(File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), appName)),
                    "application/vnd.android.package-archive")

        }
        startActivity(intent1)

    }


}

========================上面的activity沒有解決重複下載的問題,參加下面的

package com.openfood.rider.ui.activity

import android.Manifest
import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.IBinder
import android.provider.Settings
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.support.v4.content.FileProvider
import com.openfood.rider.R
import com.openfood.rider.base.BaseActivity
import com.openfood.rider.base.BaseBean
import com.openfood.rider.mvp.bean.AppUpdateBean
import com.openfood.rider.mvp.contract.AppUpdateContract
import com.openfood.rider.mvp.presenter.AppUpdatePresenter
import com.openfood.rider.showToast
import com.openfood.rider.showToastInCenter
import com.openfood.rider.utils.DownloadService
import com.openfood.rider.view.dialog.NoUpdateDialog
import com.openfood.rider.view.dialog.UpdateDialog
import com.openfood.rider.view.loadingDialog.LoadingDialog
import kotlinx.android.synthetic.main.activity_about_us.*
import kotlinx.android.synthetic.main.layout_toolbar.*
import java.io.File

/**
 * 關於我們
 */
class AboutUsActivity : BaseActivity(), AppUpdateContract.View {

    override fun showError(msg: String, errorCode: Int) {
        showToast(msg)
    }

    companion object {

        fun actionStart(context: Context) {
            context.startActivity(Intent(context, AboutUsActivity::class.java))
        }
    }

    override fun layoutId(): Int = R.layout.activity_about_us

    override fun initData() {
    }

    private val apkPermissionCode = 996
    private val filePermissionCode = 34
    private val mPresenter by lazy { AppUpdatePresenter() }
    private var binder: DownloadService.DownBinder? = null
    private val appName = "updateApp.apk"

    private val connection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {

        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder) {
            binder = service as DownloadService.DownBinder
        }
    }

    override fun setAppUpdate(bean: BaseBean<AppUpdateBean>) {
        //app版本更新
        val manager = packageManager
        var versionCode = 0
        try {
            val info = manager.getPackageInfo(packageName, 0)
            versionCode = info.versionCode
        } catch (e: Exception) {
            e.printStackTrace()
        }

        if (versionCode < bean.data.id.toInt()) {
            //顯示有新版本
            UpdateDialog.showDialog(fragmentManager, bean.data).setOnItemClickListener(object : UpdateDialog.OnItemClickListener {
                override fun onItemClick(url: String) {
                    //跳轉到play store更新
                    val intent = Intent(Intent.ACTION_VIEW)
                    intent.data = Uri.parse("market://details?id=$packageName")
                    if (intent.resolveActivity(packageManager) != null) {
                        startActivity(intent)
                    } else {
                        intent.data = Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
                        if (intent.resolveActivity(packageManager) != null) {
                            startActivity(intent)
                        } else {
                            showToast("請先安裝play store或者瀏覽器")
                        }
                    }

                    //下面是軟件內更新
//                    binder?.startDownload(appName
//                            , url
//                            , listener)
                }
            })
        } else {
            //當前是最新
            NoUpdateDialog.showDialog(fragmentManager, 0)
        }
    }

    private lateinit var loadingDialog: LoadingDialog

    override fun showLoading() {
        loadingDialog = LoadingDialog(this, "")
        loadingDialog.show()
    }

    override fun dismissLoading() {
        loadingDialog.close()
    }

    @SuppressLint("SetTextI18n")
    override fun initView() {
        mPresenter.attachView(this)

        val info = packageManager.getPackageInfo(packageName, 0)
        version.text = "V${info.versionName}"

        //請求外部文件讀寫權限
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //請求權限
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), filePermissionCode)
        }

        initDown()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == apkPermissionCode) {
            //權限申請到了,裝apk
            initApkInstall()
        }
    }


    private val listener = object : DownloadService.OnFinishListener {

        override fun onFinish() {
            //安卓8.0外部apk安裝權限
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (!packageManager.canRequestPackageInstalls()) {
                    //去打開權限
                    val uri = Uri.parse("package:$packageName")
                    val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, uri)
                    startActivityForResult(intent, apkPermissionCode)
                } else {
                    //有權限了,直接安裝
                    initApkInstall()
                }
            } else {
                //小於安卓8.0直接安裝
                initApkInstall()
            }
        }

        override fun onDownLoading() {
            //正在下載中
            showToastInCenter(resources.getString(R.string.update_ing))
        }

        override fun onDownError() {
            //下載錯誤
            showToastInCenter(resources.getString(R.string.update_error))
        }

        override fun onDownPause() {
            //下載暫停
            showToastInCenter(resources.getString(R.string.update_pause))
        }

    }

    override fun initEvent() {


        //檢查更新
        checkUpdate.setOnClickListener {
            mPresenter.requestAppUpdate("2")
        }
        //功能介紹
        function.setOnClickListener {
            FunctionActivity.actionStart(this)
        }
        //用戶協議
        agreement.setOnClickListener {
            AgreementActivity.actionStart(this)
        }

    }

    private fun initDown() {
        val intent = Intent(this, DownloadService::class.java)
        startService(intent)
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }


    /**
     * 安裝apk
     */
    private fun initApkInstall() {
        //Android獲取一個用於打開APK文件的intent
        val intent1 = Intent(Intent.ACTION_VIEW)
        // 由於沒有在Activity環境下啓動Activity,設置下面的標籤
        intent1.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        if (Build.VERSION.SDK_INT >= 24) { //判讀版本是否在7.0以上
            /**
             * 下面的參數需要改
             */
            //參數1 上下文, 參數2 Provider主機地址 和AndroidManifest配置文件中保持一致   參數3  共享的文件
            val apkUri = FileProvider.getUriForFile(this, "com.openfood.rider.fileprovider",
                    File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), appName))
            //添加這一句表示對目標應用臨時授權該Uri所代表的文件
            intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent1.setDataAndType(apkUri, "application/vnd.android.package-archive")
        } else {
            intent1.setDataAndType(Uri.fromFile(File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), appName)),
                    "application/vnd.android.package-archive")
        }
        startActivity(intent1)

    }

    override fun start() {
    }

    override fun initToolBar() {
        centerTitle.text = resources.getString(R.string.about_us)
        leftIcon.setImageResource(R.drawable.icon_back)
        leftIcon.setOnClickListener {
            finish()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        mPresenter.detachView()
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章