onPause耗时导致Activity pause timeout for ActivityRecord的ANR分析

activity的几个生命周期都是在主线程中执行的,因此在主线程中执行耗时操作后再次点击屏幕按键会产生ANR。还有特别需要注意在onPause中尽量不要做过多的耗时操作,可以将耗时操作移动到onStop中,在ActivityStack.java类中,我们可以看到生命周期的超时时间:

  • onPause是500毫秒
  • onStop和onDestory是10秒

 onPause只有500毫秒,根本无法执行较多的释放清理任务,因此,我们可以将耗时较多的操作放到onStop中,起码这样不会在onPause中产生ANR异常

// How long we wait until giving up on the last activity to pause.  This
    // is short because it directly impacts the responsiveness of starting the
    // next activity.
    private static final int PAUSE_TIMEOUT = 500;

    // How long we wait for the activity to tell us it has stopped before
    // giving up.  This is a good amount of time because we really need this
    // from the application in order to get its saved state.
    private static final int STOP_TIMEOUT = 10 * 1000;

    // How long we wait until giving up on an activity telling us it has
    // finished destroying itself.
    private static final int DESTROY_TIMEOUT = 10 * 1000;

 例如下面这两处,都是在生命周期里sleep了9秒,onPause就会触发ANR,而在onStop里则正常响应:

    @Override
    protected void onPause() {
        super.onPause();
        try {
            Log.d(TAG, "onPause 准备sleep9秒");
            Thread.sleep(9000);
            Log.d(TAG, "onPause sleep9秒完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop start");
        try {
            Log.d(TAG, "onStop 准备sleep9秒");
            Thread.sleep(9000);
            Log.d(TAG, "onStop sleep9秒完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

当出现ANR时,我们可以搜索关键字:

WindowManager|ActivityManager|dispatching timed out|NOT RESPONDING|dispatching timed out|pause timeout for ActivityRecord

如果有trace日志的话可以搜索:
Cmd line|main|crashType|ANR

下面通过如下的例子,验证下ANR的产生过程:

“在主线程做了耗时操作”并不一定会引起ANR的发生,除非在耗时操作过程中有用户交互,用户点击或者触摸界面后没有及时响应才会造成ANR的发生因为ANR的意思是应用没有响应,但是耗时操作实际上并不一定会导致没有响应。

完整项目可见:https://github.com/buder-cp/CustomView/blob/master/buder_DN_view/buderdn03/src/main/java/com/test/buderdn03/Main2Activity.java
看下面的程序:

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class Main2Activity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        initView();
        Log.d(TAG, "onCreate start");
    }

    @Override
    protected void onPause() {
        super.onPause();
        try {
            Log.d(TAG, "onPause 准备sleep9秒");
            Thread.sleep(9000);
            Log.d(TAG, "onPause sleep9秒完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop start");
        try {
            Log.d(TAG, "onStop 准备sleep9秒");
            Thread.sleep(9000);
            Log.d(TAG, "onStop sleep9秒完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    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() {
        //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");

    }
}

运行日志:

点击按钮后,主线程sleep了30秒,中间我们没有任何手指触摸操作,30秒后,程序顺利完成了textView的文字更新任务,期间并没有发生ANR。但是如果在等待的30秒期间我们按了返回或者再次点击按钮,过5秒就会出现ANR了

出现ANR如图所示,当然,将点击中的sleep代码移动到onPause声明周期中,同样会出现ANR。


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值