Android啟動過程-萬字長文(Android14)

来源:https://www.cnblogs.com/anywherego/p/18221943
-Advertisement-
Play Games

在電腦啟動過程和Linux內核Kernel啟動過程介紹了電腦啟動和內核載入,本篇文章主要介紹Android系統是如何啟動的。 一、Android啟動流程 Android系統的啟動流程與Linux接近: sequenceDiagram participant Bootloader as 引導載入程 ...


電腦啟動過程Linux內核Kernel啟動過程介紹了電腦啟動和內核載入,本篇文章主要介紹Android系統是如何啟動的。

一、Android啟動流程

Android系統的啟動流程與Linux接近:

sequenceDiagram participant Bootloader as 引導載入程式 participant Kernel as 內核 participant Init as init 進程 participant Zygote as Zygote 進程 participant SystemServices as 系統服務 participant Launcher as 應用程式(桌面) Bootloader->>Kernel: 載入內核 Kernel->>Kernel: 初始化 Kernel->>Init: 啟動 init 進程 Init->>Init: 讀取系統配置文件 Init->>Zygote: 啟動 Zygote 進程 Zygote->>Zygote: 預載入常用的 Java 類和資源 Zygote->>SystemServices: 啟動系統服務 SystemServices->>Launcher: 啟動桌面程式
  • 1.引導載入程式(Bootloader)啟動: 當設備上電或者重啟時,首先會由引導載入程式負責啟動。引導載入程式通常存儲在設備的固件中,它的主要任務是初始化硬體,並載入並啟動操作系統內核。引導載入程式會首先運行自身的初始化代碼,然後載入操作系統內核到記憶體中。
  • 2.內核載入: 引導載入程式會根據預定義的配置從設備存儲中載入操作系統內核。在Android設備中,通常使用的是Linux內核。引導載入程式將內核載入到記憶體中的指定位置。
  • 3.內核初始化: 一旦內核載入到記憶體中,引導載入程式會將控制權轉交給內核。內核開始執行初始化過程,包括對硬體進行初始化、建立虛擬文件系統、創建進程和線程等。
  • 4.啟動 init 進程: 內核初始化完成後,會啟動名為init的用戶空間進程。init進程是Android系統的第一個用戶空間進程,它負責系統的進一步初始化和啟動。init進程會讀取系統配置文件(例如 init.rc),並根據其中的指令啟動系統服務和應用程式。
  • 5.啟動 Zygote 進程: 在init進程啟動後,會啟動名為Zygote的進程。Zygote進程是Android應用程式的孵化器,它會預載入常用的Java類和資源,以加速應用程式的啟動。
  • 6.啟動系統服務: 在Zygote進程啟動後,還會啟動一系列系統服務,例如SurfaceFlinger、ActivityManager、PackageManager等。這些系統服務負責管理系統的各個方面,包括顯示、應用程式生命周期、包管理等。
  • 7.啟動桌面程式: 一旦系統服務啟動完成,Android系統就處於可用狀態。就會啟動桌面程式,用戶可以開始啟動應用程式並使用設備進行各種操作了。

電腦啟動過程Linux內核Kernel啟動過程已經介紹了電腦啟動和內核載入,所以本篇文章從Zygote進程開始介紹。

二、Zygote進程(孵化器進程)

1.Zygote簡介

  • Zygote進程是一個用戶進程,由init進程(1號進程)fork而來。
  • Zygote進程的主要任務是載入系統的核心類庫(如Java核心庫和Android核心庫)和安卓系統服務(SystemService),然後進入一個迴圈,等待請求來創建新的 Android 應用程式進程。
  • Zygote進程通過fork的方式創建新的應用程式進程。

2.Zygote進程的創建

Zygote進程在系統啟動時由init進程創建。init進程是Linux系統中的第一個用戶空間進程,它通過解析init.rc文件來啟動各種服務和進程,包括Zygote。具體流程如下:

2.1 啟動init進程:

  • 系統啟動後,內核會載入並運行init進程
  • init進程讀取並解析init.rc配置文件。

init進程的啟動可參考Linux內核Kernel啟動過程

2.2 init.rc腳本:

init.rc文件中包含啟動Zygote的指令腳本的主要代碼:

// 創建zygote服務
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
// 創建zygote socket,與系統和應用程式做通信
socket zygote stream 660 root system
// 定義了zygote服務重啟時的一些操作
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server:

  • service zygote: 定義一個名為zygote的服務
  • /system/bin/app_process:這是啟動Zygote進程的可執行文件,64位系統為app_process64
  • -Xzygote:標誌表明這是一個Zygote進程啟動的特殊模式。
  • /system/bin:指定進程的工作目錄。
  • --zygote:告訴app_process以Zygote模式啟動。
  • --start-system-server:Zygote啟動時還要啟動System Server進程,這是Android系統中管理關鍵系統服務的核心進程。

class main:

  • 將Zygote服務歸類為main類別。
  • Android系統在啟動過程中會啟動所有“main”類別的服務。

socket zygote stream 660 root system:
創建了一個名為zygote的UNIX域Socket套接字,用於其他進程與Zygote進程通信。

onrestart write /sys/android_power/request_state wake
當zygote服務重啟時,系統應該將“/sys/android_power/request_state”文件的內容設置為“wake”,以喚醒設備。

onrestart write /sys/power/state on
當zygote服務重啟時,系統應該將“/sys/power/state”文件的內容設置為 “on”,以打開電源。

onrestart restart media
當zygote服務重啟時,系統應該重啟媒體服務(如音頻、視頻等),以恢復媒體功能。

onrestart restart netd
當zygote服務重啟時,系統應該重啟網路守護進程(netd),以恢復網路功能。

2.3 app_process文件

/system/bin/app_process是Android中的一個關鍵可執行文件,負責啟動Zygote進程和應用進程。

Android14的app_process源碼地址

2.3.1 main方法

app_process主入口點是main方法,它是整個進程啟動流程的起點。以下是其主要代碼和解釋:

以下是關鍵代碼說明:

int main(int argc, char* const argv[])
{
    // 創建並初始化AppRuntime對象runtime
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    
    // 初始化參數zygote,startSystemServer,application,niceName,className
    // 代碼見源碼,此處略
    
    // 解析命令行參數
    // 代碼見源碼,此處略

    // 構建傳遞給 Java 初始化類的參數列表
    // 代碼見源碼,此處略
    
    if (zygote) {
        // 調用AppRuntime的start方法,開始載入ZygoteInit類
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (!className.isEmpty()) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

2.3.2 AppRuntime類(AndroidRuntime)

AppRuntime繼承自AndroidRuntime(ART),是Android中的一個關鍵類,負責管理和啟動 Android 應用程式或系統服務的 Java 虛擬機 (JVM)。

Android14的AndroidRuntime源碼地址

2.3.2.1 AndroidRuntime類的start方法

app_process的main方法調用了AppRuntime的start方法,也就是AppRuntime的父類AndroidRuntime的start方法

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    // 初始化Java Native Interface (JNI)。JNI是Java和C/C++之間的介面,它允許Java代碼和C/C++代碼相互調用
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    
    JNIEnv* env;  // JNIEnv環境指針
    // 初始化虛擬機
    if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
        return;
    }

    // 註冊JNI方法
    if (startReg(env) < 0) {
        return;
    }

    /*
     * 以下代碼執行後,當前線程(即運行 AndroidRuntime::start 方法的線程)將成為Java虛擬機(JVM)的主線程,並且在調用env->CallStaticVoidMethod啟動指定的Java類的 main 方法後,這個方法不會返回,直到 JVM 退出為止。(官方文檔說明)
     */
    
    // 將"com.android.internal.os.ZygoteInit"轉換為"com/android/internal/os/ZygoteInit"
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        // 沒有找到ZygoteInit.main()方法
    } else {
        // 通過JNI調用ZygoteInit.main()方法
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
    }
}
  • 創建虛擬機
  • 註冊JNI方法
  • 通過JNI調用ZygoteInit.main()

3.Zygote進程

AndroidRuntimestart方法,通過JNI調用ZygoteInit.main(),系統第一次進入Java層(ZygoteInit是系統運行的第一個Java類),當前線程也正式成為Java虛擬機(JVM)的主線程。

Android14的ZygoteInit源碼地址

3.1 ZygoteInit.main()

通過main方法完成資源預載入、啟動系統服務等功能,為Launcher桌面程式做準備。

public static void main(String[] argv) {
    // 創建ZygoteServer
    ZygoteServer zygoteServer = null;
    ...
    // 預載入資源
    preload(bootTimingsTraceLog);
    ...
    // 初始化ZygoteServer
    zygoteServer = new ZygoteServer(isPrimaryZygote);
    ...
    // 通過fork的形式初始化SystemServer
    Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
    if (r != null) {
        r.run();
        return;
    }
    ...
    // 啟動Loop,監聽消息
    caller = zygoteServer.runSelectLoop(abiList);
    ...
}

3.2 ZygoteInit.preload()預載入

通過preload方法預載入系統常用的類、資源和庫,能夠顯著減少應用啟動時的延遲,並通過共用這些預載入的內容來降低記憶體使用,提高系統性能。

static void preload(TimingsTraceLog bootTimingsTraceLog) {
    preloadClasses(); //載入常用的Java類
    preloadResources(); //載入常用的資源(如佈局、圖片等)
    preloadOpenGL(); //載入OpenGL庫
    preloadSharedLibraries(); //載入常用的本地共用庫
    preloadTextResources(); //載入常用的文本資源
    ...
}

3.2.1 常用類

  • Android框架中的基礎類,如Activity、Service、BroadcastReceiver等。
  • 常用的UI組件類,如TextView、Button、ImageView等。

3.2.2 常用資源

常用佈局文件(layout)。
常用圖片資源(drawable)。
常用字元串(strings.xml)。

3.2.3 常用庫

標準C庫(libc.so)。
圖形處理庫(libskia.so)。
OpenGL ES庫(libGLESv2.so)。

3.3 啟動System Server

System Server是Android系統中的關鍵進程,負責啟動和管理核心系統服務。

啟動過程的核心代碼:

public static void main(String argv[]) {
    // 初始化ZygoteServer
    ZygoteServer zygoteServer = new ZygoteServer();
    // 啟動System Server
    if (startSystemServer) {
        startSystemServer(abiList, socketName, zygoteServer);
    }
    // 進入Zygote的主迴圈,等待新進程的啟動請求
    zygoteServer.runSelectLoop();
}

private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
    /* 調用native方法fork系統伺服器 */
    int pid = Zygote.forkSystemServer(...);
    if (pid == 0) {
        // 在子進程中執行System Server的main方法
        handleSystemServerProcess(parsedArgs);
    }
}

private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
    // 通過反射調用SystemServer的main方法
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    Class<?> clz = cl.loadClass("com.android.server.SystemServer");
    Method m = clz.getMethod("main", new Class[] { String[].class });
    m.invoke(null, new Object[] { parsedArgs.remainingArgs });
}

  • ZygoteServer是一個Socket,Zygote進程通過這個Socket和SystemService及其他應用程式進程做通信
  • 通過fork創建的SystemServer進程是一個獨立運行的進程,避免SystemServer進程受到其他進程的影響。
  • 關於SystemServer,後面還會更詳細的介紹

三、系統服務 System Server

Zygote中通過Zygote.forkSystemServer方法創建了System Server進程,然後通過Java的反射機制調用com.android.server.SystemServermain方法來啟動System Server。

Android14的System Server源碼地址

3.1 SystemServer.java

SystemServer.javamain方法調用了自身的run方法,在run方法中啟動了具體的系統服務,代碼如下:

public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    // 初始化系統屬性,時區、語言、環境等,代碼略
    ...
    // 載入本地服務
    System.loadLibrary("android_servers");
    ...
    // 初始化系統上下文
    createSystemContext();
    // 初始化主線模塊
    ActivityThread.initializeMainlineModules();
    ...
    // 創建系統服務管理器
    mSystemServiceManager = new SystemServiceManager(mSystemContext);
    ...

    /* 啟動系統服務 */
    // 啟動引導服務
    startBootstrapServices(t);
    // 啟動核心服務
    startCoreServices(t);
    // 啟動其他服務
    startOtherServices(t);
    // 啟動 APEX 服務
    startApexServices(t);
    ...
}

3.2 System Server啟動的主要服務

以下為System Server啟動的主要服務列表,具體實現可在源碼中查看。

服務名稱 功能說明
Activity Manager Service (AMS) 管理應用程式的生命周期,包括啟動和停止應用、管理任務和活動棧、處理廣播等
Package Manager Service (PMS) 管理應用包的安裝、卸載、更新、許可權分配等
System Config Service 管理系統配置和資源
Power Manager Service 管理設備的電源狀態和電源策略,如休眠、喚醒等
Display Manager Service 管理顯示設備,如屏幕亮度、顯示模式等
User Manager Service 管理用戶賬戶和用戶信息
Battery Service 監控和管理電池狀態和電池使用情況
Vibrator Service 控制設備的振動功能
Sensor Service 管理設備的感測器,如加速度計、陀螺儀等
Window Manager Service (WMS) 管理視窗和顯示內容,包括視窗的創建、刪除、佈局等
Input Manager Service 管理輸入設備,如觸摸屏、鍵盤等
Alarm Manager Service 提供定時任務調度功能
Connectivity Service 管理網路連接,如 Wi-Fi、移動數據等
Network Management Service 管理網路介面和網路連接
Telephony Registry 管理電話和簡訊服務
Input Method Manager Service (IMMS) 管理輸入法框架
Accessibility Manager Service 管理無障礙服務,為有特殊需要的用戶提供輔助功能
Mount Service 管理存儲設備的掛載和卸載
Location Manager Service 管理位置服務,如 GPS 和網路定位
Search Manager Service 管理系統搜索功能
Clipboard Service 管理剪貼板功能
DevicePolicy Manager Service 管理設備的安全策略和企業管理功能
Status Bar Service 管理狀態欄顯示和操作
Wallpaper Manager Service 管理壁紙設置和操作
Media Router Service 管理媒體設備路由

在系統服務全部啟動完成後,就開始啟動系統桌面程式Launcher了。

四、桌面程式Launcher

sequenceDiagram participant SystemServer participant ActivityManagerService participant ActivityTaskManagerService participant RootWindowContainer participant ActivityStartController participant Home SystemServer->>ActivityManagerService: systemReady() ActivityManagerService->>ActivityTaskManagerService: startHomeOnAllDisplays ActivityTaskManagerService->>RootWindowContainer: startHomeOnAllDisplays RootWindowContainer->>ActivityStartController: startHomeActivity ActivityStartController->>Home: Home application (Launcher) is started
  • SystemServer 啟動所有服務: SystemServer類在run方法中調用startOtherServices方法,啟動其他系統服務,包括ActivityManagerService。
  • ActivityManagerService準備系統: ActivityManagerService 在systemReady方法中調用mAtmInternal.startHomeOnAllDisplays方法,開始在所有顯示器上啟動桌面程式。
  • ActivityTaskManagerService啟動: Home Activity:ActivityTaskManagerService 調用RootWindowContainer的startHomeOnAllDisplays方法。
  • RootWindowContainer迴圈所有顯示器: RootWindowContainer 遍歷每個顯示器,並調用startHomeOnDisplay方法。
  • 啟動Home Activity: 在每個顯示器上,通過TaskDisplayArea調用ActivityStartController的startHomeActivity方法,最終調用ActivityManagerService的startActivity方法啟動Home Activity。
  • Home應用啟動: ActivityManagerService處理啟動請求,啟動Home應用的Activity展示桌面界面。

核心代碼流轉:

4.1 SystemServer.java

Android14的System Server源碼地址

private void run() {
    ...
    // 啟動其他服務
    startOtherServices(t);
    ...
}

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    ...
    // 啟動ActivityManagerService
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();
    ...
    // 啟動Launcher
    mActivityManagerService.systemReady(...)
    ...
}

4.2 桌面程式Launcher(Home)的啟動流程

4.2.1 ActivityManagerService.java

Android14的ActivityManagerService源碼地址

public void systemReady(final Runnable goingCallback, TimingsTraceAndSlog t) {
    ...
    // 在所有顯示器上啟動Launcher
    mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
    ...
}
  • 此行代碼最終會調用到ActivityTaskManagerService.javastartHomeOnAllDisplays方法

4.2.2 ActivityTaskManagerService.java

Android14的ActivityTaskManagerService源碼地址

void startHomeOnAllDisplays(int userId, String reason) {
    synchronized (mGlobalLock) {
        // 在所有顯示器上啟動Launcher
        return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
    }
}
  • 此行代碼最終會調用到RootWindowContainer.javastartHomeOnAllDisplays方法

4.2.3 RootWindowContainer.java

Android14的RootWindowContainer源碼地址


boolean startHomeOnAllDisplays(int userId, String reason) {
    boolean homeStarted = false;
    for (int i = getChildCount() - 1; i >= 0; i--) {
        final int displayId = getChildAt(i).mDisplayId;
        // 在每一個顯示器上啟動桌面程式
        homeStarted |= startHomeOnDisplay(userId, reason, displayId);
    }
    return homeStarted;
}

boolean startHomeOnDisplay(int userId, String reason, int displayId) {
    return startHomeOnDisplay(userId, reason, displayId, false, false);
}

boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting, boolean fromHomeKey) {
    ...
    // 調用startHomeOnTaskDisplayArea
    return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
                    result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                            allowInstrumenting, fromHomeKey),false);
}

boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
        boolean allowInstrumenting, boolean fromHomeKey) {
    ...
    Intent homeIntent = mService.getHomeIntent();
    mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
            taskDisplayArea);
    ...
    return true;
}

  • 最終通過Intent和startHomeActivity方法啟動了桌面程式

五、總結

通過上述介紹,從用戶按下電源鍵開始,經過Bootloader啟動、內核啟動、init進程啟動、Zygote進程啟動、SystemServer進程啟動,以及系統應用的啟動,最終進入桌面環境。

每個階段都的核心工作:

  1. Bootloader啟動:初始化硬體並載入內核。

  2. 內核啟動:內核是操作系統的核心,負責管理系統資源和硬體設備。

  3. init進程啟動:init進程通過解析init.rc文件來啟動和配置系統服務。

  4. Zygote進程啟動:Zygote是Android系統的獨有設計,負責創建應用進程。通過預載入資源和共用記憶體,Zygote大大提高了應用啟動的速度和系統資源的利用率。

  5. SystemServer進程啟動:SystemServer進程啟動了大量系統服務,如Activity Manager和Package Manager等,這些服務構成了Android系統的骨幹,管理和協調應用的運行。

  6. 啟動系統應用:Launcher應用的啟動標志著系統啟動的完成。用戶進入桌面,可以開始正常使用設備。

通過深入理解Android的啟動流程,可以更有效地進行系統開發和維護,從而提供更高性能和更穩定的用戶體驗。

如有任何疑問或建議,歡迎留言討論。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 【標題】ODBC驅動類問題定位方法 【需求分類】故障分析 【關鍵字】ODBC 【需求描述】由於我們的ODBC介面目前尚不完善,經常會遇見ODBC介面能力不足導致應用功能無法運行的問題,需要定位手段確定底層是哪個介面報錯 【需求原因分析】方便一線資料庫管理員初步分析故障 【分析方法】 ● 由於我們的O ...
  • MySQL語句優化包括創建合適的索引、使用覆蓋索引、避免不必要的子查詢、通過 EXPLAIN 分析查詢計劃、進行批量操作、減少鎖定範圍、使用預編譯語句和查詢緩存,以顯著提高查詢性能和資料庫運行效率。 ...
  • 一、概述 1.什麼是語言基礎類庫 語言基礎類庫,也稱為標準庫或核心庫,是編程語言提供的一組內置的基礎功能和常用工具的集合。它通常包含了各種數據結構、演算法、輸入輸出處理、字元串處理、日期時間處理、文件操作、網路通信等功能,為開發者提供了便捷的編程介面,以減少開發工作量並提高代碼的可讀性和可維護 ...
  • 前言 請求響應是指客戶端發送請求給伺服器,伺服器接收到請求後返回的響應。響應包含了伺服器處理請求的結果,並將結果返回給客戶端。 頁面調試是指在開發過程中,通過調試工具分析頁面的運行狀況,查找問題和修複錯誤。常用的頁面調試工具包括瀏覽器的開發者工具和調試插件,可以檢查頁面的網路請求、HTML代碼、 ...
  • 在Android啟動過程-萬字長文(Android14)中介紹了Android系統的啟動過程,本篇文章將繼續介紹桌面應用Launcher。 一、Launcher介紹 在Android啟動過程-萬字長文(Android14)中提到Launcher是Android系統啟動後,由SystemServerA ...
  • 前言 Cookie是一種存儲在用戶電腦上的小文本文件,用於在用戶訪問網站時存儲和提取信息。它由網站伺服器發送到用戶的瀏覽器,並存儲在用戶的電腦上。每當用戶訪問該網站時,瀏覽器將發送該Cookie回伺服器,以用於識別用戶和存儲用戶的首選項和其他信息。 Cookie可以用於跟蹤用戶的行為,例如記 ...
  • 如何在 Flutter 中分享視頻到抖音 話不多說,先上效果: 原理 發佈內容至抖音 H5 場景_移動/網站應用_抖音開放平臺 (open-douyin.com) 本教程沒有接入抖音原生 SDK 以及任何第三方插件,使用抖音的 h5 分享介面配合 url_launcher 插件實現跳轉至抖音分享頁面 ...
  • 前言 頁面跳轉是指在瀏覽器中從當前頁面跳轉到另一個頁面的操作。可以通過點擊鏈接、輸入網址、提交表單等方式實現頁面跳轉。 瀏覽記錄是指記錄用戶在瀏覽器中瀏覽過的頁面的歷史記錄。當用戶跳轉到一個新頁面時,該頁面會被加入瀏覽記錄中,用戶可以通過瀏覽器的後退按鈕或者瀏覽歷史列表來查看和訪問之前瀏覽過的頁 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...