java-难以理解Android应用中的复杂多线程
作者:互联网
我在理解我的应用程序中的多线程方面遇到了很大的问题,并且因此发现了一个错误.我检查了所有的可能性,但仍然遇到各种错误(有时是意外的).
也许有人可以给我建议,我应该怎么做.
在我的项目中,我使用了两个外部库:
> GraphView-提供用于图形绘制的视图
> EventBus-提供界面,方便应用程序组件之间的通信
至于应用程序,其结构如下:
MainActivity
/ \
/ \
Thread Fragment
(ProcessThread) (GraphFragment)
这个想法是ProcessThread计算数据并通过EventBus向GraphFragment提供恒定的值流.在GraphFragment中,我有一个GraphView所需的系列.
要根据example实时更新图形,我需要制作一个新的Runnable,所以我做了一个:
private class PlotsRun implements Runnable{
@Override
public void run() {
mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);
counter++;
mHandler.post(this);
}
}
当我从fragment的onResume()方法启动它时,一切都像一个魅力一样工作.
不幸的是,正如我提到的那样,我正在使用另一个线程的外部数据.为了在GraphFragment中获取它,我正在使用(根据documentation)onEventMainThread()方法.
在这里,无论我做什么,我都无法传递数据来更新PlotsRun对象中的图形.到目前为止,我已经尝试过:
>使用队列-在onEventMainThread中添加值并获取PlotsRun.事实证明,runnable的读取速度快于方法更新队列的速度.
>创建各种缓冲区-结果与Queue完全相同.
>调用mSeries1.appendData(new DataPoint(counter,getRandom()),true,100);直接从onEventMainThread-在某个时候它会冻结.
>在我的可运行对象内部创建onEvent()方法并从那里调用mHandler.post()-它阻止了UI,并且更新看起来像快照.
>使用提到的所有内容(带或不带sync()块).
对于我来说,很难理解的是这个可运行的(正确的)运行状态.
由于官方Android博客上的版本为said,因此您无法通过非UI线程更新UI.这就是为什么我不能在GraphFragment中使用另一个线程的原因.但是,当我检查了可运行对象时,它正在主线程(UI)上运行.这就是为什么我不能在那里创建无限的while循环,而不得不调用mHandler.post(this)的原因.
而且它仍然像另一个线程一样工作,因为它比onEventMainThread方法更快(更频繁地调用).
我该怎么做才能使用ProcessThread中的数据更新图形(或应显示的位置)?
编辑1:
回答@Matt Wolfe的请求时,我包括我认为是此问题代码中最重要的部分,其中所有必需的变量均显示了它们的声明方式.这是非常简化的示例:
主要活动:
private ProcessThread testThread = new ProcessThread();
@Override
protected void onResume() {
super.onResume();
testThread.start();
}
private class ProcessThread extends Thread{
private float value = 0f;
private ReadingsUpdateData updater = new ReadingsUpdateData(values);
public void run() {
while(true) {
value = getRandom();
updater.setData(value);
EventBus.getDefault().post(updater);
}
}
}
GraphFragment:
private LineGraphSeries<DataPoint> mSeries1;
long counter = 0;
private Queue<ReadingsUpdateData> queue;
@Override
public void onResume() {
super.onResume();
mTimer2.run();
}
public void onEventMainThread(ReadingsUpdateData data){
synchronized(queue){
queue.add(data);
}
}
private class PlotsRun implements Runnable{
@Override
public void run() {
if (queue.size()>0) {
mSeries1.appendData(new DataPoint(counter, queue.poll()), true, 100);
counter++;
}
mHandler.post(this);
}
}
因此,在快速运行中添加了if in runnable来进行保护.但这不应该在这里,因为总应该有东西(至少我希望如此).
要添加的另一件事-当我将简单的Log.d放在onEventMainThread中并对其进行计数时,它正在正确更新并正确显示其值,但是不幸的是logcat不是主UI.
编辑2:
这主要是对@MattWolfe comment的回复
mHandler只是在GrapgFragment中声明和创建的变量:
private final Handler mHandler = new Handler();
private Runnable mTimer2;
是的,没错,我正在使用mHandler.post(),没有任何延迟.我会尝试使用一些延迟来看看是否有任何区别.
我之前没有提到的是,ProcessThread还向其他片段提供数据-不用担心它们不会互相干扰或共享任何资源.这就是为什么我使用EventBus.
编辑3:
这是我在GraphFragment和runOnMainThread方法中与另一个线程一起使用的另一个代码的代码:
private MyThread thread = new MyThread();
private class MyThread extends Thread {
Queue<ReadingsUpdateData> inputList;
ReadingsUpdateData msg;
public MyThread() {
inputList = new LinkedList<>();
}
public void run() {
while(true) {
try{
msg = inputList.poll();
} catch(NoSuchElementException nse){
continue;
}
if (msg == null) {
continue;
}
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);
counter++;
}
});
}
}
public void onEvent(ReadingsUpdateData data){
inputList.add(data);
}
}
不幸的是,它都不起作用.
解决方法:
首先,
您后面的示例中的runnable部分只是为了动画化实时数据更新,您可以选择调用appendData()而不创建新的runnable.您需要从主线程调用appendData().
其次,
您可以直接从onEventMainThread函数调用appendData()函数,但正如您所指出的那样,这种方法有时会挂起UI,此行为的一个可能原因是您可能过于频繁地发布事件,而过于频繁地更新UI最终会挂起用户界面.您可以执行以下操作来避免这种情况:
过于频繁地更新用户界面也可能会挂起用户界面,
这是一个解决方案:
在ProcessThread中放入一些逻辑以保存上次发送的事件时间,并在发送新事件之前对其进行比较,如果相差小于1秒,则将其保存以供稍后发送,并且在下一次计算完成后,再次比较时间(如果是)现在大于1秒,而不是按数组发送事件,或者可能仅发送最新事件,因为最新的计算可以代表图形的最新状态,对吗?
希望有帮助!
编辑:(以回应评论1& 2)
我不确定您尝试过的是发布更新的代码会带来更好的主意.但我认为您尝试在onEventMainThread或可运行的PlotsRun中实现时间检查功能,对吗?如果可以的话,恐怕对您没有多大帮助.您需要做的是在ProcessThread中实施此时间检查检查,并且仅在达到阈值时间时才发布新事件.由于以下原因:
1-后端上的EventBus自动创建一个新的可运行对象,并在其中调用onEventMainThread.因此,在ProcessThread中处理时间检查将在内存中生成较少的不需要的可运行对象,从而减少内存消耗.
2-也无需维护队列并生成新的可运行对象,只需更新onEventMainThread中的数据即可.
以下是仅提供概念证明的最低限度代码,您需要根据需要进行更新:
ProcessThread类:
private class ProcessThread extends Thread{
private static final long TIME_THRESHOLD = 100; //100 MS but can change as desired
private long lastSentTime = 0;
private float value = 0f;
private ReadingsUpdateData updater = new ReadingsUpdateData(values);
public void run() {
while(true) {
if (System.currentTimeMillis() - lastSentTime < TIME_THRESHOLD) {
try {
Thread.sleep(TIME_THRESHOLD - (System.currentTimeMillis() - lastSentTime));
} catch (InterruptedException e) {}
}
value = getRandom();
updater.setData(value);
EventBus.getDefault().post(updater);
lastSentTime = System.currentTimeMillis();
}
}
}
onEventMainThread方法:
public void onEventMainThread(ReadingsUpdateData data){
mSeries1.appendData(new DataPoint(counter, data), true, 100);
counter++;
}
标签:android-graphview,multithreading,greenrobot-eventbus,java,android 来源: https://codeday.me/bug/20191028/1951376.html