【问题标题】:Android While-Loop using Thread使用线程的 Android While-Loop
【发布时间】:2014-09-14 13:28:33
【问题描述】:

我正在尝试创建一个线程,该线程将运行一个 While 循环,并在 while 循环内执行将更改 UI 的操作:

public void GameHandler()
{
    layout = (RelativeLayout) findViewById(R.id.MyLayout);
    params=new RelativeLayout.LayoutParams(30,40);
    btn1 = (Button) findViewById(R.id.MovingObj);
    xBounds=new int[2];
    yBounds=new int[2];
    xBounds[0]=0;
    xBounds[1]=layout.getWidth();
    yBounds[0]=0; //will change once i set up the screen settings !
    yBounds[1]=layout.getHeight();
    selectFlyingObject();

    Runnable myRunnable = new Runnable() {
        @Override
        public void run() {
            while (CurrentXLocation <= xBounds[1] + 10) {
                CurrentXLocation = CurrentXLocation + 1;
                params.leftMargin = CurrentXLocation;
                params.topMargin = 50;
                btn1.setLayoutParams(params);
                SystemClock.sleep(1000 - SpeedRate);
            }
        }
    };
    Thread myThread = new Thread(myRunnable);
    myThread.start();
}

但是我的第一个问题是出现以下错误:

 Only the original thread that created a view hierarchy can touch its views.

第二个问题是我听说在后台使用线程会消耗太多的设备 CPU,这可能会导致我的应用程序崩溃。确实会发生这样的事情吗? 谢谢你的回答

【问题讨论】:

  • “我听说在后台使用线程会消耗太多的设备 CPU”。这有点像说“我听说冬天会太冷”。虽然在某些情况下可能是这样,但这取决于很多因素。如果您想避免 ANR 等,最好将繁重的计算放在其他(非 UI)线程中。
  • @Ordous:我建议复制的问题的答案描述了 OP 针对这个问题提出的问题的解决方案(我过去自己使用过的解决方案)。我想说这与这个问题有很大关系。
  • 如果我只想更新 android ui 一次,您发布的线程将非常有帮助,但我的线程每 1 秒或更短时间更新一次 ui!如何使用您每秒发布的线程中的信息来更改按钮的坐标???
  • @kasf 在你让你的线程“休眠”后发布它,在循环内..

标签: android multithreading loops user-interface while-loop


【解决方案1】:

“我听说...”并不是一个特别可靠的信息来源。在 Android 中使用后台线程绝对没问题。每秒产生不死线程确实会导致应用程序崩溃,就像制作无限循环一样 - 并且使用来自其他开发人员的相同 cmets。

您的“问题”是不言自明的——正如每个关于 UI 工具包中 Java 并发性的教程所述——不要从后台线程更新 UI。使用众多机制中的一种向 UI 线程发送信号以执行此操作。在您的情况下 - Timer 非常适合(它会每 x 秒唤醒一次并执行一个可运行文件)。

【讨论】:

    【解决方案2】:

    首先,我建议阅读this article,因为您当前使用的结构有点..脏..

    无论如何,为了响应您收到的实际错误,很明显您不能从后台线程更新 UI 线程。 为什么 ?因为如果可以的话,想象一下 10 个不同的线程同时更新 UI .. 这会给应用程序带来错误的外观,不是吗? 这也意味着每个 UI 元素都需要被锁定、同步,以提供或多或少一致的行为。这将破坏让线程执行“艰苦工作”的目的,它们将变得依赖于 UI ..

    (如果有人可以给出非常明确的理由/参考指南和/或理由评论它,我会调整答案)

    因此,在 Android 中,您可以获得主 Looper,这是一个用于按顺序执行任务的实用程序,在指定线程上执行。 我们在此所做的,实际上是将我们的操作排队以在主线程上执行。

    public class GameHandler implements Runnable {
    
        Handler mHandler = new Handler(Looper.getMainLooper());
    
        Layout mLayout;
        View mView;
        int currentX, mSpeedRate;
    
        public GameHandler(Layout layout, View viewToAnimate, int speedRate) {
            mLayout = layout;
            mView = viewToAnimate;
            mSpeedRate = speedRate;
        }
    
        public void handleGame() {
    
    
            final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(30, 40);
    
            int[] xBounds = new int[2];
            int[] yBounds = new int[2];
            xBounds[0] = 0;
            xBounds[1] = mView.getWidth();
            yBounds[0] = 0; //will change once i set up the screen settings !
            yBounds[1] = mView.getHeight();
            selectFlyingObject();
    
            while ( currentX <= xBounds[1] + 10 ) {
                currentX = currentX + 1;
                params.leftMargin = currentX;
                params.topMargin = 50;
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        mView.setLayoutParams(params);
                    }
                });
                SystemClock.sleep(1000 - mSpeedRate);
            }
        }
    
        void selectFlyingObject() {
            // No idea what you're doing here, but keep in mind you're still working on the
            // background thread if you call this method from somewhere not wrapped in the mHandler
        }
    
        @Override
        public void run() {
            handleGame();
        }
    }
    

    免责声明,此代码未经测试且来自我的脑海。如果它没有做您认为应该做的事情,请随时提及。

    结构已调整,您现在要做的就是按照您尝试的方式启动线程,或者使用Executor

    Thread myThread = new Thread(new GameHandler());
    myThread.start();
    

    如果我对这个主题不够清楚,这些链接仅供参考:

    【讨论】:

    • 非常感谢 :) 稍作修改后它确实有效,我明白了它是如何工作的!再次感谢您
    • @kasf 很高兴听到。请记住,您的 UI 现在没有挂起的原因是您在发出更新 UI 的命令之后在线程上执行SystemClock.sleep()。因此,您基本上是在更新 UI 后等待。如果您删除SystemClock.sleep(),那么您将以与线程工作相同的速度更新 UI。哪个(关于)归结为在 UI 线程上进行工作,并使其再次挂起。很高兴我能提供帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 2014-01-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多