再談Android AsyncTask的優缺點

来源:http://www.cnblogs.com/yanyojun/archive/2017/02/20/6414919.html
-Advertisement-
Play Games

導語:之前做習慣了Framework層的開發,今天在武漢鬥魚公司面試APP客戶端的開發,其中一道題是講述Asynctask的優缺點,我靠,我只是知道有這麼一個東西,會用而已,看來之前的生活太過於安逸,這麼基礎的東西都不會,特意回家總結下。 轉載請註明原文鏈接:http://www.cnblogs.c ...


導語:之前做習慣了Framework層的開發,今天在武漢鬥魚公司面試APP客戶端的開發,其中一道題是講述Asynctask的優缺點,我靠,我只是知道有這麼一個東西,會用而已,看來之前的生活太過於安逸,這麼基礎的東西都不會,特意回家總結下。

轉載請註明原文鏈接:http://www.cnblogs.com/yanyojun/p/6414919.html

1、Asynctask簡介

1.1 使用方法簡介

Asynctask作為Android的基礎之一,怎麼使用就不多講解了,網上到處都是教程,建議查看Android官方API文檔:https://developer.android.google.cn/reference/android/os/AsyncTask.html

這裡只實現一個小Demo程式,供大家賞玩:

界面:

 這個程式其實特別簡單,就是兩個按鈕,點擊分別用來測試AysncTask和Handler兩種模式的實現,點擊後會有相應的Log提示。

功能簡介:

Asynctask的實現:

   private class IAsyncTask extends AsyncTask<String, Integer, String> {
        protected String doInBackground(String... args1) {
            Log.i(TAG, "doInBackground in:" + args1[0]);
            int times = 10;
            for (int i = 0; i < times; i++) {
                publishProgress(i);//提交之後,會執行onProcessUpdate方法
            }
            Log.i(TAG, "doInBackground out");
            return "over";
        }

        /**
         * 在調用cancel方法後會執行到這裡
         */
        protected void onCancelled() {
            Log.i(TAG, "onCancelled");
        }

        /**
         * 在doInbackground之後執行
         */
        protected void onPostExecute(String args3) {
            Log.i(TAG, "onPostExecute:" + args3);
        }

        /**
         * 在doInBackground之前執行
         */
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute");
        }

        /**
         * 特別贊一下這個多次參數的方法,特別方便
         * @param args2
         */
        @Override
        protected void onProgressUpdate(Integer... args2) {
            Log.i(TAG, "onProgressUpdate:" + args2[0]);
        }
    }

 

點擊第一個按鈕後會執行這裡,點擊按鈕的寫法如下:

        mBtnSyncTask.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new IAsyncTask().execute("yanlog test");
            }
        });

 

執行結果的Log如下:

 1 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//doInbackground是在10824進程,11010線程中執行
 2 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground out 
 3 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:0//剩下的都是在10824線程中執行,Android特別好的是,主線程的線程號跟進程號是一致的
 4 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:1
 5 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:2
 6 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:3
 7 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:4
 8 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:5
 9 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:6
10 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:7
11 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:8
12 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:9
13 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onPostExecute:over

 

 

 

Handler+Message實現:

 主要代碼如下:

 1     private class IHandler extends Handler{
 2         @Override
 3         public void handleMessage(Message msg){
 4             switch(msg.what){
 5                 case 1:
 6                     Log.e(TAG,"handler:"+msg.obj);
 7                     break;
 8                 default:
 9                     break;
10             }
11         }
12     }

 

其中,調用地方如下:

 1         mBtnHandlerTest.setOnClickListener(new View.OnClickListener() {
 2             @Override
 3             public void onClick(View v) {
 4                 final Handler handler = new IHandler();
 5                 new Thread(new Runnable() {
 6                     @Override
 7                     public void run() {
 8                         for (int i = 0; i < 10; i++) {
 9                             Message msg = new Message();
10                             msg.what = 1;
11                             msg.obj = new Integer(i);
12                             Log.e(TAG, "post message:" + i);
13                             handler.sendMessage(msg);
14                         }
15                     }
16                 }).start();
17             }
18         });

 

可以看到Log列印結果如下:

 1 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //可以看到提交是在9319號子進程中提交
 2 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:1
 3 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:2
 4 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:3
 5 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:4
 6 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:5
 7 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:6
 8 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:7
 9 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:8
10 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:9
11 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:0 //可以看到提交完是在9234主線程中執行。
12 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:1
13 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:2
14 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:3
15 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:4
16 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:5
17 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:6
18 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:7
19 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:8
20 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:9

 

以上,簡單梳理了下怎麼實現,不贅言。

1.2 Android 內部源碼實現

關於Handler+Message+Message Queue+Looper的實現就不介紹了,老生常談了。所以下麵主要看一下AsyncTask的源碼實現:

AsyncTask的核心方法應該是

public final AsyncTask<Params, Progress, Result> execute(Params... params)

 那我們就看下當調用了execute方法後,都發生了什麼,下麵是執行的序列圖。

我知道我畫的不夠標準了,湊合著看吧。下麵關於這個圖的一些說明。

  • 在第4步,execute的時候,這個時候可以看到,doInBackground已經轉到子線程中執行了,這個是很關鍵的一個點,我特意用了一個非同步處理的箭頭標註了。
  •  在第9步,當doInbackground執行完,執行到finish方法的時候,由通過sendMessage的方法回到了主線程中了,所以後面的onPostExecute和onCanceled都是在主線程中執行的。

嗯,就這麼多吧。關於AsyncTask的源碼我上傳到github中了,大家對照著源碼看會更清楚一點。

https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java

關於AsyncTask的源碼分析,還有一篇博客寫的很好,請參看:

http://www.infocool.net/kb/OtherMobile/201610/197431.html

備註:

源碼裡面還有三個地方值得深究下,分別是:

  • FutureTask值得看下,回頭寫了博客我把鏈接貼在這裡
  • AsyncTask中的SerialExecutor類寫的太漂亮了,回頭單獨寫一個博客欣賞下。
  • 關於上面的ThreadPollExecutor我其實沒有研究。。。回頭寫個博客研究下。

 

2、優點

簡單,快捷

這個說法就是近乎於扯淡吧,主要還是看使用習慣,我就挺喜歡用Handler的。

但是Android定義了這個東西,可以看到各種消息封裝的還是很不錯的,很規範。大家可以按照這個“優美的框架”來寫,代碼不會太出格。

3、缺點

3.1  AsyncTask實際上後臺線程之後一個!!!

今天仔細研究了下源碼,發現網上寫的大部分是錯的,AsyncTask的真正的後臺線程只有一個!!不信,看下麵的代碼:

我們首先定義一個IAsyncTAsk,其中的doInBackground方法這麼寫:

    private class IAsyncTask extends AsyncTask<String, Integer, String> {
        protected String doInBackground(String... args1) {
/*            Log.i(TAG, "doInBackground in:" + args1[0]);
            int times = 10;
            for (int i = 0; i < times; i++) {
                publishProgress(i);//提交之後,會執行onProcessUpdate方法
            }
            Log.i(TAG, "doInBackground out");*/

            Log.i(TAG, "doInBackground in thread:" + args1[0]);
            try {
                int times = 4;
                for (int i = 0; i < times; i++) {
                    Log.i(TAG, "thread alive:" + i + " for times"+args1[0]); //這個doInBackground就列印一個Log,然後sleep 20 毫秒
                    Thread.sleep(20);
                }
            } catch (Exception e) {

            }
            return "over";
        }

 

調用的地方這麼寫:

1                 int N = 5;
2                 for (int i = 0; i < N; i++) {
3                     Log.d(TAG,"asyncTask post Task:"+i);
4                     new IAsyncTask().execute("asyncTask times:"+i); //點擊Button後,在onClick方法中建立5個後臺子線程。
5                 }

 

我們來看列印結果:

 1 02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //在主線程中進行提交操作
 2 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:1
 3 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:2
 4 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:3
 5 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:4
 6 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:0 //可以看到,雖然系統開起了18067、18068、
 7 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069,18070這幾個子線程,但是這幾個子線程
 8 02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //是串列執行的!!!震驚了有沒有!!!
 9 02-20 21:48:08.253 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:0 //這不是巧合,試了好幾次都是這樣!!
10 02-20 21:48:08.273 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:0
11 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:1
12 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:1
13 02-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:1
14 02-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:1
15 02-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:1
16 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:2
17 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:2
18 02-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:2
19 02-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:2
20 02-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:2
21 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:3
22 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:3
23 02-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:3
24 02-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:3
25 02-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:3
26 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:4
27 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:4
28 02-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:4
29 02-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:4
30 02-20 21:48:08.606 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:4

 

你本來希望系統應該這麼執行

但是實際上系統是這麼執行的:

那麼從源碼看下為啥會這樣吧。

AsyncTask中預設的Exector是這個private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;看下SERIAL_EXECUTOR是這麼定義的

 1 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //這是一個串列處理的Executor
 2 .........................
 3     private static class SerialExecutor implements Executor {
 4         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
 5         Runnable mActive;
 6 
 7         public synchronized void execute(final Runnable r) {
 8             mTasks.offer(new Runnable() { //先把要執行的子線程統一丟到mTasks隊列中,這其中封裝一遍Runnable
 9                 public void run() {
10                     try {
11                         r.run();
12                     } finally {
13                         scheduleNext(); //當前面一個子線程處理完,開始處理下一個
14                     }
15                 }
16             });
17             if (mActive == null) {
18                 scheduleNext();
19             }
20         }
21 
22         protected synchronized void scheduleNext() { //依次從隊列中取下一個元素,串列執行
23             if ((mActive = mTasks.poll()) != null) {
24                 THREAD_POOL_EXECUTOR.execute(mActive); 
25             }
26         }
27     }

 

呵呵,這下子明白了吧。

Google為什麼要怎麼實現我不得而知,估計有什麼我沒有明白的好處在裡面吧。那麼有沒有辦法規避呢?

可以看到上面有一個THREAD_POOL_EXECUTOR,這個也是一個executor是這麼定義的

1     static {
2         ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3                 CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
4                 sPoolWorkQueue, sThreadFactory);
5         threadPoolExecutor.allowCoreThreadTimeOut(true);
6         THREAD_POOL_EXECUTOR = threadPoolExecutor;
7     }

 

可以看到這個是允許一定數量的子線程並行處理的。

其中參數是這麼定義的

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE_SECONDS = 30;

 按照一般理解,允許同時運行的CORE進程是4個,MAXIMUM_POOL_SIZE是17個。(註:這個數字是我用榮耀8手機跑出來的,其他手機可能會有不同)

而Android中的AsyncTask提供了一個方法:

1     public static void setDefaultExecutor(Executor exec) {
2         sDefaultExecutor = exec;
3     }

 

 所以規避方法是:通過這個方法可以設置預設的Exector,但是這個方法是hide的,也就是Google的隱藏方法,估計需要用一下反射來處理。我偷個懶,不去實現了。

4、總結

   本來嘛,我只是想簡單寫一下AsyncTask的一些相關知識,Copy一下網上的內容,但是沒有想到寫到最後,發現網上的大部分東西是錯的,或者沒有抓到重點。看來以後還是要自己親自看代碼,紙上得來終覺淺,便知此事要躬行。

  本文中用到的工程代碼可以到我的github中查看,路徑:https://github.com/YanYoJun/AsyncTaskTest

  好了,寫完了,大家看到之後覺得還可以的話,點個贊哈。


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

-Advertisement-
Play Games
更多相關文章
  • SEO全稱為Search Engine Optimization,中文解釋為搜索引擎優化。一般指通過對網站內部調整優化及站外優化,使網站滿足搜索引擎收錄排名需求,在搜索引擎中提高關鍵詞排名,從而把精準用戶帶到網站,獲得免費流量,產生直接銷售或品牌推廣的效果。 那麼,如何提高搜索引擎的搜索結果呢? 一 ...
  • 截取字元串(指定長度) 把字元串轉換成數字 使用正則表達式判斷數字 ...
  • var Type = {};for ( var i = 0, type; type = [ 'String', 'Array', 'Number' ][ i++ ]; ){ (function( type ){ Type[ 'is' + type ] = function( obj ){ retur ...
  • 點擊頁面中的鏈接,瀏覽器會根據源碼中相對URL路徑作不同的處理: (1)有協議名稱,但沒有功能變數名稱信息 對於這種形式的URL,它的協議,路徑,查詢字元串和片段ID都以它自身為準,但功能變數名稱信息的部分,以引用它的那個頁面地址為準。 (2)沒有協議名,但有功能變數名稱信息 協議名稱由原發起頁面確定,而所有接下來的URL ...
  • 一.Css規則主要由兩部分組成 1.選擇器 2.一條或多條聲明a. 選擇器主要作用是為了確定需要改變樣式的HTML元素b. 每一條聲明由一個屬性和一個值組成,使用花括弧來包圍聲明,屬性與值之間使用冒號(:)分開,多條聲明用分號(;)隔開. 例如:form {width: 200px;margin: ...
  • 1、Math.trunc() 該方法用於取出一個小數的小數部分,返回整數部分。看例子吧: 2、Math.sign() 該方法用來判斷一個數到底是正數,負數,還是0。有五中返回值。看下麵例子吧: 參數為正數時,返回1; 參數為0時,返回0; 參數為-0時,返回-0; 參數為負數時,返回-1; 參數為其 ...
  • 頁面上使用js寫了一個獲取後臺數據的方法 }); tab.html(parentStr); } }) } 其中的 發現點擊無效無效 原來是 ajax載入新dom之前js 就載入完了,事件當然沒有綁定到新載入的dom上 解決方法: 使用jquery的委托事件,將該方法委托到頁面已經存在的一個節點上 問 ...
  • 由於新項目的的需求,需要畫環形圖,由於以前都沒接觸過這一類(我是菜鳥),去cocochina山找到了一個案例,個人覺得還可以,分享一下 github 地址https://github.com/zhouxing5311/ZZCircleProgress 這個使用起來非常的簡單 作者開放了很多介面,可以 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...