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()
    }

}

 

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