国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持

這篇具有很好參考價值的文章主要介紹了Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前言

幾個月前 Compose Multiplatform 的 iOS 支持就宣布進(jìn)入了 Alpha 階段,這意味著它已經(jīng)具備了一定的可用性。

在它發(fā)布 Alpha 的時候,我就第一時間嘗鮮,但是只是淺嘗輒止,沒有做過多的探索,最近恰好有點時間,于是我又重新開始學(xué)習(xí) Compose Multiplatform ,并且嘗試移植我已有的項目使其支持 iOS,并且將移植過程整理記錄了下來,即為本文。

這次移植我選擇的依舊是這個使用 Compose 寫的計算器項目 calculator-Compose-MultiPlatform 。本來這次我想著移植一個涉及技術(shù)稍微多一點的項目的比如這個 githubAppByCompose,但是我仔細(xì)研究了一下,畢竟現(xiàn)在 Compose Multiplatform 還處于實驗階段,好多對應(yīng)的功能和庫都還沒有,所以只能選擇移植前者。

對于這個計算器項目,最開始只是一個使用 Compose 實現(xiàn)的純 Android 項目,后來移植到了支持 Android 和 桌面 端,所以其實現(xiàn)在再給它添加上 iOS 支持,也算是補(bǔ)齊了最后一個平臺了,哈哈。

在開始閱讀本文之前,我會假設(shè)你已經(jīng)了解并且知道 Compsoe 的基本使用方法。

為了更好的理解本文,可能需要首先閱讀這兩篇前置文章:

  1. 【譯】快速開始 Compose 跨平臺項目
  2. Kotlin & Compose Multiplatform 跨平臺(Android端、桌面端)開發(fā)實踐之使用 SQLDelight 將數(shù)據(jù)儲存至數(shù)據(jù)庫

前言的最后看一下運行效果:

Android 端:

Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持,ios,kotlin,cocoa,android

ios 端:

Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持,ios,kotlin,cocoa,android

桌面端:

Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持,ios,kotlin,cocoa,android

開始移植

準(zhǔn)備工作

首當(dāng)其沖,我們需要為 iOS 的支持更改編譯配置文件和添加對應(yīng)的平臺特定代碼。

在我的這個項目中,我通過以下幾個步驟為其添加了對 iOS 的支持:

更改共享代碼模塊名稱

把公用代碼模塊由 common 改為 shared ,其實這里不用改也行,只是模板配置文件中寫的 iOS 使用的公用代碼路徑是 shared ,但是直接改模塊名比改配置文件簡單多了,所以我們直接把模塊名改了就好了。

改完之后切記要檢查一下其他模塊引用的名字是否改了,以及注意檢查一下包名是否正確。

添加 native.cocoapods 插件

shared 模塊的 build.gradle.kts 文件的 plugins 增加 native.cocoapods 插件:

plugins {
    kotlin("native.cocoapods")
    // ……
}
添加 cocoapods 配置

shared 模塊的 build.gradle.kts 文件的 kotlin 下增加 cocoapods 相應(yīng)的配置:

kotlin {
	// ……

	iosX64()
    iosArm64()
    iosSimulatorArm64()

    cocoapods {
        version = "1.0.0"
        summary = "Some description for the Shared Module"
        homepage = "Link to the Shared Module homepage"
        ios.deploymentTarget = "14.1"
        podfile = project.file("../iosApp/Podfile")
        framework {
            baseName = "shared"
            isStatic = true
        }
        extraSpecAttributes["resources"] = "['src/commonMain/resources/**', 'src/iosMain/resources/**']"
    }

    // ……
}
配置 iOS 源集

shared 模塊的 build.gradle.kts 文件的 kotlin 中的 sourceSets 下增加 iOS 的源集配置:

kotlin {
	// ……

	sourceSets {
		// ……
		val iosX64Main by getting
        val iosArm64Main by getting
        val iosSimulatorArm64Main by getting
        val iosMain by creating {
            dependsOn(commonMain)
            iosX64Main.dependsOn(this)
            iosArm64Main.dependsOn(this)
            iosSimulatorArm64Main.dependsOn(this)
        }
	}
}
添加其他插件

在項目根目錄下的 settings.gradle.kts 文件的 pluginManagement 中的 plugins 增加插件配置:

pluginManagement {
	//……

	plugins {
		kotlin("jvm").version(extra["kotlin.version"] as String)

		// ……
	}
}
添加 iOS 項目文件

直接把官方模板中的 iosAPP 模塊整個目錄復(fù)制到項目根目錄來。

需要注意的是,其實這個 iosAPP 目錄并不是一個 idea 模塊,而是一個 Xcode 項目。但是目前暫時不需要知道這是什么,只需要把相應(yīng)的文件整個復(fù)制到自己項目中就行了。

然后把官方模版中的 sahred -> iosMain 文件夾整個復(fù)制到 我們項目的 sahred 模塊根目錄中。

適配代碼

在這一節(jié)中,主要需要適配的有兩種類型的代碼:

一是之前就已經(jīng)在項目中聲明了的 expect 函數(shù),需要為 iOS 也加上對應(yīng)的 actual 函數(shù)。

二是需要將原本使用到的 jvm 相關(guān)或者說所有使用 java 實現(xiàn)的庫和相關(guān)代碼都需要重新編寫或適配。

因為不同于 Android 和 桌面端,kotlin 最終會被編譯成 jvm 代碼,在 iOS 端,kotlin 會編譯成 native 代碼,所以所有使用 java 寫的代碼將無法再使用。

這也就是我前言中說的為啥不選擇移植更復(fù)雜的項目的原因,就是因為我在其中引用了大量的使用 java 編寫的第三方庫,而這些第三方庫又暫時沒有使用純 kotlin 實現(xiàn)的可用替代品。

下面,我們就開始適配代碼。

更改入口

為了保證三端界面一致,我們將原本的UI界面再額外的抽出一個統(tǒng)一的入口函數(shù) APP(),將其放到 shared 模塊的 common 包下:

@Composable
fun APP(
    standardChannelTop: Channel<StandardAction>? = null,
    programmerChannelTop: Channel<ProgrammerAction>? = null,
) {
    val homeChannel = remember { Channel<HomeAction>() }
    val homeFlow = remember(homeChannel) { homeChannel.consumeAsFlow() }
    val homeState = homePresenter(homeFlow)


    val standardChannel = standardChannelTop ?: remember { Channel() }
    val standardFlow = remember(standardChannel) { standardChannel.consumeAsFlow() }
    val standardState = standardPresenter(standardFlow)

    val programmerChannel = programmerChannelTop ?: remember { Channel() }
    val programmerFlow = remember(programmerChannel) { programmerChannel.consumeAsFlow() }
    val programmerState = programmerPresenter(programmerFlow)

    CalculatorComposeTheme {
        val backgroundColor = MaterialTheme.colors.background

        Surface(
            modifier = Modifier.fillMaxSize(),
            color = backgroundColor
        ) {
            HomeScreen(
                homeChannel,
                homeState,
                standardChannel,
                standardState,
                programmerChannel,
                programmerState
            )
        }
    }
}

并且,因為不同平臺需要差異化實現(xiàn)部分功能,以及目前我還沒找到一個好使的支持跨平臺的依賴注入庫,所以我索性將所有 控制(channel) 和 狀態(tài)(state) 都提升到了最頂層,作為參數(shù)傳遞給下面的 Compose 函數(shù)。

然后,更改三端各自的入口函數(shù):

Android (android 模塊下的 MainActivity.kt 文件)
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            APP()
        }
    }
}
desktop (dektop 模塊下的 Main.kt 文件)
fun main() = application {

    val state = if (Config.boardType.value == KeyboardTypeStandard) {
        rememberWindowState(size = defaultWindowSize, position = defaultWindowPosition)
    } else {
        rememberWindowState(size = landWindowSize, position = defaultWindowPosition)
    }

    val standardChannel = remember { Channel<StandardAction>() }
    val programmerChannel = remember { Channel<ProgrammerAction>() }


    Window(
        onCloseRequest = ::exitApplication,
        state = state,
        title = Text.AppName,
        icon = painterResource("icon.png"),
        alwaysOnTop = Config.isFloat.value,
        onKeyEvent = {
            if (isKeyTyped(it)) {
                val btnIndex = asciiCode2BtnIndex(it.utf16CodePoint)
                if (btnIndex != -1) {
                    if (Config.boardType.value == KeyboardTypeStandard) {
                        standardChannel.trySend(StandardAction.ClickBtn(btnIndex))
                    }
                    else {
                        programmerChannel.trySend(ProgrammerAction.ClickBtn(btnIndex))
                    }
                }
            }
            true
        }
    ) {
        APP()
    }
}

iOS ( shared模塊 下的 main.ios.kt 文件)
fun MainViewController() = ComposeUIViewController {
    APP()
}

注意,不同于其他平臺,iOS 的入口函數(shù)在 shared模塊 中。

當(dāng)然,你要是想直接改 iosAPP 目錄中的代碼,那也不是不行,只是對于我們安卓開發(fā)來說,還是直接改 shared 更方便點。

實現(xiàn) iOS 的 平臺代碼

之前我們的項目中有幾個地方的實現(xiàn)依賴于平臺,所以寫了一些 expect 函數(shù),現(xiàn)在我們需要給 iOS 實現(xiàn)對應(yīng)的 actual 函數(shù)。

首先在 shared 模塊的 iosMain 包中創(chuàng)建一個包路徑,保持和 commonMainexpect 函數(shù)包一致:

Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持,ios,kotlin,cocoa,android

注意: 包路徑一定要一致,不然會編譯失敗,我就在這里踩了坑,沒注意到包名不一樣, debug 了好久。

這個項目中的平臺差異函數(shù)主要有四個:

控制振動

因為我對 iOS 一竅不通,所以不知道怎么寫,索性直接留空了:

actual fun vibrateOnClick() {

}

actual fun vibrateOnError() {

}

actual fun vibrateOnClear() {

}

actual fun vibrateOnEqual() {

}

控制屏幕旋轉(zhuǎn)和顯示小窗

這里同上,不知道怎么寫,直接留空:

actual fun showFloatWindows() {

}

actual fun changeKeyBoardType(changeTo: Int) {

}

數(shù)據(jù)庫(sqldelight)

actual fun createDriver(): SqlDriver {
    return NativeSqliteDriver(HistoryDatabase.Schema, "history.db")
}

關(guān)于使用 sqldelight 的詳細(xì)介紹,可以看前言中的前置文章了解。

其實這里這樣寫是編譯不通過的,因為還沒加 sqldelight 依賴,下面介紹一下怎么加依賴,這里又是一個大坑。

給 iOS 添加 sqldelight 支持

首先,在 shared 模塊下的 build.gradle.kts 文件中的 kotlin -> sourceSets -> iosMain 添加 sqldelight 的 驅(qū)動依賴:

kotlin {
	// ……

	sourceSets {
		// ……

	    val iosMain by creating {
	    	// ……

	        dependencies {
	            implementation("app.cash.sqldelight:native-driver:2.0.0")
	        }
        }
	}
}

此時如果你直接 sync gradle 后編譯運行,大概率會報錯:

Undefined symbols for architecture arm64:
"_sqlite3_bind_text16", referenced from:
_SQLiter_SQLiteStatement_nativeBindString in app(combined.o)
"_sqlite3_bind_int64", referenced from:
_SQLiter_SQLiteStatement_nativeBindLong in app(combined.o)
"_sqlite3_last_insert_rowid", referenced from:
_SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in app(combined.o)
"_sqlite3_reset", referenced from:
_SQLiter_SQLiteConnection_nativeResetStatement in app(combined.o)
"_sqlite3_changes", referenced from:
_SQLiter_SQLiteStatement_nativeExecuteForChangedRowCount in app(combined.o)
_SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in app(combined.o)
"_sqlite3_open_v2", referenced from:
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_db_config", referenced from:
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_busy_timeout", referenced from:
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_trace", referenced from:
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_bind_parameter_index", referenced from:
_SQLiter_SQLiteConnection_nativeBindParameterIndex in app(combined.o)
"_sqlite3_column_bytes", referenced from:
_SQLiter_SQLiteConnection_nativeColumnGetString in app(combined.o)
_SQLiter_SQLiteConnection_nativeColumnGetBlob in app(combined.o)
"_sqlite3_finalize", referenced from:
_SQLiter_SQLiteStatement_nativeFinalizeStatement in app(combined.o)
"_sqlite3_column_text", referenced from:
_SQLiter_SQLiteConnection_nativeColumnGetString in app(combined.o)
"_sqlite3_column_name", referenced from:
_SQLiter_SQLiteConnection_nativeColumnName in app(combined.o)
"_sqlite3_bind_double", referenced from:
_SQLiter_SQLiteStatement_nativeBindDouble in app(combined.o)
"_sqlite3_profile", referenced from:
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_close", referenced from:
_SQLiter_SQLiteConnection_nativeClose in app(combined.o)
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_prepare16_v2", referenced from:
_SQLiter_SQLiteConnection_nativePrepareStatement in app(combined.o)
"_sqlite3_column_type", referenced from:
_SQLiter_SQLiteConnection_nativeColumnIsNull in app(combined.o)
_SQLiter_SQLiteConnection_nativeColumnType in app(combined.o)
"_sqlite3_column_count", referenced from:
_SQLiter_SQLiteConnection_nativeColumnCount in app(combined.o)
"_sqlite3_bind_blob", referenced from:
_SQLiter_SQLiteStatement_nativeBindBlob in app(combined.o)
"_sqlite3_db_readonly", referenced from:
_SQLiter_SQLiteConnection_nativeOpen in app(combined.o)
"_sqlite3_column_int64", referenced from:
_SQLiter_SQLiteConnection_nativeColumnGetLong in app(combined.o)
"_sqlite3_bind_null", referenced from:
_SQLiter_SQLiteStatement_nativeBindNull in app(combined.o)
"_sqlite3_extended_errcode", referenced from:
android::throw_sqlite3_exception(sqlite3*) in app(combined.o)
android::throw_sqlite3_exception(sqlite3*, char const*) in app(combined.o)
"_sqlite3_column_double", referenced from:
_SQLiter_SQLiteConnection_nativeColumnGetDouble in app(combined.o)
"_sqlite3_column_blob", referenced from:
_SQLiter_SQLiteConnection_nativeColumnGetBlob in app(combined.o)
"_sqlite3_step", referenced from:
_SQLiter_SQLiteConnection_nativeStep in app(combined.o)
_SQLiter_SQLiteStatement_nativeExecute in app(combined.o)
_SQLiter_SQLiteStatement_nativeExecuteForChangedRowCount in app(combined.o)
_SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in app(combined.o)
"_sqlite3_clear_bindings", referenced from:
_SQLiter_SQLiteConnection_nativeClearBindings in app(combined.o)
"_sqlite3_errmsg", referenced from:
android::throw_sqlite3_exception(sqlite3*) in app(combined.o)
android::throw_sqlite3_exception(sqlite3*, char const*) in app(combined.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

這是因為 ios 的 Xcode 項目沒有添加 sqlite 依賴,我們還需要為 ios 單獨添加 sqlite 依賴。

ios 使用的是 cocoapods 進(jìn)行依賴管理,我們需要使用 pod 添加依賴。

我們有兩種選擇:

一是在 shared 模塊的 build.gradle.kts 中相應(yīng)的位置添加 pod 依賴配置。

二是直接在 pod 配置文件中添加。

這里我們就選擇直接改 pod 的配置文件。

打開項目根目錄下的 iosAPP 目錄中的 Podfile 文件,在其中添加 sqlite3 依賴:

target 'iosApp' do
  # ……
	
  pod 'sqlite3', '~> 3.42.0'

  # ……

end

添加完記得需要 sync 一下 gradle。

此時再編譯運行,大概率還是會報錯:

ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a

不用擔(dān)心,再在剛才的配置文件中加上這么一段:

# iosApp's podfile
post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.1'
        end
    end
end

此時應(yīng)該就不會有任何問題了。

適配 jvm 相關(guān)代碼

正如我們在上一節(jié)所說,由于 iOS 使用 native 代碼,所以項目中就不能再使用 java 代碼,包括引用的第三方庫也是。

在我這個項目中涉及到需要適配的主要有兩個地方。一個是進(jìn)制轉(zhuǎn)換時使用到了 java 的 Long 類的方法;另一個就是運算時使用的是 BigInteger BigDecimal 。

進(jìn)制轉(zhuǎn)換

之前的代碼使用的是 java 中的 java.lang.Long.toXXXString 。

這里適配起來其實很簡單,要么自己使用 kotlin 實現(xiàn)一個進(jìn)制轉(zhuǎn)換工具類,要么就像我一樣,直接把 Long.java 中需要的部分 CV 一下,然后使用 Android studio 的 java 轉(zhuǎn) kotlin 一鍵轉(zhuǎn)換就行了。

下面就是我轉(zhuǎn)好的工具類:

package com.equationl.common.utils

import kotlin.math.max

object LongUtil {
    val digits = charArrayOf(
        '0', '1', '2', '3', '4', '5',
        '6', '7', '8', '9', 'a', 'b',
        'c', 'd', 'e', 'f', 'g', 'h',
        'i', 'j', 'k', 'l', 'm', 'n',
        'o', 'p', 'q', 'r', 's', 't',
        'u', 'v', 'w', 'x', 'y', 'z'
    )



    fun toBinaryString(i: Long): String {
        return toUnsignedString0(i, 1)
    }

    fun toHexString(i: Long): String {
        return toUnsignedString0(i, 4)
    }

    fun toOctalString(i: Long): String {
        return toUnsignedString0(i, 3)
    }

    fun toUnsignedString0(`val`: Long, shift: Int): String {
        // assert shift > 0 && shift <=5 : "Illegal shift value";
        val mag: Int = Long.SIZE_BITS - numberOfLeadingZeros(`val`)
        val chars: Int = max((mag + (shift - 1)) / shift, 1)
        //if (COMPACT_STRINGS) {
            val buf = ByteArray(chars)
            formatUnsignedLong0(`val`, shift, buf, 0, chars)
            return buf.map { it.toInt().toChar() }.toCharArray().concatToString()
//        } else {
//            val buf = ByteArray(chars * 2)
//            java.lang.Long.formatUnsignedLong0UTF16(`val`, shift, buf, 0, chars)
//            return String(buf, UTF16)
//        }
    }

    private fun formatUnsignedLong0(
        `val`: Long,
        shift: Int,
        buf: ByteArray,
        offset: Int,
        len: Int
    ) {
        var `val` = `val`
        var charPos = offset + len
        val radix = 1 shl shift
        val mask = radix - 1
        do {
            buf[--charPos] = digits[`val`.toInt() and mask].code.toByte()
            `val` = `val` ushr shift
        } while (charPos > offset)
    }

    fun numberOfLeadingZeros(i: Long): Int {
        val x = (i ushr 32).toInt()
        return if (x == 0) 32 + numberOfLeadingZeros(i.toInt()) else numberOfLeadingZeros(
            x
        )
    }

    fun numberOfLeadingZeros(i: Int): Int {
        // HD, Count leading 0's
        var i = i
        if (i <= 0) return if (i == 0) 32 else 0
        var n = 31
        if (i >= 1 shl 16) {
            n -= 16
            i = i ushr 16
        }
        if (i >= 1 shl 8) {
            n -= 8
            i = i ushr 8
        }
        if (i >= 1 shl 4) {
            n -= 4
            i = i ushr 4
        }
        if (i >= 1 shl 2) {
            n -= 2
            i = i ushr 2
        }
        return n - (i ushr 1)
    }
}

然后更改我們的代碼中使用到的地方即可,例如:

Long.toBinaryString 改為 LongUtil.toBinaryString(long) 。

記得把導(dǎo)入的包也改了:

import java.lang.Long 改為 import com.equationl.common.utils.LongUtil

當(dāng)然,如果你的工具類直接取名叫 Long 的話,那么調(diào)用代碼就不用改了,改導(dǎo)入包就行了。

BigInteger 和 BigDecimal

接下來就是 BigInteger 和 BigInteger,同樣的思路,我們可以選擇自己使用 kotlin 寫一個功能相同的工具類,但是顯然,這兩個類可不同于進(jìn)制轉(zhuǎn)換,它涉及到的代碼量可要大多了。

好在已經(jīng)有大神寫好了純 kotlin 的支持跨平臺的 BigInteger 和 BigDecimal: kotlin-multiplatform-bignum 。我們只需要簡單的引用它就可以了。

shared 模塊下的 build.gradle.kts 文件中的 kotlin -> sourceSets -> commonMain -> dependencies 添加依賴

kotlin {
	sourceSets {
		val commonMain by getting {
			dependencies {
				implementation("com.ionspin.kotlin:bignum:0.3.8")
			}
		}
	}
}

sync gradle 后,依次修改項目中使用到 BigInteger 和 BigDecimal 地方的代碼即可。

需要注意的是,這個庫的 API 和 java 的 BigInteger 以及 BigDecimal 并非完全一致,因此需要我們逐個檢查并修改。

例如,在 java 的 BigDecimal 中,除法的 API 是: divide(BigDecimal divisor, int scale, RoundingMode roundingMode)

而在這個庫中則變?yōu)榱?divide(other: BigDecimal, decimalMode: DecimalMode? = null)

除此之外,還有一些小地方的代碼可能引用的是 java 代碼,這里就不再贅述了,按照上述兩種思路逐個適配即可。

總結(jié)

自此,我們的項目就完全移植到了完整形態(tài)的 Compose Multiplatform 中了!現(xiàn)在它已經(jīng)完全支持 Android、iOS 和 desktop 了!

不知道你們有沒有發(fā)現(xiàn),在全文中,我?guī)缀醵际窃谡f怎么適配和移植邏輯代碼,并沒有說到有關(guān) UI 的代碼。

哈哈,不是因為我忘記說了,而是因為 Compose Multiplatform 代碼真的做到了一套代碼,多平臺通用。新增加 iOS 支持完全不用動 UI 部分的代碼。

完整項目代碼: calculator-Compose-MultiPlatform文章來源地址http://www.zghlxwxcb.cn/news/detail-722761.html

到了這里,關(guān)于Kotlin & Compose Multiplatform 跨平臺開發(fā)實踐之加入 iOS 支持的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 最佳實踐-使用Github Actions來構(gòu)建跨平臺容器鏡像

    公眾號「架構(gòu)成長指南」,專注于生產(chǎn)實踐、云原生、分布式系統(tǒng)、大數(shù)據(jù)技術(shù)分享。 最近在寫K8s的相關(guān)系列文章,因為有涉及到鏡像構(gòu)建,發(fā)現(xiàn)在Mac m1的Arm架構(gòu)下構(gòu)建的部分鏡像,沒法在X86架構(gòu)下使用,不兼容。 嘗試網(wǎng)上介紹的各種方式,都已失敗告終,效果如下: 最終

    2024年02月05日
    瀏覽(25)
  • 跨平臺開發(fā)技術(shù)

    跨平臺開發(fā)技術(shù)

    個人搜集資料并總結(jié)了一些跨平臺開發(fā)技術(shù),如有不足歡迎指正。 1.簡介 QT是一個跨平臺的C++圖形用戶界面應(yīng)用程序框架。它為應(yīng)用程序開發(fā)者提供建立藝術(shù)級圖形所需的所有功能。它是完全面向?qū)ο蟮?,容易擴(kuò)展,并且允許真正的組件編程。 2.優(yōu)勢 使用Qt開發(fā)的程序可以運

    2024年02月08日
    瀏覽(91)
  • C語言跨平臺游戲開發(fā)

    通常我們認(rèn)為, 純C (即不使用C艸)很難實現(xiàn)跨平臺的游戲。這是由于它支持的圖形庫非常少,一般需要調(diào)用系統(tǒng)句柄才能進(jìn)行圖形化。但是很顯然這是一個及其費時費力還容易出錯的方式。所以,在這篇文章里,我希望給大家介紹一些 比較 輕松的制作C語言的跨平臺游戲的

    2023年04月15日
    瀏覽(19)
  • 跨平臺開發(fā)方案的三個時代

    跨平臺開發(fā)方案的三個時代

    跨平臺開發(fā)從本質(zhì)上講是為了增加業(yè)務(wù)代碼的復(fù)用率,減少因為要適配多個平臺帶來的工作量,從而降低開發(fā)成本。在提高業(yè)務(wù)專注度的同時,能夠為用戶提供一致的用戶體驗,實現(xiàn)“多快好省”的效果。 跨平臺是跨哪些平臺?怎么樣的跨平臺邏輯?從當(dāng)前的實際情況來看,

    2024年02月09日
    瀏覽(21)
  • 前端移動端開發(fā)分類及跨平臺開發(fā)框架簡述

    前端移動端開發(fā)分類及跨平臺開發(fā)框架簡述

    前端移動端主流分為以下三種:Native App ,Hybrid App ,Web App 優(yōu)點: (1)用戶體驗好 (2)性能穩(wěn)定 (3)操作速度快 (4)能夠訪問本地資源(通訊錄,相冊) (5)能夠設(shè)計出色的動效,轉(zhuǎn)場 (6)擁有系統(tǒng)級別的貼心通知或提醒 (7)用戶留存率高 缺點: (1)開發(fā)成本高

    2024年02月04日
    瀏覽(98)
  • 我想開發(fā)一款跨平臺桌面軟件,請告訴我qt、electron、tauri、pyqt、flutter分別適合開發(fā)哪些跨平臺桌面

    我想開發(fā)一款跨平臺桌面軟件,請告訴我qt、electron、tauri、pyqt、flutter分別適合開發(fā)哪些跨平臺桌面

    不同的跨平臺桌面開發(fā)工具適用于不同的應(yīng)用場景和開發(fā)者需求。以下是關(guān)于 Qt、Electron、Tauri、PyQt、Flutter 的簡要說明,以幫助你更好地選擇適合你項目的工具: Qt: 適用場景: Qt 是一個強(qiáng)大的 C++ 框架,適用于開發(fā)需要高性能和原生外觀的桌面應(yīng)用。它具有廣泛的平臺支持

    2024年02月22日
    瀏覽(95)
  • C#使用xamarin進(jìn)行跨平臺開發(fā)

    使用 Xamarin 進(jìn)行跨平臺開發(fā)可以使用 C# 和 .NET 平臺來開發(fā)移動應(yīng)用程序,同時將代碼在多個主要移動操作系統(tǒng)上運行,包括 Android 和 iOS。以下是在 C# 中使用 Xamarin 進(jìn)行跨平臺開發(fā)的一般步驟: 安裝 Xamarin : 在開始之前,你需要安裝 Xamarin 開發(fā)環(huán)境。你可以選擇安裝 Visual

    2024年02月11日
    瀏覽(96)
  • C++庫封裝mongodb(跨平臺開發(fā))

    目錄 1.開發(fā)環(huán)境準(zhǔn)備 2.編譯mongo-c-driver (linux環(huán)境) ?3.編譯mongo-c-driver (windows環(huán)境)

    2024年02月11日
    瀏覽(28)
  • 前端跨平臺開發(fā)框架:簡化多端開發(fā)的利器

    ?? 前端開發(fā)工程師、技術(shù)日更博主、已過CET6 ?? 阿珊和她的貓_ CSDN 博客專家、23年度博客之星前端領(lǐng)域TOP1 ?? ???高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》 ?? 藍(lán)橋云課 簽約作者、上架課程《Vue.js 和 Egg.js 開發(fā)企業(yè)級健康管理項目》、《帶你

    2024年03月17日
    瀏覽(47)
  • Flutter:跨平臺移動應(yīng)用開發(fā)的未來

    Flutter:跨平臺移動應(yīng)用開發(fā)的未來

    Flutter的背景和概述 Flutter是由Google開發(fā)的一個開源UI工具包,用于構(gòu)建漂亮、快速且高度可定制的移動應(yīng)用程序。它于2017年首次發(fā)布,并迅速引起了開發(fā)者們的關(guān)注。Flutter采用了一種全新的方法來構(gòu)建用戶界面,通過使用自繪UI技術(shù),可以實現(xiàn)高性能的跨平臺應(yīng)用開發(fā)。 Fl

    2024年01月22日
    瀏覽(91)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包