У меня есть большая проблема с пониманием многопоточности в моем приложении и из-за этого найти ошибку. Я проверил, я думаю, все возможности, и все же я получаю различные (иногда неожиданные) ошибки.Сложность в понимании сложной многопоточности в приложении для Android
Возможно, кто-то здесь сможет мне посоветовать, что я должен делать.
В моем проекте я использую две внешние библиотеки:
- GraphView - открывается вид на графике рисунок
- EventBus - обеспечивает интерфейс для легкого обмена данными между компонентами приложения
Что касается приложения он имеет следующую структуру:
MainActivity
/ \
/ \
Thread Fragment
(ProcessThread) (GraphFragment)
Идея состоит в том, что ProcessThread
вычисляет данные и обеспечивает постоянный поток значений до GraphFragment
по порядку EventBus
. В GraphFragment
У меня есть один Series
, который требуется GraphView
.
Для обновления графиков в режиме реального времени в соответствии с example мне нужно сделать новый Runnable
, так что я сделал один:
private class PlotsRun implements Runnable{
@Override
public void run() {
mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);
counter++;
mHandler.post(this);
}
}
и когда я начинаю его из фрагмента onResume()
метод все работает как шарм.
К сожалению, как я уже упоминал, я использую внешние данные из другого потока. Чтобы получить его в GraphFragment
, я использую (в соответствии с методом documentation) onEventMainThread()
.
И здесь, независимо от того, что я сделаю, я не могу передать данные, чтобы обновить свой график в PlotsRun
объекте. До сих пор я пытался:
- с помощью
Queue
- добавить значение вonEventMainThread
и получить вPlotsRun
. Оказалось, что runnable читает быстрее, чем метод способен обновлять очередь. - создание различных буферов - результат вполне такой же, как с
Queue
. - , вызывающий
mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);
непосредственно отonEventMainThread
- в какой-то момент он получает freez. - создание
onEvent()
метод внутри мой runnable и звонок оттудаmHandler.post()
- он блокирует пользовательский интерфейс и обновления выглядят как моментальные снимки. - используя все, упомянутое с или без
synchronized()
блок.
Для меня довольно сложно понять, что это работает, который работает правильно (в какой-то момент).
Как это said на официальном блоге Android вы не можете обновлять пользовательский интерфейс из неинтерфейса. Вот почему я не могу использовать другой поток внутри GraphFragment
. Но когда я проверил свой runnable, он работает в основном потоке (UI).Вот почему я не могу создать бесконечный while loop
, вместо этого нужно позвонить mHandler.post(this)
.
И все же он ведет себя как другой поток, потому что он быстрее (чаще), затем onEventMainThread
.
Что я могу сделать, чтобы обновлять мои графики (или где я должен смотреть) с использованием данных от ProcessThread
?
EDIT1:
Отвечая на Вульфа @ Matt просить я в том числе то, что я думаю, что это самая важная часть кода для этой проблемы со всей необходимой переменной показал, как они были объявлены. Это очень упрощенный пример:
MainActivity
:
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);
}
}
Условный в работоспособной добавляется для защиты из-за этого к быстрой проблеме чтения. Но этого не должно быть, потому что всегда должно быть что-то (по крайней мере, я этого ожидаю).
Еще одна вещь, которую нужно добавить - когда я положил простой Log.d
и подсчитал переменную внутри onEventMainThread
, она обновляла и отображала ее значение правильно, но, к сожалению, logcat не является основным пользовательским интерфейсом.
EDIT2:
В основном это реакция на @MattWolfe comment
mHandler просто переменная объявлена и создана в GrapgFragment:
private final Handler mHandler = new Handler();
private Runnable mTimer2;
Да, это правильно, я использую mHandler.post()
без каких-либо задержек. Я попытаюсь использовать некоторую задержку, чтобы увидеть, есть ли разница.
Что я не упоминал ранее, так это то, что ProcessThread
предоставляет также данные другим фрагментам - не беспокойтесь, они не мешают друг другу или не разделяют какие-либо ресурсы. Вот почему я использую EventBus
.
EDIT3:
Это код, который я использовал в качестве моего друга идеи с другой нитью в 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);
}
}
К сожалению, он не работает ни.
Вы заглянули в метод runOnUIThread? http://stackoverflow.com/questions/11140285/how-to-use-runonuithread – Proxy32
Это довольно сложно понять, что именно пытается сделать ваш код. Если вы хотите подавать большой набор данных в свой фрагмент и обновлять пользовательский интерфейс на основе этого набора данных, вам необходимо синхронизировать доступ к набору данных. Это означает, что вам нужен синхронизированный блок в любом месте, где вы добавляете/удаляете данные из набора данных в обоих потоках. –
Вам нужно показать больше кода для фрагмента и фоновой задачи, чтобы мы лучше поняли, как и когда вы получаете доступ к данным. Если вы можете настроить упрощенный пример, который будет лучше. –