难以理解的Andr​​oid应用复杂的多线程 [英] Difficulty in understanding complex multi threading in Android app

查看:201
本文介绍了难以理解的Andr​​oid应用复杂的多线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有我的应用程序,并因为要找到一个错误的理解多线程大问题。我检查,我认为所有的可能性,仍然我得到不同的(有时会有意想不到的)错误。

I have big problem with understanding multi threading in my application and because of that finding a bug. I've checked I think all possibilities and still I am getting various (sometimes unexpected) errors.

也许有人在这里将能够指点我,我应该做的。

Maybe someone here will be able to advice me, what I should do.

在我的项目,我用两个外部库:

In my project I am using two external libraries:

  • GraphView - provides views for graph drawing
  • EventBus - provides interface for easy communication between app components

对于应用它具有结构是这样的:

As for the app it has the structure like this:

           MainActivity
            /        \
           /          \
        Thread        Fragment
   (ProcessThread)   (GraphFragment)

的想法是, ProcessThread 计算数据和 GraphFragment throught EventBus 。在 GraphFragment 我有一个系列所要求 GraphView

The idea is that ProcessThread computes data and provides constant stream of values to GraphFragment throught EventBus. In GraphFragment I have one Series required by GraphView.

要根据例如我需要实时更新图一个新的的Runnable 所以我做了一:

To update graphs in real time according to the example I need to make a new Runnable so I did one:

private class PlotsRun implements Runnable{

        @Override
        public void run() {
            mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);
            counter++;
            mHandler.post(this);
        }
}

和,当我从片段的 onResume()方法,一切都开始它正在像一个魅力。

and when I start it from fragment's onResume() method everything is working like a charm.

不幸的是,因为我已经提到过我使用从另一个线程外部数据。为了得到它在<​​code> GraphFragment 我使用(根据的文档 onEventMainThread()方法。

Unfortunately as I've mentioned I am using external data from another thread. To get it inGraphFragment I am using (according to the documentation) onEventMainThread() method.

和这里无论什么我会尽我不能通过数据来更新我的图PlotsRun 对象。到目前为止,我已经试过:

And in here no matter what I'll do I can't pass data to update my graph in PlotsRun object. So far I've tried:


  • 使用队列 - 在 onEventMainThread 增加价值并获得 PlotsRun 。原来,这是可运行比阅读速度的方法是能够更新队列中。

  • 创建各种缓冲 - 结果是相当同样与队列

  • 调用 mSeries1.appendData(新数据点(计数器,getRandom()),真实,100); 直接从 onEventMainThread - 在某些时候它会FREEZ

  • 里面创建可运行我的的onEvent()方法,并从那里调用 mHandler.post() - 它是阻塞UI和更新看起来像快照。

  • 与使用或提及的一切,而不同步()块。

  • using Queue - add value in onEventMainThread and get in PlotsRun. It turned out that runnable is reading faster than method is able to update queue.
  • creating various buffers - the result is quite this same as with Queue.
  • calling mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100); directly from onEventMainThread - at some point it gets freez.
  • creating onEvent() method inside my runnable and call from there mHandler.post() - it is blocking UI and updates looks like snapshots.
  • using everything mentioned with or without synchronized() block.

什么是相当困难的,我理解是这样的可运行哪些工作正常(在某种程度上)。

What is quite difficult for me to understand is this runnable which is working correctly (at some point).

由于它是在Android官方博客,你可以从非UI线程T更新UI。这就是为什么我不能用在 GraphFragment 另一个线程。但是,当我检查我可以运行它是在主线程(UI)上运行。这就是为什么我不能创造无限的 while循环,而不是有必须调用 mHandler.post(本)

As it is said on official Android blog you can't update UI from non UI thread. This is why I can't use another thread inside GraphFragment. But when I've checked my runnable it is running on main thread (UI). This is why I can't create infinite while loop there instead have to call mHandler.post(this).

和它仍然表现得像另一个线程,因为它更快(称为更频繁),那么 onEventMainThread 方法。

And still it behaves like another thread because it is faster (called more frequently) then onEventMainThread method.

我能做些什么才能够更新我的图形(或者我应该看看)利用 ProcessThread

What can I do to be able to update my graphs (or where I should look) using data from ProcessThread?

EDIT1:

在回答有关@马特·沃尔夫要求我包括我认为是一个code这个问题,显示它们是如何声明的所有必需的变量中最重要的组成部分。这是非常简单的例子:

Answering on @Matt Wolfe request I am including what I think is the most important part of a code for this problem with all required variable shown how they are declared. It is very simplified example:

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);
        }
    }

如果在可运行添加的保护,因为这对快速阅读的问题。但它不应该在这里,因为应该总是一些(至少我希望)。

The if in runnable is added for protection because of this to fast reading problem. But it shouldn't be here because there should always be something (at least I expect that).

还有一点要补充 - 当我把简单的 Log.d 和里面的 onEventMainThread 这是更新计数变量并显示它的值正确,但遗憾的是logcat的不是主界面。

One more thing to add - when I put simple Log.d and counting variable inside onEventMainThread it was updating and displaying it's value correctly, but unfortunately logcat isn't main UI.

EDIT2:

这主要是响应了@MattWolfe <一个href=\"http://stackoverflow.com/questions/30403513/difficulty-in-understanding-complex-multi-threading-in-android-app?noredirect=1#comment48897550_30403513\">comment

This is mainly response for @MattWolfe comment

该mHandler只是变量声明和GrapgFragment创建:

The mHandler is just variable declared and created in GrapgFragment:

private final Handler mHandler = new Handler();
private Runnable mTimer2;

是的,这是正确的,我使用 mHandler.post()没有任何延迟。我会尝试使用一些延迟,看看是否有任何区别。

Yes, that is right I am using mHandler.post() without any delay. I'll try using some delay to see if there is any difference.

我没有提到早前的是, ProcessThread 也提供数据到其他片段 - 不要担心他们不互相干扰或共享任何资源。这就是为什么我使用 EventBus

What I didn't mention earlier is that the ProcessThread is providing also data to other fragments - don't worry they don't interfere with each other or share any resources. This is why I am using EventBus.

EDIT3:

这一个code,我已经在 GraphFragment 作为我的另一个想法与另一个线程和 runOnMainThread 方法:

This a code that I've used as my another idea with another thread in GraphFragment and runOnMainThread method:

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);
        }
    }

不幸的是,它不工作既不

Unfortunately, it isn't working neither.

推荐答案

所有首先,

在后面的例子可运行的部分仅仅是动画的实时数据更新的缘故,你可以选择调用 appendData(),而无需创建一个新的可运行。你需要调用 appendData()从主线程虽然。

The runnable part in your followed example is just for the sake of animating realtime data updating, You can choose to call appendData() without creating a new runnable. You need to call appendData() from main thread though.

二,

您可以从 onEventMainThread 函数直接调用 appendData()的功能,但正如你指出,这方法有时会挂起的用户界面,一个可能的原因这种行为,你很可能发布事件的过于频繁,UI更新过于频繁地在某些时候将最终挂UI。你可以做到以下几点,以避免这种情况:

You can call the appendData() function directly from your onEventMainThread function, but as you pointed out that this approach sometimes hangs the UI, One possible reason for this behaviour is that you are probably posting events too frequently, Updating UI too frequently would ultimately hang the UI at some point. You can do the following to avoid this:

更新UI过于频繁也可能会挂起的UI,
这里有一个解决方案:

把一些逻辑 ProcessThread 去年发送的事件都保存和发送一个新的前比较,如果差值小于1秒比保存以后发送并且在下一计算完成后,再次进行比较时,如果是大于1秒现在,比发送事件阵列,或者可以是发送只是最新事件,作为最新的计算可以重新present最新状态图中的吧?

Put some logic in ProcessThread to save last sent event time and compare it before sending a new one and if the difference is less than 1 second than save it for sending later and when the next computation is done, compare the time again, if it is greater than 1 second now, than send the events in array or may be send just the latest event, as the latest computation can represent the latest state of graph right?

希望帮助!

编辑:(响应注释1和2)

我不知道你试过可能会发布更新后的code会给一个更好的想法是什么。但我认为你试过 onEventMainThread 实施时间检查功能,或者在 PlotsRun 可运行的,是正确的?如果是的话比我恐怕不会有多少对你的帮助。你需要做的,而不是什么是贯彻落实这次检查内部ProcessThread检查,如果达到阈值时间只有后新的事件。对于原因如下:

I am not sure what you tried may be posting your updated code would give a better idea. but I think you tried to implement time check functionality in onEventMainThread or in PlotsRun runnable, is that correct? If yes than I am afraid that wouldn't be of much of help for you. What you need to do instead is to implement this time checking check inside ProcessThread and only post new event if threshold time is reached. For following reasons:

1 EventBus在后台自动创建一个新的可运行和电话 onEventMainThread 。因此,处理内部 ProcessThread 时检查会催生少不必要的可运行到这将导致内存消耗更少的内存。

1- EventBus on the backend automatically creates a new runnable and calls onEventMainThread in it. So, handling time check inside ProcessThread would spawn less unwanted runnables into the memory which would result in less memory consumption.

2 - 也没必要维持队列中,产生新的可运行,只需更新 onEventMainThread 数据。

2- Also no need to maintain queue and spawn new runnables, just update the data in onEventMainThread.

下面是一个最低限度code 以提供概念验证而已,您需要根据您的需要来更新它:

Below is a bare minimum code to provide proof of concept only, You would need to update it according to your needs:

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++;
}

这篇关于难以理解的Andr​​oid应用复杂的多线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆