gpt4 book ai didi

java - 难以理解 Android 应用程序中的复杂多线程

转载 作者:太空宇宙 更新时间:2023-11-03 13:19:39 25 4
gpt4 key购买 nike

我在理解我的应用程序中的多线程方面遇到了很大的问题,因此发现了一个错误。我已经检查了所有可能性,但仍然遇到各种(有时是意外)错误。

也许这里有人可以建议我,我应该做什么。

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

  • GraphView - 提供绘图 View
  • EventBus - 为应用程序组件之间的轻松通信提供接口(interface)

  • 至于应用程序,它的结构如下:
               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);
    }
    }

    当我从 fragment 的 onResume() 开始时方法一切都像魅力一样工作。

    不幸的是,正如我所提到的,我正在使用来自另一个线程的外部数据。获取它 GraphFragment我正在使用(根据 documentation ) onEventMainThread()方法。

    在这里,无论我做什么,我都无法传递数据来更新 PlotsRun 中的图表目的。到目前为止,我已经尝试过:
  • 使用 Queue - 在 onEventMainThread 中添加值(value)进入PlotsRun .事实证明,runnable 的读取速度比方法能够更新队列的速度快。
  • 创建各种缓冲区 - 结果与 Queue 完全相同.
  • 调用mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);直接来自 onEventMainThread - 在某些时候它会卡住。
  • 创建 onEvent()我的runnable中的方法并从那里调用mHandler.post() - 它阻塞了 UI,更新看起来像快照。
  • 使用所有提到的有或没有 synchronized()堵塞。

  • 我很难理解的是这个运行正常(在某些时候)。

    因为它是 said在官方 Android 博客上,您无法从非 UI 线程更新 UI。这就是为什么我不能在 GraphFragment 中使用另一个线程的原因.但是当我检查了我的可运行文件时,它正在主线程(UI)上运行。这就是为什么我不能创建无限 while loop而是必须调用 mHandler.post(this) .

    而且它的行为仍然像另一个线程,因为它比 onEventMainThread 更快(更频繁地调用)方法。

    我该怎么做才能使用 ProcessThread 中的数据更新我的图表(或我应该查看的位置) ?

    EDIT1:

    在回答@Matt Wolfe 请求时,我包括了我认为解决此问题的代码中最重要的部分,其中所有必需的变量都显示了它们是如何声明的。这是一个非常简化的例子:
    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);
    }
    }

    由于这个快速阅读问题,添加了 if in runnable 以进行保护。但它不应该在这里,因为应该总是有一些东西(至少我期望如此)。

    还有一件事要补充——当我把简单的 Log.d和计数变量 onEventMainThread它正在更新并正确显示它的值,但不幸的是 logcat 不是主 UI。

    EDIT2:

    这主要是对@MattWolfe comment 的回应

    mHandler 只是在 GrapgFragment 中声明和创建的变量:
    private final Handler mHandler = new Handler();
    private Runnable mTimer2;

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

    我之前没有提到的是 ProcessThread正在向其他 fragment 提供数据 - 不用担心它们不会相互干扰或共享任何资源。这就是我使用 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);
    }
    }

    不幸的是,它也不起作用。

    最佳答案

    首先,

    您后面的示例中的可运行部分只是为了动画实时数据更新,您可以选择调用 appendData()无需创建新的可运行文件。您需要调用appendData()虽然来自主线程。

    其次,

    您可以调用appendData()直接从您的 onEventMainThread 中运行功能,但正如您指出的那样,这种方法有时会挂起 UI,这种行为的一个可能原因是您可能正在发布事件 过于频繁 , 过于频繁地更新 UI 最终会在某个时候挂起 UI。您可以执行以下操作来避免这种情况:

    过于频繁地更新 UI 也可能会导致 UI 挂起,
    这是一个解决方案:


    ProcessThread 中添加一些逻辑保存上次发送的事件时间并在发送新事件之前进行比较,如果差异小于 1 秒,则保存以供稍后发送,当下一次计算完成时,再次比较时间,如果现在大于 1 秒,而不是发送数组中的事件,或者可能只发送最新的事件,因为最新的计算可以代表图的最新状态,对吗?

    希望有帮助!

    编辑: (回应评论 1 和 2)

    我不确定您尝试发布的更新代码是否会提供更好的主意。但我认为您尝试在 onEventMainThread 中实现时间检查功能或在 PlotsRun可运行,对吗?如果是,那恐怕对您没有太大帮助。相反,您需要做的是在 ProcessThread 中实现这次检查检查,并且仅在达到阈值时间时才发布新事件。出于以下原因:

    1-后端的EventBus自动创建一个新的runnable并调用onEventMainThread在里面。所以,处理时间检查里面ProcessThread会在内存中产生更少不需要的可运行文件,从而减少内存消耗。

    2- 也不需要维护队列和生成新的可运行文件,只需更新 onEventMainThread 中的数据.

    以下是 最低码 仅提供概念证明,您需要根据需要对其进行更新:
    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方法:
    public void onEventMainThread(ReadingsUpdateData data){
    mSeries1.appendData(new DataPoint(counter, data), true, 100);
    counter++;
    }

    关于java - 难以理解 Android 应用程序中的复杂多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30403513/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com