2013-12-22 3 views
-1

Я пытаюсь создать приложение, которое при касании экрана в верхней части экрана появляется снежинка и медленно падает. Я добавляю каждую снежинку к арраисту, чтобы я мог сделать каждую снежинку осенью. Вот мой код:Called From Wrong Thread Exception Android

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(); 
} 
+0

Как вы инициализируете 'snowArray'? Вы уверены, что он доступен из 'myThread' – NitroNbg

+2

Вы вызываете код рисунка (?) В потоке, это не поток пользовательского интерфейса, и, вероятно, он пытается добавить снежинки в окно? Попробуйте отправить свой 'letTheSnowFall()' в '' '' '' '' '' '' Обработчик' или использовать метод 'runOnUiThread'. – Darwind

ответ

1

Во-первых, вы не можете обновить пользовательский интерфейс от фонового потока.

Во-вторых, бесконечная фоновая нить без задержек является чрезвычайно плохим кодом.

В-третьих, просьба не звонить setContentView() дважды в onCreate(). Вам не нужен второй (setContentView(layout)).

Самый легкий вес способ для достижения этой цели является использование postDelayed(), доступный на любом View (например, layout), чтобы организовать, чтобы получить контроль в будущем после задержки. Вы можете отменить postDelayed() работу по телефону removeCallbacks(), проходя в том же Runnable, как вы использовали с postDelayed():

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

(код из this sample project)

В вашем случае, run() обновит свои снежинки вместо показывая Toast.

+0

Так что бы корень был в моем случае, будет ли он одним и тем же? – Cj1m

+0

@ Cj1m: Это может быть одно и то же, или любой другой «вид», такой как ваш «макет». – CommonsWare

0

Если я правильно понимаю, снежинки - это виды. Таким образом, snowArray имеет тип ArrayList<SubtypeOfView>. Если это так, исключение выбрасывается, потому что вы вызываете метод View.setY() из потока, отличного от основного. Почти все методы, связанные с инфраструктурой графического интерфейса Android, должны вызываться из основного потока (поток, который выполняет обратные вызовы, такие как Activity.onCreate()).

+0

они фактически ImageViews, но да – Cj1m

+0

В любом случае ваш подход не самый лучший для решения этой проблемы. Если вы хотите использовать только библиотеку Android, я бы предложил реализовать пользовательский вид (подкласс View), который будет содержать код рендеринга. В противном случае вы должны использовать более совершенную графическую библиотеку. Библиотека Android слишком ограничена. Одним из примеров такой библиотеки является LibGDX, который на самом деле является игровым движком. – GareginSargsyan

0

Я предполагаю, что ваш snowArray содержит кучу экземпляров View (или один из многих его подклассов)? В этом случае вы пытаетесь изменить компонент GUI из потока, отличного от GUI, который не разрешен и, таким образом, выбрасывает CalledFromWrongThreadException.

«Простое» решение состояло бы в том, чтобы вызвать Activity.runOnUiThread, где вам нужно изменить пользовательский интерфейс. Это полезно, если вы сделали некоторую обработку в отдельном потоке и теперь готовы показать результаты в графическом интерфейсе.

Однако вы хотите сделать много обновлений графического интерфейса пользователя. Это еще хуже, поскольку вы делаете обновления в бесконечном цикле: while(true) { letTheSnowFall(); }. Эта вещь действительно бесконечно: она никогда не остановится, и ваш снег будет постоянно падать навсегда, при этом Y-координата быстро увеличивается и, в конце концов, переполняется. Вы не увидите, что это произойдет, поскольку, как только графический интерфейс обновлен, вы сразу же обновляете его: есть без задержки.

То, что вы пытаетесь сделать, намного проще: вы просто хотите, чтобы снег падал. Вы хотите, чтобы анимировать ваши виды снежных чешуек, начиная с их текущего положения и заканчивая на некотором расстоянии вниз. Android имеет a whole package for animations вместе с некоторыми very good guides.

+0

Я планирую удалить снег, когда он уходит с экрана, и добавляет небольшую задержку, чтобы сделать его «менее бесконечным». – Cj1m

+0

Тем не менее, вы должны заглянуть в анимацию. Это упростит вашу жизнь, так как вы можете полагаться на структуру, чтобы управлять временем и анимацией. Вы также можете использовать более продвинутые функции, такие как ускорители или наборы анимаций. Кроме того, большинство анимаций в Android можно сделать с помощью графического процессора, что приводит к гораздо более плавному опыту. –

+0

В результате мне придется переписать много приложений? – Cj1m

1

Чтобы обновить представление, вам необходимо обновить его из основного потока. Ясно, что вы делаете это неправильно. Вы можете передать информацию в основной поток и обновить ее. Возможно, вы можете попробовать обновить представление с помощью встроенных потоков Android (AsyncTasks), вычислить результаты на doInBackGround() и опубликовать результаты по методу onPostExecute().

Смежные вопросы