thread services example create android multithreading

services - Android "Solo el hilo original que creó una jerarquía de vistas puede tocar sus vistas".



threadpool android (20)

He construido un reproductor de música simple en Android. La vista para cada canción contiene un SeekBar, implementado así:

public class Song extends Activity implements OnClickListener,Runnable { private SeekBar progress; private MediaPlayer mp; // ... private ServiceConnection onService = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder rawBinder) { appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer progress.setVisibility(SeekBar.VISIBLE); progress.setProgress(0); mp = appService.getMP(); appService.playSong(title); progress.setMax(mp.getDuration()); new Thread(Song.this).start(); } public void onServiceDisconnected(ComponentName classname) { appService = null; } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.song); // ... progress = (SeekBar) findViewById(R.id.progress); // ... } public void run() { int pos = 0; int total = mp.getDuration(); while (mp != null && pos<total) { try { Thread.sleep(1000); pos = appService.getSongPosition(); } catch (InterruptedException e) { return; } catch (Exception e) { return; } progress.setProgress(pos); } }

Esto funciona bien. Ahora quiero un temporizador que cuente los segundos / minutos del progreso de la canción. Así que puse un TextView en el diseño, lo obtengo con findViewById() en onCreate() , y lo puse en run() después de progress.setProgress(pos) :

String time = String.format("%d:%d", TimeUnit.MILLISECONDS.toMinutes(pos), TimeUnit.MILLISECONDS.toSeconds(pos), TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes( pos)) ); currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time);

Pero esa última línea me da la excepción:

android.view.ViewRoot $ CalledFromWrongThreadException: Solo el hilo original que creó una jerarquía de vistas puede tocar sus vistas.

Sin embargo, estoy haciendo básicamente lo mismo aquí que con SeekBar : crear la vista en onCreate y luego tocarla en run() , y no me da esta queja.


Al usar AsyncTask, actualice la interfaz de usuario en el método onPostExecute

@Override protected void onPostExecute(String s) { // Update UI here }


Debe mover la parte de la tarea en segundo plano que actualiza la interfaz de usuario en el hilo principal. Hay un simple código para esto:

runOnUiThread(new Runnable() { @Override public void run() { // Stuff that updates the UI } });

Documentación para Activity.runOnUiThread .

Simplemente anide esto dentro del método que se ejecuta en segundo plano y luego copie y pegue el código que implementa cualquier actualización en medio del bloque. Incluya solo la menor cantidad de código posible, de lo contrario, comenzará a anular el propósito del hilo de fondo.


En mi caso, las personas que llaman muchas veces en poco tiempo obtendrán este error, simplemente puse el chequeo de tiempo en reposo para no hacer nada si es demasiado corto, por ejemplo, ignorar si la función se llama en menos de 0,5 segundos:

private long mLastClickTime = 0; public boolean foo() { if ( (SystemClock.elapsedRealtime() - mLastClickTime) < 500) { return false; } mLastClickTime = SystemClock.elapsedRealtime(); //... do ui update }


En mi caso, tengo EditText en el adaptador, y ya está en el hilo de la interfaz de usuario. Sin embargo, cuando se carga esta actividad, se bloquea con este error.

Mi solución es que necesito eliminar <requestFocus /> de EditText en XML.


Esta es la traza de pila de la excepción mencionada.

at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6149) at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:843) at android.view.View.requestLayout(View.java:16474) at android.view.View.requestLayout(View.java:16474) at android.view.View.requestLayout(View.java:16474) at android.view.View.requestLayout(View.java:16474) at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352) at android.view.View.requestLayout(View.java:16474) at android.widget.RelativeLayout.requestLayout(RelativeLayout.java:352) at android.view.View.setFlags(View.java:8938) at android.view.View.setVisibility(View.java:6066)

Así que si vas a cavar entonces vienes a saber.

void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }

Donde mThread se inicializa en el constructor como abajo

mThread = Thread.currentThread();

Todo lo que quiero decir es que cuando creamos una vista particular, la creamos en el subproceso de la interfaz de usuario y luego intentamos modificarla en un subproceso de trabajo.

Podemos verificarlo a través del siguiente fragmento de código

Thread.currentThread().getName()

cuando inflamos el diseño y luego donde se está obteniendo la excepción.


Estaba trabajando con una clase que no contenía una referencia al contexto. Entonces no me fue posible usar runOnUIThread(); Utilicé view.post(); y fue resuelto.

timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { final int currentPosition = mediaPlayer.getCurrentPosition(); audioMessage.seekBar.setProgress(currentPosition / 1000); audioMessage.tvPlayDuration.post(new Runnable() { @Override public void run() { audioMessage.tvPlayDuration.setText(ChatDateTimeFormatter.getDuration(currentPosition)); } }); } }, 0, 1000);


Esto está lanzando explícitamente un error. Dice el hilo que creó una vista, solo que puede tocar sus vistas. Es porque la vista creada está dentro del espacio de ese hilo. La creación de vista (GUI) ocurre en el hilo de la interfaz de usuario (principal). Por lo tanto, siempre usas el hilo de la interfaz de usuario para acceder a esos métodos.

En la imagen anterior, la variable de progreso está dentro del espacio del subproceso de la interfaz de usuario. Por lo tanto, solo el hilo de la interfaz de usuario puede acceder a esta variable. Aquí, estás accediendo al progreso a través de la nueva Hebra (), y es por eso que recibiste un error.


Esto me sucedió cuando doInBackground un cambio en la interfaz de usuario de un doInBackground de Asynctask lugar de usar onPostExecute .

Tratar con la interfaz de usuario en onPostExecute resolvió mi problema.


He estado en esta situación, pero encontré una solución con el objeto Handler.

En mi caso, quiero actualizar un ProgressDialog con el patrón de observador . Mi vista implementa observador y anula el método de actualización.

Por lo tanto, mi hilo principal crea la vista y otro hilo llama al método de actualización que actualiza el ProgressDialop y ...:

Solo el hilo original que creó una jerarquía de vistas puede tocar sus vistas.

Es posible resolver el problema con el objeto Handler.

A continuación, diferentes partes de mi código:

public class ViewExecution extends Activity implements Observer{ static final int PROGRESS_DIALOG = 0; ProgressDialog progressDialog; int currentNumber; public void onCreate(Bundle savedInstanceState) { currentNumber = 0; final Button launchPolicyButton = ((Button) this.findViewById(R.id.launchButton)); launchPolicyButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showDialog(PROGRESS_DIALOG); } }); } @Override protected Dialog onCreateDialog(int id) { switch(id) { case PROGRESS_DIALOG: progressDialog = new ProgressDialog(this); progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); progressDialog.setMessage("Loading"); progressDialog.setCancelable(true); return progressDialog; default: return null; } } @Override protected void onPrepareDialog(int id, Dialog dialog) { switch(id) { case PROGRESS_DIALOG: progressDialog.setProgress(0); } } // Define the Handler that receives messages from the thread and update the progress final Handler handler = new Handler() { public void handleMessage(Message msg) { int current = msg.arg1; progressDialog.setProgress(current); if (current >= 100){ removeDialog (PROGRESS_DIALOG); } } }; // The method called by the observer (the second thread) @Override public void update(Observable obs, Object arg1) { Message msg = handler.obtainMessage(); msg.arg1 = ++currentPluginNumber; handler.sendMessage(msg); } }

Esta explicación se puede encontrar en esta página , y debe leer el "Ejemplo de ProgressDialog con un segundo hilo".


Mi solución a esto:

private void setText(final TextView text,final String value){ runOnUiThread(new Runnable() { @Override public void run() { text.setText(value); } }); }

Llame a este método en un hilo de fondo.


Para mí, el problema era que estaba invocando onProgressUpdate() explícitamente desde mi código. Esto no debería hacerse. Llamé publishProgress() lugar y eso resolvió el error.


Por lo general, cualquier acción que involucre la interfaz de usuario debe realizarse en el subproceso principal o en la interfaz de usuario, es decir, en la que se onCreate() y el manejo de eventos. Una forma de estar seguro de eso es usar Activity.runOnUiThread , otra es usar Handlers.

ProgressBar.setProgress() tiene un mecanismo para el cual siempre se ejecutará en el subproceso principal, por eso funcionó.

Ver roscado sin dolor .


Puede usar el controlador para eliminar la vista sin alterar el subproceso de la interfaz de usuario principal. Aquí está el código de ejemplo

new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { //do stuff like remove view etc adapter.remove(selecteditem); } });


Resuelto: solo ponga este método en la clase doInBackround ... y pase el mensaje

public void setProgressText(final String progressText){ Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // Any UI task, example progressDialog.setMessage(progressText); } }; handler.sendEmptyMessage(1); }


Si no desea utilizar la API runOnUiThread , de hecho puede implementar AsynTask para las operaciones que AsynTask unos segundos en completarse. Pero en ese caso, también después de procesar su trabajo en doinBackground() , debe devolver la vista finalizada en onPostExecute() . La implementación de Android permite que solo el hilo principal de la interfaz de usuario interactúe con las vistas.


Tuve un problema similar, y mi solución es fea, pero funciona:

void showCode() { hideRegisterMessage(); // Hides view final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { showRegisterMessage(); // Shows view } }, 3000); // After 3 seconds }


Use este código, y no necesita runOnUiThread función runOnUiThread :

private Handler handler; private Runnable handlerTask; void StartTimer(){ handler = new Handler(); handlerTask = new Runnable() { @Override public void run() { // do something textView.setText("some text"); handler.postDelayed(handlerTask, 1000); } }; handlerTask.run(); }


Veo que has aceptado la respuesta de @ providence. Por si acaso, también puede utilizar el controlador también! Primero, haz los campos int.

private static final int SHOW_LOG = 1; private static final int HIDE_LOG = 0;

A continuación, crea una instancia de manejador como un campo.

//TODO __________[ Handler ]__________ @SuppressLint("HandlerLeak") protected Handler handler = new Handler() { @Override public void handleMessage(Message msg) { // Put code here... // Set a switch statement to toggle it on or off. switch(msg.what) { case SHOW_LOG: { ads.setVisibility(View.VISIBLE); break; } case HIDE_LOG: { ads.setVisibility(View.GONE); break; } } } };

Hacer un método.

//TODO __________[ Callbacks ]__________ @Override public void showHandler(boolean show) { handler.sendEmptyMessage(show ? SHOW_LOG : HIDE_LOG); }

Finalmente, ponga esto en el método onCreate() .

showHandler(true);


Yo uso Handler con Looper.getMainLooper() . Funcionó bien para mí.

Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { // Any UI task, example textView.setText("your text"); } }; handler.sendEmptyMessage(1);


runOnUiThread( new Runnable(){ .. esto poniendo runOnUiThread( new Runnable(){ .. inside run() :

thread = new Thread(){ @Override public void run() { try { synchronized (this) { wait(5000); runOnUiThread(new Runnable() { @Override public void run() { dbloadingInfo.setVisibility(View.VISIBLE); bar.setVisibility(View.INVISIBLE); loadingText.setVisibility(View.INVISIBLE); } }); } } catch (InterruptedException e) { e.printStackTrace(); } Intent mainActivity = new Intent(getApplicationContext(),MainActivity.class); startActivity(mainActivity); }; }; thread.start();