Android11 FallbackHome启动和关闭流程分析
Android 7.0引入了新特性:Direct Boot Mode,设备启动后进入的一个新模式,直到用户解锁(unlock)设备此阶段结束。在这个模式下,系统调用 resolveHomeActivity 找到的是FallbackHome ,而不是我们的桌面应用。所以系统开始启动的是 FallbackHome 这个"桌面"。
03-13 16:58:41.359 431 431 D test10 : ===getDefaultTaskDisplayArea===
03-13 16:58:41.359 431 431 D test10 : comp:null
ResolveInfo:ResolveInfo{b15783 com.android.settings/.FallbackHome p=-1000 m=0x108000}
03-13 16:58:41.361 431 431 D test10 : bestChoice:ResolveInfo{b15783 com.android.settings/.FallbackHome p=-1000 m=0x108000}
03-13 16:58:41.361 431 431 D test10 : aInfo:ActivityInfo{67e5d00 com.android.settings.FallbackHome}
那为什么找到是FallbackHome 呢?FallbackHome 位于 com.android.settings这个包下,看一下主配置文件
//packages\apps\Settings\AndroidManifest.xml<activity android:name=".FallbackHome"//......<intent-filter android:priority="-1000"><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.HOME" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity><application android:label="@string/settings_label"//......android:directBootAware="true"android:name=".SettingsApplication"android:appComponentFactory="androidx.core.app.CoreComponentFactory">
FallbackHome设置了HOME属性,且settings application配置了directBootAware属性,所以找到的是FallbackHome
FallbackHome的启动
FallbackHome的启动主要分为两个阶段
- systemserver告知Zygote要创建新进程
- 新进程通知AMS启动FallbackHome
systemserver告知Zygote要创建新进程
systemserver之间的调用这里就不详细分析,可以看一下调用的堆栈
startSpecificActivity r:ActivityRecord{eebff5 u0 com.android.settings/.FallbackHome t2}
03-13 16:58:41.400 431 431 D test10 : java.lang.Exception
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStackSupervisor.startSpecificActivity(ActivityStackSupervisor.java:979)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:1970)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:1516)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.resumeFocusedStacksTopActivities(RootWindowContainer.java:2311)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.startActivityInner(ActivityStarter.java:1736)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.startActivityUnchecked(ActivityStarter.java:1525)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.executeRequest(ActivityStarter.java:1189)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStarter.execute(ActivityStarter.java:670)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityStartController.startHomeActivity(ActivityStartController.java:207)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnTaskDisplayArea(RootWindowContainer.java:1549)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnDisplay(RootWindowContainer.java:1491)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnDisplay(RootWindowContainer.java:1475)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.RootWindowContainer.startHomeOnAllDisplays(RootWindowContainer.java:1456)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.wm.ActivityTaskManagerService$LocalService.startHomeOnAllDisplays(ActivityTaskManagerService.java:6727)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.am.ActivityManagerService.systemReady(ActivityManagerService.java:9689)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.SystemServer.startOtherServices(SystemServer.java:2257)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.SystemServer.run(SystemServer.java:599)
03-13 16:58:41.400 431 431 D test10 : at com.android.server.SystemServer.main(SystemServer.java:415)
03-13 16:58:41.400 431 431 D test10 : at java.lang.reflect.Method.invoke(Native Method)
03-13 16:58:41.400 431 431 D test10 : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
03-13 16:58:41.400 431 431 D test10 : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:925)
可以看出,首先在ActivityManagerService的systemReady方法中调用,一直执行到ActivityStackSupervisor的startSpecificActivity方法
void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {//......final boolean isTop = andResume && r.isTopRunningActivity();mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");}
通过 startProcessAsync请求 Zygote创建 FallbackHome进程。startProcessAsync经过一步步的调用,最后调用到ProcessList的start方法
//frameworks\base\services\core\java\com\android\server\am\ProcessList.java
@GuardedBy("mService")boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,boolean mountExtStorageFull, String abiOverride) {//......// Start the process. It will either succeed and return a result containing// the PID of the new process, or else throw a RuntimeException.final String entryPoint = "android.app.ActivityThread";return startProcessLocked(hostingRecord, entryPoint, app, uid, gids,runtimeFlags, zygotePolicyFlags, mountExternal, seInfo, requiredAbi,instructionSet, invokeWith, startTime);}
注意这里的一个参数entryPoint为 “android.app.ActivityThread”,调用其重载的方法,最后调用到Process的start方法
//frameworks\base\core\java\android\os\Process.javapublic static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();public static ProcessStartResult start(......) {return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,runtimeFlags, mountExternal, targetSdkVersion, seInfo,abi, instructionSet, appDataDir, invokeWith, packageName,zygotePolicyFlags, isTopApp, disabledCompatChanges,pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,
ZYGOTE_PROCESS是 ZygoteProcess类对象,调用其start方法,进而调用其startViaZygote方法
private Process.ProcessStartResult startViaZygote( ...... ){//一系列的参数设置,省略......//这里的processClass为android.app.ActivityThreadargsForZygote.add(processClass);if (extraArgs != null) {Collections.addAll(argsForZygote, extraArgs);}synchronized(mLock) {// The USAP pool can not be used if the application will not use the systems graphics// driver. If that driver is requested use the Zygote application start path.return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi),zygotePolicyFlags,argsForZygote);}}
1,调用openZygoteSocketIfNeeded 打开和 Zygote的链接
2,调用zygoteSendArgsAndGetResult发送参数并得到结果
先来看看openZygoteSocketIfNeeded 方法
@GuardedBy("mLock")private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {try {attemptConnectionToPrimaryZygote();if (primaryZygoteState.matches(abi)) {return primaryZygoteState;}//......}@GuardedBy("mLock")private void attemptConnectionToPrimaryZygote() throws IOException {if (primaryZygoteState == null || primaryZygoteState.isClosed()) {primaryZygoteState =ZygoteState.connect(mZygoteSocketAddress, mUsapPoolSocketAddress);//......}}
调用ZygoteState的connect
static ZygoteState connect(@NonNull LocalSocketAddress zygoteSocketAddress,@Nullable LocalSocketAddress usapSocketAddress)throws IOException {DataInputStream zygoteInputStream;BufferedWriter zygoteOutputWriter;final LocalSocket zygoteSessionSocket = new LocalSocket();if (zygoteSocketAddress == null) {throw new IllegalArgumentException("zygoteSocketAddress can't be null");}try {zygoteSessionSocket.connect(zygoteSocketAddress);zygoteInputStream = new DataInputStream(zygoteSessionSocket.getInputStream());zygoteOutputWriter =new BufferedWriter(new OutputStreamWriter(zygoteSessionSocket.getOutputStream()),Zygote.SOCKET_BUFFER_SIZE);} catch (IOException ex) {try {zygoteSessionSocket.close();} catch (IOException ignore) { }throw ex;}return new ZygoteState(zygoteSocketAddress, usapSocketAddress,zygoteSessionSocket, zygoteInputStream, zygoteOutputWriter,getAbiList(zygoteOutputWriter, zygoteInputStream));}
注意这里的 zygoteSocketAddress 即 Zygote.PRIMARY_SOCKET_NAME(zygote),调用connect连接名为zygote的socket服务,并初始化输入输出流,封装在ZygoteState对象中返回。该对象作为参数,传个zygoteSendArgsAndGetResult方法。 再来看一下zygoteSendArgsAndGetResult这个方法
@GuardedBy("mLock")private Process.ProcessStartResult zygoteSendArgsAndGetResult(ZygoteState zygoteState, int zygotePolicyFlags, @NonNull ArrayList<String> args)throws ZygoteStartFailedEx {//......if (shouldAttemptUsapLaunch(zygotePolicyFlags, args)) {try {return attemptUsapSendArgsAndGetResult(zygoteState, msgStr);} catch (IOException ex) {// If there was an IOException using the USAP pool we will log the error and// attempt to start the process through the Zygote.Log.e(LOG_TAG, "IO Exception while communicating with USAP pool - "+ ex.getMessage());}}return attemptZygoteSendArgsAndGetResult(zygoteState, msgStr);}private Process.ProcessStartResult attemptZygoteSendArgsAndGetResult(ZygoteState zygoteState, String msgStr) throws ZygoteStartFailedEx {try {final BufferedWriter zygoteWriter = zygoteState.mZygoteOutputWriter;final DataInputStream zygoteInputStream = zygoteState.mZygoteInputStream;zygoteWriter.write(msgStr);zygoteWriter.flush();// Always read the entire result from the input stream to avoid leaving// bytes in the stream for future process starts to accidentally stumble// upon.Process.ProcessStartResult result = new Process.ProcessStartResult();result.pid = zygoteInputStream.readInt();result.usingWrapper = zygoteInputStream.readBoolean();if (result.pid < 0) {throw new ZygoteStartFailedEx("fork() failed");}return result;} catch (IOException ex) {zygoteState.close();Log.e(LOG_TAG, "IO Exception while communicating with Zygote - "+ ex.toString());throw new ZygoteStartFailedEx(ex);}}
使用前面封装的ZygoteState 中的输入输出流,写入数据并返回结果。在 Android 11 Zygote启动流程 一文中提到,Zygote启动的时候,创建了Zygote服务端,并调用runSelectLoop,进入循环等待,等待客户端的请求。接收到请求调用processOneCommand方法。在processOneCommand方法中,先fork子进程,子进程fork成功,就在子进程中返回一个Runnable,并执行其run方法。这里的子进程是FallbackHome进程
新进程通知AMS启动FallbackHome
返回的Runnable是通过handleChildProc方法得到的
private Runnable handleChildProc(ZygoteArguments parsedArgs,FileDescriptor pipeFd, boolean isZygote) {//......Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);if (parsedArgs.mInvokeWith != null) {//} else {if (!isZygote) {return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mDisabledCompatChanges,parsedArgs.mRemainingArgs, null /* classLoader */);} else {return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mRemainingArgs, null /* classLoader */);}}}
剩下的就和systemserver启动流程一样,只不过这里是调用前面提到的android.app.ActivityThread类的main方法 。具体参考Android 11 SystemServer启动流程。来看看ActivityThread的main方法
public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");//......Looper.prepareMainLooper();//......ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);//......Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}
创建 ActivityThread 对象并执行其attach方法
@UnsupportedAppUsageprivate void attach(boolean system, long startSeq) {sCurrentActivityThread = this;mSystemThread = system;if (!system) {android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",UserHandle.myUserId());RuntimeInit.setApplicationObject(mAppThread.asBinder());final IActivityManager mgr = ActivityManager.getService();try {mgr.attachApplication(mAppThread, startSeq);//......
获取AMS代理类对象,通过binder调用,调用AMS的attachApplication方法
@Overridepublic final void attachApplication(IApplicationThread thread, long startSeq) {if (thread == null) {throw new SecurityException("Invalid application interface");}synchronized (this) {int callingPid = Binder.getCallingPid();final int callingUid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();attachApplicationLocked(thread, callingPid, callingUid, startSeq);Binder.restoreCallingIdentity(origId);}}
继续调用attachApplicationLocked方法
@GuardedBy("this")private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {//......//创建Application并执行其oncreate方法thread.bindApplication(processName, appInfo, providerList,instr2.mClass,profilerInfo, instr2.mArguments,instr2.mWatcher,instr2.mUiAutomationConnection, testMode,mBinderTransactionTrackingEnabled, enableTrackAllocation,isRestrictedBackupMode || !normalMode, app.isPersistent(),new Configuration(app.getWindowProcessController().getConfiguration()),app.compat, getCommonServicesLocked(app.isolated),mCoreSettingsObserver.getCoreSettingsLocked(),buildSerial, autofillOptions, contentCaptureOptions,app.mDisabledCompatChanges);//......// See if the top visible activity is waiting to run in this process...if (normalMode) {try {didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());//启动Activity} catch (Exception e) {Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);badApp = true;}}}
接下来就是AMS通知FallbackHome进程,执行FallbackHome的生命周期。FallbackHome就会启动起来
FallbackHome的退出
来看一下FallbackHome的onCreate方法
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//......registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));maybeFinish();}
注册了一个ACTION_USER_UNLOCKED的广播,并在这里调用了maybeFinish方法。接收到这个广播也是调用maybeFinish
private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {maybeFinish();}};
所以接下来看看maybeFinish到底干了什么
private void maybeFinish() {if (getSystemService(UserManager.class).isUserUnlocked()) {final Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {if (UserManager.isSplitSystemUser()&& UserHandle.myUserId() == UserHandle.USER_SYSTEM) {// This avoids the situation where the system user has no home activity after// SUW and this activity continues to throw out warnings. See b/28870689.return;}Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?");mHandler.sendEmptyMessageDelayed(0, 500);} else {Log.d(TAG, "User unlocked and real home found; let's go!");getSystemService(PowerManager.class).userActivity(SystemClock.uptimeMillis(), false);finish();}}}
如果系统已经解锁并且查找到的Launcher不是自己时就finish自己。所以可以理解为:FallbackHome注册了ACTION_USER_UNLOCKED这个广播,当接收到这个广播时,如果系统已经解锁并且查找到的Launcher不是自己时就finish自己。ACTION_USER_UNLOCKED这个广播是在哪里发出的呢?
在Android11开机动画退出流程分析一文中,有分析到,开机动画的退出是在performEnableScreen 方法中。退出开机动画后,调用AMS的bootAnimationComplete方法,然后调用到其finishBooting,我们从finishBooting开始分析
final void finishBooting() {//......mUserController.sendBootCompleted(new IIntentReceiver.Stub() {@Overridepublic void performReceive(Intent intent, int resultCode,String data, Bundle extras, boolean ordered,boolean sticky, int sendingUser) {synchronized (ActivityManagerService.this) {mOomAdjuster.mCachedAppOptimizer.compactAllSystem();requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);}}});//......}
调用到UserController对象的sendBootCompleted方法,其源码路径为frameworks\base\services\core\java\com\android\server\am\UserController.java
void sendBootCompleted(IIntentReceiver resultTo) {final boolean systemUserFinishedBooting;// Get a copy of mStartedUsers to use outside of lockSparseArray<UserState> startedUsers;synchronized (mLock) {systemUserFinishedBooting = mCurrentUserId != UserHandle.USER_SYSTEM;startedUsers = mStartedUsers.clone();}for (int i = 0; i < startedUsers.size(); i++) {UserState uss = startedUsers.valueAt(i);if (systemUserFinishedBooting && uss.mHandle.isSystem()) {// On Automotive, at this point the system user has already been started and// unlocked, and some of the tasks we do here have already been done. So skip those// in that case.// TODO(b/132262830): this workdound shouldn't be necessary once we move the// headless-user start logic to UserManager-landSlog.d(TAG, "sendBootCompleted(): skipping on non-current system user");continue;}finishUserBoot(uss, resultTo);}}
继续调用finishUserBoot,最终调用到 finishUserUnlocking
private boolean finishUserUnlocking(final UserState uss) {//......mHandler.obtainMessage(USER_UNLOCK_MSG, userId, 0, uss).sendToTarget();//......
}
发送USER_UNLOCK_MSG消息,接收消息后,调用 finishUserUnlocked
void finishUserUnlocked(final UserState uss) {// Dispatch unlocked to external appsfinal Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);unlockedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);mInjector.broadcastIntent(unlockedIntent, null, null, 0, null,null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,Binder.getCallingUid(), Binder.getCallingPid(), userId);//......finishUserUnlockedCompleted(uss);//发送开机广播}
可以看出,就是在这里发出解锁广播和开机广播的。
总结
本文简单的介绍了FallbackHome启动和关闭的代码调用流程。
启动流程主要分为以下几步
- Systemserver进程通过socket,通知Zygote创建新进程
- 新进程创建成功,新进程通知Systemserver可以启动FallbackHome
- Systemserver通知FallbackHome,执行其生命周期
FallbackHome退出的话,是接收到ACTION_USER_UNLOCKED广播,判断是否解锁并且查找到的Launcher不是自己时,就退出自己
相关文章:
Android11 FallbackHome启动和关闭流程分析
Android 7.0引入了新特性:Direct Boot Mode,设备启动后进入的一个新模式,直到用户解锁(unlock)设备此阶段结束。在这个模式下,系统调用 resolveHomeActivity 找到的是FallbackHome ,而不是我们的…...
elasticsearch-java api 8 升级
es client api 升级 背景 公司项目从sring-boot2 升级到了spring-boot3 ,es的服务端也跟着升级到了es8 ,而es的客户端7和服务端8 是不兼容的, 客户端es 7使用的是: elasticsearch-rest-high-level-client es 8 升级到…...
HCIA_IP路由基础问题?
目录 1. 什么是路由?2. 什么是路由器?3. 什么是路由信息?4. 路由器信息和路由表的区别?5. 路由表的生成方式?6.直连路由生效条件是什么?7.Inloopback0是什么接口?8.最优路由选择的原则ÿ…...

(黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
(黑马出品_高级篇_01)SpringCloudRabbitMQDockerRedis搜索分布式 微服务技术——保护 今日目标1.初识Sentinel1.1.雪崩问题及解决方案1.2.服务保护技术对比1.3.Sentinel介绍和安装1.3.1.初识Sentinel1.3.2.安装Sentinel 1.…...

高架学习笔记之信息系统分类概览
目录 零、前言 一、业务处理系统(TPS) 概念 功能 特点 二、管理信息系统(MIS) 概念 功能 组成 三、决策支持系统(DSS) 概念 功能 特点 组成 1. 数据仓库 2. 数据挖掘工具 3. 决策模型 4. 可视化界面 四、专家系统(ES) 概念 特点 组成 求解过程 专家系统…...

2023新版mapinfo美化电子地图 新版2013Arcgis shp电子地图 下载
2023新版MapInfo和电子地图美化,以及2013版ArcGIS的SHP电子地图设计,是地理信息系统(GIS)领域中的两个重要话题。下面将分别对这两个主题进行描述。 样图: 链接:https://pan.baidu.com/s/1WB4AGsycyBGagVq5…...

BUUCTF-Ezsql1
1.打开靶机 打开第一个链接 2.万能密码 使用万能密码:a or 1 # 密码为随意 第二个用kali打开 3.ssh连接靶机 ssh ctf284490d0-7600-4c65-9160-5ced02f45633.node5.buuoj.cn -p 28191 由题可知密码为123456 4.找到并修改index.php文件 找到index.php文件 #内容如…...

LiveGBS流媒体平台GB/T28181功能-大屏播放上大屏支持轮巡播放分屏轮巡值守播放监控视频轮播大屏轮询播放轮播
LiveGBS支持-大屏播放上大屏支持轮巡播放分屏轮巡值守播放监控视频轮播大屏轮询播放轮播 1、轮播功能2、分屏展示3、选择轮播通道4、配置轮播间隔(秒)5、点击开始轮播6、轮播停止及全屏7、搭建GB28181视频直播平台 1、轮播功能 视频监控项目使用过程中,有时需要大屏…...
npm和pnpm安装、更换镜像源
安装pnpm 1 wins 在系统中搜索框 输入“Windos PowerShell”右击“管理员身份运行” 2 输入“set-ExecutionPolicy RemoteSigned”回车,根据提示输入A,回车 3 输入 pnpm -v 查看版本 如果没有版本好就是没有安装 pnpm 输入安装命令 npm install -g pnpm 4 再次 …...
springcloud 复习day1~[自动装配]
package com.gavin.eureka_server;public class First {private String auto"自动装配";public String getAuto() {return auto;}public void setAuto(String auto) {this.auto auto;} }package com.gavin.eureka_server;public class Second { }装配:实现ImportSe…...
模块化开发在不同编程语言中的实现方式有何异同?并以LabVIEW为例进行说明
模块化开发是一种软件设计方法,它将一个大型程序分解成独立的、可以单独开发和测试的模块或组件。这种方法提高了代码的可重用性、可维护性和可测试性。不同编程语言实现模块化开发的方式各有特色,但都遵循基本的设计原则,如封装、接口抽象和…...

外贸网站文章批量生成器
随着全球贸易的不断发展,越来越多的企业开始关注外贸市场,而拥有高质量的内容是吸引潜在客户的关键之一。然而,为外贸网站生产大量优质的文章内容可能是一项耗时且繁琐的任务。因此,外贸网站文章批量生成软件成为了解决这一难题的…...

maven一点通
1.maven简介 Maven是一个基于Java的工程构建工具,用于管理和构建项目的依赖关系。它提供了一种标准的项目结构和一组约定,使得项目的开发、构建、部署和文档化更加容易和可靠。 Maven的主要功能包括: 依赖管理:Maven可以自动下载…...

超越标签的探索:K-means与DBSCAN在数据分析中的新视角
最近在苦恼为我的数据决定分组问题,在查找资料时,恰好看到机器学习中的无监督学习的聚类分析,正好适用于我的问题,但是我之前学机器学习时。正好没有学习无监督部分,因为我认为绝大多数问题都是有标签的监督学习&#…...

linux板子vscode gdb 远程调试
板子:hi3556v200 交叉编译工具:arm-himix200-linux 主机:win10虚拟机的ubuntu16.4 gdb:gdb-8.2.tar.gz 1.在ubuntu交叉编译gdb(Remote g packet reply is too long解决) 建议修改gdb8.2/gdb目录下面的remote.c解决…...
nginx代理服务器配置
nginx代理服务器配置 需要配置环境需求 1、一台1.1.1.1服务器,一台2.2.2.2服务器 前端包路径在1.1.1.1 /etc/dist 下 后端服务在2.2.2.2 上 暴露端口为9999 2、需求 现在需要访问 1.1.1.1:80访问到2.2.2.2 上面的9999后端服务 3、配置nginx ①:在…...

基于Matlab的视频人面检测识别,Matalb实现
博主简介: 专注、专一于Matlab图像处理学习、交流,matlab图像代码代做/项目合作可以联系(QQ:3249726188) 个人主页:Matlab_ImagePro-CSDN博客 原则:代码均由本人编写完成,非中介,提供…...

VSCode创建用户代码片段-案例demo
示例 - 在线生成代码片段 Vue3代码片段 {"vue3": {scope": "javascript,typescript,html,vue","prefix": "vue3","body": ["<template>","$1","</template>",""…...

河南大学-数字图像处理-图像变换
计算机与信息工程学院实验报告 序号:20 姓名:__杨馥瑞___ 学号:_2212080042_ 专业:__数据科学与大数据技术 年级:___2022级_____ 课程:数字图像处理 主讲教师:张延锋 辅导教师&#x…...

华为OD七日集训第3期 - 按算法分类,由易到难,循序渐进,玩转OD
目录 一、适合人群二、本期训练时间三、如何参加四、七日集训第 3 期五、精心挑选21道高频100分经典题目,作为入门。第1天、逻辑分析第2天、字符串处理第3天、矩阵第4天、深度优先搜索dfs算法第5天、回溯法第6天、二分查找第7天、图、正则表达式 大家好,…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...

代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...