1. 程式人生 > >主執行緒中Thread.Sleep()是否會導致ANR

主執行緒中Thread.Sleep()是否會導致ANR

前言:

1.對Thread.sleep(long duration)的認知。
       由於CPU分配的每個執行緒的時間片極為短暫(一般為幾十毫秒),所以,CPU通過不停地切換執行緒執行,這樣就給程式設計師一種錯覺,以為多個執行緒是在同時執行。sleep就是正在執行的執行緒主動讓出CPU,CPU去執行其他執行緒,在sleep指定的時間過後,CPU才會回到這個執行緒上繼續往下執行.
 
2.對ANR的理解。

ANR定義:

Application Not Responding,意思是”應用沒有響應“
3.對耗時操作和Thread.sleep(long duration)的認知。
    通常情況下,某些同學對耗時操作的理解就是執行了執行了一定耗時邏輯(比如,while迴圈或者進行了網路請求之類操作)。認為Thread.sleep(long duration)是讓出了當前執行緒的cpu執行權,相當於當前執行緒的休眠,所以不屬於耗時。
    這樣理解比較狹隘,所謂耗時,即當前執行緒停滯不前,不在執行後面的邏輯,因此兩者都能滿足,只不過一個耗時操作把時間耗在了執行耗時邏輯,一個耗時把時間耗在了休眠上。正是基於此,所以大家才會經常使用Thread.sleep(long duration)來模擬耗時操作。
 
正文:

    以前我的理解就是 “在主執行緒做了耗時操作”就會引起ANR,現在我覺得我是錯誤的,為什麼呢?
因為ANR的意思是應用沒有響應,但是耗時操作實際上 並不一定會導致沒有響應。

我對沒有響應的理解是:

 有人(事件或操作)發出了一個請求,但是主執行緒沒有對這個人進行反饋(可能是沒時間、可能是不想理、可能是手被綁住了沒有辦法理你),這個叫沒有響應

下面舉個例子來驗證下。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private TextView testText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }

    private void initView() {
        Button btnTest = findViewById(R.id.btn_test);
        testText = findViewById(R.id.tv_test);
        btnTest.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                testSleep();
            }
        });

    }

    public void testSleep() {
        //todo:10s之後本應該進行更新ui操作,但是由於此時主執行緒處於休眠狀態,因此待主執行緒結束休眠之後才會進行更新ui操作

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "準備更新text");
                testText.setText("update btn text");
                Log.d(TAG, "更新text完成");
            }
        }, 10000);


        try {
            Log.d(TAG, "準備sleep30秒");
            Thread.sleep(30000);
            Log.d(TAG, "sleep30秒完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "first update");
        testText.setText("This is the first update");

    }
   
}

先看執行日誌:
這裡寫圖片描述

這段程式碼在 onCreate 中 sleep 了 30秒,然後更新testText,會出現 ANR 嗎?

答案是

可能會,也可能不會

不會出現ANR的情況:
    如果點選了”測試按鈕“,之後的30s之內,我們沒有進行手動觸控操作(即沒有進行任何操作),則不會發生ANR,這是因為這段程式碼裡面的sleep休眠了執行緒,程式碼裡面的更新操作根本沒有在 sleep的時候被觸發(處於休眠狀態),也就沒有了傳送請求的前提條件,所以並沒有發生ANR。

會出現ANR的情況:
    但是如果使用者手動進行了觸控操作(比如點選螢幕或者按返回鍵),相當於有一個請求的事件了,而主執行緒又被休眠了,超過了規定的時間就會觸發ANR提示。

如圖:
這裡寫圖片描述

好了,你現在對ANR是不是有了進一步的認識呢。
 
補充:

在android裡面對導致ANR的耗時時常進行了常量定義
Android N 的 ANR時間

Service 超時

// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000; // 前臺

// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; // 後臺

Broadcast 超時

// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;  // 前臺
static final int BROADCAST_BG_TIMEOUT = 60*1000;  // 後臺

InputDispatching 超時

 // How long we wait until we timeout on key dispatching.
 static final int KEY_DISPATCHING_TIMEOUT = 5*1000;

ontentProvider 超時

// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;

好了,至此完結,小夥伴如