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。