Android Framework學習五:APP啟動過程原理及速度優化

文章目錄

  • APP啟動優化概述
  • APP啟動流程
    • 點擊圖片啟動APP的過程
    • 啟動觸發
    • Zygote 與應用進程創建
      • Zygote
      • 進程的創建
      • 應用進程初始化
    • Application
    • Activity 啟動與顯示
  • 優化啟動時黑白屏現象
    • 可優化的階段
      • Application階段
        • 相關優化
      • Activity階段
      • 數據加載階段
  • Framework學習系列文章

APP啟動優化概述

  1. 優化方向
    在這里插入圖片描述

  2. 啟動優化流程
    在這里插入圖片描述

APP啟動流程

在這里插入圖片描述

點擊圖片啟動APP的過程

這張圖展示了 Android 系統中應用程序(App)啟動的完整流程,涉及多個關鍵組件及其交互
在這里插入圖片描述

啟動觸發

  1. Launcher:用戶在手機桌面點擊應用圖標,Launcher(桌面啟動器)響應點擊事件。通過Binder機制向service_manager查詢ActivityTaskManagerService(ATMS,活動管理服務)服務。
  2. service_manager 和 Binder:service_manager是系統中管理服務的組件,通過Binder這種進程間通信機制,Launcher獲取到AMS服務的相關信息。Binder是 Android 系統中實現進程間通信的重要機制,提供高效穩定的通信能力。
  3. system_server:system_server進程是 Android 系統核心進程,包含ActivityManagerService和WindowManagerService等重要服務。AMS負責管理應用程序的生命周期、Activity 的啟動與切換等;WindowManagerService負責窗口的管理和顯示等。

在這里插入圖片描述

具體代碼如下:

  • Launcher調用Activity.startActivity啟動Activity
    Activity.java
@Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {...if (options != null) {startActivityForResult(intent, -1, options);} else {startActivityForResult(intent, -1);}}public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {...Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken,this,intent, requestCode, options);...}
  • Instrumentation.execStartActivity調用
    Instrumentation.java
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {...// ActivityTaskManagerServiceint result = ActivityTaskManager.getService().startActivity(whoThread,who.getBasePackageName(),who.getAttributionTag(),intent,intent.resolveTypeIfNeeded(who.getContentResolver()),token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); ...
}
  • ActivityTaskManagerService.startActivityAsUser
    ActivityTaskManagerService::startActivity調用startActivityAsUser
    ActivityTaskManager.getService()其實返回的是ATMS的binder
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);

至此,startActivity 的工作重心成功地從 應用進程(app) 轉移到了系統進程(system_service) 的 ATMS 中。

private int startActivityAsUser(IApplicationThread caller, String callingPackage,@Nullable String callingFeatureId, Intent intent, String resolvedType,IBinder resultTo, String resultWho, int requestCode, int startFlags,ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {assertPackageMatchesCallingUid(callingPackage);enforceNotIsolatedCaller("startActivityAsUser");userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");// getActivityStartController().obtainStarter是ActivityStarterreturn getActivityStartController().obtainStarter(intent, "startActivityAsUser").setCaller(caller).setCallingPackage(callingPackage).setCallingFeatureId(callingFeatureId).setResolvedType(resolvedType).setResultTo(resultTo).setResultWho(resultWho).setRequestCode(requestCode).setStartFlags(startFlags).setProfilerInfo(profilerInfo).setActivityOptions(bOptions).setUserId(userId).execute();}
  • ActivityStarter.execute
    ActivityStarter.java
int execute() {...res = executeRequest(mRequest);...}
private int executeRequest(Request request) {...ActivityRecord sourceRecord = null;ActivityRecord resultRecord = null;...mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,restrictedBgActivity, intentGrants);...return mLastStartActivityResult;}
  • ActivityStarter.startActivityUnchecked
private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,boolean restrictedBgActivity, NeededUriGrants intentGrants) {....try {...result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);} finally {...}postStartActivityProcessing(r, result, startedActivityRootTask);return result;}
  • ActivityStarter.startActivityInner
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,int startFlags, boolean doResume, ActivityOptions options, Task inTask,boolean restrictedBgActivity, NeededUriGrants intentGrants) {setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,voiceInteractor, restrictedBgActivity);...// RootWindowContainermRootWindowContainer.resumeFocusedTasksTopActivities(mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);...return START_SUCCESS;}
  • RootWindowContainer.resumeFocusedTasksTopActivities
boolean resumeFocusedTasksTopActivities(Task targetRootTask, ActivityRecord target, ActivityOptions targetOptions,boolean deferPause) {...boolean result = false;if (targetRootTask != null && (targetRootTask.isTopRootTaskInDisplayArea()|| getTopDisplayFocusedRootTask() == targetRootTask)) {// Taskresult = targetRootTask.resumeTopActivityUncheckedLocked(target, targetOptions,deferPause);}...return result;}
  • Task.resumeTopActivityInnerLocked
    Task.java
 private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {...if (mResumedActivity != null) {pausing |= startPausingLocked(userLeaving, false , next); // 這里會調用Launcher的pause}...// ActivityTaskSupervisormStackSupervisor.startSpecificActivity(next, true, false);...}
  • ActivityStackSupervisor::startSpecificActivity
    ActivityStackSupervisor會判斷進程是否存在,存在則調用realStartActivityLocked,不存在則調用startProcessAsync創建其實是通過Zygote來fork創建進程,進程創建后還是會調用realStartActivityLocked的。
    在這里插入圖片描述

Zygote 與應用進程創建

Zygote

Zygote:AMS通過Zygote進程來創建新的應用程序進程(App)。Zygote 是 Android 系統中一個特殊的進程,是所有 Java 應用程序進程的孵化器,它在系統啟動時就已啟動并處于就緒狀態。Zygote 通過fork系統調用創建新的應用進程,新進程會繼承 Zygote 進程的一些初始化狀態,提高應用啟動效率。

進程的創建

在這里插入圖片描述
來看看mService.startProcessAsync

  • ActivityManagerInternal.startProcess()
/*** Activity manager local system service interface.*/
public abstract class ActivityManagerInternal {/** Starts a given process. */// 調用的是ActivityManagerService.LocalService.startProcess()--》ActivityManagerService.startProcessLockedpublic abstract void startProcess(String processName, ApplicationInfo info,boolean knownToBeDead, boolean isTop, String hostingType, ComponentName hostingName);
}
  • ActivityManagerService.startProcessLocked
/*** Process management.*/final ProcessList mProcessList;@GuardedBy("this")final ProcessRecord startProcessLocked(String processName,ApplicationInfo info, boolean knownToBeDead, int intentFlags,HostingRecord hostingRecord, int zygotePolicyFlags, boolean allowWhileBooting,boolean isolated, boolean keepIfLarge) {// ProcessList,最終調用的是ProcessList.startProcess()-->Process.startreturn mProcessList.startProcessLocked(processName, info, knownToBeDead, intentFlags,hostingRecord, zygotePolicyFlags, allowWhileBooting, isolated, 0 /* isolatedUid */,keepIfLarge, null /* ABI override */, null /* entryPoint */,null /* entryPointArgs */, null /* crashHandler */);}
  • Process.start
/*** State associated with the zygote process.*/public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess();public static ProcessStartResult start(@NonNull final String processClass,@Nullable final String niceName,int uid, int gid, @Nullable int[] gids,int runtimeFlags,int mountExternal,int targetSdkVersion,@Nullable String seInfo,@NonNull String abi,@Nullable String instructionSet,@Nullable String appDataDir,@Nullable String invokeWith,@Nullable String packageName,int zygotePolicyFlags,boolean isTopApp,@Nullable long[] disabledCompatChanges,@Nullable Map<String, Pair<String, Long>>pkgDataInfoMap,@Nullable Map<String, Pair<String, Long>>whitelistedDataInfoMap,boolean bindMountAppsData,boolean bindMountAppStorageDirs,@Nullable String[] zygoteArgs) {return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,runtimeFlags, mountExternal, targetSdkVersion, seInfo,abi, instructionSet, appDataDir, invokeWith, packageName,zygotePolicyFlags, isTopApp, disabledCompatChanges,pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppsData,bindMountAppStorageDirs, zygoteArgs);}
  • ZygoteProcess.start
    ZygoteProcess 最核心的作用是借助與 Zygote 進程的通信,創建新的應用進程
public final Process.ProcessStartResult start(...) {return startViaZygote(processClass, niceName, uid, gid, gids,runtimeFlags, mountExternal, targetSdkVersion, seInfo,abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,packageName, zygotePolicyFlags, isTopApp, disabledCompatChanges,pkgDataInfoMap, allowlistedDataInfoList, bindMountAppsData,bindMountAppStorageDirs, zygoteArgs);}

會通過LocalSocket與Zygote建立socket連接,溝通Zygote fork進程
在這里插入圖片描述

  • ZygoteConnection.forkAndSpecialize
    這個時候還是在.cpp中執行,fork子進程,native方法,內部實現就是c語言的fork函數調用。
    在這里插入圖片描述
  • ZygoteConnection.handleChildProc
    ZygoteConnection.java這個時候跑到java,這里是子進程的代碼了,
    在這個函數中會調用ZygoteInit
    在這里插入圖片描述
  • ZygoteInit.ZygoteInit
    ZygoteInit.java
    在這里插入圖片描述
  • nativeZygoteInit
    AndroidRuntime.cpp
    在這里插入圖片描述
    在這里插入圖片描述
  • ProcessState構造創建Binder
    ProcessState.cpp
    在這里插入圖片描述構造ProcessState時就會調用open_driver(“/dev/binder”),打開binder驅動設備,然后通過mmap向binder驅動申請空間。
    在這里插入圖片描述
    mmap時,會申請BINDER_VM_SIZE大小的空間,不到1M,1M減2個內存頁的大小。
    在這里插入圖片描述
    然后接著onZygoteInit會調用proc->processThreadPool()啟動binder線程池,默認最大線程數為15。
  • RuntimeInit.findStaticMain
    與Zygote通過socket通信時,會傳參數"android.app.ActivityThread"過來,通過findStaticMain定位到ActivityThread.main。
    在這里插入圖片描述
    在這里插入圖片描述

這樣fork進程后會跳轉到新進程的main入口,就是ActivityThread.main函數。

應用進程初始化

  • ActivityThread:應用進程創建后,首先執行ActivityThread.main()方法,這是應用程序主線程(UI 線程)的入口。接著依次調用AT.attach()和AT.bindApplication()方法,完成主線程與系統的綁定以及應用程序的初始化相關操作 。
ActivityThread.javapublic static void main(String[] args) {...Looper.prepareMainLooper();...ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);...Looper.loop();...}private void attach(boolean system, long startSeq) {final IActivityManager mgr = ActivityManager.getService();try {mgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}

ActivityManagerService.java

private boolean attachApplicationLocked(@NonNull IApplicationThread thread,int pid, int callingUid, long startSeq) {...// 發送MH.BIND_APPLICATION的Message,在Handler中調用了Application的onCreatethread.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);...// 這里會最終調用到ActivityStackSupervisor.realStartActivityLockeddidSomething = mAtmInternal.attachApplication(app.getWindowProcessController());...}
  • realStartActivityLocked會調用到ActivityThread.performLaunchActivity()
    進程創建后,開始啟動Activity
    ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {// 創建Context,Context:初始化過程中會構建Context(上下文),它是應用程序環境的抽象類,提供了應用程序運行所需的各種信息和操作接口,如資源訪問、系統服務獲取等。ContextImpl appContext = createBaseContextForActivity(r);...//反射創建Activityjava.lang.ClassLoader cl = appContext.getClassLoader();activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);...// AMS通過反射創建APP的Application對象,調用Application的attach函數,這個其實是Application.attachBaseContent(),然后才是調用Application.onCreate()方法,Application是應用程序全局的一個基類,可用于在應用生命周期內保存全局狀態和進行一些初始化操作Application app = r.packageInfo.makeApplication(false, mInstrumentation);...// 調用Activity.attachactivity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.configCallback,r.assistToken);...activity.setTheme(theme);...// 調用Activity.onCreatemInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
}

Application

在這里插入圖片描述
如圖:
createApplContext創建Context,
newApplication是通過反射創建Application對象app并會調用attachBaseContext,
callApplicationOnCreate則是調用onCreate,
在這里插入圖片描述如圖:
newApplication中是調用的反射創建Application,然后調用attachBaseContext
在這里插入圖片描述
ApplicationThread.scheduleTransaction,調用ActivityThread.handleLaunchActivity

Activity 啟動與顯示

  • Activity 啟動:ActivityThread中performLaunchActivity()方法先創建Application后再啟動Activity。先調用Activity.onCreate()方法創建Activity實例,完成Activity的初始化工作,如設置布局等。
  • 設置布局與關聯窗口:通過Activity.setContentView()設置Activity的布局文件。然后調用Activity.onResume()方法,此時AMS將Activity與WindowManagerService進行關聯,完成窗口的創建和顯示相關操作,最終將應用界面展示給用戶。

優化啟動時黑白屏現象

從啟動到顯示應用APP的首頁過程經歷如下過程:
在這里插入圖片描述
這過程默認是顯示黑白屏或是背景圖的。

可優化的階段

Application階段

在這里插入圖片描述
上面的階段中,從attachBaseContent開始都是可以優化的階段。
注意Provider是在onCreate之后,如下圖:
在這里插入圖片描述
makeApplication中會調用Application的onCreate的,然后installContentProviders才會調用。

相關優化
  • 4.x 之前的 multidex 加載優化:在早期 Android 版本(4.x 之前),應用如果包含多個 dex 文件(multidex),需要特殊的加載優化策略,因為當時系統對多 dex 支持不完善。
  • 加固熱修復導致延遲:應用經過加固和熱修復處理后,在執行 attachBaseContext 時可能會出現延遲情況,這是因為加固和熱修復操作增加了額外的代碼加載和處理邏輯。
  • 開源庫中的 Provider 初始化:很多開源庫會使用 Content Provider 來提供數據共享等功能,這里需要對其進行初始化。
  • 自己項目的 provider 初始化:項目中自定義的 Content Provider 也需要在此步驟進行初始化,確保其能夠正常工作。
  • Application::onCreate:優化最大的部分為異步、按需加載、懶加載優化。通過異步加載和按需、懶加載機制,可以避免在應用啟動時一次性加載過多資源,從而加快應用啟動速度,提升用戶體驗。

Activity階段

在這里插入圖片描述

  1. Activity::onCreate
  • XML 文件解析,反射:Android 通過解析布局的 XML 文件來創建 View,這個過程會用到反射機制實例化 XML 中定義的 View 對象。
  • 異步并發構建 Viewtree:為加快構建速度,可采用異步并發方式構建 View 樹,避免主線程阻塞,提升用戶體驗。
  1. View構建
  • 將 Inflate 過程最大異步化:布局填充(Inflate)是將 XML 布局文件轉化為 View 對象的過程,將其異步化,能減少主線程負載,防止 ANR(應用無響應)。
  • 利用 X2C 解決加載速度:X2C(XML to Java Class)技術可將 XML 布局轉換為 Java 代碼,減少解析 XML 的開銷,加快 View 的加載速度。
  1. View顯示
  • View 的度量布局和顯示:通過正確的測量(Measure)、布局(Layout)和繪制(Draw)流程,確保 View 能正確顯示在屏幕上。
  • 層級優化:減少 View 層級嵌套,避免過度復雜的布局結構,降低繪制和渲染成本。
  • 布局優化:精簡布局,合理使用布局容器,如用 ConstraintLayout 替代嵌套的 LinearLayout 等,提升布局渲染效率。

數據加載階段

在這里插入圖片描述

  1. 數據預加載:在用戶觸發數據展示操作前,提前加載數據。比如在應用啟動時或進入某個頁面之前,提前加載該頁面可能用到的數據,減少用戶等待時間,提升響應速度。
  2. 數據緩存機制:將已加載的數據存儲在緩存中(如內存緩存、磁盤緩存 ),當再次需要相同數據時,優先從緩存讀取,避免重復從原始數據源獲取,降低網絡請求或磁盤讀取開銷,加快數據展示速度。
  3. 顯示數據加載優先級調度:根據 UI 界面中不同數據展示區域的重要性,設置數據加載優先級。例如,先加載屏幕可見區域的數據,后加載不可見區域的數據;或者優先加載用戶更關注的關鍵信息數據,確保重要數據優先展示,提升用戶體驗。

Framework學習系列文章

Android Framework學習一:系統框架、啟動過程
Android Framework學習二:Activity創建及View繪制流程
Android Framework學習三:zygote
Android Framework學習四:APP速度優化
Android Framework學習五:APP啟動過程原理及速度優化
作者:帥得不敢出門

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79894.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79894.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79894.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Web 實時通信技術:WebSocket 與 Server-Sent Events (SSE) 深入解析

一、WebSocket&#xff1a; &#xff08;一&#xff09;WebSocket 是什么&#xff1f; WebSocket 是一種網絡通信協議&#xff0c;它提供了一種在單個 TCP 連接上進行全雙工通信的方式。與傳統的 HTTP 請求 - 響應模型不同&#xff0c;WebSocket 允許服務器和客戶端在連接建立…

MySQL(8)什么是主鍵和外鍵?

主鍵&#xff08;Primary Key&#xff09;和外鍵&#xff08;Foreign Key&#xff09;是關系數據庫中用于定義和維護表之間關系的重要概念。以下是詳細的解釋、示例代碼和操作步驟。 主鍵&#xff08;Primary Key&#xff09; 定義 主鍵是表中的一個或多個字段&#xff0c;其…

任意復雜度的 JSON 數據轉換為多個結構化的 Pandas DataFrame 表格

以下是一個 完整、結構清晰、可運行的 Python 工具&#xff0c;用于將任意復雜度的 JSON 數據轉換為多個結構化的 Pandas DataFrame 表格。該工具支持嵌套對象、嵌套數組&#xff0c;并通過主鍵和外鍵建立表之間的關聯關系。 if __name__ "__main__":# 示例 JSON 數…

【SSL部署與優化?】??HTTP/2與HTTPS的協同效應

HTTP/2與HTTPS的協同效應&#xff1a;為何HTTP/2強制要求TLS 1.2&#xff1f; HTTP/2是HTTP協議的現代升級版&#xff0c;旨在通過多路復用、頭部壓縮等技術提升性能。然而&#xff0c;HTTP/2的設計與部署與HTTPS&#xff08;TLS加密&#xff09;緊密相關&#xff0c;甚至強制…

爬蟲請求頻率應控制在多少合適?

爬蟲請求頻率的控制是一個非常重要的問題&#xff0c;它不僅關系到爬蟲的效率&#xff0c;還涉及到對目標網站服務器的影響以及避免被封禁的風險。合理的請求頻率需要根據多個因素來綜合考慮&#xff0c;以下是一些具體的指導原則和建議&#xff1a; 一、目標網站的政策 查看網…

使用Visual Studio將C#程序發布為.exe文件

說明 .exe 是可執行文件&#xff08;Executable File&#xff09;的擴展名。這類文件包含計算機可以直接運行的機器代碼指令&#xff0c;通常由編程語言&#xff08;如 C、C、C#、Python 等&#xff09;編譯或打包生成。可以用于執行自動化操作&#xff08;執行腳本或批處理操…

分布式1(cap base理論 鎖 事務 冪等性 rpc)

目錄 分布式系統介紹 一、定義與概念 二、分布式系統的特點 三、分布式系統面臨的挑戰 四、分布式系統的常見應用場景 CAP 定理 BASE 理論 BASE理論是如何保證最終一致性的 分布式鎖的常見使用場景有哪些&#xff1f; 1. 防止多節點重復操作 2. 資源互斥訪問 3. 分…

常見相機焦段的分類及其應用

相機焦段是指鏡頭的焦距范圍&#xff0c;決定了拍攝時的視角、畫面范圍和透視效果。不同焦段適合不同的拍攝場景和主題&#xff0c;以下是常見焦段的分類及其應用&#xff1a; 一、焦段的核心概念 焦距&#xff1a;鏡頭光學中心到成像傳感器的距離&#xff08;單位&#xff1a…

H5S 視頻監控AWS S3 對象存儲

本文介紹一下如何使用S3對象存儲作為H5S 存儲空間進行錄像存儲 然后創建一個對象存儲&#xff0c;本文以minio 為例(實際項目親測天翼云)&#xff1a; 首先安裝 s3fs 如果是redhat系列&#xff0c;使用如下命令 sudo yum install epel-release sudo yum install s3fs-fuse …

算法第十八天|530. 二叉搜索樹的最小絕對差、501.二叉搜索樹中的眾數、236. 二叉樹的最近公共祖先

530. 二叉搜索樹的最小絕對差 題目 思路與解法 第一想法&#xff1a; 一個二叉搜索樹的最小絕對差&#xff0c;從根結點看&#xff0c;它的結點與它的最小差值一定出現在 左子樹的最右結點&#xff08;左子樹最大值&#xff09;和右子樹的最左結點&#xff08;右子樹的最小值…

Nginx 動靜分離在 ZKmall 開源商城靜態資源管理中的深度優化

在 B2C 電商高并發場景下&#xff0c;靜態資源&#xff08;圖片、CSS、JavaScript 等&#xff09;的高效管理直接影響頁面加載速度與用戶體驗。ZKmall開源商城通過對 Nginx 動靜分離技術的深度優化&#xff0c;將靜態資源響應速度提升 65%&#xff0c;帶寬成本降低 40%&#xf…

PostgREST:無需后端 快速構建RESTful API服務

在現代 Web 開發中&#xff0c;API 已成為連接前后端的核心橋梁&#xff0c;傳統的做法是通過后端框架來構建API接口&#xff0c;然后由前后端人員進行聯調。 PostgREST是基于無服務器的一種實現方案&#xff0c;允許開發者將PostgreSQL數據庫直接暴露為RESTful API&#xff0…

MySQL——九、鎖

分類 全局鎖表級鎖行級鎖 全局鎖 做全庫的邏輯備份 flush tables with read lock; unlock tables;在InnoDB引擎中&#xff0c;我們可以在備份時加上參數–single-transaction參數來完成不加鎖的一致性數據備份 mysqldump --single-transaction -uroot -p123456 itcast>…

基于 Kubernetes 部署容器平臺kubesphere

一 前言&#xff1a; k8s 大家都已經非常熟悉了&#xff0c;網上流傳著非常多的搭建部署文檔&#xff0c;有kubeadmin的有二進制的&#xff0c;還有基于第三方的部署工具的&#xff0c;反正是各種部署方法都有&#xff0c;k8s部署技術熱門可見一斑。但是不管哪種部署都需要了解…

RDD算子-行為算子

RDD 算子探秘&#xff1a;行為算子的深度解析與實戰應用? 在 Spark 的 RDD 編程模型中&#xff0c;轉換算子負責構建數據處理的邏輯流程&#xff0c;但真正觸發計算并產生最終結果的是行為算子&#xff08;Action Operators&#xff09;。與轉換算子的惰性求值特性不同&#…

Oracle — PL-SQL

介紹 Oracle PL/SQL是專為Oracle數據庫設計的過程化編程語言&#xff0c;深度融合SQL語句與結構化編程邏輯&#xff0c;旨在高效處理復雜數據操作與業務規則。其核心特征為“塊結構”&#xff0c;程序由聲明、執行、異常處理三部分組成&#xff0c;支持模塊化開發&#xff0c;顯…

高防ip支持哪些網絡協議

高防IP通常支持多種網絡協議&#xff0c;以提供全面的網絡安全防護。以下是一些主要支持的網絡協議及其相關說明&#xff1a; TCP協議&#xff08;傳輸控制協議&#xff09;&#xff1a; TCP協議是最常見的傳輸協議&#xff0c;廣泛應用于互聯網通信。高防IP通過對TCP協議的防…

Flutter基礎()

導航欄 appBar: AppBar() title: const Text(搜索) //標題 backgroundColor: Colors.blue //背景顏色 centerTitle: true //標題居中leading 屬性 作用&#xff1a; 放置在應用欄左側的控件&#xff0c;通常是一個圖標按鈕&#xff0c;用于導航或打開菜單。 AppBar(le…

ESP系列單片機選擇指南:結合實際場景的最優選擇方案

前言 在物聯網(IoT)快速發展的今天&#xff0c;ESP系列單片機憑借其優異的無線連接能力和豐富的功能特性&#xff0c;已成為智能家居、智慧農業、工業自動化等領域的首選方案。本文將深入分析各款ESP芯片的特點&#xff0c;結合典型應用場景&#xff0c;幫助開發者做出最優選擇…

搭建Caffeine+Redis多級緩存機制

本地緩存的簡單實現方案有HashMap&#xff0c;CucurrentHashMap&#xff0c;成熟的本地緩存方案有Guava 與 Caffeine &#xff0c;企業級應用推薦下面說下兩者的區別 1. 核心異同對比 特性Guava CacheCaffeine誕生背景Google Guava 庫的一部分&#xff08;2011年&#xff09;…