Lesson 80. Handler. A little theory. A clear example of use

Lesson 80. Handler. A little theory. A clear example of use


In this lesson:

– We understand what Handler is and why he is needed

For a complete understanding of the lesson, it is advisable to have an idea threads (Threads) in Java.

It’s just that you don’t explain what it is Handler. You can try to read the official description, but it is quite non-trivial and little written. I will try to tell here in a nutshell.

On Android up to flow (Thread) can be tied message queue. We can put it there message, And the system will in turn monitor and send messages to processing. At the same time, we can specify that the message goes to processing not at once, but after a certain time.

Handler is a mechanism that allows work with a message queue. It is tied to a specific thread (thread) and works with its queue. Handler knows put messages in the queue. At the same time, he puts himself in the quality the recipient of this message. And when the time comes, the system retrieves messages from the queue and sends to its addressee (ie Handler) for processing.

Handler gives us two interesting and useful features:

1) to implement delayed by time code execution

2) code execution not in its flow

I suspect that it is not very clear what a Handler is, and most importantly – why do you need it :). In the next few lessons we will deal with this, and everything will be clear.

In this lesson we will make a small addition. It will emulate any lasting impact, such as uploading files and displaying the number of files downloaded in TextView. With this example, we’ll see why a Handler might be needed.

Let’s create a project:

Project name: P0801_Handler
Build Target: Android 2.3.3
Application name: Handler
Package name: ru.startandroid.develop.p0801handler
Create Activity: MainActivity

strings.xml:



	Handler
	Start
	Test

main.xml:



	
	
	
	
	
	

ProgressBar we will always spin. Later, it will become clear why. TextView – to display information about uploading files. button Start will start downloading. button Test will just log the word test.

Kodyma MainActivity.java:

package ru.startandroid.develop.p0801handler;

import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";

  Handler h;
  TextView tvInfo;
  Button btnStart;

  /** Called when the activity is first created. */
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    tvInfo = (TextView) findViewById(R.id.tvInfo);
  }

  public void onclick(View v) {
    switch (v.getId()) {
    case R.id.btnStart:
      for (int i = 1; i <= 10; i++) {
        // долгий процесс
        downloadFile();
        // обновляем TextView
        tvInfo.setText("Закачано файлов: " + i);
        // пишем лог
        Log.d(LOG_TAG, "Закачано файлов: " + i);
      }
      break;
    case R.id.btnTest:
      Log.d(LOG_TAG, "test");
      break;
    default:
      break;
    }
  }

  void downloadFile() {
    // пауза - 1 секунда
    try {
      TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

In the button handler Start we organize a loop for uploading files. In each iteration of the loop, we execute a method downloadFile (Which emulates uploading the file), we are updating TextView and write to the log that the number of downloaded files has changed. Together, we need to protect 10 files and after uploading each of them log and screen should show how many files have already been uploaded.

When the button is pressed Test - just print the message.

downloadFile - emulates the file upload, it just pauses in one second.

We will save everything and launch the application.

We see that ProgressBar rotates. Press the button Test, The test appears in the logs. All right, the app responds to our actions.

Now place the AVD on the monitor screen so that it does not overlap the tab logs in Eclipse (LogCat). We will need to see them at the same time.

If we click StartThen we have to watch as it is updated TextView and it is written log after uploading another file. But in fact it will be a little wrong. Our application simply "hangs up" and stops responding to clicks. will stop ProgressBarThat will not be updated TextView, And there will be no distress button Test. That is UI (Screen) will not be available to us. And only by logs will it be clear that the program actually works and the files are downloaded. click Start and make sure.

The screen "hangs" and the logs go. Once all 10 files have been uploaded, the application will come to life and respond to your clicks again.

And why? Because screen work is ensured basic application flow. And we occupied all this basic flow for your needs. In our case, as if uploading files. And once we're done downloading the files - the stream released, And the screen began to refresh and respond.

For those who have experience in Java coding, I have not discovered anything new. For others, I hope I have a clear explanation. One thing to understand here is - the main stream of the application is responsible for the screen. This flow is by no means cannot be loaded something heavy - the screen just stops refreshing and responds to clicks. If you have long-running tasks - they should be brought to separate stream. Let's try to do this.

rewrite onclick:

  public void onclick(View v) {
    switch (v.getId()) {
    case R.id.btnStart:
      Thread t = new Thread(new Runnable() {
        public void run() {
          for (int i = 1; i <= 10; i++) {
            // долгий процесс
            downloadFile();
            // обновляем TextView
            tvInfo.setText("Закачано файлов: " + i);
            // пишем лог
            Log.d(LOG_TAG, "i = " + i);
          }
        }
      });
      t.start();
      break;
    case R.id.btnTest:
      Log.d(LOG_TAG, "test");
      break;
    default:
      break;
    }
  }

That is, we just place the whole loop into a new stream and run it. Now uploading files will go into this new threads. And the main stream will be busy and will be able to easily draw the screen and respond to clicks. So we will see a TextView change after each uploaded file and ProgressBar is spinning. And, in general, whether we can fully interact with the application. It would seem that here is happiness 🙂

We will save everything and launch the application. press Start.

The application crashed with error. We look at the log of errors in LogCat. There are lines:

android.view.ViewRoot $ CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

and

at ru.startandroid.develop.p0801handler.MainActivity $ 1.run (MainActivity.java:37)

We see that the code we have in MainActivity.java in 37row:

tvInfo.setText("Закачано файлов: " + i);

An error occurred while trying to execute this code (not in the main thread) "Only the original thread that created a view can touch its views». If in Russian, then "Only the original thread that created the view components can interact with them». That is, work with view components is only available from basic flow. And the new streams we create don't have access to screen elements.

That is, on the one hand main stream cannot be loaded difficult tasks not to "hang" the screen. On the other hand - new threadsDesigned to accomplish difficult tasks, have no access to the screen, And we won't be able to show them to the user that our difficult task is somehow moving.

This is where Handler will help us. The plan is as follows:

- we create basically a Handler thread
- in the stream of uploading files we turn to Handler and with his help we place a message for him.
- the system takes this message, sees that the recipient is a Handler, and sends a message to the Handler for processing
- Handler updates TextView after receiving the message

How is it different from our previous attempt to update TextView from another thread? So that Handler was generated in basically stream, and it will process the message to it in the main stream, which means it will have access to the screen components and will be able to change the text in TextView. We can easily access Handler from any other thread because the main stream monopolizes only UI access. And class elements (in our case this is the Handler in MainActivity.java) are available in any thread. So Handler will act as "bridge»Between streams.

rewrite the method onCreate:

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    tvInfo = (TextView) findViewById(R.id.tvInfo);
    btnStart = (Button) findViewById(R.id.btnStart);
    h = new Handler() {
      public void handleMessage(android.os.Message msg) {
        // обновляем TextView
        tvInfo.setText("Закачано файлов: " + msg.what);
        if (msg.what == 10) btnStart.setEnabled(true);
      };
    };
  }

Here we create a Handler and implement the handleMessage message processing method. We extract from the message the attribute what is the number of files downloaded. If it is 10, that is, all files are uploaded, we activate the Start button. (We put the number of uploaded files ourselves - now you will see how)

method onclick we will rewrite as follows:

  public void onclick(View v) {
    switch (v.getId()) {
    case R.id.btnStart:
      btnStart.setEnabled(false);
      Thread t = new Thread(new Runnable() {
        public void run() {
          for (int i = 1; i <= 10; i++) {
            // долгий процесс
            downloadFile();
            h.sendEmptyMessage(i);
            // пишем лог
            Log.d(LOG_TAG, "i = " + i);
          }
        }
      });
      t.start();
      break;
    case R.id.btnTest:
      Log.d(LOG_TAG, "test");
      break;
    default:
      break;
    }
  }

We're deactivating the button Start before starting downloading files. It's just protection so you can't run multiple downloads at once. And in the download process, after each uploaded file, we send (sendEmptyMessage) to Handler a message with the number of files already downloaded. Handler will receive this message, extract the number of files and update TextView.

We all save and launch the application. press the button Start.

button Start became inactive because we turned it off. And TextView is updated, ProgressBar rotates and the Test button is pressed. That is, files are downloading and the application continues to work seamlessly, reflecting the download status.

When all files are protected, the Start button will become active again.

We summarize all of the above.

1) At first, we tried to load the application with a heavy task in the main thread. This caused us to lose the screen - it stopped updating and responding to clicks. This is because the screen is responsible for the main stream of the application, and it was heavily loaded.

2) We created a separate thread and executed all the heavy code there. And it would work, but we needed to refresh the screen while we were working. And from the non-main stream, there is no link to the screen. The screen is only available from the main stream.

3) We created Handler in the main thread. And from the new thread, they sent a message to Handler to let us update the screen. As a result, Handler helped us refresh the screen not from the main stream.

A rather complicated lesson came out. Probably little that is clear. Don't worry, in this tutorial I just showed in what situation Handler might be useful. We will discuss the methods of working with him in the following lessons.

P.S.

Eclipse can emphasize Handler in yellow and swear by:This Handler class should be static or leaks might occurHe thus informs us that our code is a little bad and can cause memory leaks. He is absolutely right here, but I will still follow this scheme in my lessons so as not to make it complicated.

And in the forum, I separately wrote why the leak may occur and how it can be fixed. When you finish the topic Handlers, be sure to look there and read - http://forum.startandroid.ru/viewtopic.php?f=30&t=1870.

In the next lesson:

- we send the simplest message to Handler




Discuss in the forum [52 replies]

Leave a Comment