app版本更新,通知形式显示安装包下载进度

2024-09-05 16:18

本文主要是介绍app版本更新,通知形式显示安装包下载进度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

也是公司的项目需要,就稍微研究了下,参考网上一些不错的思路,但其适用版本都比较早,所以通知做了适配了Android 8.0,及权限问题等问题。

原理;下载apk过程中,发起一个通知,并不断发起最新进度的相同ID的通知,覆盖上一个通知,达到显示当前下载进度的效果。

demo已上传:https://download.csdn.net/download/u013370255/10603681

下面简单贴一下代码,及一些需要注意和说明的地方。

1.运行时权限:permissionsdispatcher
用法百度下,多的泛,我就不多说了,

// Permissionimplementation 'com.github.hotchemi:permissionsdispatcher:3.1.0'annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:3.1.0'implementation 'com.android.support:support-v4:27.1.1'

同时AndroidManifast申请以下权限:

    <!--允许程序设置内置sd卡的写权限--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!--允许程序获取网络状态--><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><!--允许程序访问WiFi网络信息--><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><!--允许程序打开网络套接字--><uses-permission android:name="android.permission.INTERNET" /><!--android8.0安装apk权限--><uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

获取文件存储权限,本当提示弹窗确认权限,这里偷个懒,直接通过了

@RuntimePermissions
public class BaseActivity extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);}@Overrideprotected void onResume() {super.onResume();BaseActivityPermissionsDispatcher.storageWithPermissionCheck(this);}@Overrideprotected void onPause() {super.onPause();}@Overrideprotected void onDestroy() {super.onDestroy();}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);// NOTE: delegate the permission handling to generated methodBaseActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);}@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)void storage() {//动态权限}@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)void showRationaleForStorage(final PermissionRequest request) {// TODO: 2018/8/16 当提示弹窗确认权限,这里偷个懒,直接通过了 request.proceed();}@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)void showDeniedForStorage() {}@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)void showNeverAskForStorage() {}
}
  1. MainActivity 调用,传入你的下载路径,保存的文件名
public class MainActivity extends BaseActivity {String path = "你的下载路径";String name = "下载后的文件名";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = new Intent(this, UpdateService.class);Bundle bundle = new Bundle();bundle.putString("path",path);bundle.putString("name",name);intent.putExtras(bundle);startService(intent);}
}

3.主要服务UpdateService ,避免手机应用进程划掉就不下载了,并在AndroidManifast进行注册

<service android:name=".Download.UpdateService" />
public class UpdateService extends Service {UpdateManagerNotification mUpdateManagerNotification;/*** 首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。* 如果服务已在运行,则不会调用此方法。该方法只被调用一次*/@Overridepublic void onCreate() {Log.i("bb","onCreate invoke");//下载情况通知,点击通知的操作,这里表示跳转到MainActivitymUpdateManagerNotification = new UpdateManagerNotification(getApplicationContext());Intent intentLoGo = new Intent(getApplicationContext(), MainActivity.class);PendingIntent piLoGo = PendingIntent.getActivity(getApplicationContext(), 0, intentLoGo, 0);mUpdateManagerNotification.showNotification(getApplicationContext(),piLoGo,"软件更新","软件更新",getApplicationContext().getString(R.string.app_name),"软件更新",getApplicationContext().getString(R.string.app_name));super.onCreate();}/*** 绑定服务时才会调用* 必须要实现的方法* @param intent* @return*/@Nullable@Overridepublic IBinder onBind(Intent intent) {Log.i("bb","onBind invoke");return null;}/*** 每次通过startService()方法启动Service时都会被回调。* @param intent* @param flags* @param startId* @return*/@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Bundle bundle = intent.getExtras();if(bundle != null){String path = bundle.getString("path","");String name = bundle.getString("name","");UpdateDownloadRequest mUpdateDownloadRequest = new UpdateDownloadRequest(new UpdateDownloadListener() {@Overridepublic void onStarted() {}@Overridepublic void onProgressChanged(int progress, String downloadUrl) {Message message = new Message();message.what = progress;mUpdateManagerNotification.handler.sendMessage(message);Log.i("bb","progress = " + progress);}@Overridepublic void onFinished(float completeSize, String downloadUrl) {Message message = new Message();message.what = 100;mUpdateManagerNotification.handler.sendMessage(message);}@Overridepublic void onFailure(Exception e) {Log.e("Exception:/","Update Download Exception = " + e.getMessage());}});mUpdateDownloadRequest.downLoadApk(getApplication(),path,name);}return super.onStartCommand(intent, flags, startId);}/*** 服务销毁时的回调*/@Overridepublic void onDestroy() {Log.i("bb","onDestroy invoke");super.onDestroy();}
}

4.通知实现,适配Android8.0,系统布局(大布局)
这里需要注意的是通知图标和提示文字需要你自己定,并且代码中的图标logo_alpha必须是32*32的白色切图,这个好像是Google的规范,注意下。
关键代码

这句代码可以去除Android8.0的声音和震动,不加的话,即使你不设置也会有声音或震动。
channel.setSound(null, null);
public class UpdateManagerNotification {private Context mContext;NotificationManager notificationManager;NotificationChannel channel;NotificationCompat.Builder builder;@SuppressLint ("HandlerLeak")public Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);if(msg.what == 100){//下完安装,并清除通知//android O后必须传入NotificationChannelif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){notificationManager.cancel(Constants.NOTIFICATIONID_APP);}else {NotificationManagerCompat managerCompat = NotificationManagerCompat.from(mContext);managerCompat.cancel(Constants.NOTIFICATIONID_APP);}}else if(msg.what >= 0 && msg.what < 100){//下载进度builder.setProgress(100, msg.what, false);builder.setContentText("下载进度:" + msg.what + "%");builder.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE);//android O后必须传入NotificationChannelif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){notificationManager.notify(Constants.NOTIFICATIONID_APP, builder.build());}else {NotificationManagerCompat managerCompat = NotificationManagerCompat.from(mContext);managerCompat.notify(Constants.NOTIFICATIONID_APP, builder.build());}}}};public UpdateManagerNotification(Context context) {this.mContext = context;}/*** 生成通知* @param context* @param pi* 例如:* Intent intentYiChe = new Intent(context, YiCheRecordActivity.class);* Bundle bundle = new Bundle();* intentYiChe.putExtras(bundle);* PendingIntent piYiChe = PendingIntent.getActivity(context, 0, intentYiChe, 0);* @param ticker 			标题* @param contentTitle		标题* @param bigContentTitle	标题* @param summaryText		标题* @param Message	显示内容*/public void showNotification(Context context, PendingIntent pi,String ticker, String contentTitle, String bigContentTitle, String summaryText,String Message){//android O后必须传入NotificationChannelif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);if(notificationManager != null){//ChannelId为"1",ChannelName为"Channel1"channel = new NotificationChannel("4","运维通更新通知通道", NotificationManager.IMPORTANCE_DEFAULT);channel.enableLights(true); //是否在桌面icon右上角展示小红点channel.setLightColor(Color.YELLOW); //小红点颜色channel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知channel.setSound(null, null);notificationManager.createNotificationChannel(channel);builder = new NotificationCompat.Builder(context,"4");setNotification(builder,context,pi,ticker,contentTitle,bigContentTitle,summaryText,Message);notificationManager.notify(Constants.NOTIFICATIONID_APP, builder.build());}}else {builder = new NotificationCompat.Builder(context,null);setNotification(builder,context,pi,ticker,contentTitle,bigContentTitle,summaryText,Message);NotificationManagerCompat managerCompat = NotificationManagerCompat.from(context);managerCompat.notify(Constants.NOTIFICATIONID_APP, builder.build());}}/*** 设置大布局通知参数* @param builder* @param context* @param pi* @param ticker* @param contentTitle* @param bigContentTitle* @param summaryText* @param Message*/private void setNotification(NotificationCompat.Builder builder, Context context, PendingIntent pi,String ticker, String contentTitle, String bigContentTitle, String summaryText,String Message){builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher)).setTicker(ticker).setContentTitle(contentTitle).setWhen(System.currentTimeMillis()).setContentIntent(pi).setAutoCancel(false)//设置通知被点击一次是否自动取消.setOngoing(false).setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE).setProgress(100, 0, false);//大布局通知在4.1以后才能使用,BigTextStyleNotificationCompat.BigTextStyle textStyle = null;if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {textStyle = new NotificationCompat.BigTextStyle();textStyle.setBigContentTitle(bigContentTitle)// 标题.setSummaryText(summaryText).bigText(Message);// 内容builder.setStyle(textStyle);}builder.setContentText(Message);if(SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {builder.setSmallIcon(R.drawable.logo_alpha);} else {builder.setSmallIcon(R.mipmap.ic_launcher);}}
}

5.接口,简单的罗列下几个阶段,方便回调

public interface UpdateDownloadListener {void onStarted();void onProgressChanged(int progress, String downloadUrl);void onFinished(float completeSize, String downloadUrl);void onFailure(Exception e);
}

6.apk下载线程与安装方法

public class UpdateDownloadRequest{private UpdateDownloadListener listener;public UpdateDownloadRequest(UpdateDownloadListener listener) {this.listener = listener;}/*** 下载APK文件* @param context* @param url* @param filename*/public void downLoadApk(final Context context,final String url, final String filename) {new Thread() {@Overridepublic void run() {try {File file = getFileFromServer(context,url,filename);sleep(1000);installApk(file, context);} catch (Exception e) {listener.onFailure(e);e.printStackTrace();}}}.start();}/*** 安装软件包* @param file* @param context*/private void installApk(File file, final Context context) {Intent intent = new Intent();intent.setAction(Intent.ACTION_VIEW);intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");context.startActivity(intent);}/*** 下载文件* @param context* @param path* @param filename* @return* @throws Exception*/private File getFileFromServer(Context context, String path,String filename) throws Exception {// 单线程从服务器下载软件包if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {URL url = new URL(path);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(10000);long beforeTime = System.currentTimeMillis();int count = conn.getContentLength(); //文件总大小 字节InputStream is = conn.getInputStream();File file = new File(PathManger.getApkDir().getAbsoluteFile(),filename);FileOutputStream fos = new FileOutputStream(file);BufferedInputStream bis = new BufferedInputStream(is);byte[] buffer = new byte[1024];int len;int total = 0;while ((len = bis.read(buffer)) != -1) {fos.write(buffer, 0, len);total += len;//1秒 更新2次进度 非常重要 否则 系统会慢慢卡死if (System.currentTimeMillis() - beforeTime > 500) {listener.onProgressChanged((int) (((double) total / (double) count) * 100), path);}}fos.close();bis.close();is.close();listener.onFinished(100,path);return file;} else {return null;}}
}

总结:
A.有些次要代码就不贴了,看demo就好了,都是一些公共的方法。
B.这里没有考虑重复下载的问题(就是下载过程中有调起了服务的问题),建议在业务逻辑上去避免吧,或者你也可以优化一下这个下载线程。

这篇关于app版本更新,通知形式显示安装包下载进度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139454

相关文章

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Debian如何查看系统版本? 7种轻松查看Debian版本信息的实用方法

《Debian如何查看系统版本?7种轻松查看Debian版本信息的实用方法》Debian是一个广泛使用的Linux发行版,用户有时需要查看其版本信息以进行系统管理、故障排除或兼容性检查,在Debia... 作为最受欢迎的 linux 发行版之一,Debian 的版本信息在日常使用和系统维护中起着至关重要的作

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

macOS怎么轻松更换App图标? Mac电脑图标更换指南

《macOS怎么轻松更换App图标?Mac电脑图标更换指南》想要给你的Mac电脑按照自己的喜好来更换App图标?其实非常简单,只需要两步就能搞定,下面我来详细讲解一下... 虽然 MACOS 的个性化定制选项已经「缩水」,不如早期版本那么丰富,www.chinasem.cn但我们仍然可以按照自己的喜好来更换

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

你的华为手机升级了吗? 鸿蒙NEXT多连推5.0.123版本变化颇多

《你的华为手机升级了吗?鸿蒙NEXT多连推5.0.123版本变化颇多》现在的手机系统更新可不仅仅是修修补补那么简单了,华为手机的鸿蒙系统最近可是动作频频,给用户们带来了不少惊喜... 为了让用户的使用体验变得很好,华为手机不仅发布了一系列给力的新机,还在操作系统方面进行了疯狂的发力。尤其是近期,不仅鸿蒙O

电脑显示hdmi无信号怎么办? 电脑显示器无信号的终极解决指南

《电脑显示hdmi无信号怎么办?电脑显示器无信号的终极解决指南》HDMI无信号的问题却让人头疼不已,遇到这种情况该怎么办?针对这种情况,我们可以采取一系列步骤来逐一排查并解决问题,以下是详细的方法... 无论你是试图为笔记本电脑设置多个显示器还是使用外部显示器,都可能会弹出“无HDMI信号”错误。此消息可能

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入

Ubuntu 24.04 LTS怎么关闭 Ubuntu Pro 更新提示弹窗?

《Ubuntu24.04LTS怎么关闭UbuntuPro更新提示弹窗?》Ubuntu每次开机都会弹窗提示安全更新,设置里最多只能取消自动下载,自动更新,但无法做到直接让自动更新的弹窗不出现,... 如果你正在使用 Ubuntu 24.04 LTS,可能会注意到——在使用「软件更新器」或运行 APT 命令时,