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

2024-02-21

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

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

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

  • 图形视图 https://github.com/jjoe64/GraphView- 提供图形绘制视图
  • EventBus https://github.com/greenrobot/EventBus- 提供应用程序组件之间轻松通信的接口

至于应用程序,它的结构如下:

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

这个想法是ProcessThread计算数据并提供恒定的值流GraphFragment贯穿EventBus. In GraphFragment我有一个Series要求由GraphView.

根据数据实时更新图表example http://www.android-graphview.org/documentation/realtime-updates我需要制作一个新的Runnable所以我做了一个:

private class PlotsRun implements Runnable{

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

当我从片段开始时onResume()方法一切都很顺利。

不幸的是,正如我所提到的,我正在使用来自另一个线程的外部数据。为了把它放进去GraphFragment我正在使用(根据文档 https://github.com/greenrobot/EventBus/blob/master/HOWTO.md) onEventMainThread() method.

在这里,无论我做什么,我都无法传递数据来更新我的图表PlotsRun目的。到目前为止我已经尝试过:

  • using Queue- 增加价值onEventMainThread并进入PlotsRun。事实证明,可运行的读取速度比方法更新队列的速度快。
  • 创建各种缓冲区 - 结果与Queue.
  • calling mSeries1.appendData(new DataPoint(counter, getRandom()), true, 100);直接来自onEventMainThread- 在某些时候它会冻结。
  • 创造onEvent()我的可运行对象中的方法并从那里调用mHandler.post()- 它阻塞了 UI 并且更新看起来像快照。
  • 使用或不使用提到的所有内容synchronized() block.

对我来说很难理解的是这个可运行的程序(在某些时候)可以正常工作。

因为它是said http://android-developers.blogspot.com/2009/05/painless-threading.html在官方 Android 博客上,您无法从非 UI 线程更新 UI。这就是为什么我不能在里面使用另一个线程GraphFragment。但是当我检查我的可运行程序时,它正在主线程(UI)上运行。这就是为什么我无法创造无限while loop那里必须打电话mHandler.post(this).

而且它的行为仍然像另一个线程,因为它更快(调用更频繁)onEventMainThread method.

我该怎么做才能使用来自的数据更新我的图表(或我应该查看的位置)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);
        }
    }

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

还要补充一件事 - 当我简单地说Log.d并计算里面的变量onEventMainThread它正在更新并正确显示它的值,但不幸的是 logcat 不是主 UI。

EDIT2:

这主要是对@MattWolfe的回应comment https://stackoverflow.com/questions/30403513/difficulty-in-understanding-complex-multi-threading-in-android-app?noredirect=1#comment48897550_30403513

mHandler 只是在 GrapgFragment 中声明和创建的变量:

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

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

我之前没有提到的是ProcessThread还向其他片段提供数据 - 不用担心它们不会互相干扰或共享任何资源。这就是我使用的原因EventBus.

EDIT3:

这是我在另一个线程中用作另一个想法的代码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);
        }
    }

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


首先,

下面的示例中的可运行部分只是为了动画实时数据更新,您可以选择调用appendData()无需创建新的可运行程序。你需要打电话appendData()不过从主线程。

第二,

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

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

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

希望有帮助!

Edit:(回应评论1和2)

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

1-后端的EventBus自动创建一个新的可运行对象并调用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 method:

public void onEventMainThread(ReadingsUpdateData data){
    mSeries1.appendData(new DataPoint(counter, data), true, 100);
    counter++;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

难以理解 Android 应用程序中的复杂多线程 的相关文章

  • Android:canvas.drawBitmap() 方法无法正常工作

    我已经发布了两个与此相关的问题 请参考此自定义饼图 1 https stackoverflow com questions 28343600 customize pie chart in quarter shape at the botto
  • 带有 backstack Resume 的嵌套片段

    在我的应用程序中有几个fragments in an activity我正在维护一个backStack对于这些fragment 一切都很好 但其中有一个嵌套的片段 当我把它放入backStack然后再次按后退按钮恢复 该片段看起来与先前的内
  • PhoneGap 1.4 封装 Sencha Touch 2.X - 性能怎么样?

    我正在构建一个多平台平板电脑应用程序 仅使用其 Webview 使用 Phonegap 1 4 对其进行包装 然后使用 Sencha Touch 2 框架发挥我的魔力 我所说的多平台是指 iOS 5 X 和 Android 3 0 目前 到
  • 如何使用 JMagick 转换色彩空间?

    如何使用 JMagick API 转换色彩空间 例如 CMYK gt RGB 和 RGB gt CMYK None
  • Java继承,扩展类如何影响实际类

    我正在查看 Sun 认证学习指南 其中有一段描述了最终修饰符 它说 如果程序员可以自由地扩展我们所知的 String 类文明 它可能会崩溃 他什么意思 如果可以扩展 String 类 我是否不会有一个名为 MyString 的类继承所有 S
  • fprintf() 线程安全吗?

    我正在为野人就餐问题的某些变量编写一个 C 解决方案 现在 我创建线程 每个线程都将 FILE 获取到同一个调试文件 在线程内我正在使用 fprintf 进行一些打印 打印的语句不受任何类型的互斥锁等保护 我没有在调试文件中观察到任何交错行
  • 在 Java 中获取并存储子进程的输出

    我正在做一些需要我开始子处理 命令提示符 并在其上执行一些命令的事情 我需要从子进程获取输出并将其存储在文件或字符串中 这是我到目前为止所做的 但它不起作用 public static void main String args try R
  • 轻松的反应

    我有一个与这里描述的类似的案例 动态更改RESTEasy服务返回类型 https stackoverflow com questions 3786781 dynamically change resteasy service return
  • Java Swing - 如何禁用 JPanel?

    我有一些JComponents on a JPanel我想在按下 开始 按钮时禁用所有这些组件 目前 我通过以下方式显式禁用所有组件 component1 setEnabled false 但是有什么办法可以一次性禁用所有组件吗 我尝试禁用
  • 以编程方式向 LinearLayout 添加边框

    我该如何添加以编程方式LinearLayout 的边框 假设我们创建了这个布局 LinearLayout TitleLayout new LinearLayout getApplicationContext TitleLayout setO
  • Spring @Cacheable 和 @Async 注解

    我需要缓存一些异步计算的结果 具体来说 为了克服这个问题 我尝试使用 Spring 4 3 缓存和异步计算功能 作为示例 我们采用以下代码 Service class AsyncService Async Cacheable users C
  • 在java中以原子方式获取多个锁

    我有以下代码 注意 为了可读性 我尽可能简化了代码 如果我忘记了任何关键部分 请告诉我 public class User private Relations relations public User relations new Rela
  • java XMLSerializer 避免复杂的空元素

    我有这个代码 DocumentBuilderFactory factory DocumentBuilderFactory newInstance DocumentBuilder builder factory newDocumentBuil
  • 如何解决 greenDAO 在执行 InsertOrReplace 时“不存在这样的表错误”?

    我正在使用 greenDAO 并且已成功生成所有必需的类和实体 并且我可以看到我的表已创建 但是在要替换的行上放置断点后 我收到一条错误消息 告诉我 不存在这样的表错误 try appTimeUsageDao insertOrReplace
  • Android View Canvas onDraw 未执行

    我目前正在开发一个自定义视图 它在画布上绘制一些图块 这些图块是从多个文件加载的 并将在需要时加载 它们将由 AsyncTask 加载 如果它们已经加载 它们只会被绘制在画布上 这工作正常 如果加载了这些图片 AsyncTask 就会触发v
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • Java RMI - 客户端超时

    我正在使用 Java RMI 构建分布式系统 它必须支持服务器丢失 如果我的客户端使用 RMI 连接到服务器 如果该服务器出现故障 例如电缆问题 我的客户端应该会收到异常 以便它可以连接到其他服务器 但是当服务器出现故障时 我的客户端什么也
  • MiniDFSCluster UnsatisfiedLinkError org.apache.hadoop.io.nativeio.NativeIO$Windows.access0

    做时 new MiniDFSCluster Builder config build 我得到这个异常 java lang UnsatisfiedLinkError org apache hadoop io nativeio NativeIO
  • Android Espresso 单击按钮时出现错误

    我正在尝试使用 espresso 框架为 Android 应用程序编写一些 UI 测试 现在我只是检查启动屏幕上是否存在所有元素 然后尝试单击登录按钮 单击按钮时 测试由于错误而失败 我似乎无法理解为什么会发生这种情况 我的测试代码是 Ru
  • View.post(),以及当Runnables被执行时

    我最初的问题是需要知道我的根的高度和宽度View这样我就可以进行程序化的布局更改 就我的目的而言 我不一定需要在onCreate 对于我来说 以编程方式添加我的孩子就足够了View根布局完成后 因此我很乐意使用onWindowFocusCh

随机推荐

  • 从 iOS 服务检测屏幕开/关

    我正在开发一个作为服务在后台运行的网络监控应用程序 当屏幕打开或关闭时是否可以收到通知 来电 它通过使用以下代码存在于Android中 private void registerScreenOnOffReceiver IntentFilte
  • 使用nodejs进行heroku部署失败

    我正在尝试将本地文件推送到 heroku 并遇到以下错误 我的代码在github https github com asimkh apps tree haz 有人可以帮我吗 谢谢 heroku buildpacks set heroku n
  • 如何检测 STAMINA 模式?

    您能帮助我 如何以编程方式检测索尼设备上的 STAMINA 模式吗 我想通知用户 如果 STAMINA 打开 我的应用程序将无法正常工作 因为它阻止了 AlarmManager 并且设备在我需要时不会被唤醒 我也想知道这个 我想一个天真的方
  • Flask WTform 对多个字段进行验证

    根据两个或多个条目验证 WTform 的最佳方法是什么 IE 在下面的表格中 我想验证数据库中尚不存在具有所提供名称和地址的公司 class CompanyForm FlaskForm name StringField Company Na
  • 如何在bash中生成笛卡尔积?

    我想生成这样的文件 笛卡尔积 1 3 X 1 5 1 1 1 2 1 3 1 4 1 5 2 1 2 2 2 3 2 4 2 5 3 1 3 2 3 3 3 4 3 5 我可以使用嵌套循环来做到这一点 例如 for i in seq 3 d
  • 无法执行dex:Java堆空间 Java堆空间

    在 Eclipse IDE 中执行 Web 驱动程序脚本时 出现 Unable toexecute dex Java heap space Java heap space 错误 我已经使用 Android SDK 和 AVD Manager
  • Java 流压缩两个列表

    我有一个人员哈希集 一个人有名字 姓氏和年龄 例如 Person Hans Man 36 我的任务是获取 17 岁以上人员的列表 按年龄对他们进行排序 并将名字与姓氏连接起来 比如 汉斯 曼 另一个名字 另一个名字 我刚刚被允许导入 imp
  • 禁用 Reactjs 中的依赖下拉选项

    我正在制作一个简单的反应应用程序 其中有一些下拉列表 其中一个依赖于另一个 gt 这里下拉菜单 1 的值为游戏类型 例如Indoor and Outdoor gt 这里下拉菜单 2 的值为运动类型 例如Chess Tennis and Fo
  • “perf stat”输出是什么意思?

    I use perf stat 命令对一些事件进行统计 root root test perf stat a e r81d0 r82d0 v a r81d0 71800964 1269047979 1269006431 r82d0 2665
  • Graphics.Drawstring 在 PictureBox 中看起来不错,但在 Bitmap 中却很糟糕

    我正在尝试使用DrawString将文本写为图像 然后将其旋转 90 度 它无论是在位图上还是直接在 PictureBox 上都可以正常工作 但最大的区别在于质量 这PictureBox绘制的文本质量很好而且看起来很漂亮 当我把它画在图像上
  • 使用 jimp 在 Node.js 中调整图像大小并获取新图像的路径

    我正在使用 jimp 调整 node js 中的图像大小 我成功地降低了图像质量 但有点困惑如何获取新图像的路径 Jimp read test jpg function err test if err throw err test resi
  • 事件循环和Promise之间有什么关系[重复]

    这个问题在这里已经有答案了 我很好奇Event Loop和Promise之间的关系 演示暴露了这个问题 我预计p1 fulfilled出现在中间 因为它们将任务排队到同一个任务队列中并逐个执行 var p1 new Promise func
  • Fortran 中的断言

    Fortran 是否有与 C 等效的标准函数 关键字assert 我找不到assert我在 Fortran 2003 标准中提到过 我发现了几种使用预处理器的方法 但是在这个answer https stackoverflow com a
  • Sparklyr 处理分类变量

    Sparklyr 处理分类变量 我来自 R 背景 习惯于在后端处理分类变量 作为因子 对于 Sparklyr 来说 使用起来相当混乱string indexer or onehotencoder 例如 我有许多变量在原始数据集中被编码为数值
  • R 中的 For 循环与 while 循环

    我在 R 工作时注意到一件奇怪的事情 当我有一个使用 for 循环和 while 循环实现的计算从 1 到 N 的平方的简单程序时 行为并不相同 在这种情况下我不关心矢量化或应用函数 fn1 lt function N for i in 1
  • pandas dataframe:loc 与查询性能

    我在 python 中有 2 个数据框 我想查询数据 DF1 4M 记录 x 3 列 这query功能好像多了 效率比loc功能 DF2 2K 记录 x 6 列 这loc功能似乎多了 效率比query功能 两个查询都返回一条记录 模拟是通过
  • 即使禁用 SSR,评估 SSR 模块也会出错 - svelte-kit

    我希望我的应用程序中的路线之一不要在服务器端渲染 这样做的方法是export const ssr false在模块脚本或设置中ssr false in svelte config js如中提到的精简文档 https kit svelte d
  • 在 null 上调用成员函数 helper()

    我正在尝试找出 codeigniter 但目前无法插入到我的数据库中 我一直在关注官方文档 成功从数据库中读取数据 我的错误完整 遇到未捕获的异常类型 错误 消息 在 null 上调用成员函数 helper 文件名 Library WebS
  • Android 录音

    我想知道如何使用 android 中的 AudioRecord 功能而不是 MediaRecorder 来录制我们的声音和播放 请给我示例代码或网址 提前致谢 我认为您可以从使用以下原型代码开始 import android media A
  • 难以理解 Android 应用程序中的复杂多线程

    我在理解应用程序中的多线程方面遇到了很大的问题 因此发现了一个错误 我已经检查过我认为所有的可能性 但仍然遇到各种 有时是意外的 错误 也许这里有人可以建议我 我应该做什么 在我的项目中 我使用两个外部库 图形视图 https github