Error using sendMessage() method of the Handler class. "Message not posted or already removed"

Asked

Viewed 224 times

3

I am studying about Threads and Services and I know that to run a longer process, such as looking for internet signal or download, I must put it to run in a Background Thread and not in the UI (User Interface) Thread. In the app below I did an example of code running in the background and as this process progresses there are updates in the progress bar, but when I run the application, I get an error saying the following:

java.lang.Illegalstateexception: The specified message Queue Synchronization Barrier token has not been posted or has already been Removed.

Below is the code:

package com.gabrielm.myapplication;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {

    Handler mHandler;
    Button mButton;
    ProgressBar mProgressBar;

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

        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        mButton = (Button) findViewById(R.id.button);

        mHandler = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                mProgressBar.setProgress(msg.arg1);
            }
        };

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new Thread(new Runnable() {
                    @Override
                    public void run() {

                        Message msg = Message.obtain();
                        for (int i = 0; i < 100; i++) {

                            msg.arg1 = i;
                            mHandler.sendMessage(msg);

                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }
        });
    }
}

Searching the internet for a possible solution to the problem, I found that the method sendMessage(msg) always send a new object of the type Message for the queue of messages each time I want to send a new message. I mean, I can’t reuse the same object, I always have to create a new one every time I want to send a data to the handleMessage(Message msg);. So what I did was remove the line of code Message msg = Message.obtains(); from the place where it was and put inside the block for, because so each time for executes, it creates a new object of type Message.

This small change in the code made the program work, but I’m not sure if what I did was the right way to do it. So I’d like to know if the logic I’ve developed here is right.

2 answers

2

Gabriel, your research has brought you accurate information. It is not possible to send the same Message object several times, so we always have to create a new instance. Or rather, instead of creating an instance using new Message(), we can use Message.obtain() that will return an instance of Message coming from a recycled object pool, which is less costly than the first option.

While the constructor of Message is public, the best way to get one of These is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of Recycled Objects.

  • Thanks for the feedback, it’s good to know I’m on the right track. It took me a few hours to get that right, but I think I’ve finally managed to dilute that content. By the way, I was wondering if I should use Message msg = Message.obtains() or Message msg = new Message(), but now I understand that the best is Message.obtains(), since it is reusing a recycled object and not creating a new one. Anyway, thank you for your reply, it was of great help.

2


Running background code on Android using Handlers and Threads is an often complicated and error-prone task, because a special class has been created to facilitate the execution of asynchronous tasks: the Async Task.

To Asyntask provides the following methods:

  1. onPreExecute : Method called before the long-term task is executed. It is usually used to prepare and start showing a bar ( empty yet ).

  2. doInBackground : Performs a long-term task ( BD access, downloading a document, etc. ).

    Heed: Within this method there should be no access to view elements, if this is done, you will receive a type error java.lang.Runtimeexception: Can’t create Handler Inside thread that has not called Looper.prepare()"

    If you want to manipulate the view, you should use the methods onPreExecute() and onPostExecute()

    However, if you really need to access the view within the method doInBackground() put the view access method inside the method runOnUiThread() and put it inside the doInBackground():

    @Override
    protected String doInBackground(String... parametros) {
    
        // Realiza tarefa de longa duração ...
    
        // Acessa a view
        runOnUiThread(new Runnable() {
    
            public void run() {
    
                // faça o acesso a view aqui
            }
        });
    } 
    
  3. onProgressUpdate : Used to update the status of long-duration task ( e. g. increment a Progress bar). This method is called every time a method is called publishProgress()

  4. onPostExecute : Called as soon as the long-duration task is completed. It is used to access and handle the return variable of the long-duration task

It is important to know that, like the Asynctask is an abstract class, it can only be extended, and not instantiated, moreover, by having generic parameters in its constructor ( AsyncTask <Params, Progress, Result> ), by extending the class Asynctask you must provide the type of these generic parameters, where:

  • Params: Type of method input parameter doInBackground()
  • Progress: Type of method input parameter onProgressUpdate()
  • Result: Type of method input parameter onPostExecute()

So to create a Asynctask that downloads a file, for example, you could do:

// A classe vai receber um conjuntos de URLs como entrada,
// vai enviar um Integer a cada chamada ao método publishProgress()
// e vai enviar um Integer quando a tarefa for finalizada
private class DownloadArquivos extends AsyncTask<URL, Integer, Integer> {

    protected Long doInBackground(URL... urls) {

     // numero de downloads executados
     int downloads = 0;

     for (int i = 0; i < urls.lenght ; i++) {

         downloads += downloadFile(urls[i]);
         // downloadFile() é o seu método de longa duração

         // Atualiza o progresso enviando a porcentagem de arquivos
         // baixados até o momento
         publishProgress( (int) ((i / (float) urls.lenght) * 100) );
     }

     return downloads;
 }

 protected void onProgressUpdate(Integer... progress) {

      // método da sua view que atualiza o progress bar
      setProgressPercent(progress[0]);
 }

 protected void onPostExecute(Int result) {

     // método da sua view que mostra uma mensagem
    // após todos os downloads serem efetuados
     showDialog( result + " arquivos baixados com sucesso!" );
 }
 }

Obs.: Code withdrawn and adapted from: http://developer.android.com/intl/pt-br/reference/android/os/AsyncTask.html

  • Yes, I agree. The abstract class Asynctask is the most recommended for this type of process that uses UI Thread and Background Thread, but what was in doubt was about the Handler class. One question that came up is that with Handler I can handle a process that uses any two Threads, be it UI Thread and a Background Thread or two in Background. Asynctask is only for one Background Thread and one UI Thread. This is correct or can work with any two Threads using Asynctask?

  • Another thing, researching more on the subject, I realized that in the method ofInBackground() I do not need to create a Thread, because the code within this method is already running in the background. That was something.

  • @Gabrielm.Linassi can run two threads in parallel and in the background task, but for this you will need to use the method executeOnExecutor() ( take a look at official documentation ). As for the second question: Yes, doInBackground already runs in the background ( as the name :D ) so you can directly run your method within it without creating a new thread

Browser other questions tagged

You are not signed in. Login or sign up in order to post.