Gradle 自定義插件

来源:https://www.cnblogs.com/skymxc/archive/2019/10/24/gradle-custom-plugin.html
-Advertisement-
Play Games

插件被用來封裝構建邏輯和一些通用配置。將可重覆使用的構建邏輯和預設約定封裝到插件里,以便於其他項目使用。 你可以使用你喜歡的語言開發插件,但是最終是要編譯成位元組碼在 JVM 運行的。 Gradle 有兩種插件,腳本插件和二進位插件。 ...


思維導圖

使用版本 5.6.2

插件被用來封裝構建邏輯和一些通用配置。將可重覆使用的構建邏輯和預設約定封裝到插件里,以便於其他項目使用。

你可以使用你喜歡的語言開發插件,但是最終是要編譯成位元組碼在 JVM 運行的。

Gradle 有兩種插件,腳本插件和二進位插件。

關於插件的介紹,可以參考我的另一篇文章 Gradle 插件

這裡講的自定義插件是二進位插件,二進位插件可以打包發佈,有利於分享。

可以在三個地方定義插件

  • 在腳本里
  • 在 buildSrc 下
  • 在單獨的項目里

三個地方的插件的用途目的不同。

在腳本里的插件

其他項目無法使用,只能在本腳本里使用。

在 buildSrc 下

在項目的 buildSrc 目錄下的插件,這個項目里的所有(子)項目都可以使用。

在單獨的項目里

你可以為你的插件創建一個項目,這個項目可以打包發佈 JAR,提供給其他任何項目使用。

創建一個插件

建議使用靜態語言,例如 Java ,Kotlin,開發工具建議使用 IntelliJ IDEA 。

一個插件就是個實現了 Plugin 的類。

當插件被應用到項目時,Gradle 會實例化這個插件並調用 Plugin.apply() 方法,並將這個項目的實例當做參數傳遞進去。插件就可以對這個項目進行各種配置了。

CustomPLugin.java

// 定義一個插件
class CustomPLugin implements Plugin<Project>{

    @Override
    void apply(Project target) {
        // do something
    }
}

前面說到可以在三個地方創建插件,現在來一一實現下。

在腳本里創建一個插件

可以在 build.gradle 腳本里任意地方定義。

build.gradle

// 定義一個插件
class CustomPLugin implements Plugin<Project>{

    @Override
    void apply(Project target) {
      //添加一個任務
     target.task('hello', group: 'util') {
         doLast {
             logger.quiet("Hello Plugin.")
         }
     }
    }
}

//直接在腳本里應用
apply plugin:CustomPLugin

在 gradle 視窗就可以看到應用插件後的添加的任務
添加的任務

雙擊任務或者命令行輸入都可以執行 hello 任務

gradle hello

在項目的 buildSrc 目錄下創建項目

這裡使用的是 Groovy 。

在這個目錄下創建項目會被 Gradle 自動識別的。

結構如下

buildSrc 目錄結構

  1. 在項目根目錄下創建目錄 buildSrc
  2. 在 buildSrc 下按照 java 工程或者 groovy 工程(這取決於你用什麼語言)新建目錄

$projectDir/buildSrc/src/main/groovy

  1. 在 groovy 創建你的包 (可能現在還不能被識別為項目,那就創建目錄),例如 com.github.skymxc
  2. 在包里創建插件,也就是創建一個實現了 Plugin 的類。

這裡做簡單的示範:

在插件里為 jar 任務添加一個操作:生成記錄文件

JarLogPlugin.groovy

/**
 * 輸出 生成記錄到指定文件
 */
class JarLogPlugin implements Plugin<Project> {
    @Override
    void apply(Project target) {
        //增加一個擴展配置用來接收參數
        target.extensions.create("log", LogExtension)

        //添加一個任務
        target.task(type: Jar,group:'util','jarWithLog',{
            doLast {
                //使用配置
                def file = target.log.outputPath;
                if (file==null){
                    file = new File(target.projectDir,"/log/jarlog.txt").getPath()
                }
                println "存儲目錄是 ${file}"
                def content = "${getArchiveFileName().get()}---${getNow()}\n"
                writeFile(file,content)
            }
        })

        //為 jar 任務添加一個 操作,
        target.tasks.jar.doLast {
            println "當前時間是 ${getNow()},打了一個 jar-> ${version}"
            //存到指定文件記錄
            def file = new File(target.projectDir,"/log/jarlog.txt");
            def content = "${version}---${getNow()}\n"
            writeFile(file.getAbsolutePath(),content)
        }
    }

    def String getNow(){
        def dateFormat = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS");
        return dateFormat.format(Calendar.getInstance().getTime());
    }

    def void writeFile(String path,String content){
        def file = new File(path);
        if (!file.exists()){
            if (!file.getParentFile().exists()){
                file.getParentFile().mkdirs();
            }
            file.createNewFile();
        }
        FileWriter writer = new FileWriter(file.getAbsolutePath(),true);
        BufferedWriter bufferedWriter = new BufferedWriter(writer);
        bufferedWriter.write(content);
        bufferedWriter.close();
    }
}

配置 DSL

上面使用了一個擴展來接收參數, 普通的對象就可以,例如

LogExtension.groovy

class LogExtension {
    String outputPath;
}

擴展在這裡就是用來為插件配置 DSL 用的。

//為 項目添加了一個 LogExtension 類型的屬性 名字是 log
project.extensions.create("log", LogExtension)

插件可以使用 DSL 接收參數,在插件或者任務里直接通過 Project 實例訪問即可。

def file = project.log.outputPath;

插件創建完成後,在項目的里就可以使用了。

現在可以使用類名應用插件了。

build.gradle

import com.github.skymxc.JarLogPlugin

apply plugin: JarLogPlugin

插件應用成功後就可以使用 DSL 為插件配置參數。

配置記錄文件地址:

build.gradle

log {
    outputPath rootProject.projectDir.getPath()+"\\record\\jar.txt"
}

為插件創建 ID

  1. 在 main 目錄下創建 resources 文件夾
  2. 在 resources 目錄下創建 META-INF 文件夾
  3. 在 META-INF 目錄下創建 gradle-plugins 文件夾
  4. 在 gradle-plugins 目錄下創建 properties 文件,名字就是你的插件 ID。
  5. 在 id.properties 文件里通過 implementation-class 指向你的實現類。

例如

src / main / resources / META-INF / gradle-plugins / com.github.skymxc.sample.properties

implementation-class= com.github.skymxc.JarLogPlugin

然後就可以使用插件 ID 了

plugins {
    id 'com.github.skymxc.sample'
}

關於插件 id 的規範:

  • 可以包含任何字母數字字元 “ . ”和 “ - ”。
  • 必須至少包含一個 “ . ” 。
  • 一般使用小寫的反向功能變數名稱。(類似包名)
  • 不能以 “ . ” 結尾。
  • 不能包含連續的 “ . ” 。

關於 Groovy 的語法,可以參考 Groovy 語法

在單獨的項目里創建插件

這次仍然是使用 Groovy 語言。

這裡的插件項目其實就是一個 Groovy 項目,當然了你如果使用 Java 語言就是一個 Java 工程。

創建一個工程

創建出來的項目就是這樣子,標準的 Groovy 工程目錄

創建Groovy工程

更改 build.gradle 腳本,配置項目

  1. 應用 maven-publih 插件
  2. 添加 Gradle 和 Groovy 的依賴
  3. 配置上傳任務

最後就是這樣子

plugins {
    id 'groovy'
    id 'maven-publish'
}

group 'com.github.skymxc'
version '1.0.0'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

//使用 groovy 和 gradle 依賴
dependencies {
    compile gradleApi()
    compile localGroovy()
}
publishing {
    repositories {
        maven {
            name 'local'
            url 'file://E:/libs/localMaven'
        }
    }
    publications {
        maven(MavenPublication) {
            groupId = 'com.github.skymxc'
            artifactId = 'plugin'
            version = '1.0.0'
            from components.java
        }
    }

}

創建兩個插件:

一個是上面創建的那個,就不重覆粘貼了。

另一個插件 Greet,配置一個任務,簡單的輸出一句話。

class Greet implements Plugin<Project> {
    @Override
    void apply(Project target) {
        target.extensions.create("hello", Hello)
        target.task("hello") {
            doLast {
                println "message -> ${target.hello.message}"
            }
        }
    }
}

Hello.groovy

class Hello {
    String message
}

插件 ID 的配置是跟上面一樣的。

目錄結構圖

執行 maven-publish 的 publish 任務,將插件發佈到指定倉庫。

gradlew -p plugin publish

發佈成功後的倉庫

發佈成功的圖片

插件創建完成了,也發佈了,下麵就是使用這個插件了。

這裡對插件的使用就簡單介紹一下,詳細的可以查看之前的這篇介紹:Gradle 插件

  1. 在根項目的 build.gradle 配置倉庫,添加依賴
buildscript {
    repositories {
        maven {
            url 'file://E:/libs/localMaven'
        }
    }
    dependencies {
        classpath 'com.github.skymxc:plugin:1.0.2'
    }
}
  1. 應用插件

我分別在兩個 Java 項目里使用了插件:

  • 一個是使用 id 的方式
  • 一個是使用類名的方式

lib_2/ build.gradle 使用 類名的方式

······

apply plugin:'com.github.skymxc.greet'

hello{
    message '使用了 com.github.skymxc.greet 插件'
}

······

lib_1/ build.gradle 使用 id 的方式

plugins {
    id 'java'
    id 'com.github.skymxc.jarlog'
}

······

logConfigure {
    outputPath rootProject.projectDir.getPath()+"\\record\\jar.txt"
}

應用插件後的 gradle 視圖,可以看到已經添加的任務。

gradle 視圖-任務

使用 java-gradle-plugin 開發插件

像上面一樣創建一個項目,不過這次是一個 java 項目,然後應用這個插件。

java-gradle-plugin 可以減少重覆代碼,它自動的應用 java 插件,添加 gradleApi() 依賴。

plugins {
    id 'java-gradle-plugin'
}

使用 gradlePlugin {} 配置塊可以配置開發的每一個插件,不用手動創建對應的屬性文件了。

gradlePlugin {
    plugins {
        greetPlugin {
            id = 'com.github.skymxc.greet'
            implementationClass = 'com.github.skymxc.GreetPlugin'
        }

        jarWithLogPlugin {
            id = 'com.github.skymxc.jar-log'
            implementationClass = 'com.github.skymxc.JarWithLogPlugin'
        }
    }
}

插件會在 jar 文件里自動生成對應的 META-INF 目錄。

配合 maven-publish 可以為每個插件創建對應的發佈任務。

在發佈時也會為每個插件發佈對應的 “插件標記工件” 。

插件標記工件

關於 插件標記工件這裡插一下:

每個 maven 工件都是由三部分標識的

  • groupId
  • artifactId
  • version

平常我們添加依賴的這樣的:

implementation 'groupId:artifactId:version'

而我們的插件是通過 id 應用的,怎麼通過 id 找到對應的工件呢,這就有了“插件標記工件”。
應用插件時會把 id 映射成這樣:plugin.id: plugin.id.gradle.plugin:plugin.version

即:

  • plugin.id
  • plugin.id.gradle.plugin
  • plugin.version

舉個上面的例子:com.github.skymxc.greet 插件對應的工件就是:

com.github.skymxc.greet:com.github.skymxc.greet.gradle.plugin:1.0.0

部分代碼:

plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
}

group 'com.github.skymxc'
version '1.0.0'


gradlePlugin {
    plugins {
        greetPlugin {
            id = 'com.github.skymxc.greet'
            implementationClass = 'com.github.skymxc.GreetPlugin'
        }

        jarWithLogPlugin {
            id = 'com.github.skymxc.jar-log'
            implementationClass = 'com.github.skymxc.JarWithLogPlugin'
        }
    }
}

publishing {
    repositories {
        maven {
            name 'local'
            url 'file://E:/libs/localMaven'
        }
    }
}

maven-publish 的任務

簡單介紹一下 maven-publish 的發佈任務

  • generatePomFileFor${PubName}Publication

    為名字為 PubName 的的發佈創建一個 POM 文件,填充已知的元數據,例如項目名稱,項目版本和依賴項。POM文件的預設位置是build / publications / $ pubName / pom-default.xml。

  • publish${PubName}PublicationTo${RepoName}Repository

    將 PubName 發佈 發佈到名為 RepoName 的倉庫。
    如果倉庫定義沒有明確的名稱,則 RepoName 預設為 “ Maven”。

  • publish${PubName}PublicationToMavenLocal

    將 PubName 發佈以及本地發佈的 POM 文件和其他元數據複製到本地Maven緩存中
    (通常為$USER_HOME / .m2 / repository)。

  • publish

    依賴於:所有的 publish${PubName}PublicationTo${RepoName}Repository 任務
    將所有定義的發佈發佈到所有定義的倉庫的聚合任務。不包括複製到本地 Maven 緩存的任務。

  • publishToMavenLocal

    依賴於:所有的 publish${PubName}PublicationToMavenLocal 任務

    將所有定義的發佈(包括它們的元數據(POM文件等))複製到本地Maven緩存。

這張圖列出了為每個插件生成的對應的任務。
插件對應的發佈任務

執行發佈任務 publish 後可以在對應的倉庫查看

發佈後的倉庫圖1
發佈後的倉庫圖2

發佈插件後的使用

  1. 配置倉庫,這次在 settings.gradle 里配置
pluginManagement {
    repositories {
        maven {
            url 'file://E:/libs/localMaven'
        }
    }
}
  1. 使用插件
plugins {
    id 'java'
    id 'com.github.skymxc.greet' version '1.0.13'
    id 'com.github.skymxc.jar-log' version '1.0.0'
}

應用插件後就可以在 Gradle 的視窗看到對應的任務了。

然後可以根據需要配置 DSL 。

為插件配置 DSL

和插件的交互就是通過 DSL 配置塊進行的。

那怎麼為插件配置 DSL 呢,答案是隨便一個普通類都可以的。

通過 Gradle 的 API 可以將一個普通的類添加為 Project 的擴展,即 Project 的屬性。

舉個例子,插件里的任務需要兩個參數:文件地址,文件名字,就要通過 DSL 配置的方式解決。

JarLogExtension.java 一個普通的類,有兩個屬性,分別是 name , path

package com.github.skymxc.extension;

public class JarLogExtension {
    private String name;
    private String path;

    //省略 setter/getter
}

在插件里將這個類添加為項目的擴展。

public class JarWithLogPlugin implements Plugin<Project> {

    @Override
    public void apply(Project target) {
        //添加擴展
        target.getExtensions().add("jarLog", JarLogExtension.class);
        //創建任務
        target.getTasks().create("jarWithLog", JarWithLogTask.class);
    }
}

應用插件後就可以在腳本里使用這個 DSL 配置了。

build.gradle

······

/**
 * 為 jarWithLog 配置的 DSL
 */
jarLog {
    path getBuildDir().path+"/libs"
    name "record.txt"
}

······

接下來就是在插件或者任務里獲取 DSL 配置的參數了。

可以通過名字或者類型獲取到這個擴展對象。

public class JarWithLogTask extends Jar {

    @TaskAction
    private void writeLog() throws IOException {
      //獲取到配置
        JarLogExtension extension = getProject().getExtensions().getByType(JarLogExtension.class);

        File file = new File(extension.getPath(),extension.getName());
        String s = file.getAbsolutePath();
        String content = getNow()+" --- "+getArchiveFileName().get();
        System.out.println("path --> "+s);
        writeFile(s,content);
    }
}

嵌套 DSL

在我們日常的使用中,嵌套 DSL 很常見,那怎麼實現的呢。

hello {
    message '使用 pluginManagement 管理插件'
    user {
        name 'mxc'
        age 18
    }
}

現在我來實現下:

首先是創建裡面的嵌套對象,需要註意的是要為 DSL 配置對應的方法。

UserData.java

package com.github.skymxc.extension;

/**
 * 為了實踐嵌套 DSL 建的
 */
public class UserData {
    private String name;
    private int age;
    public String getName() {
        return name;
    }

    /**
     * 註意此方法 沒有 set
     * @param name
     */
    public void name(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void age(int age) {
        this.age = age;
    }

}

然後是外層的 DSL 對應的類,因為有 DSL 嵌套,所以要使用閉包

package com.github.skymxc.extension;

import org.gradle.api.Action;

/**
 * 為 HelloTask 創建的擴展,用於接收配置參數
 */
public class HelloExtension {

    private String message;
    private final UserData user = new UserData();


    /**
     * 註意此方法沒有 set
     * @param action
     */
    public void user(Action<? super UserData> action) {
        action.execute(user);
    }

    // 省略其他 getter/setter
}

最後就是添加到項目的擴展了,和前面一樣

public class GreetPlugin implements Plugin<Project> {
    @Override
    public void apply(Project target) {
        target.getExtensions().create("hello", HelloExtension.class);
        target.getTasks().create("hello", HelloTask.class);
    }
}

在任務中的獲取也是一樣的

HelloExtension hello = getProject().getExtensions().getByType(HelloExtension.class);
UserData user = hello.getUser();

集合對象

再看一個 DSL 配置,這種集合嵌套也經常見到,下麵也來簡單實現一下。

fruits {
    apple {
        color '紅色'
    }

    grape {
        color '紫紅色'
    }

    banana {
        color '黃色'
    }

    orange {
        color '屎黃色'
    }

}

這種配置是配合 NamedDomainObjectContainer 實現的,它接收一個類,這個類必須有一個包含 name 參數的構造方法。

Fruit.java

/**
 * 必須有一個 name 屬性,並且有一個 name 參數的構造函數
 */
public class Fruit {

    private String name;
    private String color;

    public Fruit(String name) {
        this.name = name;
    }

    public void color(String color){
        setColor(color);
    }

    //省略 setter/getter
}

配置一個 Factory

FruitFactory.java

import org.gradle.api.NamedDomainObjectFactory;
import org.gradle.internal.reflect.Instantiator;

public class FruitFactory implements NamedDomainObjectFactory<Fruit> {

    private Instantiator instantiator;

    public FruitFactory(Instantiator instantiator) {
        this.instantiator = instantiator;
    }

    @Override
    public Fruit create(String name) {
        return instantiator.newInstance(Fruit.class, name);
    }
}

接著就是創建 NamedDomainObjectContainer 對象並添加到 Project 。

GreetPlugin.java

public class GreetPlugin implements Plugin<Project> {
    @Override
    public void apply(Project target) {

        Instantiator instantiator = ((DefaultGradle)target.getGradle()).getServices().get(Instantiator.class);

        NamedDomainObjectContainer<Fruit> fruits = target.container(Fruit.class,new FruitFactory(instantiator));

        target.getExtensions().add("fruits",fruits);

        target.getTasks().create("printlnFruits", ShowFruitTask.class);
    }
}

現在應用這個插件就可以在腳本里使用上述的 DSL 配置了。

最後是 DSL 配置的接收了

public class ShowFruitTask extends DefaultTask {

    @TaskAction
    public void show(){
        NamedDomainObjectContainer<Fruit> fruits = (NamedDomainObjectContainer<Fruit>) getProject().getExtensions().getByName("fruits");

        fruits.forEach(fruit -> {
            String format = String.format("name: %s , color: %s", fruit.getName(), fruit.getColor());
            getLogger().quiet("fruit : {}",format);
        });
    }
}

關於自定義插件的相關介紹就這些了,更詳細的文檔可以查看 Gradle 用戶手冊

這篇文章的源碼已經放在 github 上:GradlePractice

資料

  • 自定義插件 https://docs.gradle.org/current/userguide/custom_plugins.html
  • 開發輔助插件 https://docs.gradle.org/current/userguide/java_gradle_plugin.html
  • 使用插件 https://docs.gradle.org/current/userguide/plugins.html
  • 發佈 https://docs.gradle.org/current/userguide/publishing_overview.html
  • maven 發佈插件 https://docs.gradle.org/current/userguide/publishing_maven.html
  • Gradle 教程 https://gradle.org/guides/?q=Plugin%20Development
  • Gradle DSL https://blog.csdn.net/zlcjssds/article/details/79229209

End


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

-Advertisement-
Play Games
更多相關文章
  • 1.資料庫:是一個長期存儲在電腦內的、有組織的、有共用的、統一管理的數據集合。它是一個按數據結構來存儲的和管理數據的電腦軟體系統,即資料庫包含兩層含義:保管數據的“倉庫”,以及數據管理的方法和技術。 2.資料庫的特點:實現數據共用,減少數據冗餘;採用特定的數據類型;具有較高的數據獨立性;具有統一 ...
  • 一、安裝MyCat 1.安裝準備環境 1.1 安裝JDK 因為MyCat是java開發的,所以需要java虛擬機環境,在Linux節點中安裝JDK是必須的。 1.2 放開相關埠 在主從節點上都放開對埠3306的訪問,或者直接關閉防火牆。 臨時關閉 service iptables stop se ...
  • 今天從另一個系統往mysql資料庫寫入數據,發現中文變成了????? 檢查資料庫的設置 ,server對應字元集是latinl 調整mysql參數配置,配置文件目錄/etc/mysql/mysql.conf.d/ 添加一行:character-set-server = utf8 然後重啟mysql服 ...
  • 網上些解決方法,就是關閉審計,之前也有同事推薦這樣,下麵就是關閉審計的步驟。 VALUE=DB即審計開啟,改成FALSE即可。 有時還需要將另一個參數修改 ...
  • 在觸發器的“觸發”過程中,有兩個臨時表inserted和deleted發生了作用。這兩個特殊的臨時表inserted和deleted,僅僅在觸發器運行時存在,它們在某一特定時間和某一特定表相關。 CREATE TABLE [dbo].[A] ( [id] INT IDENTITY (1, 1) NO ...
  • 1、首先切換到Oracle用戶 [oracle@oracletest ~]$ cd /u01/app/oracle/product/11.2.0/db_1/bin/ 標紅字體部分資料庫可能不一樣,有的是dbhome_1,以自己實際配置目錄為準 [oracle@oracletest bin]$ vi ...
  • 語法 摘錄 ...
  • 不懂數據分析的 growth hacker 不是好運營。近日我想要統計我家產品 [xue.cn](https://xue.cn/) 用戶的編程自學行為的頻次,且在不給技術開發部門帶來任何新需求的情況下自力更生。那麼,我該如何定義並統計這個數據指標呢? ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...