背景:
这个应用程序是一个简单的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()
我记录了我代码的每一部分:
-
从高水平开始,深入到可能的原因。
-
如果这不起作用/产生结果,看看你是否有其他类,在我的例子中是ServiceClass,记录下来。
这就是我发现我的代码没有进一步执行的地方,所以追溯到调用代码,我发现我使用了一个Intent,它等待Intent完成后再继续,从而导致我的代码挂起。
解决方案:
应该使用的是用于服务的两种意向之一。
- 服务
- IntentService
这些都是经常被问到的并且有很好的文档记录的对象。关于这两种意图类型之间的差异,StackOverflow上有很多问题,这里和这里有两个排名很高的问题,还有一个小的UI和服务问题
我将使用IntentService。
希望这能有所帮助!