iOS App穩定性指標及監測

来源:https://www.cnblogs.com/jukaiit/archive/2017/12/29/8145388.html
-Advertisement-
Play Games

一個App的穩定性,主要決定於整體的系統架構設計,同時也不可忽略編程的細節,正所謂“千里之堤,潰於蟻穴”,一旦考慮不周,看似無關緊要的代碼片段可能會帶來整體軟體系統的崩潰。尤其因為蘋果限制了熱更新機制,App本身的穩定性及容錯性就顯的更加重要,之前可以通過發佈熱補丁的方式解決線上代碼問題,現在就需要 ...


  一個App的穩定性,主要決定於整體的系統架構設計,同時也不可忽略編程的細節,正所謂“千里之堤,潰於蟻穴”,一旦考慮不周,看似無關緊要的代碼片段可能會帶來整體軟體系統的崩潰。尤其因為蘋果限制了熱更新機制,App本身的穩定性及容錯性就顯的更加重要,之前可以通過發佈熱補丁的方式解決線上代碼問題,現在就需要在提交之前對App開發周期內的各個指標進行實時監測,儘量讓問題暴漏在開發階段,然後及時修複,減少線上出問題的幾率 。針對一個App的開發周期,它的穩定性指標主要有以下幾個環節構成,用一個腦圖表示如下:

 

1 開發過程

開發過程中,主要是通過監控記憶體使用及泄露,CPU使用率,FPS,啟動時間等指標,以及常見的UI的主線程監測,NSAssert斷言等,最好能在Debug模式下,實時顯示在界面上,針對出現的問題及早解決。

記憶體問題

記憶體問題主要包括兩個部分,一個是iOS中常見迴圈引用導致的記憶體泄露 ,另外就是大量數據載入及使用導致的記憶體警告。

mmap

雖然蘋果並沒有明確每個App在運行期間可以使用的記憶體最大值,但是有開發者進行了實驗和統計,一般在占用系統記憶體超過20%的時候會有記憶體警告,而超過50%的時候,就很容易Crash了,所以記憶體使用率還是儘量要少,對於數據量比較大的應用,可以採用分步載入數據的方式,或者採用mmap方式。mmap 是使用邏輯記憶體對磁碟文件進行映射,中間只是進行映射沒有任何拷貝操作,避免了寫文件的數據拷貝。 操作記憶體就相當於在操作文件,避免了內核空間和用戶空間的頻繁切換。之前在開發輸入法的時候 ,詞庫的載入也是使用mmap方式,可以有效降低App的記憶體占用率,具體使用可以參考的文章。

迴圈引用

迴圈引用是iOS開發中經常遇到的問題,尤其對於新手來說是個頭疼的問題。迴圈引用對App有潛在的危害,會使記憶體消耗過高,性能變差和Crash等,iOS常見的記憶體主要以下三種情況:

Delegate

代理協議是一個最典型的場景,需要你使用弱引用來避免迴圈引用。ARC時代,需要將代理聲明為weak是一個即好又安全的做法:

@property (nonatomic, weak) id <MyCustomDelegate> delegate;

NSTimer

NSTimer我們開發中會用到很多,比如下麵一段代碼

- (void)viewDidLoad {
    [super viewDidLoad];

    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self
                                            selector:@selector(doSomeThing)
                                            userInfo:nil
                                            repeats:YES];
}

- (void)doSomeThing {
}

- (void)dealloc {
     [self.timer invalidate];
     self.timer = nil;
}

這是典型的迴圈引用,因為timer會強引用self,而self又持有了timer,所有就造成了迴圈引用。那有人可能會說,我使用一個weak指針,比如

__weak typeof(self) weakSelf = self;
self.mytimer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(doSomeThing) userInfo:nil repeats:YES];

但是其實並沒有用,因為不管是weakSelf還是strongSelf,最終在NSTimer內部都會重新生成一個新的指針指向self,這是一個強引用的指針,結果就會導致迴圈引用。那怎麼解決呢?主要有如下三種方式:

  • 使用類方法
  • 使用weakProxy
  • 使用GCD timer

具體如何使用,我就不做具體的介紹,網上有很多可以參考。

Block

Block的迴圈引用,主要是發生在ViewController中持有了block,比如:

@property (nonatomic, copy) LFCallbackBlock callbackBlock;

同時在對callbackBlock進行賦值的時候又調用了ViewController的方法,比如:

self.callbackBlock = ^{
    [self doSomething];
}];

就會發生迴圈引用,因為:ViewController->強引用了callback->強引用了ViewController,解決方法也很簡單:

__weak __typeof(self) weakSelf = self;
self.callbackBlock = ^{
  [weakSelf doSomething];
}];

原因是使用MRC管理記憶體時,Block的記憶體管理需要區分是Global(全局)、Stack(棧)還是Heap(堆),而在使用了ARC之後,蘋果自動會將所有原本應該放在棧中的Block全部放到堆中。全局的Block比較簡單,凡是沒有引用到Block作用域外面的參數的Block都會放到全局記憶體塊中,在全局記憶體塊的Block不用考慮記憶體管理問題。(放在全局記憶體塊是為了在之後再次調用該Block時能快速反應,當然沒有調用外部參數的Block根本不會出現記憶體管理問題)。

所以Block的記憶體管理出現問題的,絕大部分都是在堆記憶體中的Block出現了問題。預設情況下,Block初始化都是在棧上的,但可能隨時被收回,通過將Block類型聲明為copy類型,這樣對Block賦值的時候,會進行copy操作,copy到堆上,如果裡面有對self的引用,則會有一個強引用的指針指向self,就會發生迴圈引用,如果採用weakSelf,內部不會有強類型的指針,所以可以解決迴圈引用問題。

那是不是所有的block都會發生迴圈引用呢?其實不然,比如UIView的類方法Block動畫,NSArray等的類的遍歷方法,也都不會發生迴圈引用,因為當前控制器一般不會強引用一個類。

其他記憶體問題

1 NSNotification addObserver之後,記得在dealloc裡面添加remove;

2 動畫的repeat count無限大,而且也不主動停止動畫,基本就等於無限迴圈了;

3 forwardingTargetForSelector返回了self。

記憶體解決思路:

1 通過Instruments來查看leaks

2 集成Facebook開源的FBRetainCycleDetector

3 集成MLeaksFinder

具體原理及使用,可以參考鏈接。

CPU使用率

CPU的使用也可以通過兩種方式來查看,一種是在調試的時候Xcode會有展示,具體詳細信息可以進入Instruments內查看,通過查看Instruments的time profile來定位並解決問題。另一種常見的方法是通過代碼讀取CPU使用率,然後顯示在App的調試面板上,可以在Debug環境下顯示信息,具體代碼如下:

int result;
mib[0] = CTL_HW;
mib[1] = HW_CPU_FREQ;
length = sizeof(result);
if (sysctl(mib, 2, &result, &length, NULL, 0) < 0)
{
 	perror("getting cpu frequency");
}
printf("CPU Frequency = %u hz\n", result);

FPS監控

目前主要使用CADisplayLink來監控FPS,CADisplayLink是一個能讓我們以和屏幕刷新率相同的頻率將內容畫到屏幕上的定時器。我們在應用中創建一個新的 CADisplayLink 對象,把它添加到一個runloop中,並給它提供一個 target 和selector 在屏幕刷新的時候調用,需要註意的是添加到runloop的common mode裡面,代碼如下:

- (void)setupDisplayLink {
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(linkTicks:)];
    [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)linkTicks:(CADisplayLink *)link
{
    //執行次數
    _scheduleTimes ++;

    //當前時間戳
    if(_timestamp == 0){
        _timestamp = link.timestamp;
    }
    CFTimeInterval timePassed = link.timestamp - _timestamp;

    if(timePassed >= 1.f)
        //fps
        CGFloat fps = _scheduleTimes/timePassed; 
        printf("fps:%.1f, timePassed:%f\n", fps, timePassed);
    }
}

啟動時間

點評App裡面本身就包含了很多複雜的業務,比如外賣、團購、到綜和酒店等,同時還引入了很多第三方SDK比如微信、QQ、微博等,在App初始化的時候,很多SDK及業務也開始初始化,這就會拖慢應用的啟動時間。

App的啟動時間t(App總啟動時間) = t1(main()之前的載入時間) + t2(main()之後的載入時間)。 

t1 = 系統dylib(動態鏈接庫)和自身App可執行文件的載入; 

t2 = main方法執行之後到AppDelegate類中的didFinishLaunchingWithOptions方法執行結束前這段時間,主要是構建第一個界面,並完成渲染展示。

針對t1的優化,優化主要有如下:

  • 減少不必要的framework,因為動態鏈接比較耗時;
  • 檢查framework應當設為optional和required,如果該framework在當前App支持的所有iOS系統版本都存在,那麼就設為required,否則就設為optional,因為optional會有些額外的檢查;
  • 合併或者刪減一些OC類,這些我會在後續的靜態檢查中進行詳解;

針對t2的時間優化,可以採用:

  • 非同步初始化部分操作,比如網路,數據讀取;
  • 採用延遲載入或者懶載入某些視圖,圖片等的初始化操作;
  • 對與圖片展示類的App,可以將解碼的圖片保存到本地,下次啟動時直接載入解碼後的圖片;
  • 對實現了+load()方法的類進行分析,儘量將load里的代碼延後調用。

UI的主線程監測

我們都知道iOS的UI的操作一定是在主線程進行,該監測可以通過hook UIView的如下三個方法

-setNeedsLayout,
-setNeedsDisplay,
-setNeedsDisplayInRect 

確保它們都是在主線程執行。子線程操作UI可能會引起什麼問題,蘋果說得並不清楚,但是在實際開發中,我們經常會遇到整個App的動畫丟失,很大原因就是UI操作不是在主線程導致。

2 靜態分析過程

靜態分析在這裡,我主要介紹兩方面,一個是正常的code review機制,另外一個就是代碼靜態檢查工具

code review

組內的code review機制,可以參考團隊之前的OpenDoc - 前端團隊CodeReview制度,iOS客戶端開發,會在此基礎上進行一些常見手誤及Crash情況的重點標記,比如:

1 我們開發中首先都是在測試環境開發,開發時可以將測試環境的url寫死到代碼中,但是在提交代碼的時候一定要將他改為線上環境的url,這個就可以通過gitlab中的重點比較部分字元串,給提交者一個強力的提示;

2 其他常見Crash的重點檢查,比如NSMutableString/NSMutableArray/NSMutableDictionary/NSMutableSet 等類下標越界判斷保護,或者 append/insert/add nil對象的保護;

3 ARC下的release操作,UITableViewCell返回nil,以及前面介紹的常見的迴圈引用等。

code review機制,一方面是依賴寫代碼者的代碼習慣及質量,另一名依賴審查者的經驗和細心程度,即使讓多人revew,也可能會漏過一些錯誤,所以我們又添加了代碼的靜態檢查。

代碼靜態檢查

代碼靜態分析(Static Program Analysis)是指在不運行程式的條件下,由代碼靜態分析工具自動對程式進行分析的方法. iOS常見的靜態掃描工具有Clang Static Analyzer、OCLint、Infer,這些主要是用來檢查可能存在的問題,還有Deploymate用來檢查api的相容性。

Clang Static Analyzer

Clang Static Analyzer是一款靜態代碼掃描工具,專門用於針對C,C++和Objective-C的程式進行分析。已經被Xcode集成,可以直接使用Xcode進行靜態代碼掃描分析,Clang預設的配置主要是空指針檢測,類型轉換檢測,空判斷檢測,記憶體泄漏檢測這種等問題。如果需要更多的配置,可以使用開源的Clang項目,然後集成到自己的CI上。

OCLint

OCLint是一個強大的靜態代碼分析工具,可以用來提高代碼質量,查找潛在的bug,主要針對 C、C++和Objective-C的靜態分析。功能非常強大,而且是出自國人之手。OCLint基於 Clang 輸出的抽象語法樹對代碼進行靜態分析,支持與現有的CI集成,部署之後基本不需要維護,簡單方便。

OCLint可以發現這些問題

  • 可能的bug - 空的 if / else / try / catch / finally 語句
  • 未使用的代碼 - 未使用的局部變數和參數
  • 複雜的代碼 - 高圈複雜度, NPath複雜, 高NCSS
  • 冗餘代碼 - 多餘的if語句和無用的括弧
  • 壞味道的代碼 - 過長的方法和過長的參數列表
  • 不好的使用 - 倒邏輯和入參重新賦值

對於OCLint的與原理和部署方法,可以參考團隊成員之前的文章:靜態代碼分析之OCLint的那些事兒,每次提交代碼後,可以在打包的過程中進行代碼檢查,及早發現有問題的代碼。當然也可以在合併代碼之前執行對應的檢查,如果檢查不通過,不能合併代碼,這樣檢查的力度更大。

Infer

Infer facebook開源的靜態分析工具,Infer可以分析 Objective-C, Java 或者 C 代碼,報告潛在的問題。Infer效率高,規模大,幾分鐘能掃描數千行代碼;
C/OC中捕捉的bug類型主要有:

1:Resource leak
2:Memory leak
3:Null dereference
4:Premature nil termination argument

只在 OC中捕捉的bug類型

1:Retain cycle
2:Parameter not null checked
3:Ivar not null checked

結論

Clang Static Analyzer和Xcode集成度更高、更好用,支持命令行形式,並且能夠用於持續集成。OCLint有更多的檢查規則和定製,和很多工具集成,也同樣可用於持續集成。Infer效率高,規模大,幾分鐘能掃描數千行代碼;支持增量及非增量分析;分解分析,整合輸出結果。infer能將代碼分解,小範圍分析後再將結果整合在一起,兼顧分析的深度和速度,所以根據自己的項目特點,選擇合適的檢查工具對代碼進行檢查,減少人力review成本,保證代碼質量,最大限度的避免運行錯誤。

3 測試過程

前面介紹了很多指標的監測,代碼靜態檢查,這些都是性能相關的,真正決定一個App功能穩定是否的是測試環節。測試是發佈之前的最後一道卡,如果bug不能在測試中發現,那麼最終就會觸達用戶,所以一個App的穩定性,很大程度決定它的測試過程。iOS App的測試包括以下幾個層次:單元測試,UI測試,功能測試,異常測試。

單元測試

XCTest是蘋果官方提供的單元測試框架,與Xcode集成在一起,由此蘋果提供了很詳細的文檔XCTest。

Xcode單元測試包含在一個XCTestCase的子類中。依據約束,每一個 XCTestCase 子類封裝一個特殊的有關聯的集合,例如一個功能、用例或者一個程式流。同時還提供了XCTestExpectation來處理非同步任務的測試,以及性能測試measureBlock(),還包括很多第三方測試框架比如:KiWi,Quick,Specta等,以及常用的mock框架OCMock。

單元測試的目的是將程式中所有的源代碼,隔離成最小的可測試單元,以確保每個單元的正確性,如果每個單元都能保證正確,就能保證應用程式整體相當程度的正確性。但是在實際的操作過程中,很多公司都很難徹底執行單元測試,主要就是單元測試代碼量甚至多功能開發,比較難於維護。

對於測試用例覆蓋度多少合適這個話題,也是仁者見仁智者見智,其實一個軟體覆蓋度在50%以上就可以稱為一個健壯的軟體了,要達到70,80這些已經是非常難了,不過我們常見的一些第三方開源框架的測試用例覆蓋率還是非常高的,讓人咋舌。例如,AFNNetWorking的覆蓋率高達87%,SDWebImage的覆蓋率高達77%。

UI測試

Xcode7中新增了UI Test測試,UI測試是模擬用戶操作,進而從業務處層面測試,常用第三方庫有KIF,appium。關於XCTest的UI測試,建議看看WWDC 2015的視頻UI Testing in Xcode。 UI測試還有一個核心功能是UI Recording。選中一個UI測試用例,然後點擊圖中的小紅點既可以開始UI Recoding。你會發現:隨著點擊模擬器,自動合成了測試代碼。(通常自動合成代碼後,還需要手動的去調整)

功能測試

功能測試跟上述的UT和UI測試有一些相通的地方,首先針對各個模塊設計的功能,測試是否達到產品的目的,通常功能測試主要是測試及產品人員,然後還需要進行專項測試,比如我們公司的雲測平臺,會對整個App的性能,穩定性,UI等都進行整體評測,看是否達到標準,對於大規模的活動,還需要進行服務端的壓力測試,確保整個功能無異常。測試通過後,可以進行estFlight測試,到最後正式發佈。

功能測試還包括如下場景:系統相容性測試,屏幕解析度相容性測試,覆蓋安裝測試,UI是否符合設計,消息推送等,以及前面開發過程中需要監控的記憶體、cpu、電量、網路流量、冷啟動時間、熱啟動時間、存儲、安裝包的大小等測試。

異常測試

異常測試主要是針對一些不常規的操作

  • 使用過程中的來電時及結束後,界面顯示是否正常;
  • 狀態欄為兩倍高度時,界面是否顯示正常;
  • 意外斷電後,數據是否保存,數據是否有損害等;
  • 設備充電時,不同電量時的App相應速度及操作流暢度等;
  • 其他App的相互切換,前後臺轉換時,是否正常;
  • 網路變化時的提示,弱網環境下的網路請求成功率等;
  • 各種monkey的隨機點擊,多點觸摸測試等是否正常;
  • 更改系統時間,字體大小,語言等顯示是否正常;
  • 設備存儲不夠時,是否能正常操作;

異常測試有很多,App針對自身的特點,可以選擇性的進行邊界和異常測試,也是保證App穩定行的一個重要方面。

4 發佈及監控

因為移動App的特點,即使我們通過了各種測試,產品最終發佈後,還是會遇到很多問題,比如Crash,網路失敗,數據損壞,賬號異常等等。針對已經發佈的App,主要有一下方式保證穩定性:

熱修複

目前比較流行的熱修複方案都是基於JSPatch、React Native、Weex、lua+wax。

JSPatch能做到通過js調用和改寫OC方法。最根本的原因是 Objective-C 是動態語言,OC上所有方法的調用/類的生成都通過 objective-c Runtime 在運行時進行,我們可以通過類名和方法名反射得到相應的類和方法,也可以替換某個類的方法為新的實現,還可以新註冊一個類,為類添加方法。JSPatch 的原理就是:JS傳遞字元串給OC,OC通過 Runtime 介面調用和替換OC方法。

React Native 是從 Web 前端開發框架 React 延伸出來的解決方案,主要解決的問題是 Web 頁面在移動端性能低的問題,React Native 讓開發者可以像開發 Web 頁面那樣用 React 的方式開發功能,同時框架會通過 JavaScript 與 Objective-C 的通信讓界面使用原生組件渲染,讓開發出來的功能擁有原生App的性能和體驗。

Weex阿裡開源的,基於Vue+Native的開發模式,跟RN的主要區別就在React和Vue的區別,同時在RN的基礎上進行了部分性能優化,總體開發思路跟RN是比較像的。

但是在今年上半年,蘋果以安全為理由,開始拒絕有熱修複功能的應用,但其實蘋果拒的不是熱更新,拒的是從網路下載代碼並修改應用行為,蘋果禁止的是“基於反射的熱更新“,而不是 “基於沙盒介面的熱更新”。而大部分框架(如 React Native、weex)和游戲引擎(比如 Unity、Cocos2d-x等)都屬於後者,所以不在被警告範圍內。而JSPatch因為在國內大部分應用來做熱更新修複bug的行為,所以才回被蘋果禁止。

降級

用戶使用App一段時間後,可能會遇到這樣的情況:每次打開App時閃退,或者正常操作到某個界面時閃退,無法正常使用App。這樣的用戶體驗十分糟糕,如果沒有一個好的解決方案,很容易被用戶刪除App,導致用戶量的流失。因為熱更新基本不能使用,那就只能是App自身修複能力。目前常用的修複能力有:

  • 啟動Crash的監控及修複

1 在應用起來的時候,記錄flag並保存本地,啟動一個定時器,比如5秒鐘內,如果沒有發生Crash,則認為用戶操作正常,清空本地flag。

2 下次啟動,發現有flag,則表明上次啟動Crash,如果flag數組越大,則說明Crash的次數越多,這樣就需要對整個App進行降級處理,比如登出賬號,清空Documents/Library/Caches目錄下的文件。

  • 具體業務下的Crash及修複

針對某些具體業務Crash場景,如果是上線的前端頁面引起的,可以先對前端功能進行回滾,或者隱藏入口,等修複完畢後再上線,如果是客戶端的某些異常,比如資料庫升遷問題,主要是進行業務資料庫修複,緩存文件的刪除,賬號退出等操作,儘量只修複此業務的相關的數據。

  • 網路降級

比如點評App,本身有CIP(公司內部自己研發的)長連接,接入騰訊雲的WNS長連接,UDP連接,HTTP短連接,如果CIP伺服器發生問題,可以及時切換到WNS連接,或者降級到Http連接,保證網路連接的成功率。

線上監控

Crash監控

Crash是對用戶來說是最糟糕的體驗,Crash日誌能夠記錄用戶閃退的崩潰日誌及堆棧,進程線程信息,版本號,系統版本號,系統機型等有用信息,收集的信息越詳細,越能夠幫助解決崩潰,所以各大App都有自己崩潰日誌收集系統,或者也可以使用開源或者付費的第三方Crash收集平臺。

端到端成功率監控

端到端監控是從客戶端App發出請求時計時,到App收到數據數據的成功率,統計對象是:網路介面請求(包括H5頁面載入)的成敗和端到端延時情況。端到端監控SDK提供了監控上傳介面,調用SDK提供的監控API可以將數據上報到監控伺服器中。

整個端到端監控的可以在多個維度上做查詢端到端成功率、響應時間、訪問量的查詢,維度包括:返回碼、網路、版本、平臺、地區、運營商等。

用戶行為日誌

用戶行為日誌,主要記錄用戶在使用App過程中,點擊元素的時間點,瀏覽時長,跳轉流程等,然後基於此進行用戶行為分析,大部分應用的推薦演算法都是基於用戶行為日誌來統計的。某些情況下,Crash分析需要查詢用戶的行為日誌,獲取用戶使用App的流程,幫助解決Crash等其他問題。

代碼級日誌

代碼級別的日誌,主要用來記錄一個App的性能相關的數據,比如頁面打開速度,記憶體使用率,CPU占用率,頁面的幀率,網路流量,請求錯誤統計等,通過收集相關的上下文信息,優化App性能。

總結

雖然現在市面上第三方平臺已經很成熟,但是各大互聯公司都會自己開發線上監控系統,這樣保證數據安全,同時更加靈活。因為移動用戶的特點,在開發測試過程中,很難完全覆蓋所有用戶的全部場景,有些問題也只會在特定環境下才發生,所以通過線上監控平臺,通過日誌回撈等機制,及時獲取特定場景的上下文環境,結合數據分析,能夠及時發現問題,並後續修複,提高App的穩定性。

 

全文總結

本文主要從開發測試發佈等流程來介紹了一個App穩定性指標及監測方法,開發階段主要針對一些比較具體的指標,靜態檢查主要是掃描代碼潛在問題,然後通過測試保證App功能的穩定性,線上降級主要是在儘量不發版的情況下,進行自修複,配合線上監控,信息收集,用戶行為記錄,方便後續問題修複及優化。本文觀點是作者從事iOS開發的一些經驗,希望能對你有所幫助,觀點不同歡迎討論。

 

參考:

微信mars 的高性能日誌模塊 xlog 
基於 CADisplayLink 的 FPS 指示器詳解 
今日頭條iOS客戶端啟動速度優化 
微信讀書 iOS 性能優化總結 
移動端監控體系之技術原理剖析 
美團點評移動網路優化實踐 
iOS 啟動連續閃退保護方案 
微信 SQLite 資料庫修複實踐
轉自https://zhuanlan.zhihu.com/p/28108686

 


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

-Advertisement-
Play Games
更多相關文章
  • 在centos中安裝mysql詳細步驟說明 條件:需要兩個虛擬機,一臺為主伺服器master,一臺為從伺服器slave 1 在master主服務中,創建用於同步的用戶 mysql> grant all privileges on *.* to 'rpl'@'192.168.178.162' iden ...
  • 後端開發:1、高級java軟體架構師實戰培訓視頻教程2、大型SpringMVC,Mybatis,Redis,Solr,Nginx,SSM分散式電商項目視頻教程3、Spark Streaming實時流處理項目實戰4、Java校招面試 Google面試官親授5、Java開發企業級許可權管理系統6、Java ...
  • Where exists 2之前按照個人理解講了基本的select 用法。當然 exists 並不僅僅只能更在select之後。比如update 也可以使用 where exists 繼續之前的講解,我從網上看到說。Where exists 和 In 效率不一樣,就來做個試驗對比一下如何不同。首先創 ...
  • 最近新接觸Mysql,昨天新建一個表用於存儲表結構信息: 然後查詢tablist表: 看看有哪些列沒有comment於是: select * from tablist where COLUMN_COMMENT is null; 查到的結果居然是Empty set。不過從以上查詢結果和navicat都 ...
  • CocoaPods安裝使用詳解 2017.12 首先,很有必要瞭解一下CocoaPods、Ruby和RubyGems,以及它們之間的關係。 CocoaPods是第三方庫的輔助管理工具,依賴於Ruby。 Ruby是一種簡捷的面向對象腳本語言。 RubyGems相當於Ruby的一個管理工具。 以下幾個官 ...
  • 在工作中有時候需要把activity當成dialog使用,其實做法挺簡單的。 1、設置activity的style 2、把該style應用給該activity 要註意一點,如果style的parent是:@android:style/Theme.Dialog,如上面所示 那麼該DialogActiv ...
  • 一:ndk環境搭建 1:開發環境 我使用的是android studio 2.3.3版本,搭建ndk開發環境比較簡單,打開File Settings Appearance&Behavior System Settings Android SDK,選擇SDK Tools,將CMake,LLDB,NDK ...
  • 當下最流行的網路請求組合,retrofit2+okhttp+rxjava+mvp 這裡是封裝記錄篇 首先分模塊,比如登錄 先來說封裝後的使用 package com.fragmentapp.login.presenter; import android.util.Log; import com.fr ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...