Android 检查更新

devtools/2025/3/11 4:37:48/

首先服务类

java">public class UpdateService extends Service {private static final String NOTIFY_CHANNEL_ID = "com.jianke.api.UpdateService";public static final String BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK = "BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK";public static final String BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK_SUCCESS = "BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK_SUCCESS";public static boolean isRunning = false; //是否正在运行public static final String URL = "url"; //Tagpublic static final String ICON = "icon"; //Tagpublic static final String MD5 = "md5"; //Tagprivate NotificationCompat.Builder builder;private Handler handler;//Handler对象private int lastPercent = 0;private NotificationManager notificationManager;//Class to notify the user of events that happen.private AuthInstallApkBroadcastReceiver mAuthInstallApkBroadcastReceiver;private String fileName = String.valueOf(System.currentTimeMillis());private UpdateListener updateListener;private class AuthInstallApkBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {installApk();}}@Overridepublic void onCreate() {super.onCreate();mAuthInstallApkBroadcastReceiver = new AuthInstallApkBroadcastReceiver();IntentFilter intentFilter = new IntentFilter(UpdateService.BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK_SUCCESS);LocalBroadcastManager.getInstance(this).registerReceiver(mAuthInstallApkBroadcastReceiver, intentFilter);}@Overridepublic void onDestroy() {LocalBroadcastManager.getInstance(this).unregisterReceiver(mAuthInstallApkBroadcastReceiver);updateListener = null;super.onDestroy();isRunning = false;}@Overridepublic IBinder onBind(Intent intent) {return new UpdateServiceBinder();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {isRunning = true;if (intent == null) {return super.onStartCommand(intent, flags, startId);}String action = intent.getAction();if (!TextUtils.isEmpty(action)) {Toast.makeText(getApplicationContext(), "action is null", Toast.LENGTH_SHORT).show();} else {String url = intent.getStringExtra(URL);final String md5 = intent.getStringExtra(MD5);int icon = intent.getIntExtra(ICON, android.R.drawable.sym_def_app_icon);if (TextUtils.isEmpty(url)) {
//                if (BuildConfig.DEBUG) {throw new RuntimeException("获取APK更新地址失败");
//                }
//                return super.onStartCommand(intent, flags, startId);} else {startUpdate(url, icon);}handler = new Handler(getMainLooper()) {@Overridepublic void handleMessage(Message msg) {if (builder != null) {switch (msg.what) {case 1:builder.setProgress(100, (Integer) msg.obj, false);builder.setContentText((Integer) msg.obj + "%");if (notificationManager == null || builder == null) {return;}notificationManager.notify(R.id.update_notification_id, builder.build());break;case 2:notificationManager.cancel(R.id.update_notification_id);getFileMD5(md5);break;case 3:  //校验md5 结果处理 以及安装String fileHash = (String) msg.obj;LogUtils.d("md5===" + fileHash);// 校验hash 忽略 md5大小写if (msg.arg1 == 1 && (TextUtils.isEmpty(md5) || (!TextUtils.isEmpty(fileHash) && fileHash.equalsIgnoreCase(md5)))) {isRunning = false;if(updateListener != null) updateListener.onMd5Checked(getApkPath());installApk();} else {isRunning = false;if(updateListener != null) updateListener.onError("文件错误");ToastUtil.setToast("文件错误");stopSelf();}break;case 4:isRunning = false;if(updateListener != null) updateListener.onError(msg.obj + "");ToastUtil.setToast(msg.obj + "");notificationManager.cancel(R.id.update_notification_id);stopSelf();break;default:break;}}}};}return super.onStartCommand(intent, flags, startId);}public void setUpdateListener(UpdateListener updateListener) {this.updateListener = updateListener;}/*** 校验文件md5** @param md5*/private void getFileMD5(String md5) {if (!TextUtils.isEmpty(md5)) {new Thread(new Runnable() {   //300M 耗时 ≈ 3S@Overridepublic void run() {try {File file = new File(getApkPath());String fileHash = MD5Utils.digestMD5(file);sendCheckFileMsg(true, fileHash);} catch (Exception e) {e.printStackTrace();sendCheckFileMsg(false, "");}}}).start();} else {  //md5 空算成功sendCheckFileMsg(true, "");}}private void sendCheckFileMsg(boolean success, String hash) {Message msg = Message.obtain();msg.what = 3;msg.arg1 = success ? 1 : 0;msg.obj = hash;handler.sendMessage(msg);}private void installApk() {File file = new File(getApkPath());if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !getPackageManager().canRequestPackageInstalls()) {ToastUtil.setToast("请授权安装应用");requestAutoInstallApk();return;}Intent intent = new Intent();intent.setAction(Intent.ACTION_VIEW);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {Uri apkUri = FileProvider.getUriForFile(this, getPackageName() + ".updateprovider", file);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.setDataAndType(apkUri, "application/vnd.android.package-archive");} else {intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");intent.addCategory("android.intent.category.DEFAULT");}startActivity(intent);stopSelf();}private void requestAutoInstallApk() {isRunning = false;LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_UPDATE_VERSION_AUTH_INSTALL_APK));}/*** 开始下载** @param url apk的url*/private void startUpdate(String url, int icon) {createNotification(icon); //创建通知栏进度startDownLoad(url);}/*** 创建通知栏进度*/private void createNotification(int icon) {notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationChannel channel = new NotificationChannel(NOTIFY_CHANNEL_ID,"TAG",NotificationManager.IMPORTANCE_DEFAULT);channel.enableLights(true);channel.setShowBadge(true);channel.setDescription("update version");notificationManager.createNotificationChannel(channel);}notificationManager.cancel(R.id.update_notification_id);builder = new NotificationCompat.Builder(getApplicationContext(), NOTIFY_CHANNEL_ID);builder.setSmallIcon(icon).setContentTitle("正在下载 ...").setContentText("0%");builder.setPriority(2 | Notification.DEFAULT_ALL);builder.setProgress(100, 0, false);builder.setOnlyAlertOnce(true);builder.setOngoing(true);if (notificationManager == null) {return;}notificationManager.notify(R.id.update_notification_id, builder.build());}/*** 开始下载** @param url*/private void startDownLoad(String url) {DownLoadManager.getInstance().downLoad(this, url, new DownLoadManager.DownLoadListener() {@Overridepublic void onStartLoading(long totalSize) {// Do nothing because of auto-generatedif(updateListener != null) updateListener.onStart(totalSize);}@Overridepublic void onLoading(long currentSize, float percent, float speed) {int tempPercent = (int) (percent * 100);if (tempPercent >= 0 && lastPercent != tempPercent) {  //避免频繁调用通知Message msg = Message.obtain();msg.what = 1;msg.obj = tempPercent;handler.sendMessage(msg);lastPercent = tempPercent;if(updateListener != null) updateListener.onLoading(currentSize, percent, speed);}}@Overridepublic void onLoadingFinish(long totalSize) {Message msg = Message.obtain();msg.what = 2;handler.sendMessage(msg);if(updateListener != null) updateListener.onLoadingFinish(totalSize);}@Overridepublic void onFailure(String error) {Message msg = Message.obtain();msg.what = 4;msg.obj = error;handler.sendMessage(msg);}}, new File(getApkPath()));}/*** 获取apk下载路径** @return*/private String getApkPath() {boolean old = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;return old ?getFileStreamPath(fileName + ".apk").getAbsolutePath(): getAppRootDir() + fileName + ".apk";}/*** 获取sdcard的绝对路径** @return*/public String getSDcardDir() {String sdcardPath = null;if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {sdcardPath = Environment.getExternalStorageDirectory().getAbsolutePath();} else {sdcardPath = getApplicationContext().getFilesDir().getAbsolutePath();}return sdcardPath;}/*** 获取应用跟目录** @return*/public String getAppRootDir() {return getFilesDir().getAbsolutePath() + "/";}public class UpdateServiceBinder extends Binder {public UpdateService getService(){return UpdateService.this;}}
}
UpdateListener
java">public interface UpdateListener {/*** @description 开始升级* @param totalSize 安装包体积*/void onStart(long totalSize);/*** @description 正在下载*/void onLoading(long currentSize, float percent, float speed);/*** @description 下载完成* @param totalSize 安装包体积*/void onLoadingFinish(long totalSize);/*** @description md5校验成功*/void onMd5Checked(String path);/*** @description 升级失败* @param error*/void onError(String error);
}
DownLoadManager
java">public class DownLoadManager {private static final String MAIN = "main"; //Tagprivate static DownLoadManager instance = new DownLoadManager(); //单例对象/*** 对外公布的单例对象** @return*/public static DownLoadManager getInstance() {return instance;}/*** 下载** @param uri        url* @param listener   下载DownLoadListener监听对象* @param targetFile 目标文件*/public void downLoad(final Context context, final String uri, final DownLoadListener listener,final File targetFile) {if (MAIN.equalsIgnoreCase(Thread.currentThread().getName())) {new Thread() {@Overridepublic void run() {downloadNewThread(context, uri, listener, targetFile);};}.start();} else {downloadNewThread(context, uri, listener, targetFile);}}/*** 新开一个线程执行下载操作** @param uri* @param listener* @param targetFile*/private void downloadNewThread(Context context, String uri, DownLoadListener listener,File targetFile) {FileOutputStream fileOutputStream = null;InputStream inputStream = null;//try {URL url = new URL(uri);//获取连接HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(60 * 1000);connection.setReadTimeout(60 * 1000);connection.setRequestProperty("Connection", "Keep-Alive");connection.setRequestProperty("Charset", "UTF-8");connection.setDoInput(true);connection.setUseCaches(false);//打开连接connection.connect();//获取内容长度int contentLength = connection.getContentLength();if (listener != null) {listener.onStartLoading(contentLength);}File parent = targetFile.getParentFile();if (!parent.exists()) {parent.mkdirs();} else if (!parent.isDirectory()) {if (parent.delete()) {parent.mkdirs();}}//输入流inputStream = connection.getInputStream();//输出流boolean old = Build.VERSION.SDK_INT < Build.VERSION_CODES.N;fileOutputStream = old ?context.openFileOutput(targetFile.getName(), Context.MODE_WORLD_READABLE): new FileOutputStream(targetFile);byte[] bytes = new byte[1024];long totalReaded = 0;int temp_Len;long currentTime = System.currentTimeMillis();while ((temp_Len = inputStream.read(bytes)) != -1) {totalReaded += temp_Len;
//                Log.i("XXXX", "run: totalReaded:" + totalReaded);
//                long progress = totalReaded * 100 / contentLength;
//                Log.i("XXXX", "run: progress:" + progress);fileOutputStream.write(bytes, 0, temp_Len);if (listener != null) {listener.onLoading(totalReaded, ((float) totalReaded) / contentLength,((float) temp_Len) / System.currentTimeMillis()- currentTime);}currentTime = System.currentTimeMillis();}if (listener != null) {listener.onLoadingFinish(contentLength);}} catch (Exception e) {e.printStackTrace();if (listener != null) {listener.onFailure(e.getMessage());}} finally {try {if (fileOutputStream != null) {fileOutputStream.close();}if (inputStream != null) {inputStream.close();}} catch (IOException e) {e.printStackTrace();}}}/*** 声明DownLoadListener监听器*/public interface DownLoadListener {/*** 开始下载** @param totalSize*/void onStartLoading(long totalSize);/*** 下载中** @param currentSize byte* @param percent* @param speed       byte/second*/void onLoading(long currentSize, float percent, float speed);/*** 下载完成** @param totalSize*/void onLoadingFinish(long totalSize);/*** 下载失败** @param error*/void onFailure(String error);}
}

使用

 var intent = Intent(this@MainActivity, UpdateService::class.java).apply {putExtra(UpdateService.URL, it.downloadUrl)putExtra(UpdateService.ICON, R.drawable.ic_launcher)}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {this@MainActivity.startForegroundService(intent)} else {this@MainActivity.startService(intent)}


http://www.ppmy.cn/devtools/166188.html

相关文章

【定制开发】碰一碰发视频系统定制开发,支持OEM

在短视频营销爆发的2025年&#xff0c;"碰一碰发视频"技术已成为实体商家引流标配。某连锁餐饮品牌通过定制化开发&#xff0c;单月视频发布量突破10万条&#xff0c;获客成本降低80%&#xff01;本文将深入解析该系统的技术架构与开发要点&#xff0c;助你快速搭建高…

linux的top指令解析

这张图片显示的是 Linux 系统中 top 命令的输出&#xff0c;它用于实时监控系统的资源使用情况&#xff0c;包括 CPU、内存和进程等信息。以下是对图片中几个关键参数的解释&#xff1a; 系统负载 (Load Average) 1分钟、5分钟、15分钟负载平均值&#xff1a;这三个数值表示系…

vue实现一个pdf在线预览,pdf选择文本并提取复制文字触发弹窗效果

[TOC] 一、文件预览 1、安装依赖包 这里安装了disjs-dist2.16版本&#xff0c;安装过程中报错缺少worker-loader npm i pdfjs-dist2.16.105 worker-loader3.0.8 2、模板部分 <template><div id"pdf-view"><canvas v-for"page in pdfPages&qu…

【GoTeams】-5:引入Docker

本文目录 1. Dokcer-compose回顾下Docker知识编写docker-compose.yaml运行docker 2. 部署go服务编写dockerfile 1. Dokcer-compose 这里简单先用一下win版本的Docker&#xff0c;后期开发好了部署的时候再移植到服务器下进行docker部署。 输入命令docker-compose version 就可…

系统架构设计师—系统架构设计篇—特定领域软件体系结构

文章目录 概述领域分类垂直域水平域 系统模型基本活动参与角色 概述 特定领域软件架构&#xff08;Domain Specific Software Architecture&#xff0c;DSSA&#xff09;是在一个特定应用领域中&#xff0c;为一组应用提供组织结构参考的标准团建体系结构。 领域分类 垂直域…

FreeRTOS第17篇:FreeRTOS链表实现细节05_MiniListItem_t:FreeRTOS内存优化

文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 1 为什么需要迷你列表项? 在嵌入式系统中,内存资源极其宝贵。FreeRTOS为满足不同场景需求,设计了标准列表项(ListItem_…

用Deepseek写一个 HTML 和 JavaScript 实现一个简单的飞机游戏

大家好&#xff01;今天我将分享如何使用 HTML 和 JavaScript 编写一个简单的飞机游戏。这个游戏的核心功能包括&#xff1a;控制飞机移动、发射子弹、敌机生成、碰撞检测和得分统计。代码简洁易懂&#xff0c;适合初学者学习和实践。 游戏功能概述 玩家控制&#xff1a;使用键…

vulnhub靶场之【digitalworld.local系列】的torment靶机

前言 靶机&#xff1a;digitalworld.local-torment&#xff0c;IP地址为192.168.10.12 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.6 kali采用VMware虚拟机&#xff0c;靶机选择使用VMware打开文件&#xff0c;都选择桥接网络 这里官方给的有两种方式&#xff0c…