【软件干货】Android应用进程如何保活?
1.Android 应用进程保活方法介绍
在Android应用程序中,为了保证应用的正常运行和稳定性,有时需要对应用进程进行保活。以下是一些实现进程保活的方法:
1、使用前台服务(Foreground Service):将服务调用startForeground()方法,并传入一个通知对象,将该服务置于前台运行状态。这样可以使得该服务的优先级更高,从而减少被系统杀死的概率。
2、使用JobScheduler:使用setPeriodic()方法可以让应用程序周期性地执行任务,从而避免长时间占用CPU资源,setPersisted(true)方法则表示当设备重启后,该任务仍然需要继续执行。
3、使用AlarmManager:使用这个API可以让应用程序在指定的时间间隔内执行任务。例如,可以设置一个闹钟,每隔一段时间唤醒应用程序并执行一些操作。
4、使用守护进程:启动一个后台守护进程,监控应用程序的状态并在应用程序被杀死时重新启动它,使用守护进程需要申请额外的权限。
5、使用双进程保活:启动两个相互绑定的进程,在其中一个进程被杀死时,另一个进程可以重新启动它。
6、使用WorkManger: 这是目前比较新的保活机制,用于取代JobScheduler。
需要注意的是,为了避免滥用和浪费系统资源,Android系统不断升级后,已经严格限制应用程序使用过多的后台资源和保活机制。
2.JobScheduler 用法简介
JobScheduler 是系统服务,由系统负责调度第三方应用注册的JobScheduler ,定时完成指定任务。
在应用中创建一个 JobService 服务,JobService 需要 API Level 21以上才可以使用,该服务注册时必须声明 android.permission.BIND_JOB_SERVICE 权限:
<!-- JobScheduler 拉活 --><serviceandroid:name=".jobscheduler.KeepAliveJobService"android:enabled="true"android:exported="true"android:permission="android.permission.BIND_JOB_SERVICE"></service>
通常使用JobScheduler需要以下几个步骤:
1、获取 JobScheduler 对象:通过Binder机制获取该JobScheduler系统服务;
// 创建 JobSchedulerJobScheduler jobScheduler =(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
2、指定 JobScheduler 任务信息 JobInfo:绑定任务 ID,指定任务的运行组件,也就是之前创建并注册的 JobService, 最后要设置该任务在重启后也要执行;
// 第一个参数指定任务 ID// 第二个参数指定任务在哪个组件中执行// setPersisted 方法需要 android.permission.RECEIVE_BOOT_COMPLETED 权限// setPersisted 方法作用是设备重启后 , 依然执行 JobScheduler 定时任务JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10,new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName())).setPersisted(true);
3、设置时间信息:7.0 以下的系统可以设置间隔, 7.0 以上的版本需要设置延迟执行,否则无法启动;
// 7.0 以下的版本, 可以每隔 5000 毫秒执行一次任务if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){jobInfoBuilder.setPeriodic(5_000);}else{// 7.0 以上的版本 , 设置延迟 5 秒执行// 该时间不能小于 JobInfo.getMinLatencyMillis 方法获取的最小值jobInfoBuilder.setMinimumLatency(5_000);}
4、开启定时任务;
// 开启定时任务jobScheduler.schedule(jobInfoBuilder.build());
5、7.0 以上的特殊处理,由于在7.0 以上的系统中设置了延迟执行,需要在JobService 的 onStartJob 方法中再次开启一次 JobScheduler 任务执行,也就是重复上述1 ~ 4执行, 这样就实现了周期性执行的目的;
public class KeepAliveJobService extends JobService {@Overridepublic boolean onStartJob(JobParameters params) {Log.i("KeepAliveJobService", "JobService onStartJob 开启");if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){// 如果当前设备大于 7.0 , 延迟 5 秒 , 再次执行一次startJob(this);}return false;}}
3.WorkManager用法简介
WorkManager是适合用于持久性工作的推荐解决方案,它可处理三种类型的持久性工作:
1、立即执行:必须立即开始且很快就完成的任务,可以加急。
2、长时间运行:运行时间可能较长(有可能超过 10 分钟)的任务。
3、可延期执行:延期开始并且可以定期运行的预定任务。
通常使用WorkManager需要以下几个步骤:
1、将依赖项添加到应用的build.gradle文件中;
2、定义工作:工作使用 Worker 类定义,doWork() 方法在 WorkManager 提供的后台线程上异步运行。如需为 WorkManager 创建一些要运行的工作,则需扩展 Worker 类并替换 doWork() 方法;
public class XxxWorker extends Worker {publicXxxWorker(@NonNull Context context,@NonNull WorkerParameters params) {super(context, params);}@Overridepublic Result doWork() {// Do the work herexxxxx();// Indicate whether the work finished successfully with the Resultreturn Result.success();}}
3、创建 WorkRequest:定义工作后,必须使用WorkManager 服务进行调度该工作才能运行;
WorkRequest xxxWorkRequest =new OneTimeWorkRequest.Builder(XxxWorker.class).build();
4.将 WorkRequest 提交给系统:需要使用enqueue()方法将WorkRequest提交到WorkManager;
WorkManager.getInstance(myContext).enqueue(uploadWorkRequest);
在定义工作时要考虑要考虑下面常见的需求:
1、调度一次性工作还是重复性工作;
2、工作约束条件是怎样的,例如要求连接到 Wi-Fi 网络或正在充电;
3、确保至少延迟一定时间再执行工作;
4、设置重试和退避策略;
5、输入数据如何传递给工作等等。
4.双进程保活
双进程保活的方式就是在运行了一个主进程之外,还运行了一个 “本地前台进程”,并绑定“远程前台进程”, “远程前台进程”与“本地前台进程”实现了相同的功能,代码基本一致,这两个进程都是前台进程,都进行了提权,并且互相绑定,当监听到绑定的另外一个进程突然断开连接,则本进程再次开启前台进程提权,并且重新绑定对方进程,以达到拉活对方进程的目的。
双进程保活的实现步骤如下:
1、定义 AIDL 接口 IMyAidlInterface,每个服务中都需要定义继承 IMyAidlInterface.Stub 的 Binder 类,作为进程间通信的桥梁( 这是个默认的 AIDL 接口 ),监听进程的连接断开;
// Declare any non-default types here with import statementsinterface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);}
2、实现一个判定服务运行工具:
import android.app.Activity;import android.app.ActivityManager;import android.content.Context;import android.text.TextUtils;import org.w3c.dom.Text;import java.util.List;public class ServiceUtils {/*** 判定 Service 是否在运行* @param context* @return*/public static boolean isServiceRunning(Context context, String serviceName){if(TextUtils.isEmpty(serviceName)) return false;ActivityManager activityManager =(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);// 最多获取 200 个正在运行的 ServiceList<ActivityManager.RunningServiceInfo> infos =activityManager.getRunningServices(200);// 遍历当前运行的 Service 信息, 如果找到相同名称的服务 , 说明某进程正在运行for (ActivityManager.RunningServiceInfo info: infos){if (TextUtils.equals(info.service.getClassName(), serviceName)){return true;}}return false;}}
3、定义一个用于本地与远程连接的类:
class Connection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 服务绑定成功时回调}@Overridepublic void onServiceDisconnected(ComponentName name) {// 再次启动前台进程startService();// 绑定另外一个远程进程bindService();}}
4、定义一个本地前台服务类:
import android.app.Notification;import android.app.NotificationChannel;import android.app.NotificationManager;import android.app.Service;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.graphics.Color;import android.os.Build;import android.os.IBinder;import android.os.RemoteException;import androidx.core.app.NotificationCompat;import static androidx.core.app.NotificationCompat.PRIORITY_MIN;/*** 本地前台服务*/public class LocalForegroundService extends Service {/*** 远程调用 Binder 对象*/private MyBinder myBinder;/*** 连接对象*/private Connection connection;/*** AIDL 远程调用接口* 其它进程调与该 RemoteForegroundService 服务进程通信时 , 可以通过 onBind 方法获取该 myBinder 成员* 通过调用该成员的 basicTypes 方法 , 可以与该进程进行数据传递*/class MyBinder extends IMyAidlInterface.Stub {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString) throws RemoteException {// 通信内容}}@Overridepublic IBinder onBind(Intent intent) {return myBinder;}@Overridepublic void onCreate() {super.onCreate();// 创建 Binder 对象myBinder = new MyBinder();// 启动前台进程startService();}private void startService(){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){// startForeground();// 创建通知通道NotificationChannel channel = new NotificationChannel("service","service", NotificationManager.IMPORTANCE_NONE);channel.setLightColor(Color.BLUE);channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);// 正式创建service.createNotificationChannel(channel);NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "service");Notification notification = builder.setOngoing(true).setSmallIcon(R.mipmap.ic_launcher).setPriority(PRIORITY_MIN).setCategory(Notification.CATEGORY_SERVICE).build();// 开启前台进程 , API 26 以上无法关闭通知栏startForeground(10, notification);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){startForeground(10, new Notification());// API 18 ~ 25 以上的设备 , 启动相同 id 的前台服务 , 并关闭 , 可以关闭通知startService(new Intent(this, CancelNotificationService.class));} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){// 将该服务转为前台服务// 需要设置 ID 和 通知// 设置 ID 为 0 , 就不显示已通知了 , 但是 oom_adj 值会变成后台进程 11// 设置 ID 为 1 , 会在通知栏显示该前台服务// 8.0 以上该用法报错startForeground(10, new Notification());}}/*** 绑定 另外一个 服务* LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定*/private void bindService(){// 绑定另外一个 服务// LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定// 创建连接对象connection = new Connection();// 创建本地前台进程组件意图Intent bindIntent = new Intent(this, RemoteForegroundService.class);// 绑定进程操作bindService(bindIntent, connection, BIND_AUTO_CREATE);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 绑定另外一个服务bindService();return super.onStartCommand(intent, flags, startId);}}
5、定义一个远程前台服务类:
import android.app.Notification;import android.app.NotificationChannel;import android.app.NotificationManager;import android.app.Service;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.graphics.Color;import android.os.Build;import android.os.IBinder;import android.os.RemoteException;import androidx.core.app.NotificationCompat;import static androidx.core.app.NotificationCompat.PRIORITY_MIN;/*** 远程前台服务*/public class RemoteForegroundService extends Service {/*** 远程调用 Binder 对象*/private MyBinder myBinder;/*** 连接对象*/private Connection connection;/*** AIDL 远程调用接口* 其它进程调与该 RemoteForegroundService 服务进程通信时 , 可以通过 onBind 方法获取该 myBinder 成员* 通过调用该成员的 basicTypes 方法 , 可以与该进程进行数据传递*/class MyBinder extends IMyAidlInterface.Stub {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString) throws RemoteException {// 通信内容}}@Overridepublic IBinder onBind(Intent intent) {return myBinder;}@Overridepublic void onCreate() {super.onCreate();// 创建 Binder 对象myBinder = new MyBinder();// 启动前台进程startService();}private void startService(){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){// startForeground();// 创建通知通道NotificationChannel channel = new NotificationChannel("service","service", NotificationManager.IMPORTANCE_NONE);channel.setLightColor(Color.BLUE);channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);// 正式创建service.createNotificationChannel(channel);NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "service");Notification notification = builder.setOngoing(true).setSmallIcon(R.mipmap.ic_launcher).setPriority(PRIORITY_MIN).setCategory(Notification.CATEGORY_SERVICE).build();// 开启前台进程 , API 26 以上无法关闭通知栏startForeground(10, notification);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){startForeground(10, new Notification());// API 18 ~ 25 以上的设备 , 启动相同 id 的前台服务 , 并关闭 , 可以关闭通知startService(new Intent(this, CancelNotificationService.class));} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2){// 将该服务转为前台服务// 需要设置 ID 和 通知// 设置 ID 为 0 , 就不显示已通知了 , 但是 oom_adj 值会变成后台进程 11// 设置 ID 为 1 , 会在通知栏显示该前台服务// 8.0 以上该用法报错startForeground(10, new Notification());}}/*** 绑定 另外一个 服务* LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定*/private void bindService(){// 绑定 另外一个 服务// LocalForegroundService 与 RemoteForegroundService 两个服务互相绑定// 创建连接对象connection = new Connection();// 创建本地前台进程组件意图Intent bindIntent = new Intent(this, LocalForegroundService.class);// 绑定进程操作bindService(bindIntent, connection, BIND_AUTO_CREATE);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 绑定另外一个服务bindService();return super.onStartCommand(intent, flags, startId);}}
6、启动两个服务:
import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.os.Bundle;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startService(new Intent(this, LocalForegroundService.class));startService(new Intent(this, RemoteForegroundService.class));}}
5.双进程保活+JobScheduler整合方案
这种方案是在 JobService的onStartJob 方法中判定“双进程保活”中的双进程是否挂了 ,如果这两个进程挂了,就重新将挂掉的进程重启。
这里给出一个双进程保活+JobScheduler整合方案中JobScheduler部分的示意代码,而双进程保活部分保持不变。
public class KeepAliveJobService extends JobService {@Overridepublic boolean onStartJob(JobParameters params) {Log.i("KeepAliveJobService", "JobService onStartJob 开启");if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){// 如果当前设备大于 7.0 , 延迟 5 秒 , 再次执行一次startJob(this);}// 判定本地前台进程是否正在运行boolean isLocalServiceRunning =ServiceUtils.isServiceRunning(this, LocalForegroundService.class.getName());if (!isLocalServiceRunning){startService(new Intent(this, LocalForegroundService.class));}// 判定远程前台进程是否正在运行boolean isRemoteServiceRunning =ServiceUtils.isServiceRunning(this, RemoteForegroundService.class.getName());if (!isRemoteServiceRunning){startService(new Intent(this, RemoteForegroundService.class));}return false;}@Overridepublic boolean onStopJob(JobParameters params) {Log.i("KeepAliveJobService", "JobService onStopJob 关闭");return false;}public static void startJob(Context context){// 创建 JobSchedulerJobScheduler jobScheduler =(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);// 第一个参数指定任务 ID// 第二个参数指定任务在哪个组件中执行// setPersisted 方法需要 android.permission.RECEIVE_BOOT_COMPLETED 权限// setPersisted 方法作用是设备重启后 , 依然执行 JobScheduler 定时任务JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(10,new ComponentName(context.getPackageName(), KeepAliveJobService.class.getName())).setPersisted(true);// 7.0 以下的版本, 可以每隔 5000 毫秒执行一次任务if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N){jobInfoBuilder.setPeriodic(5_000);}else{// 7.0 以上的版本 , 设置延迟 5 秒执行// 该时间不能小于 JobInfo.getMinLatencyMillis 方法获取的最小值jobInfoBuilder.setMinimumLatency(5_000);}// 开启定时任务jobScheduler.schedule(jobInfoBuilder.build());}}
参考文献
1、【Android 进程保活】应用进程拉活 ( 双进程守护 + JobScheduler 保活):https://hanshuliang.blog.csdn.net/article/details/115607584
2、【Android 进程保活】应用进程拉活 ( 双进程守护保活 ):https://hanshuliang.blog.csdn.net/article/details/115604667
3、【Android 进程保活】应用进程拉活 ( JobScheduler 拉活):https://hanshuliang.blog.csdn.net/article/details/115584240
4、Android实现进程保活的思路:https://blog.csdn.net/gs12software/article/details/130502312
5、WorkManager 使用入门:https://developer.android.google.cn/develop/background-work/background-tasks/persistent/getting-started
相关文章:
【软件干货】Android应用进程如何保活?
1.Android 应用进程保活方法介绍 在Android应用程序中,为了保证应用的正常运行和稳定性,有时需要对应用进程进行保活。以下是一些实现进程保活的方法: 1、使用前台服务(Foreground Service):将服务调用startForeground()方法&…...
neo4j部署保姆级教程
由于公司是基于大数据架构的,让部署neo4j数据库,之前没有接触过,然后紧急学了一下,并且从网上找了一些教程,决定还是记录下来,后续有时间了会在出一篇使用教程 环境准备(root用户) …...
【STM32CubeMX开发】-2.2-TIM_输出一个PWM信号
目录 1 Tim定时器的时钟源 2 Tim定时器的配置 2.1 PWM配置 2.2 中断配置 3 生成代码 4 测试结果 结尾 1 Tim定时器的时钟源 TIM3的时钟来源自APB1 Timer clocks,时钟树上所有总线频率均设置为了STM32F0能达到的最高频率,此时APB1 Timer clocks …...
Ngx+Lua+Redis 快速存储POST数据
系统几万台设备有windows有安卓还有linux系统,每个设备三分钟就会向服务器post设备的硬件信息,数据格式json,后台管理界面只需要最新的数据,不需要历史数据,业务逻辑非常简单,PHP代码就几行,已经…...
go-delve的使用
go-delve的非交互使用方式: dlv要执行的命令文件:cmd.dlv goroutines exit 执行非交互命令: yes n | dlv --allow-non-terminal-interactivetrue attach $pid --init cmd.dlv --end--...
Python网络爬虫技术详解
Python网络爬虫技术详解 引言 网络爬虫(Web Crawler),又称网络蜘蛛(Web Spider)或网络机器人(Web Robot),是一种按照一定规则自动抓取互联网信息的程序或脚本。它们通过遍历网页链…...
Golang | Leetcode Golang题解之第474题一和零
题目: 题解: func findMaxForm(strs []string, m, n int) int {dp : make([][]int, m1)for i : range dp {dp[i] make([]int, n1)}for _, s : range strs {zeros : strings.Count(s, "0")ones : len(s) - zerosfor j : m; j > zeros; j--…...
算法刷题技巧
算法题:https://leetcode.cn/studyplan/top-100-liked/ 哈希表 使用哈希表,增删改查的时间复杂度均为O(1)。何时使用哈希表? 在某个区域内查找一个已知元素,可以使用哈希表作为这个区域根据一个特征对元素进行分类,特征…...
BMS、EMS PCS 简介
1 储能系统的构成 完整的电化学储能系统主要由电池组、电池管理系统(BMS)、能量管理系统(EMS)、储能变流器(PCS)以及其他电气设备构成。 在储能系统中,电池组将状态信息反馈给电池管理系统BMS&…...
spug3发布项目
一、启动spug项目 1.spug代码仓库地址: spug: 开源运维平台:面向中小型企业设计的无 Agent的自动化运维平台,整合了主机管理、主机批量执行、主机在线终端、文件在线上传下载、应用发布、任务计划、配置中心、监控、报警等一系列功能。 - Gitee.com 注…...
鸿蒙HarmonyOS开发:应用权限的基本概念及如何申请应用权限详细介绍
文章目录 一、访问控制二、应用权限1、应用权限管控2、权限使用的基本原则3、授权方式4、权限等级 三、申请应用权限1、选择申请权限的方式2、声明权限3、声明样例4、二次向用户申请授权5、具体实现示例6、效果展示 四、应用权限列表1、system_grant(系统授权&#…...
mac 桌面版docker no space left on device
报错信息 docker pull镜像时报: failed to register layer: Error processing tar file(exit status 1): write /home/admin/oceanbase_bak/bin/observer: no space left on device 解决 增加 docker 虚拟磁盘大小。 调整完点击重启即可。...
基于CIM的街镇基层治理统一指挥平台建设方案
1 项目概述 1.1 建设背景 社区作为人民生活的重要区域,往往需要对社区内人员、房屋、基本设施、日常业务进行规范管理,以保证其正常运行,但是传统的社区治理方式已不能满足新时代社会发展的要求,如人工采集录入信息、人员现场巡逻等,这些工作方式不仅工作量大,而且效率…...
PostgreSQL学习笔记三:数据类型和运算符
数据类型和运算符 PostgreSQL 支持多种数据类型和运算符,以下是一些常见的数据类型和运算符的概述: 数据类型 基本数据类型 整数类型: SMALLINT:2 字节,范围 -32,768 到 32,767。INTEGER:4 字节࿰…...
ROS理论与实践学习笔记——6 ROS机器人导航(仿真)
在 ROS 中,机器人导航(Navigation)是由多个功能包组合而成的系统,统称为导航功能包集(navigation stack)。它提供了一个全面的框架,使得移动机器人能够自主导航到指定目标点,同时避开…...
uniapp开发微信小程序,button的open-type=“share“ 分享给个人跳转到首页问题
当使用button标签带上open-type"share"属性,点击之后可分享当前页面给微信好友,但是分享之后朋友点开跳转到了首页问题。 需要使用 onShareAppMessage 函数 export default {onShareAppMessage(res) {if (res.from button) {// 来自页面内分…...
【jQuery】 jQuery基础及选择器介绍(基本选择器 层次选择器 属性选择器 过滤选择器)
文章目录 jQuery基础1. 优势2. 版本3. 基本语法4. 选择器基本选择器层次选择器属性选择器过滤选择器基本过滤选择器可见性过滤选择器 注意事项 jQuery基础 jQuery 是一个功能强大且易于使用的 JavaScript 库,它极大地简化了前端开发的工作。无论是 DOM 操作、事件处…...
网站在对抗机器人攻击的斗争中失败了
95% 的高级机器人攻击都未被发现,这一发现表明当前的检测和缓解策略存在缺陷。 这表明,虽然一些组织可能拥有基本的防御能力,但他们没有足够的能力应对更复杂的攻击。 例如利用人工智能和机器学习来模仿人类行为的攻击。 这些统计数据强调…...
Centos7 搭建logstash
下载并安装公共签名密钥: sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch 创建一个名为 /etc/yum.repos.d/logstash.repo 的文件,并添加以下内容: [logstash-7.x] nameElastic repository for 7.x packages baseu…...
面试题:Redis(五)
1. 面试题 面试问 记录对集合中的数据进行统计 在移动应用中,需要统计每天的新增用户数和第2天的留存用户数; 在电商网站的商品评论中,需要统计评论列表中的最新评论; 在签到打卡中,需要统计一个月内连续打卡的用户数&…...
从原型到实战:基于快马生成代码快速开发可用的worldmonitor疫情监控系统
从原型到实战:基于快马生成代码快速开发可用的worldmonitor疫情监控系统 最近在做一个全球疫情数据监控系统的项目,正好用到了InsCode(快马)平台来快速生成基础代码,然后在这个基础上进行二次开发。整个过程非常顺畅,特别是平台的…...
啪」的一声脆响,空气击穿时那道紫色电弧总能让人心头一紧。咱们今天用COMSOL做个好玩的——计算两根针尖电极间的击穿电压,看看电场怎么在金属尖角处「拧麻花
comsol放电电极击穿空气模拟,计算击穿间隙的电压,周围附近的电场老规矩,先画个直径10mm的球头圆柱电极,对面放个尖角曲率半径0.1mm的针电极,间隙留5mm。材料库选「空气」,但要注意击穿模型得用自定义的。物…...
从变砖到重生:红魔全系9008深度救砖指南与实战解析
1. 什么是9008模式?为什么能救砖? 当你发现红魔手机卡在开机界面、反复重启甚至完全黑屏时,大概率是遇到了传说中的"变砖"。这时候高通芯片隐藏的9008模式就是最后的救命稻草。简单来说,9008模式相当于电脑的BIOS界面&…...
Asp.Net MVC杂谈之:—步步打造表单验证框架[重排版](1)
在实际使用中,我们可以考虑多种形式来进行这一验证(注:本文目前只研究服务器端验证的情况),最直接的方式莫过于对每个表单值手动用C#代码进行验证了,比如: if(!Int32.TryParse(Request.Form[“age”], out age)){ xxxx… } If(age < xxx || age > xxx){ xxxx… }…...
PowerBI进阶:除了DATEADD,这3种方法也能玩转同比环比(附场景选择指南)
PowerBI时间智能函数深度对比:突破DATEADD局限的实战指南 当你已经能熟练使用DATEADD计算同比环比,却发现报表加载速度越来越慢,或是遇到非标准财年分析需求时,是时候重新审视PowerBI的时间智能函数工具箱了。本文将带你深入剖析四…...
SpringBoot3.3.1+Elasticsearch8.13.4日期转换踩坑实录:LocalDateTime保存为时间戳的完整方案
SpringBoot3.3.1与Elasticsearch8.13.4时间类型转换实战:从踩坑到优雅解决 最近在升级技术栈到SpringBoot3.3.1时,发现与Elasticsearch8.13.4的集成出现了一个棘手的问题:LocalDateTime类型在保存和查询时表现异常。这让我花了整整两天时间排…...
CPU 亲和性
CPU 亲和性本质CPU 亲和性 让进程 / 线程只在指定的 CPU 核心上运行的调度约束。内核里叫:sched_affinity(调度亲和性)作用:提高 L1/L2/L3 缓存命中率减少 上下文切换(context switch)避免跨 NUMA 节点访问…...
从代码到部署:手把手复现CenterPoint(PyTorch版)在KITTI数据集上的完整流程
从零实现CenterPoint:KITTI数据集3D目标检测全流程实战指南 为什么选择CenterPoint进行3D目标检测? 在自动驾驶和机器人感知领域,3D目标检测一直是核心技术难题。传统基于锚框(Anchor-based)的方法在处理旋转物体时表现…...
收藏!阿里后端转大模型应用层,2年Agent/RAG经验,斩获字节30%涨幅offer|小白程序员必看学习路径
作为一名从传统后端开发起步的程序员,我毕业后顺利入职阿里,做了一年后端开发工作后,敏锐捕捉到大模型应用层的爆发趋势,果断转型深耕。经过两年的Agent、RAG相关开发实践,最终成功拿到字节跳动Agent开发岗位offer&…...
CVE-2024-36401复现
一.漏洞概述 CVE-2024-36401 是 GeoServer 中的一个严重级远程代码执行漏洞(CVSS 9.8),允许未经身份验证的远程攻击者在服务器上执行任意代码。该漏洞源于 GeoServer 调用的 GeoTools 库 API 在评估 XPath 表达式时存在不安全处理࿰…...
