Android 版本更新------后台服务和前台通知栏实现

前言:最近公司项目版本需要把以前的版本更新代码更新一下,换成Service和前台通知栏形式的,所以在此记录一下。

目录

1.检测版本更新(省略)

2.创建Service开启前台通知栏,并且执行下载任务,将下载进度通知到通知栏,下载完成后进行安装

  完整的Service代码

Service所用到的变量补充

2.开启一个透明InstallApkActivity执行安装Apk操作

  Activity代码

工具类代码

3.注意事项:因为24版本之后读取文件需要配置FileProvider,不然无法读取到apk文件

在res目录下建一个xml文件夹,然后创建file_path.xml文件

AndroidManifest.xml配置

OnlineFileProvider类代码


1.检测版本更新(省略)

这一步一般是通过后台接口对比当前APP版本是否低于服务器版本,从而获取最新版本的下载链接。这一步比较简单所以省略了。

2.创建Service开启前台通知栏,并且执行下载任务,将下载进度通知到通知栏,下载完成后进行安装

  完整的Service代码

package com.qtz.online.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import com.qtz.online.R;
import com.qtz.online.common.AppService;
import com.qtz.online.event_bus.DownloadApkMessage;
import com.qtz.online.mvp.activitys.InstallApkActivity;
import com.qtz.online.network.callback.ServiceDownloadObserver;
import com.qtz.online.network.client.DownLoadClient;
import com.qtz.online.utils.LogUtil;

import org.greenrobot.eventbus.EventBus;

import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;

/**
 * @author Created by PengGuiChu on 2020/12/19 19:32.
 * @explain  版本更新服务
 */
public class DownloadApkService extends Service {
    private static final String TAG=DownloadApkService.class.getSimpleName();
    private String url;//下载
    private String fileDir;
    private String fileName;
    private NotificationManager notificationManager;
    private NotificationCompat.Builder notificationBuilder;
    private static final String name="download_apk";
    private static final String id="Download_APK";
    private static final int startForegroundId=777;
    private int downLoadProgress;
    @Override
    public void onCreate() {
        super.onCreate();
        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationBuilder = getNotificationBuilder(notificationManager,getString(R.string.version_upgrade),getString(R.string.downloading,"0%"));
        notificationBuilder.setProgress(100,0,false);
        startForeground(startForegroundId,notificationBuilder.build());
//        notificationManager.notify(2,notificationBuilder.build());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent!=null){
            url=intent.getStringExtra("url");
            fileDir=intent.getStringExtra("fileDir");
            fileName=intent.getStringExtra("fileName");
            //该下载请求是我项目的下载请求,需要替换成你项目的下载文件的请求
            downloadFile(DownLoadClient.getService(AppService.class).download(url), new ServiceDownloadObserver<ResponseBody>(fileDir,fileName) {
                @Override
                public void onDownloadStart() {

                }

                @Override
                public void onDownloading(long progress, long total) {
                    int down= (int) (progress*100/total);
                    if (down!=downLoadProgress){
                        downLoadProgress=down;
                        if (notificationBuilder!=null){
                            notificationBuilder.setContentText(getString(R.string.downloading,downLoadProgress+"%"));
                            notificationBuilder.setProgress(100, downLoadProgress,false);
                        }
                        if (notificationManager!=null){
                            notificationManager.notify(startForegroundId,notificationBuilder.build());
                        }
                    }
                }

                @Override
                public void onDownloadSuccess(ResponseBody responseBody, String filePath) {
                    LogUtil.d(TAG,"下载完成,开启一个空白Activity执行安装窗口,保证用户在任何地方都能开启安装步骤");
//                    EventBus.getDefault().post(new DownloadApkMessage(0,filePath));
                    Intent intent=new Intent(getApplicationContext(), InstallApkActivity.class);
                    intent.putExtra("filePath",filePath);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                    stopForeground(true);
                    stopSelf();
                }

                @Override
                public void onDownloadError(String msg) {
                    LogUtil.d(TAG,"下载出错");
                    EventBus.getDefault().post(new DownloadApkMessage(1,msg));
                    stopForeground(true);
                    stopSelf();
                }
            });
        }
        return super.onStartCommand(intent, flags, startId);
    }

    private NotificationCompat.Builder getNotificationBuilder(NotificationManager notificationManager, String title, String message) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(DownloadApkService.id, DownloadApkService.name,
                    NotificationManager.IMPORTANCE_LOW);
            //闪光灯
            channel.enableLights(false);
            //是否允许震动
            channel.enableVibration(false);
            //设置可绕过  请勿打扰模式
            channel.setBypassDnd(false);
            channel.setSound(null,null);
            notificationManager.createNotificationChannel(channel);
        }
        NotificationCompat.Builder notification = new NotificationCompat.Builder(this, DownloadApkService.id);
        notification.build().flags= Notification.FLAG_ONGOING_EVENT;
        notification.setContentTitle(title);
        notification.setDefaults(NotificationCompat.DEFAULT_VIBRATE);
        notification.setContentText(message);
        notification.setSmallIcon(R.mipmap.ic_launcher);
        notification.setAutoCancel(false);
        notification.setCategory(Notification.CATEGORY_PROGRESS);
        return notification;
    }

    protected void downloadFile(Observable<ResponseBody> downloadObservable, final ServiceDownloadObserver<ResponseBody> downloadObserver) {
        downloadObservable.subscribeOn(Schedulers.io())//请求网络 在调度者的io线程
                .observeOn(Schedulers.io()) //指定线程保存文件
                .doOnNext(downloadObserver::saveFile)
                .observeOn(AndroidSchedulers.mainThread()) //在主线程中更新ui
                .subscribe(downloadObserver);
    }
}

Service所用到的变量补充

<string name="version_upgrade">发现新版本</string>
<string name="downloading">下载中…%1$s</string>

2.开启一个透明InstallApkActivity执行安装Apk操作

  Activity代码

package com.qtz.online.mvp.activitys;

import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.qtz.online.R;
import com.qtz.online.utils.InstallApkUtil;
import java.io.File;

public class InstallApkActivity extends AppCompatActivity {
    private InstallApkUtil installApkUtil;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_install_apk);
        String filePath = getIntent().getStringExtra("filePath");
        initInstallApkUtil(new File(filePath));
    }

    private void initInstallApkUtil(File file) {
        if (installApkUtil == null) {
            installApkUtil = new InstallApkUtil(InstallApkActivity.this, file.getAbsolutePath());
        }
        installApkUtil.installApk();
    }

    //更具用户授权回调执行安装操作
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 101) {
            if (installApkUtil != null) {
                installApkUtil.installApk();
            }
        }else if (requestCode==102){
            finish();
        }
    }
}

工具类代码

package com.qtz.online.utils;

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;

import com.qtz.online.BuildConfig;
import com.qtz.online.base.BaseActivity;
import com.qtz.online.view.ChoiceDialog;

import org.greenrobot.eventbus.EventBus;

import java.io.File;

/**
 * @author Created by PengGuiChu on 2020/12/19 15:53.
 * @explain
 */
public class InstallApkUtil {
    private AppCompatActivity baseActivity;
    private String apkPath;
    private ChoiceDialog choiceDialog;

    public InstallApkUtil(AppCompatActivity baseActivity, String apkPath) {
        this.baseActivity = baseActivity;
        this.apkPath=apkPath;
    }

    public void installApk() {
        if (Build.VERSION.SDK_INT >= 26) {
            //来判断应用是否有权限安装apk
            boolean installAllowed = baseActivity.getPackageManager().canRequestPackageInstalls();
            //有权限
            if (installAllowed) {
                //安装apk
                install();
            } else {
//这里弹出一个选择对话框,提示用户需要授权第三方安装应用,需要自己实现
                if (choiceDialog==null){
                    choiceDialog = new ChoiceDialog(baseActivity, new ChoiceDialog.ClauseDialogCallBack() {
                        @Override
                        public void onExit() {
                            baseActivity.finish();
                        }

                        @Override
                        public void onAgree() {
                            Uri packageURI = Uri.parse("package:" + baseActivity.getPackageName());
                            Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, packageURI);
                            baseActivity.startActivityForResult(intent, 101);
                        }
                    }, "安装应用需要打开未知来源权限,请去设置中开启权限", "权限申请", "取消", "前往");
                }
                choiceDialog.show();
            }
        } else {
            install();
        }
    }

    public void install() {
        if (Build.VERSION.SDK_INT >= 24) {
            Uri apkUri = FileProvider.getUriForFile(baseActivity,
                    BuildConfig.APPLICATION_ID + ".myprovider", new File(apkPath));
            Intent installIntent = new Intent(Intent.ACTION_VIEW);
            installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
            baseActivity.startActivityForResult(installIntent,102);
        } else {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setDataAndType(Uri.parse("file://" + apkPath), "application/vnd.android.package-archive");
            baseActivity.startActivityForResult(intent,102);
        }
    }

}

3.注意事项:因为24版本之后读取文件需要配置FileProvider,不然无法读取到apk文件

在res目录下建一个xml文件夹,然后创建file_path.xml文件

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-cache-path path="apk" name="."/>
</paths>

因为我是把apk下载在了Android/Data/包名/cache/apk目录下所以这样配置,这里需要根据你的具体情况进行配置

 

  • <root-path/> 代表设备的根目录new File("/");
  • <files-path/> 代表context.getFilesDir()
  • <cache-path/> 代表context.getCacheDir()
  • <external-path/> 代表Environment.getExternalStorageDirectory()
  • <external-files-path>代表context.getExternalFilesDirs()
  • <external-cache-path>代表getExternalCacheDirs()

 

AndroidManifest.xml配置

<provider
            android:name=".utils.OnlineFileProvider"
            android:authorities="${applicationId}.myprovider"
            android:exported="false"
            android:grantUriPermissions="true">

            <!-- 元数据 -->
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_path" />
        </provider>

OnlineFileProvider类代码

package com.qtz.online.utils;

import androidx.core.content.FileProvider;

/**
 * @author Created by PengGuiChu on 2020/12/19 16:39.
 * @explain
 */
public class OnlineFileProvider extends FileProvider {
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值