ANR,代码在单独的线程上运行



背景:

这个应用程序是一个简单的Wifi管理器,可以收集/扫描并在列表视图中显示结果。由于这次扫描是按间隔扫描的,所以我创建了一个线程,并在扫描之间设置了thread.sleep(间隔(,所以在处理完成后,它会休眠X毫秒。

我对代码进行了一些"更改"/更新,现在我的应用程序进入ANR状态,并显示调试器消息"Signal Catcher"]: reacting to signal 3

完全错误:

07-01 10:14:16.772 10027-10034/com.cynetstudios.wifimanager I/art: Thread[2,tid=10034,WaitingInMainSignalCatcherLoop,Thread*=0xa9007000,peer=0x12d1a0a0,"Signal Catcher"]: reacting to signal 3
07-01 10:14:17.101 10027-10034/com.cynetstudios.wifimanager I/art: Wrote stack traces to '/data/anr/traces.txt'

问题:

添加权限之前:

在main的onCreate()中,我用代码启动扫描循环并开始输出数据,这起到了作用,并按预期输出数据。

线程代码:

    t = new Thread(new Runnable() {
        public void run() {
            while (!bStopThread) {
                ThreadCounter++;
                if (bSafe) {
                    initWiFiArrays(); //Scans and inserts data into lists
                    CreateSetAdapter(); //Converts lists into adapter, and sets adapter to ExpandableListView
                    threadRefresh.setText("# Refreshed Times : " + String.valueOf(ThreadCounter));
                    writeResultsToFile(); //Write results to file
                } else
                    stopScan();
                try {
                    Thread.sleep(scanInterval);
                } catch (Exception x) {
                    x.printStackTrace();
                }
            }
        }
    });
    t.start();

添加权限后:

在做了一些研究后,我发现我应该从onCreate中删除这个Initiate scan方法,因为它会阻止任何线程调用。我将它添加到onStart中,并添加了一个方法RunOnUITHD(Runnable r),它只会访问UI来更新基本代码。

新线程代码:

     t = new Thread(new Runnable() {
        public void run() {
            while (!bStopThread) {
                ThreadCounter++;
                if (bSafe) {
                    initWiFiArrays();
                    CreateSetAdapter();
                    RunOnUITHD(new Runnable() {
                        @Override
                        public void run() {
                            threadRefresh.setText("# Refreshed Times : " + String.valueOf(ThreadCounter));
                        }
                    });
                    writeResultsToFile();
                } else
                    stopScan();
                try {
                    Thread.sleep(scanInterval);
                } catch (Exception x) {
                    x.printStackTrace();
                }
            }
        }
    });
    t.start();

处理器定义为:handler = new Handler(context.getMainLooper());

RunOnUITHD

private void RunOnUITHD(Runnable runnable) {
    handler.post(runnable);
}

我仍在接受ANR(最新的ANR追踪(,我不知道自己做错了什么。

更新:

我记录了主线程代码的每一部分,它按预期运行,循环和睡眠,但UI没有更新。。。

将Logging代码添加到new Runnable()中,我注意到以下内容:

将断点添加到ThreadCounter(例如ThreadCounter==11(应该会导致10个"Updaing Thread…"one_answers"Finished update…"循环,如下面的代码所示。

RunOnUITHD(new Runnable() {
    @Override
    public void run() {
        logm("POST HANDLER: updating Thread Refresh Text");
        threadRefresh.setText("# Refreshed Times : " + String.valueOf(ThreadCounter));
        logm("POST HANDLER: Finished update Thread Refresh Text");
    }
});

相反,我只注意到日志中显示的4个循环。

运行进一步的测试ThreadCounter==3,记录的循环数=1,所有进一步的测试都会导致更新setText的1个记录的循环。

由于这个线程是通过@Override onStart()checkPermissions()的自定义方法启动的,所以我显示了一条toast消息,然后是checkPermissions

@Override
protected void onStart() {
    super.onStart();
    Toast.makeText(main.this, "Starting Service", Toast.LENGTH_SHORT).show();
    logm("checking permissions in 'onStart'");
    checkPermissions();
}

通过这些测试,Toast消息显示(不会消失(,setText在1个循环中更新,这意味着threadRefresh TextView包含以下文本:"#Refreshed Times:1">

原来我没有死锁,这是由于Intents中的一个糟糕的选择。

原因:

我用的是Intent。这创建了一个创建PendingIntent(用于点击通知以重新打开应用程序(的服务,这锁定了我的UI线程。所以我上面的代码中没有任何内容影响/导致问题!

我是如何解决的:

使用日志。不是自定义日志,只是简单的Log.i()

我记录了我代码的每一部分:

  1. 从高水平开始,深入到可能的原因。

  2. 如果这不起作用/产生结果,看看你是否有其他类,在我的例子中是ServiceClass,记录下来。

这就是我发现我的代码没有进一步执行的地方,所以追溯到调用代码,我发现我使用了一个Intent,它等待Intent完成后再继续,从而导致我的代码挂起。

解决方案:

应该使用的是用于服务的两种意向之一。

  • 服务
  • IntentService

这些都是经常被问到的并且有很好的文档记录的对象。关于这两种意图类型之间的差异,StackOverflow上有很多问题,这里和这里有两个排名很高的问题,还有一个小的UI和服务问题

我将使用IntentService。

希望这能有所帮助!

最新更新