Difficulty in understanding complex multi threading in Android app

给你一囗甜甜゛ 提交于 2019-12-05 22:38:00

First of All,

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.

Secondly,

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:

Updating UI too frequently might also hang the UI, Here's a Solution:

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?

Hope that helps!

Edit: (in response to Comment 1 & 2)

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 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- Also no need to maintain queue and spawn new runnables, just update the data in onEventMainThread.

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

ProcessThread class:

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 method:

public void onEventMainThread(ReadingsUpdateData data){
    mSeries1.appendData(new DataPoint(counter, data), true, 100);
    counter++;
}

Your PlotsRun is in fact too quick: as soon as it finishs its execution, it requeues itself on the main thread loop execution by calling mHandler.post(processPlots);.

First, you need to make your data buffer independent of the data collector and the data visualizer: make an object that can receive (from collector) and deliver (to visualizer) data. So, each component can works quite independently. And your data object is independent of any thread. Your data collector can push data to your data object when it needs, and your main thread can query your data object based on a regular timer.

Then, put a lock on this buffer so that neither of the 2 other objects that need to access the data buffer can do it simultaneously (that will result in a crash). This lock can be a simple synchronized in the method declaration.

That should insure that your app doesn't crash because of concurrency access (that should be your main problem I think).

Then, you can start to optimize your data object by creating additional buffers to store temporary data if main data collection is already in use when new data arrive, or make a copy of actual data for it to be always available to main thread even when new data is currently being added when the main thread query for values.

I would setup something like this:

public class MainActivity extends Activity {

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

    @Override
    protected void onResume() {
        super.onResume();
        testThread.start();
    }

}



public class GraphFragment extends Fragment {

  private Handler mHandler;
  private Queue<ReadingsUpdateData> queue;

  @Override
  public void onActivityCreated(Bundle state) {
    super.onActivityCreated(state);
    mHandler = new Handler(Looper.getMainLooper());
  }

  public void onEvent(ReadingsUpdateData data){
    synchronized(queue){
        queue.add(data);
    }

    if (mHandler != null) {
       mHandler.post(processPlots);
    }
  }

  //implement pause/resume to register/unregister from event bus


 private Runnable processPlots = new Runnable {

        @Override
        public void run() {
            synchronized(queue) {
              if (queue.size()>0) {
                mSeries1.appendData(new DataPoint(counter, queue.poll()), true, 100);
                counter++;
              }
            }
        }
    }          

}

Try using an AsyncTask that can be executed from your Fragment or Activity. Here is a link to the Android Docs for AsyncTask

public class SomeAsyncTask extends AsyncTask<Object,Void, Object>{
        @Override
        protected void onPreExecute(){

        }
        @Override
        protected Object doInBackground(Object… params) {
        //make your request for any data here
           return  getData();


        }
        @Override
        protected void onPostExecute(Object object){
        //update your UI elements here
        mSeries1. appendData(object);           
        }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!