Android-API調用學習總結

一、Postman檢查API接口是否支持

? ? ? ? 1.“HTTP Request” 來創建一個新的請求。——請求構建界面,這是你進行所有 API 調用的地方。
? ? ? ? 2.設置請求方法和 URL:

????????選擇請求方法: 在 URL 輸入框左側,有一個下拉菜單。點擊它,選擇你想要測試的 HTTP 請求方法(例如:GET, POST, PUT, DELETE 等)。

GET:獲取數據

????????區別: GET 請求通常用于從服務器獲取或讀取數據。它不應該對服務器上的數據產生任何修改。因此,GET 被認為是“安全”和“冪等”的。安全(Safe): 發送 GET 請求不會改變服務器的狀態或數據。冪等(Idempotent): 多次發送相同的 GET 請求,結果都將是相同的,不會產生副作用。

工作原理:

  1. 客戶端(瀏覽器、Postman 等)將請求參數附加在 URL 的查詢字符串中(.../users?id=123)。

  2. 請求頭(Headers)和 URL 組成請求信息發送給服務器。

  3. 服務器根據 URL 和參數定位資源,然后返回資源的表示(通常是 JSON 或 XML 格式)給客戶端。

  4. 因為參數在 URL 中,所以 GET 請求的長度有限制,并且不適合傳輸敏感數據。

POST:創建數據

????????區別: POST 請求用于向服務器提交數據以創建新資源。它會改變服務器的狀態,因此不安全非冪等不安全(Unsafe): 發送 POST 請求可能會改變服務器上的數據。非冪等(Non-Idempotent): 多次發送相同的 POST 請求,可能會導致創建多個重復的資源。例如,多次提交一個訂單表單,可能會創建多份訂單。

工作原理:

  1. 客戶端將請求數據放在請求的**主體(Body)**中發送給服務器。請求體可以包含任何類型的數據,如表單數據、JSON、文件等,因此沒有長度限制。

  2. 服務器接收到請求體中的數據后,處理并創建一個新資源。

  3. 通常,服務器在創建成功后會返回一個 201 Created 的狀態碼,并在響應頭中包含新創建資源的 URL。

PUT:更新數據

????????區別: PUT 請求用于更新或替換服務器上已存在的資源。它與 POST 的主要區別在于冪等性不安全(Unsafe): 會修改服務器上的數據。冪等(Idempotent): 多次發送相同的 PUT 請求,結果與發送一次請求是相同的。如果資源不存在,它通常會創建新資源。

工作原理:

  1. 客戶端通過 URL 明確指定要更新的資源(例如:.../users/123)。

  2. 客戶端將完整的、更新后的資源表示放在請求主體中發送給服務器。如果請求中缺少某個字段,服務器通常會將其刪除或設為空。

  3. 服務器用請求主體中的數據完全替換 URL 指定的資源。

  4. 成功后,服務器通常返回 200 OK204 No Content 狀態碼。

DELETE:刪除數據

????????區別: DELETE 請求用于刪除服務器上指定的資源不安全(Unsafe): 會改變服務器上的數據。冪等(Idempotent): 多次發送相同的 DELETE 請求,結果與發送一次是相同的。第一次請求會刪除資源,后續請求會返回“資源未找到”或成功,但不會產生新的副作用。

工作原理:

  1. 客戶端通過 URL 明確指定要刪除的資源(例如:.../users/123)。

  2. 服務器收到請求后,會刪除該資源。

  3. 成功后,服務器通常返回 200 OK204 No Content 狀態碼。

????????輸入請求 URL: 在請求方法右側的輸入框中,輸入你要測試的 API 接口的完整 URL。例如:https://api.example.com/users

? ? ? ?3. API 接口通常需要一些參數來完成請求。Postman 提供了多種添加參數的方式,根據 API 的要求來選擇。
  1. URL 參數(Params):

    • 如果你的 API 需要在 URL 中傳遞參數(例如:https://api.example.com/users?id=123),你可以在 URL 輸入框下方找到 "Params" 選項卡。

    • 點擊這個選項卡,Postman 會自動將你在 URL 中輸入的參數拆分出來,或者你也可以在這里手動添加鍵值對(Key-Value pairs)。

  2. 請求頭(Headers):

    • 許多 API 接口需要通過請求頭來傳遞信息,比如 認證令牌(Authorization Token)內容類型(Content-Type)

    • 點擊 "Headers" 選項卡,添加你需要的鍵值對。例如:

      • Key: Authorization

      • Value: Bearer your_token_here

二、Android中APIService調用

1.Retrofit 接口定義,用于調用 Spotify 的 RESTful API。通過接口interface調用

2.Retrofit的注釋詳解

????????它們告訴 Retrofit 如何構建 HTTP 請求。例如下列

第一個示例

@GET("v1/me/top/{type}")

@GET:指的是HTTP請求方式為GET

其中的"v1/me/top/{type}":這是一個相對 URL 路徑。Retrofit 會將其與您在構建 Retrofit 實例時提供的基本 URL(例如 https://api.spotify.com/)拼接起來,形成完整的請求地址。

object RetrofitClient {private const val BASE_URL = "https://api.spotify.com/"// 配置 OkHttpClientprivate val okHttpClient = OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {level = HttpLoggingInterceptor.Level.BODY // 打印所有請求和響應的詳細日志}).connectTimeout(30, TimeUnit.SECONDS) // 連接超時.readTimeout(30, TimeUnit.SECONDS)    // 讀取超時.writeTimeout(30, TimeUnit.SECONDS)   // 寫入超時.build()// 創建 Retrofit 實例val spotifyApiService: SpotifyApiService by lazy {Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient).addConverterFactory(GsonConverterFactory.create()).build().create(SpotifyApiService::class.java)}
}

?第二個示例

suspend fun getTopItems(@Header("Authorization") authorization: String, // Access Token@Path("type") type: String, // "artists" 或 "tracks"@Query("time_range") timeRange: String? = null, // long_term, medium_term, short_term@Query("limit") limit: Int? = null, // 數量@Query("offset") offset: Int? = null // 偏移量): TopItemsResponse<Item> // 使用泛型 <*> 因為 items 可以是 Artist 或 Track

????????@Header:用于在 HTTP 請求頭中添加鍵值對。"Authorization":這是 HTTP 請求頭的鍵名,通常用于傳遞身份驗證令牌。authorization: String:這個參數的值會通過 @Header 注解,被添加到請求頭中。您需要在這里傳入您的 Access Token,例如 "Bearer BQC..."

????????@Path:用于將方法參數的值插入到 URL 路徑中。在這里,它會將方法參數 type 的值(如 "artists")替換掉 URL 路徑中的 {type} 占位符。

????????@Query:用于向 URL 中添加查詢參數(Query Parameters)。"time_range":這是查詢參數的鍵名。Retrofit 會將其與方法參數的值組合成 ?time_range=... 這樣的形式。

????????suspend fun ... : TopItemsResponse<Item>

????????TopItemsResponse:這是一個自定義的數據類(或接口),用于映射 API 返回的 JSON 響應。Retrofit 會使用一個 JSON 轉換器(如 Moshi 或 Gson)將 JSON 數據自動解析成這個對象。

????????<Item>:這是一個泛型(Generic)。在 getTopItems 方法中,使用了泛型 <Item>,因為 items 列表中的對象可以是 Artist,也可以是 Track。這種設計使得代碼更加靈活,但需要您在調用時指定具體的類型,或者在運行時進行類型檢查。

????????在 getTopArtistItems 方法中,更具體地指定了 <Artist>,這意味著這個方法專門用于獲取藝術家數據,返回的 items 列表將是 Artist 對象的列表。這是一種更類型安全的做法,通常更推薦。

、Android中API的數據模型創建

@Serializable 的作用是告訴 Kotlinx.serialization 庫,這個數據類需要被序列化和反序列化

  • 序列化 (Serialization):將 Kotlin 對象(例如 TopItemsResponse 的實例)轉換成另一種格式,比如 JSON 字符串。

  • 反序列化 (Deserialization):將 JSON 字符串轉換回 Kotlin 對象。

????????在 Android 開發中,當您從網絡 API 獲取數據時,通常會得到一個 JSON 格式的字符串。為了在您的應用中方便地使用這些數據,您需要將其轉換為一個 Kotlin 對象,這就是反序列化。當您需要向服務器發送數據時(例如 POST 或 PUT 請求),您需要將 Kotlin 對象轉換為 JSON 字符串,這就是序列化

@Serializable
data class TopItemsResponse<T>(val href: String,val limit: Int,val next: String?,val offset: Int,val previous: String?,val total: Int,val items: List<T> // 泛型 T 可以是 Artist 或 Track
)

@Serializable 解決了以下幾個核心問題:

  1. 自動化數據轉換

    • 在沒有 @Serializable 的情況下,您需要手動編寫代碼來解析 JSON 字符串,并為每個字段賦值。這非常繁瑣且容易出錯。

    • 有了 @Serializable,您只需要定義好數據類,Kotlinx.serialization 庫會為您自動完成 JSON 和 Kotlin 對象之間的轉換。

  2. 與 Retrofit 的無縫集成

    • 當您使用 Retrofit 調用 API 時,您可以將 TopItemsResponse 作為返回類型。Retrofit 會檢測到您使用了 Kotlinx.serialization 的 @Serializable 注解,并自動使用其 JSON 轉換器來解析 API 返回的 JSON 數據,并將其填充到 TopItemsResponse 對象中。

    • 它使得您的網絡請求代碼非常簡潔,您不需要關心底層的數據解析細節。

  3. 類型安全和泛型支持

    • 在您的 TopItemsResponse<T> 例子中,items: List<T> 是一個泛型列表。

    • Kotlinx.serialization 能夠正確處理這個泛型,它會根據您在調用時提供的具體類型(例如 ArtistTrack)來自動解析 items 列表中的每個對象。

之后的對象參數比如Image,Item都用data class數據類來生成模型

四、Android中MediaSession數據調用

1.MediaItemUtils 工具類

????????它主要用于將不同類型的數據模型(如 AlbumTrackArtist 等)轉換為 androidx.media3.common.MediaItem 對象。這個工具類通常在媒體播放應用中用于構建和管理媒體庫。

????????MediaItem: MediaItem 是一個媒體庫中條目的抽象表示。它可以代表一首歌曲、一個專輯、一個藝術家、一個播放列表或任何其他可播放或可瀏覽的內容。

  • 它包含一個唯一的 mediaId

  • 它包含一個 MediaMetadata 對象來描述該媒體條目。

  • 它還可以包含一個 sourceUri,指向實際的媒體文件或流。

????????MediaMetadata: MediaMetadata 包含媒體條目的詳細信息,如標題、藝術家、專輯、封面圖片 URI (artworkUri)、媒體類型 (mediaType) 等。

  • MediaMetadata.Builder 用于創建和配置 MediaMetadata 對象。

fun buildMediaItem(title: String,mediaId: String,isPlayable: Boolean,isBrowsable: Boolean,mediaType: @MediaMetadata.MediaType Int,subtitleConfigurations: List<MediaItem.SubtitleConfiguration> = mutableListOf(),album: String? = null,artist: String? = null,genre: String? = null,sourceUri: Uri? = null,imageUri: Uri? = null,extras: Bundle? = null,subTitle: String = "",): MediaItem {val metadata =MediaMetadata.Builder().setAlbumTitle(album).setTitle(title).setSubtitle(subTitle).setArtist(artist).setGenre(genre).setIsBrowsable(isBrowsable).setIsPlayable(isPlayable).setArtworkUri(imageUri).setMediaType(mediaType).setExtras(extras).build()return MediaItem.Builder().setMediaId(mediaId).setSubtitleConfigurations(subtitleConfigurations).setMediaMetadata(metadata).setUri(sourceUri).build()}

之后是擴展函數來構建MediaItem的模式

????????MediaItemUtils 中定義了多個擴展函數(如 Album.toMediaItem()Track.toMediaItem() 等)以及一個通用的 buildMediaItem() 函數。

buildMediaItem() 函數: 這是一個通用的輔助函數,它接受各種參數(標題、mediaIdisPlayable 等),然后使用 MediaMetadata.BuilderMediaItem.Builder 來構建一個 MediaItem

  • MediaMetadata.BuildersetTitle()setArtist()setArtworkUri() 等方法用于設置媒體的元數據。

  • MediaItem.BuildersetMediaId()setMediaMetadata()setUri() 等方法用于構建最終的 MediaItem

????????擴展函數(Extension Functions): Kotlin 的擴展函數能力在這里得到了很好的應用。例如,Album.toMediaItem() 允許你在一個 Album 對象上直接調用 .toMediaItem() 方法,使其看起來像是 Album 類本身的一個成員函數。

  • 這些擴展函數的作用是將特定數據模型(如 AlbumTrack)轉換為通用的 MediaItem 格式,以便媒體播放器(如 ExoPlayer)能夠統一處理它們。

fun Album.toMediaItem(): MediaItem {return buildMediaItem(title = this.name,mediaId = this.id,isPlayable = true,isBrowsable = true,mediaType = MediaMetadata.MEDIA_TYPE_ALBUM,imageUri = getUri(images[1].url),sourceUri = this.uri.toUri(),extras = Bundle().apply {putString(MediaItemUtils.MEDIA_EXTTERNAL_URLS, this@toMediaItem.external_urls.spotify)}).setMediaItemType(MediaItemUtils.MEDIA_ITEM_TYPE_NORMAL)}

2.SpotifyDataRepository——Spotify數據倉庫

????????它封裝了對 Spotify Web API 的網絡請求,并將返回的數據轉換為 MediaItem 對象,以便在媒體播放器應用中使用。

Retrofit 和 Spotify API 服務

suspend fun fetchTopTracks(timeRange: String = "medium_term", limit: Int = 10, offset: Int = 0):List<MediaItem>  {val result = RetrofitClient.spotifyApiService.getTopItems(authorization = "Bearer ${SPUtils.getString(SPUtils.ACCESS_TOKEN)}",type = "tracks",timeRange = timeRange,limit = limit,offset = offset)return result.items.map { it.album.toMediaItem() }}

這段代碼的核心是與 Retrofit 的集成,它是一個用于 Android 和 Java 的類型安全的 HTTP 客戶端。

  • RetrofitClient.spotifyApiService: 這是一個 Retrofit 接口的實例,它定義了所有與 Spotify Web API 交互的請求方法。每個方法都使用注解(如 @GET)來指定請求類型和 URL。

  • suspend fun: 所有的網絡請求函數都使用了 suspend 關鍵字,這表示它們是 suspend 函數suspend 函數是 Kotlin Coroutines 的核心,它們可以暫停和恢復執行,使得異步操作(如網絡請求)可以在不阻塞主線程的情況下進行。

之后的網絡請求和數據轉換流程

每個 fetch 函數都遵循一個標準的模式:

  1. 發起網絡請求: 調用 RetrofitClient.spotifyApiService 接口中的相應方法,并傳入必要的參數(如 authorization token、idlimit 等)。

  2. 等待響應: 由于這些函數是 suspend 的,它們會暫停執行直到 Retrofit 收到來自 Spotify API 的響應。

  3. 處理結果:

    • val result = RetrofitClient.spotifyApiService...: Retrofit 會自動將 JSON 響應解析成預定義的 Kotlin 數據類對象(例如,result.itemsresult.tracks.items)。

    • .map { ... }: 這是一個 Kotlin 集合的轉換操作。它遍歷 Retrofit 返回的數據列表(例如 result.items),并對每個元素應用一個轉換函數。

    • .toMediaItem(): 這是上一個代碼片段中定義的擴展函數。它負責將 Spotify 的數據模型(如 AlbumTrack)轉換為 MediaItem 對象。這個步驟至關重要,因為它將網絡獲取的原始數據轉換成媒體播放器能夠理解和使用的格式。

  4. 返回 MediaItem 列表: 函數最終返回一個 List<MediaItem>,這個列表可以直接提供給 Media3 媒體會話或播放列表。

3.onGetChildren——媒體庫服務(Media Library Service)處理客戶端(如 Android Auto、Google Assistant 或其他媒體瀏覽器)請求其子項列表的入口點。

1. MediaLibraryServiceonGetChildren

  • MediaLibraryService: 這是 Media3 庫中的一個服務,用于向客戶端公開一個可瀏覽的媒體庫樹。客戶端可以通過它來獲取媒體內容,而不僅僅是播放。

  • onGetChildren(): 這個方法是 MediaLibraryService 的核心。當客戶端(例如,汽車信息娛樂系統)想要瀏覽某個媒體項的子項時,它會調用此方法,并傳入父項的 parentId

    • 你的任務是在這個方法中,根據 parentId 決定要返回哪些子媒體項。

@OptIn(UnstableApi::class)override fun onGetChildren(session: MediaLibraryService.MediaLibrarySession,browser: MediaSession.ControllerInfo,parentId: String,page: Int,pageSize: Int,params: MediaLibraryService.LibraryParams?,): ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> {logd(TAG) { "jidouauto cloud music onGetChildren beforeinit parentId: $parentId" }return scope.future {when(parentId){SPOTIFY_MUSIC_ROOT_ID -> {LibraryResult.ofItemList(dataRepository.getSpotifyMainTabs(), params)}MEDIA_TABS_RECOMMEND ->{val tracks = dataRepository.fetchTopTracks("short_term",10,0)val artists = dataRepository.fetchTopArtistTracks("short_term",10,0)val resultList= mutableListOf<MediaItem>()resultList.addAll(tracks)resultList.addAll(artists)LibraryResult.ofItemList(resultList,params)}MEDIA_TABS_MINE ->{val result = dataRepository.fetchPlaylistTracks(20,0)LibraryResult.ofItemList(result,params)}MEDIA_ALBUM_PLAY_LIST->{val extras = params?.extrasval playlistId = extras?.getString("playlistId") ?: "-1"val result = dataRepository.fetchAlbum(playlistId)LibraryResult.ofItemList(result,params)}MEDIA_ARTIST_PLAY_LIST->{val extras = params?.extrasval playlistId=extras?.getString("playlistId")?:"-1"val result =dataRepository.fetchArtistTopTracks(playlistId,"ES")LibraryResult.ofItemList(result,params)}MEDIA_PLAY_LIST->{val extras = params?.extrasval playlistId=extras?.getString("playlistId")?:"-1"val result =dataRepository.fetchDetailTracks(playlistId,"ES")LibraryResult.ofItemList(listOf(result) ,params)}else ->{LibraryResult.ofError(SessionError.ERROR_BAD_VALUE)}}}}

2. 構建媒體庫層級結構

這段代碼使用一個 when 表達式根據 parentId 來構建一個多層級的媒體庫:

  • 根目錄 (SPOTIFY_MUSIC_ROOT_ID): 當 parentId 是根 ID 時,它會返回一個包含主要選項卡(如“推薦”、“我的音樂”)的列表。這些選項卡本身是可瀏覽的文件夾。

    • dataRepository.getSpotifyMainTabs() 返回一個 List<MediaItem>,其 isBrowsable 屬性被設置為 true

  • 子目錄(MEDIA_TABS_RECOMMEND, MEDIA_TABS_MINE 等): 當 parentId 是某個選項卡的 ID 時,它會調用 dataRepository 中的相應方法來獲取具體的內容列表。

    • MEDIA_TABS_RECOMMEND: 返回熱門歌曲和熱門藝術家的列表。

    • MEDIA_TABS_MINE: 返回用戶的播放列表。

  • 動態內容(MEDIA_ALBUM_PLAY_LIST, MEDIA_ARTIST_PLAY_LIST 等): 這部分展示了如何處理更深層次的、依賴于特定 ID 的媒體內容。

    • params?.extras: params 參數中的 extras Bundle 可以用來傳遞額外的信息。例如,當客戶端點擊一個專輯項時,它可以在 extras 中傳遞專輯的 ID。

    • extras?.getString("playlistId"): 這段代碼從 extras 中提取出 playlistId,然后用它來調用 dataRepository 的相應方法,從而獲取該專輯或藝術家的具體曲目列表。

    • LibraryResult.ofItemList(...): 這是 Media3 庫用來封裝和返回結果的類。它將獲取到的 MediaItem 列表包裝在 LibraryResult 中。

五、總結

????????整個工作流程是一個自底向上的過程,首先從 Postman 檢查開始,確保 API 接口的功能、參數和請求方式(GET, POST, PUT, DELETE)是正確的。

????????在 Android 端,這個流程由 Retrofit 庫實現。你通過定義一個 Retrofit 接口來抽象 API,并使用 @GET@Header@Path@Query 等注解來構建請求。同時,你使用 Kotlinx.serialization@Serializable 注解來創建數據模型,讓 Retrofit 自動將 JSON 響應解析為 Kotlin 對象,大大簡化了數據處理。

????????接著,一個名為 SpotifyDataRepository數據倉庫封裝了所有這些網絡請求,并利用 Kotlin 擴展函數 (toMediaItem()) 將獲取到的原始數據模型(如 Album, Track)統一轉換為 Media3 庫所需的 MediaItem 對象,這些對象包含了媒體的元數據(MediaMetadata)。

????????最后,在 MediaLibraryServiceonGetChildren 方法中,你根據客戶端請求的 parentId 來判斷用戶正在瀏覽的媒體庫層級,然后調用 SpotifyDataRepository 中相應的方法獲取數據,并將轉換好的 MediaItem 列表封裝到 LibraryResult 中返回給客戶端,從而構建一個多層級的、可瀏覽的媒體庫,供 Android Auto 等設備使用。

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

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

相關文章

《計算機網絡》實驗報告一 常用網絡命令

目 錄 1、實驗目的 2、實驗環境 3、實驗內容 3.1 ping基本用法 3.2 ifconfig/ipconfig基本用法 3.3 traceroute/tracert基本用法 3.4 arp基本用法 3.5 netstat基本用法 4、實驗結果與分析 4.1 ping命令的基本用法 4.2 ifconfig/ipconfig命令的基本用法 4.3 tracer…

MySQL深度理解-深入理解MySQL索引底層數據結構與算法

1.引言在項目中會遇到各種各樣的慢查詢的問題&#xff0c;對于千萬級的表&#xff0c;如果使用比較笨的查詢方式&#xff0c;查詢一條SQL可能需要幾秒甚至幾十秒&#xff0c;如果將索引設置的比較合理&#xff0c;可以將查詢變得仍然非常快。2.索引的本質索引&#xff1a;幫助M…

Django母嬰商城項目實踐(九)- 商品列表頁模塊

9、商品列表頁模塊 1、業務邏輯 商品模塊分為:商品列表頁 和 商品詳情頁 商品列表頁將所有商品按照一定的規則排序展示,用于可以從銷量、價格、上架時間和收藏數量設置商品的排序方式,并且在商品左側設置分類列表,選擇某一個分類可以篩選出對應的商品信息。 商品列表頁…

8、STM32每個系列的區別

1、F1和F4的系列的區別 F1采用Crotex M3內核&#xff0c;F4采用Crotex M4內核。F4比F1的主頻高。F4具有浮點數運算單元&#xff0c;F1沒有浮點單元。F4的具備增強的DSP指令集。F407的執行16位DSP指令的時間只有F1的30%~70%。F4執行32位DSP指令的時間只有F1的25% ~ 60%。F1內部S…

DeepSPV:一種從2D超聲圖像中估算3D脾臟體積的深度學習流程|文獻速遞-醫學影像算法文獻分享

Title題目DeepSPV: A deep learning pipeline for 3D spleen volume estimation from 2Dultrasound imagesDeepSPV&#xff1a;一種從2D超聲圖像中估算3D脾臟體積的深度學習流程01文獻速遞介紹1.1 臨床背景 脾腫大指脾臟增大&#xff0c;是多種潛在疾病的重要臨床指標&#x…

病歷數智化3分鐘:AI重構醫院數據價值鏈

一、方案概述本方案針對某省醫聯體醫院病例數據管理需求&#xff0c;通過AI技術實現病歷數字化→信息結構化→數據應用化的全流程改造。系統采用雙端協同架構&#xff1a; - 普通用戶端&#xff1a;為一線醫護人員提供病歷拍攝、AI識別修正、安全上傳功能 - 管理員后臺&#…

CSS+JavaScript 禁用瀏覽器復制功能的幾種方法

&#x1f6e1;? 禁用瀏覽器復制功能完整指南 網頁中禁用用戶的復制功能&#xff0c;包括 CSS 方法、JavaScript 方法、綜合解決方案以及實際應用場景。適用于需要保護內容版權、防止惡意爬取或提升用戶體驗的場景。 &#x1f4cb; 目錄 &#x1f680; 快速開始&#x1f3a8…

Java 虛擬線程在高并發微服務中的實戰經驗分享

Java 虛擬線程在高并發微服務中的實戰經驗分享 虛擬線程&#xff08;Virtual Threads&#xff09;作為Java 19引入的預覽特性&#xff0c;為我們在高并發微服務場景下提供了一種更輕量、易用的并發模型。本文結合真實生產環境&#xff0c;講述在Spring Boot微服務中引入和使用虛…

《拆解WebRTC:NAT穿透的探測邏輯與中繼方案》

WebRTC以其無需插件的便捷性&#xff0c;成為連接全球用戶的隱形橋梁。但很少有人知曉&#xff0c;每一次流暢的視頻對話背后&#xff0c;都藏著一場與網絡邊界的無聲博弈——NAT&#xff0c;這個為緩解IPv4地址枯竭而生的技術&#xff0c;既是網絡安全的屏障&#xff0c;也是端…

前端開發 React 組件優化

1. 使用 React.memo 進行組件優化問題&#xff1a;當父組件重新渲染時&#xff0c;子組件也會重新渲染&#xff0c;即使它的 props 沒有變化。解決方案&#xff1a;使用 React.memo 包裹子組件&#xff0c;讓其只在 props 變化時才重新渲染。示例場景&#xff1a;展示一個顯示計…

變頻器實習DAY12

目錄變頻器實習DAY12一、繼續&#xff0c;柔性平臺測試&#xff01;上午 王工Modbus新功能測試下午 柔性平臺繼續按照說明書再測一遍附加的小知識點中國貍花貓.git文件附學習參考網址歡迎大家有問題評論交流 (* ^ ω ^)變頻器實習DAY12 一、繼續&#xff0c;柔性平臺測試&…

Redis--多路復用

&#x1f9e9; 一、什么是“客戶端連接”&#xff1f;所謂 客戶端連接 Redis&#xff0c;指的是&#xff1a;一個程序&#xff08;客戶端&#xff09;通過網絡連接到 Redis 服務端&#xff08;比如 127.0.0.1:6379&#xff09;&#xff0c;建立一個 TCP 連接&#xff0c;雙方可…

數組——初識數據結構

一維數組數組的創建數組是一種相同類型元素的集合數組的創建方式C99 中引入了變長數組的概念&#xff0c;變長數組支持數組的大小使用變量來指定明顯這里的vs2019不支持變長數組數組初始化和不完全初始化第二個數組就是典型的不完全初始化&#xff0c;開辟了10個空間&#xff0…

技術速遞|使用 Semantic Kernel 與 A2A 協議構建多智能體解決方案

作者&#xff1a;盧建暉 - 微軟高級云技術布道師 翻譯/排版&#xff1a;Alan Wang 在快速發展的 AI 應用開發領域&#xff0c;能夠協調多個智能體已成為構建復雜企業級解決方案的關鍵。雖然單個 AI 智能體擅長特定任務&#xff0c;但復雜的業務場景往往需要跨平臺、跨框架甚至跨…

前端跨域請求原理及實踐

在前端開發中&#xff0c;"跨域"是一個繞不開的話題。當我們的頁面嘗試從一個域名請求另一個域名的資源時&#xff0c;瀏覽器往往會拋出類似Access to fetch at xxx from origin xxx has been blocked by CORS policy的錯誤。下面將深入探討跨域請求的底層原理&#…

SpringBoot07-數據層的解決方案:SQL

一、內置數據源 1-1、【回顧】Druid數據源的配置 druid的兩種導入格式 1-2、springboot提供的3種內置數據源的配置 若是不配置Druid&#xff0c; springboot提供了3中默認的數據源配置&#xff0c;它們分別是&#xff1a; 1. HikariCP&#xff08;默認&#xff09; 從 Spring…

前端自動化埋點:頁面模塊級行為跟蹤與問題定位系統??的技術設計方案

一、核心設計目標??精細化監控??&#xff1a;定位到頁面中??單個模塊??的曝光、點擊等行為。??低侵入性??&#xff1a;業務代碼與埋點邏輯解耦&#xff0c;降低開發維護成本。??鏈路可追蹤??&#xff1a;串聯用戶從曝光到操作的完整行為路徑。??實時性??&a…

Node.js 與 Java 性能對比

一、核心架構與任務模型對比Node.js 單線程事件循環 非阻塞I/O 通過V8引擎執行JavaScript&#xff0c;采用事件驅動模型&#xff0c;所有I/O操作&#xff08;如網絡請求、文件讀寫&#xff09;均為非阻塞。單線程處理所有請求&#xff0c;但通過事件循環&#xff08;Event Loo…

Python3常見接口函數

Python3常見接口函數一、基礎內置函數 輸入輸出 print()&#xff1a;輸出內容input()&#xff1a;讀取用戶輸入 類型轉換 int()、float()、str()、bool()&#xff1a;基礎類型轉換list()、tuple()、set()、dict()&#xff1a;容器類型轉換bin()、hex()、oct()&#xff1a;進制轉…

《P4092 [HEOI2016/TJOI2016] 樹》

題目描述在 2016 年&#xff0c;佳媛姐姐剛剛學習了樹&#xff0c;非常開心。現在他想解決這樣一個問題&#xff1a;給定一顆有根樹&#xff0c;根為 1 &#xff0c;有以下兩種操作&#xff1a;標記操作&#xff1a;對某個結點打上標記。&#xff08;在最開始&#xff0c;只有結…