Урок 82. Handler. Пример с более содержательными сообщениями
В этом уроке:
- создаем более содержательные сообщения для Handler
В прошлых уроках мы использовали метод sendEmptyMessage. Этот метод сам создавал сообщение Message, заполнял его атрибут what и отправлял в очередь. Кроме what у сообщения есть еще атрибуты arg1 и arg2 типа int, и obj типа Object. В этом уроке мы сами будем создавать сообщение, заполнять атрибуты и отправлять.
Создадим приложение, которое будет подключаться к серверу, запрашивать кол-во файлов готовых для загрузки, эмулировать загрузку и отображать на экране ход действий, используя горизонтальный ProgressBar и TextView.
Создадим проект:
Project name: P0821_HandlerAdvMessage
Build Target: Android 2.3.3
Application name: HandlerAdvMessage
Package name: ru.startandroid.develop.p0821handleradvmessage
Create Activity: MainActivity
strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">HandlerAdvMessage</string>
<string name="connect">Connect</string>
</resources>
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<Button
android:id="@+id/btnConnect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onclick"
android:text="@string/connect">
</Button>
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="">
</TextView>
<ProgressBar
android:id="@+id/pbDownload"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
</ProgressBar>
</LinearLayout>
MainActivity.java:
package ru.startandroid.develop.p0821handleradvmessage;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
final String LOG_TAG = "myLogs";
final int STATUS_NONE = 0; // нет подключения
final int STATUS_CONNECTING = 1; // подключаемся
final int STATUS_CONNECTED = 2; // подключено
final int STATUS_DOWNLOAD_START = 3; // загрузка началась
final int STATUS_DOWNLOAD_FILE = 4; // файл загружен
final int STATUS_DOWNLOAD_END = 5; // загрузка закончена
final int STATUS_DOWNLOAD_NONE = 6; // загрузка закончена
Handler h;
TextView tvStatus;
ProgressBar pbDownload;
Button btnConnect;
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tvStatus = (TextView) findViewById(R.id.tvStatus);
pbDownload = (ProgressBar) findViewById(R.id.pbDownload);
btnConnect = (Button) findViewById(R.id.btnConnect);
h = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case STATUS_NONE:
btnConnect.setEnabled(true);
tvStatus.setText("Not connected");
pbDownload.setVisibility(View.GONE);
break;
case STATUS_CONNECTING:
btnConnect.setEnabled(false);
tvStatus.setText("Connecting");
break;
case STATUS_CONNECTED:
tvStatus.setText("Connected");
break;
case STATUS_DOWNLOAD_START:
tvStatus.setText("Start download " + msg.arg1 + " files");
pbDownload.setMax(msg.arg1);
pbDownload.setProgress(0);
pbDownload.setVisibility(View.VISIBLE);
break;
case STATUS_DOWNLOAD_FILE:
tvStatus.setText("Downloading. Left " + msg.arg2 + " files");
pbDownload.setProgress(msg.arg1);
saveFile((byte[]) msg.obj);
break;
case STATUS_DOWNLOAD_END:
tvStatus.setText("Download complete!");
break;
case STATUS_DOWNLOAD_NONE:
tvStatus.setText("No files for download");
break;
}
};
};
h.sendEmptyMessage(STATUS_NONE);
}
public void onclick(View v) {
Thread t = new Thread(new Runnable() {
Message msg;
byte[] file;
Random rand = new Random();
public void run() {
try {
// устанавливаем подключение
h.sendEmptyMessage(STATUS_CONNECTING);
TimeUnit.SECONDS.sleep(1);
// подключение установлено
h.sendEmptyMessage(STATUS_CONNECTED);
// определяем кол-во файлов
TimeUnit.SECONDS.sleep(1);
int filesCount = rand.nextInt(5);
if (filesCount == 0) {
// сообщаем, что файлов для загрузки нет
h.sendEmptyMessage(STATUS_DOWNLOAD_NONE);
// и отключаемся
TimeUnit.MILLISECONDS.sleep(1500);
h.sendEmptyMessage(STATUS_NONE);
return;
}
// загрузка начинается
// создаем сообщение, с информацией о количестве файлов
msg = h.obtainMessage(STATUS_DOWNLOAD_START, filesCount, 0);
// отправляем
h.sendMessage(msg);
for (int i = 1; i <= filesCount; i++) {
// загружается файл
file = downloadFile();
// создаем сообщение с информацией о порядковом номере
// файла,
// кол-вом оставшихся и самим файлом
msg = h.obtainMessage(STATUS_DOWNLOAD_FILE, i,
filesCount - i, file);
// отправляем
h.sendMessage(msg);
}
// загрузка завершена
h.sendEmptyMessage(STATUS_DOWNLOAD_END);
// отключаемся
TimeUnit.MILLISECONDS.sleep(1500);
h.sendEmptyMessage(STATUS_NONE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
byte[] downloadFile() throws InterruptedException {
TimeUnit.SECONDS.sleep(2);
return new byte[1024];
}
void saveFile(byte[] file) {
}
}
В onCreate мы создаем Handler и в его методе обработки (handleMessage) прописываем всю логику изменения экрана в зависимости от приходящих сообщений. Не буду подробно это расписывать, там все просто – меняем текст, включаем/выключаем кнопку, показываем/скрываем ProgressBar, меняем значение ProgressBar. Из интересного здесь стоит отметить, что читаем мы на этот раз не только what, но и остальные атрибуты сообщения – arg1, arg2, obj. А как они заполняются, увидим далее.
В onclick создаем новый поток для загрузки файлов. Устанавливаем подключение, получаем кол-во готовых для загрузки файлов. Если файлов для загрузки нет, посылаем соответствующее сообщение в Handler и отключаемся. Если же файлы есть, мы создаем сообщение Message с помощью метода obtainMessage (int what, int arg1, int arg2). Он принимает на вход атрибуты what, arg1 и arg2. В what мы кладем статус, в arg1 - кол-во файлов, arg2 – не нужен, там просто ноль.
Далее начинаем загрузку. После загрузки каждого файла мы создаем сообщение Message c помощью метода obtainMessage (int what, int arg1, int arg2, Object obj), заполняем его атрибуты: what – статус, arg1 – порядковый номер файла, arg2 – кол-во оставшихся файлов, obj – файл. И отправляем.
По завершению загрузки отправляем соответствующее сообщение и отключаемся.
Метод getFilesCount – получение кол-ва файлов. Здесь просто генератор случайных чисел от нуля включительно до 5 не включительно.
downloadFile – эмулирует загрузку файла. ждет две секунды и возвращает массив из 1024 байтов.
saveFile – метод сохранения файла на диск. Просто заглушка. Ничего не делает.
Все сохраняем и запускаем. Жмем Connect.
Устанавливается подключение
Далее, либо начинается загрузка
либо появляется сообщение, что файлов нет
Отключаемся
Используя разные атрибуты кроме what, мы смогли передать в основной поток и использовать там более разнообразные данные.
Мы создаем сообщения с помощью разных реализаций метода obtainMessage. А почему бы не создавать напрямую объект Message с помощью его конструкторов? В принципе можно, но официальный хелп рекомендует пользоваться методами obtainMessage, потому что это эффективней и быстрее. В этом случае сообщение достается из глобального пула сообщений, а не создается с нуля.
Здесь вы можете посмотреть все реализации метода obtainMessage для формирования сообщений и использовать тот, который подходит для ситуации. Они различаются разными комбинациями входных параметров.
На следующем уроке:
- посылаем отложенные сообщения
- удаляем сообщения из очереди
- используем Handler.Callback для обработки сообщений