【iOS開發】Alamofire框架的使用一基本用法

来源:https://www.cnblogs.com/jukaiit/archive/2018/07/09/9283498.html
-Advertisement-
Play Games

Alamofire框架的使用一 —— 基本用法 對於使用Objective-C的開發者,一定非常熟悉AFNetworking這個網路框架。在蘋果推出的Swift之後,AFNetworking的作者專門用Swift來編寫一個類似AFNetworking的網路框架,稱為Alamofire。Alamofi ...


 

Alamofire框架的使用一 —— 基本用法

對於使用Objective-C的開發者,一定非常熟悉AFNetworking這個網路框架。在蘋果推出的Swift之後,AFNetworking的作者專門用Swift來編寫一個類似AFNetworking的網路框架,稱為AlamofireAlamofire地址 >>

我分兩篇文章介紹如何使用Alamofire框架。文章的內容主要是翻譯Alamofire的readme。第二篇文章 >>

功能

  • 鏈式請求/響應方法
  • URL / JSON / plist參數編碼
  • 上傳文件/數據/流/多表單數據
  • 使用請求或者斷點下載來下載文件
  • 使用URL憑據進行身份認證
  • HTTP響應驗證
  • 包含進度的上傳和下載閉包
  • cURL命令的輸出
  • 動態適配和重試請求
  • TLS證書和Public Key Pinning
  • 網路可達性
  • 全面的單元和集成測試覆蓋率

組件庫

為了讓Alamofire專註於核心網路的實現,Alamofire生態系統還有另外兩個庫:

  • AlamofireImage:一個圖片庫,包括圖像響應序列化器、UIImageUIImageView的擴展、自定義圖像濾鏡、記憶體中自動清除和基於優先順序的圖像下載系統。
  • AlamofireNetworkActivityIndicator:控制iOS應用的網路活動指示器。包含可配置的延遲計時器來幫助減少閃光,並且支持不受Alamofire管理的URLSession實例。

要求的使用環境

  • iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
  • Xcode 8.3+
  • Swift 3.1+

安裝方法

CocoaPods

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!

target '項目名稱' do
    pod 'Alamofire', '~> 4.7'
end

iOS版本和Alamofire版本可以自己根據實際情況自行更改。CocoaPods是比較常用的第三方庫管理工具,其他方法就不詳細說了。

如何使用

發請求

Alamofire.request("http://baidu.com/")

響應處理

直接在請求後面用點語法鏈接響應處理:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.request)  // 原始的URL請求
    print(response.response) // HTTP URL響應
    print(response.data)     // 伺服器返回的數據
    print(response.result)   // 響應序列化結果,在這個閉包里,存儲的是JSON數據

    if let JSON = response.result.value {
        print("JSON: \(JSON)")
    }
}

在上面的例子中,responseJSON handler直接拼接到請求後面,當請求完成後被調用。這個閉包一旦收到響應後,就會處理這個響應,並不會因為等待伺服器的響應而造成阻塞執行。請求的結果僅在響應閉包的範圍內可用。其他任何與伺服器返回的響應或者數據相關的操作,都必須在這個閉包內執行。

Alamofire預設情況下包含五種不同的響應handler:

// 響應 Handler - 未序列化的響應
func response(
    queue: DispatchQueue?,
    completionHandler: @escaping (DefaultDataResponse) -> Void)
    -> Self

// 響應數據 Handler - 序列化成數據類型
func responseData(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Data>) -> Void)
    -> Self

// 響應字元串 Handler - 序列化成字元串類型
func responseString(
    queue: DispatchQueue?,
    encoding: String.Encoding?,
    completionHandler: @escaping (DataResponse<String>) -> Void)
    -> Self

// 響應 JSON Handler - 序列化成Any類型
func responseJSON(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void)
    -> Self

// 響應 PropertyList (plist) Handler - 序列化成Any類型
func responsePropertyList(
    queue: DispatchQueue?,
    completionHandler: @escaping (DataResponse<Any>) -> Void))
    -> Self

所有的響應handler都不會對響應進行驗證。也就是說響應狀態碼在400..<500500..<600範圍內,都不會觸發錯誤。

響應 Handler

response handler不處理任何響應數據。它僅僅是從URL session delegate中轉發信息。

Alamofire.request("https://httpbin.org/get").response { response in
    print("Request: \(response.request)")
    print("Response: \(response.response)")
    print("Error: \(response.error)")

    if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
        print("Data: \(utf8Text)")
    }
}

一般情況下不建議使用這種沒有響應序列化器的handler,而應該使用下麵有特定序列化器的handler。

響應數據 Handler

responseData handler使用responseDataSerializer(這個對象把伺服器的數據序列化成其他類型)來提取伺服器返回的數據。如果沒有返回錯誤並且有數據返回,那麼響應Result將會是.successvalueData類型。

Alamofire.request("https://httpbin.org/get").responseData { response in
    debugPrint("All Response Info: \(response)")

    if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
        print("Data: \(utf8Text)")
    }
}
響應字元串 Handler

responseString handler使用responseStringSerializer對象根據指定的編碼格式把伺服器返回的數據轉換成String。如果沒有返回錯誤並且伺服器的數據成功地轉換為String,那麼響應Result將會是.successvalueString類型。

Alamofire.request("https://httpbin.org/get").responseString { response in
    print("Success: \(response.result.isSuccess)")
    print("Response String: \(response.result.value)")
}

如果沒有指定編碼格式,將會使用伺服器的HTTPURLResponse指定的格式。如果伺服器無法確定編碼格式,那麼預設使用.isoLatin1

響應 JSON Handler

responseJSON handler使用responseJSONSerializer根據指定的JSONSerialization.ReadingOptions把伺服器返回的數據轉換成Any類型。如果沒有返回錯誤並且伺服器的數據成功地轉換為JSON對象,那麼響應Result將會是.successvalueAny類型。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    debugPrint(response)

    if let json = response.result.value {
        print("JSON: \(json)")
    }
}

所有JSON的序列化,都是使用JSONSerialization完成的。

鏈式響應handler

響應handler可以鏈接在一起:

Alamofire.request("https://httpbin.org/get")
    .responseString { response in
        print("Response String: \(response.result.value)")
    }
    .responseJSON { response in
        print("Response JSON: \(response.result.value)")
    }

註意:在同一個請求中使用多個響應handler,要求伺服器的數據會被序列化多次,每次對應一個handler。

響應handler隊列

預設情況下,響應handler是在主隊列執行的。但是我們也可以自定義隊列:

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.request("https://httpbin.org/get").responseJSON(queue: utilityQueue) { response in
    print("Executing response handler on utility queue")
}

響應驗證

預設情況下,Alamofire把所有完成的請求當做是成功的請求,無論響應的內容是什麼。如果響應有一個不能被接受的狀態碼或者MIME類型,在響應handler之前調用validate將會產生錯誤。

手動驗證
Alamofire.request("https://httpbin.org/get")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseData { response in
    switch response.result {
    case .success:
        print("Validation Successful")
    case .failure(let error):
        print(error)
    }
}
自動驗證

自動驗證在200…299範圍內的狀態碼;如果請求頭中有指定Accept,那麼也會驗證響應頭的與請求頭Accept一樣的Content-Type

Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
    switch response.result {
    case .success:
        print("Validation Successful")
    case .failure(let error):
        print(error)
    }
}

響應緩存

響應緩存是使用系統的框架URLCache來處理的。它提供了記憶體和磁碟上的緩存,並允許我們控制記憶體和磁碟的大小。

預設情況下,Alamofire利用共用的URLCache

HTTP方法

HTTPMethod列舉了下麵的這些方法:

public enum HTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}

在使用Alamofire.request時,可以傳入方法參數:

Alamofire.request("https://httpbin.org/get") // 預設是get請求

Alamofire.request("https://httpbin.org/post", method: .post)
Alamofire.request("https://httpbin.org/put", method: .put)
Alamofire.request("https://httpbin.org/delete", method: .delete)

參數編碼

Alamofire支持三種參數編碼:URLJSONPropertyList。還支持遵循了ParameterEncoding協議的自定義編碼。

URL編碼

URLEncoding類型創建了一個URL編碼的查詢字元串來設置或者添加到一個現有的URL查詢字元串,或者設置URL請求的請求體。查詢字元串是否被設置或者添加到現有的URL查詢字元串,或者被作為HTTP請求體,決定於編碼的Destination。編碼的Destination有三個case:

  • .methodDependent:為GETHEADDELETE請求使用編碼查詢字元串來設置或者添加到現有查詢字元串,並且使用其他HTTP方法來設置請求體。
  • .queryString:設置或者添加編碼查詢字元串到現有查詢字元串
  • .httpBody:把編碼查詢字元串作為URL請求的請求體

一個編碼請求的請求體的Content-Type欄位被設置為application/x-www-form-urlencoded; charset=utf-8。因為沒有公開的標準說明如何編碼集合類型,所以按照慣例在key後面添加[]來表示數組的值(foo[]=1&foo[]=2),在key外麵包一個中括弧來表示字典的值(foo[bar]=baz)。

使用URL編碼參數的GET請求
let parameters: Parameters = ["foo": "bar"]

// 下麵這三種寫法是等價的
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding 預設是`URLEncoding.default`
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent))

// https://httpbin.org/get?foo=bar
使用URL編碼參數的POST請求
let parameters: Parameters = [
    "foo": "bar",
    "baz": ["a", 1],
    "qux": [
        "x": 1,
        "y": 2,
        "z": 3
    ]
]

// 下麵這三種寫法是等價的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody)

// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3
設置Bool類型參數的編碼

URLEncoding.BoolEncoding提供了兩種編碼方式:

  • .numeric:把true編碼為1false編碼為0
  • .literal:把true編碼為truefalse編碼為false

預設情況下:Alamofire使用.numeric

可以使用下麵的初始化函數來創建URLEncoding,指定Bool編碼的類型:

let encoding = URLEncoding(boolEncoding: .literal)
設置Array類型參數編碼

URLEncoding.ArrayEncoding提供了兩種編碼方式:

  • .brackets: 在每個元素值的key後面加上一個[],如foo=[1,2]編碼成foo[]=1&foo[]=2
  • .noBrackets:不添加[],例如foo=[1,2]編碼成``foo=1&foo=2`

預設情況下,Alamofire使用.brackets

可以使用下麵的初始化函數來創建URLEncoding,指定Array編碼的類型:

let encoding = URLEncoding(arrayEncoding: .noBrackets)
JSON編碼

JSONEncoding類型創建了一個JOSN對象,並作為請求體。編碼請求的請求頭的Content-Type請求欄位被設置為application/json

使用JSON編碼參數的POST請求
let parameters: Parameters = [
    "foo": [1,2,3],
    "bar": [
        "baz": "qux"
    ]
]

// 下麵這兩種寫法是等價的
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: []))

// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
屬性列表編碼

PropertyListEncoding根據關聯格式和寫選項值,使用PropertyListSerialization來創建一個屬性列表對象,並作為請求體。編碼請求的請求頭的Content-Type請求欄位被設置為application/x-plist

自定義編碼

如果提供的ParameterEncoding類型不能滿足我們的要求,可以創建自定義編碼。下麵演示如何快速自定義一個JSONStringArrayEncoding類型把JSON字元串數組編碼到請求中。

struct JSONStringArrayEncoding: ParameterEncoding {
    private let array: [String]

    init(array: [String]) {
        self.array = array
    }

    func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
        var urlRequest = urlRequest.urlRequest

        let data = try JSONSerialization.data(withJSONObject: array, options: [])

        if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
            urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        }

        urlRequest.httpBody = data

        return urlRequest
    }
}
手動URL請求參數編碼

ParameterEncodingAPI可以在創建網路請求外面使用。

let url = URL(string: "https://httpbin.org/get")!
var urlRequest = URLRequest(url: url)

let parameters: Parameters = ["foo": "bar"]
let encodedURLRequest = try URLEncoding.queryString.encode(urlRequest, with: parameters)

HTTP請求頭

可以直接在請求方法添加自定義HTTP請求頭,這有利於我們在請求中添加請求頭。

let headers: HTTPHeaders = [
    "Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
    "Accept": "application/json"
]

Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
    debugPrint(response)
}

對於那些不變的請求頭,建議在URLSessionConfiguration設置,這樣就可以自動被用於任何URLSession創建的URLSessionTask

預設的Alamofire SessionManager為每一個請求提供了一個預設的請求頭集合,包括:

  • Accept-Encoding,預設是gzip;q=1.0, compress;q=0.5
  • Accept-Language,預設是系統的前6個偏好語言,格式類似於en;q=1.0
  • User-Agent,包含當前應用程式的版本信息。例如iOS Example/1.0 (com.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0

如果要自定義這些請求頭集合,我們必須創建一個自定義的URLSessionConfigurationdefaultHTTPHeaders屬性將會被更新,並且自定義的會話配置也會應用到新的SessionManager實例。

認證

認證是使用系統框架URLCredentialURLAuthenticationChallenge實現的。

支持的認證方案
  • HTTP Basic
  • HTTP Digest
  • Kerberos
  • NTLM
HTTP Basic認證

在合適的時候,在一個請求的authenticate方法會自動提供一個URLCredentialURLAuthenticationChallenge

let user = "user"
let password = "password"

Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(user: user, password: password)
    .responseJSON { response in
        debugPrint(response)
}

根據伺服器實現,Authorization header也可能是適合的:

let user = "user"
let password = "password"

var headers: HTTPHeaders = [:]

if let authorizationHeader = Request.authorizationHeader(user: user, password: password) {
    headers[authorizationHeader.key] = authorizationHeader.value
}

Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers)
    .responseJSON { response in
        debugPrint(response)
}
使用URLCredential認證
let user = "user"
let password = "password"

let credential = URLCredential(user: user, password: password, persistence: .forSession)

Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
    .authenticate(usingCredential: credential)
    .responseJSON { response in
        debugPrint(response)
}

註意:使用URLCredential來做認證,如果伺服器發出一個challenge,底層的URLSession實際上最終會發兩次請求。第一次請求不會包含credential,並且可能會觸發伺服器發出一個challenge。這個challenge會被Alamofire接收,credential會被添加,然後URLSessin會重試請求。

將數據下載到文件

Alamofire可以把伺服器的數據下載到記憶體(in-memory)或者硬碟(on-disk)中。所有Alamofire.requestAPI下載的數據都是存儲在記憶體中。這比較適合小文件,更高效;但是不適合大文件,因為大文件會把記憶體耗盡。我們要使用Alamofire.downloadAPI把伺服器的數據下載到硬碟中。

下麵這個方法只適用於macOS。因為在其他平臺不允許在應用沙盒外訪問文件系統。下麵會講到如何在其他平臺下載文件。

Alamofire.download("https://httpbin.org/image/png").responseData { response in
    if let data = response.result.value {
        let image = UIImage(data: data)
    }
}
下載文件存儲位置

我們可以提供一個DownloadFileDestination閉包把臨時文件夾的文件移動到一個目標文件夾。在臨時文件真正移動到destinationURL之前,閉包內部指定的DownloadOptions將會被執行。目前支持的DownloadOptions有下麵兩個:

  • .createIntermediateDirectories:如果指定了目標URL,將會創建中間目錄。
  • .removePreviousFile:如果指定了目標URL,將會移除之前的文件
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
    let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let fileURL = documentsURL.appendPathComponent("pig.png")

    return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}

Alamofire.download(urlString, to: destination).response { response in
    print(response)

    if response.error == nil, let imagePath = response.destinationURL?.path {
        let image = UIImage(contentsOfFile: imagePath)
    }
}

也可以直接使用建議的下載目標API:

let destination = DownloadRequest.suggestedDownloadDestination(directory: .documentDirectory)
Alamofire.download("https://httpbin.org/image/png", to: destination)
下載進度

所有的DownloadRequest都可以使用downloadProgressAPI來反饋下載進度。

Alamofire.download("https://httpbin.org/image/png")
    .downloadProgress { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.result.value {
            let image = UIImage(data: data)
    }
}

downloadProgressAPI還可以接受一個queue參數來指定下載進度閉包在哪個DispatchQueue中執行。

let utilityQueue = DispatchQueue.global(qos: .utility)

Alamofire.download("https://httpbin.org/image/png")
    .downloadProgress(queue: utilityQueue) { progress in
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseData { response in
        if let data = response.result.value {
            let image = UIImage(data: data)
    }
}
恢復下載

如果一個DownloadRequest被取消或中斷,底層的URL會話會生成一個恢複數據。恢複數據可以被重新利用併在中斷的位置繼續下載。恢複數據可以通過下載響應訪問,然後在重新開始請求的時候被利用。

重要:在iOS 10 - 10.2, macOS 10.12 - 10.12.2, tvOS 10 - 10.1, watchOS 3 - 3.1.1中,resumeData會被後臺URL會話配置破壞。因為在resumeData的生成邏輯有一個底層的bug,不能恢復下載。具體情況可以到Stack Overflow看看。

class ImageRequestor {
    private var resumeData: Data?
    private var image: UIImage?

    func fetchImage(completion: (UIImage?) -> Void) {
        guard image == nil else { completion(image) ; return }

        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileURL = documentsURL.appendPathComponent("pig.png")

            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }

        let request: DownloadRequest

        if let resumeData = resumeData {
            request = Alamofire.download(resumingWith: resumeData)
        } else {
            request = Alamofire.download("https://httpbin.org/image/png")
        }

        request.responseData { response in
            switch response.result {
            case .success(let data):
                self.image = UIImage(data: data)
            case .failure:
                self.resumeData = response.resumeData
            }
        }
    }
}

上傳數據到伺服器

使用JOSN或者URL編碼參數上傳一些小數據到伺服器,使用Alamofire.request API就已經足夠了。如果需要發送很大的數據,需要使用Alamofire.upload API。當我們需要在後臺上傳數據時,也可以使用Alamofire.upload

上傳數據
let imageData = UIPNGRepresentation(image)!

Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}
上傳文件
let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post").responseJSON { response in
    debugPrint(response)
}
上傳多部分表單數據
Alamofire.upload(
    multipartFormData: { multipartFormData in
        multipartFormData.append(unicornImageURL, withName: "unicorn")
        multipartFormData.append(rainbowImageURL, withName: "rainbow")
    },
    to: "https://httpbin.org/post",
    encodingCompletion: { encodingResult in
        switch encodingResult {
        case .success(let upload, _, _):
            upload.responseJSON { response in
                debugPrint(response)
            }
        case .failure(let encodingError):
            print(encodingError)
        }
    }
)
上傳進度

所有的UploadRequest都可以使用uploadProgressdownloadProgress APIs來反饋上傳和下載進度。

let fileURL = Bundle.main.url(forResource: "video", withExtension: "mov")

Alamofire.upload(fileURL, to: "https://httpbin.org/post")
    .uploadProgress { progress in // 預設在主線程中執行
        print("Upload Progress: \(progress.fractionCompleted)")
    }
    .downloadProgress { progress in // 預設在主線程中執行
        print("Download Progress: \(progress.fractionCompleted)")
    }
    .responseJSON { response in
        debugPrint(response)
}

統計指標

時間表

Alamofire在一個請求周期內收集時間,並創建一個Tineline對象,它是響應類型的一個屬性。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.timeline)
}

上面的Timeline信息包括:

  • Latency: 0.428 seconds (延遲)
  • Request Duration: 0.428 seconds (請求時間)
  • Serialization Duration: 0.001 seconds (序列化時間)
  • Total Duration: 0.429 seconds (總時間)
URL會話任務指標

在iOS和tvOS 10和macOS 10.12中,蘋果發佈了新的URLSessionTaskMetrics APIs。這個任務指標封裝了關於請求和響應執行的神奇統計信息。這個API和Timeline非常相似,但是提供了很多Alamofire沒有提供的統計信息。這些指標可以通過任何響應去訪問。

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    print(response.metrics)
}

註意:這些API只能在iOS和tvOS 10和macOS 10.12中使用。所以,根據部署目標,可能需要加入版本判斷:

Alamofire.request("https://httpbin.org/get").responseJSON { response in
    if #available(iOS 10.0. *) {
        print(response.metrics)
    }
}

cURL命令輸出

調試平臺問題很讓人厭煩。慶幸的是,Alamofire的Request對象遵循了CustomStringConvertibleCustomDebugStringConvertible協議來提供一些非常有用的調試工具。

CustomStringConvertible
let request = Alamofire.request("https://httpbin.org/ip")

print(request)
// GET https://httpbin.org/ip (200)
CustomDebugStringConvertible
let request = Alamofire.request("https://httpbin.org/get", parameters: ["foo": "bar"])
debugPrint(request)

輸出:

$ curl -i \
    -H "User-Agent: Alamofire/4.0.0" \
    -H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
    -H "Accept-Language: en;q=1.0,fr;q=0.9,de;q=0.8,zh-Hans;q=0.7,zh-Hant;q=0.6,ja;q=0.5" \
    "https://httpbin.org/get?foo=bar"

第一部分完。



作者:Lebron_James
鏈接:https://www.jianshu.com/p/f8c3adb056cf
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一. 概述 在生產資料庫運行期間,有時我們需要查看當前用戶會話狀態或者是說資料庫當前是否運行良好, 應用的場景比如:當運行的應用系統響應突然變慢時需要分析資料庫的、或想分析當前的資料庫是否繁忙,是否有長時間的等待, 又或者執行一個sql的回滾狀態查看,想手動kill掉一個會話時 等等。都需要從當前的 ...
  • 版權聲明:未經博主允許不得轉載 OrmLite基礎知識 什麼是OrmLite框架,在我沒用這個框架時,不知道它有多好,用了才知道很方便哦,為了提供開發效率,Android開發者需要懂得運行多種框架進行開發。對於OrmLite框架是一種很輕量級的資料庫操作框架,它的底層是由反射機制實現的。 OrmLi ...
  • 前言 大家好,給大家帶來 的概述,希望你們喜歡 每日一句: Success is connecting with the world and making people feel. 《名揚四海》 設計思路 當我們面臨製作登錄和註冊功能的實現時,我們需要先 設計登錄界面的佈局和註冊界面的佈局,做到有完 ...
  • 前言 大家好,給大家帶來 的概述,希望你們喜歡 學習目標 1. 掌握SQLite資料庫的使用,能夠實現用資料庫來保存用戶的信息; 2. 學會運用好個人資料,以及個人資料的修改功能實現; 3. 個人資料包括用戶名,昵稱,性別,簽名,QQ號或個人社交賬號的記錄等。 資料庫的創建 資料庫類 該類繼承 創建 ...
  • //這裡有一個模擬器沙盒路徑(完整路徑) NSString* index=@"/Users/junzoo/Library/Application Support/iPhone Simulator/7.0.3/Applications/63925F20-AF97-4610-AF1C-B6B4157D1 ...
  • "使用AppleDoc快速生成iOS開發文檔 _ 皮卡丘♪~(´ε` )" "用 appledoc 生成文檔 _ Garan no dou" "xcode select_ error_ tool 'xcodebuild' requires Xcode, but active developer di ...
  • 為什麼不使用xml繪製Andoird的UI? 類型不安全 非空不安全 xml迫使你在很多佈局中寫很多相同的代碼 設備在解析xml時會耗費更多的cpu運行時間和電池 最重要的時,它允許任何代碼重用 簡單案例 findViewById() LayoutParams 使用 描述佈局參數,其中寬高都是 設置 ...
  • 添加依賴 Color 不透明的紅色 Dimensions 使用 和`sp dip(dipValue) sp(spValue)` applyRecursively() applyRecursively()應用於lambda表達式的本身,然後遞歸地運用在他的子view 以上表示,該 中的 的`textS ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...