ANR 的模拟

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Gdeer/article/details/88736652

ANR 的模拟

ANR 的模拟,需要两步:

  1. 创造 ANR 的环境
  2. 触发 ANR

一、创造 ANR 的环境

产生 ANR 的环境是主线程阻塞,我们只要造成主线程阻塞即可。

可通过以下方式来造成主线程阻塞。

1.1 sleep

public void sleepTest() {
    SystemClock.sleep(100000);
}

1.2 wait

public void waitTest() {
    String s = "";
    synchronized (s) {
        try {
            s.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

1.3 synchronized

public void synchronizedTest() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronizedInThread();
        }
    }).start();
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            synchronizedInMain();
        }
    });
}

public synchronized void synchronizedInThread() {
    SystemClock.sleep(30000);
}

public synchronized void synchronizedInMain() {
}

1.4 staticSynchronized

public void staticSynchronizedTest() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            Util.get();
        }
    }).start();
    Util.get();
}

public class Util {
    public static synchronized void get() {
        SystemClock.sleep(100000);
    }
}

1.5 无限循环

public void forTest() {
    for (; ; ) {
    }
}

二、触发 ANR

ANR 的触发点有:触摸事件ServiceBroadcastReceiverContentProvider

我们在页面设置一个按钮,点击它去产生一个 ANR。以下以 sleepTest 为例去创造 ANR 环境,第一节中所述其他方式与其效果一致。

(测试机型:mix2,Android 8.0)

2.1 触摸事件

/**
 * 点击后,阻塞主线程,然后进行用户操作
 *
 * 连续点击空白处多次(mix2 上需4次),10s 左右有 ANR 的log,再过 10s 左右弹框
 * 或点击返回键(mix2 上只需1次),10s 左右会有 ANR 的 log,再过 10s 左右弹框
 */
public void produceANR(View view) {
    sleepTest();
}

2.2 Service

2.2.1 Service 启动超时

/**
 * 点击后,阻塞主线程,再启动一个 Service
 *
 * 20s 左右会有 ANR 的log,再过 10s 左右有 ANR 的弹框
 */
public void produceANRByService(View view) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(3000);
            Intent intent = new Intent(AnrActivity.this, MyService.class);
            startService(intent);
        }
    }).start();

    sleepTest();
}

2.2.2 Service 处理超时

/**
 * 点击后,启动一个 Service,在 Service 内阻塞主线程
 *
 * 20s 左右会有 ANR 的log,再过 10s 左右有 ANR 的弹框
 */
public void produceANRByService(View view) {
    Intent intent = new Intent(AnrActivity.this, MyService.class);
    startService(intent);
}

public class MyService extends Service {
    ...
    @Override
    public void onCreate() {
        super.onCreate();
        sleepTest();
    }
}

2.2.3 对比

要注意,以上两种情况是不一样的,一个是 Service 外出了问题,一个是 Service 内出了问题。

看一下两种情况的 log:

启动超时 log:
在这里插入图片描述

处理超时 log:

在这里插入图片描述
可以发现,两个 ANR 的触发点是一致的,都是 Service。

再看一下两种情况的 traces.txt:

启动超时 traces:

在这里插入图片描述

处理超时 traces:
在这里插入图片描述

可以发现,两个 ANR 的产生环境是不一样的,一个是在启动 Service 的 Activity 里阻塞,一个是在 Service 自己的生命周期中阻塞。

所以遇到 log 中的 service(或 Receiver)ANR,需要通过 traces 来进一步判断是 Service 自己的问题还是外部环境的问题。

2.3 BroadcastReceiver

2.3.1 Receiver 接收超时

/**
 * 点击后,阻塞主线程,注册并发送一个自定义 BroadCast
 *
 * 发送广播后,60s 左右有 ANR 的 log,再过 10s 左右弹框(自定义广播默认是后台广播)
 */
public void produceANRByOwnBroadCast(View view) {
    IntentFilter filter = new IntentFilter();
    filter.addAction("anr_test");
    registerReceiver(registerReceiver, filter);

    new Thread(new Runnable() {
        @Override
        public void run() {
            SystemClock.sleep(2000);
            Intent intent = new Intent("anr_test");
            sendBroadcast(intent);
        }
    }).start();

    sleepTest();
}

/**
 * 点击后,阻塞主线程,注册一个系统 BroadCast (如 ACTION_TIME_TICK)
 *
 * 系统发送广播后,10s 左右有 ANR 的 log,再过 10s 左右弹框
 */
public void produceANRBySystemBroadCast(View view) {
    IntentFilter filter = new IntentFilter();
    filter.addAction("anr_test");
    filter.addAction(Intent.ACTION_TIME_TICK);
    registerReceiver(registerReceiver, filter);
}

前台广播超时时间为 10s,后台广播超时时间为 60s。自定义广播默认是后台广播,所以会在 60s 后产生 ANR,当通过 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND) 将其设置为前台广播后,那也会在 10s 后出现 ANR。

2.3.2 Receiver 处理超时

/**
 * 点击后,注册一个 BroadCast (如 ACTION_TIME_TICK),在广播的处理内阻塞主线程
 *
 * 收到广播后,10s 左右有 ANR 的 log,再过 10s 左右弹框
 */
public void produceANRByBroadCastHandle(View view) {
    IntentFilter filter = new IntentFilter();
    filter.addAction("anr_test");
    filter.addAction(Intent.ACTION_TIME_TICK);
    registerReceiver(registerReceiver, filter);
}

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        sleepTest();
    }
}

Receiver 的接收超时、处理超时与 Service 的启动超时、处理超时类似。从 log 看,触发点都是 Receiver,产生环境不一样。

2.3.3 对比

接收超时 log:

在这里插入图片描述

处理超时 log:

在这里插入图片描述

接收超时 traces:

在这里插入图片描述

处理超时 traces:

在这里插入图片描述

2.4 ContentProvider

用得不多,以后遇到再补充吧。

猜你喜欢

转载自blog.csdn.net/Gdeer/article/details/88736652
ANR
今日推荐