从错误的线程异常Android调用

时间:2013-12-22 21:33:01

标签: java android multithreading

我正在尝试创建一个应用程序,当您触摸屏幕时,屏幕顶部会出现雪花并慢慢下降。我将每个雪花添加到一个arraylist,以便我可以使每个雪花落下。这是我的代码:

Runnable runable = new Runnable(){
    @Override
    public void run(){
        while(true){
            letTheSnowFall();
        }
    }
};

public void letTheSnowFall(){
    for(int i = 0; i < snowArray.size(); i++){
        snowArray.get(i).setY(snowArray.get(i).getY() + 0.01f);
    }
}

我在onCreate()方法中启动线程:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    layout = (RelativeLayout) findViewById(R.id.activity_main);

    setContentView(layout);
    layout.setOnClickListener(listener);

    Thread myThread = new Thread(runable);
    myThread.start();
}

4 个答案:

答案 0 :(得分:1)

首先,您无法从后台线程更新UI。

其次,没有延迟的无限后台线程是非常糟糕的代码。

第三,请勿在{{1​​}}中拨打setContentView()两次。您不需要第二个(onCreate())。

实现此目标的最轻量级方法是使用任何setContentView(layout)(例如postDelayed())上提供的View来安排在延迟后获得控制权。您可以致电layout取消postDelayed()工作,并传入与removeCallbacks()相同的Runnable

postDelayed()

(来自this sample project的代码)

在您的情况下,/*** Copyright (c) 2012 CommonsWare, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. From _The Busy Coder's Guide to Android Development_ http://commonsware.com/Android */ package com.commonsware.android.post; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class PostDelayedDemo extends Activity implements Runnable { private static final int PERIOD=5000; private View root=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); root=findViewById(android.R.id.content); } @Override public void onResume() { super.onResume(); run(); } @Override public void onPause() { root.removeCallbacks(this); super.onPause(); } @Override public void run() { Toast.makeText(PostDelayedDemo.this, "Who-hoo!", Toast.LENGTH_SHORT) .show(); root.postDelayed(this, PERIOD); } } 会更新您的雪花,而不是显示run()

答案 1 :(得分:1)

要更新视图,您需要从主线程更新它。显然,你做错了。您可以将信息抛出到主线程并进行更新。也许,您可以尝试使用Android构建的线程(AsyncTasks)更新视图,在doInBackGround()上计算结果,并在onPostExecute()方法上发布结果。

答案 2 :(得分:0)

如果我理解正确,雪花就是观点。所以snowArray的类型为ArrayList<SubtypeOfView>。如果是,则抛出异常,因为您从主thred之外的线程调用View.setY()方法。几乎所有与Android GUI框架相关的方法都必须从主线程(执行回调,例如Activity.onCreate())的线程中调用。

答案 3 :(得分:0)

我猜你的snowArray包含一堆View(或其众多子类之一)的实例?在这种情况下,您尝试从非GUI线程修改GUI组件,这是不允许的,从而抛出CalledFromWrongThreadException

“简单”解决方案是在需要修改UI的任何地方调用Activity.runOnUiThread。如果您已经在单独的线程上完成了一些处理并且现在可以在GUI上显示结果,那么这很有用。

但是,您想要进行大量的GUI更新。更糟糕的是,因为你在无限循环中进行更新:while(true) { letTheSnowFall(); }。这件事真正无限:它永远不会停止,你的雪将永远地永远落下,Y坐标迅速增加并最终溢出。你不会看到这种情况发生,因为一旦GUI更新,你就会立即再次更新它:没有延迟

你要做的事情要简单得多:你只想让雪落下。您希望动画您的雪花视图从其当前位置开始并向下结束一段距离。 Android有a whole package for animations以及一些very good guides