Статьи

Понимание и использование потоков в Android

Потоки были неотъемлемой частью информатики и универсальным инструментом для программистов на протяжении десятилетий. Большинство современных операционных систем поддерживают потоки, и Android не является исключением. Поддержка потоков в Android позволяет программистам разделять свои программы на несколько модулей, которые выполняют команды и инструкции параллельно.

Поток — это наименьшая единица обработки, которая может быть запланирована операционной системой. Основной процесс (или программа) вашего приложения может быть разбит на потоки, которые могут быть одновременно запланированы операционной системой. Этот метод помогает программисту заботиться о более длинных процессах в отдельном потоке, так что основной поток (в котором работает пользовательский интерфейс) остается быстрым и отзывчивым для пользователя.

Основная тема и фоновые темы

Всякий раз, когда мы создаем действие, оно запускается по умолчанию в главном потоке вашего приложения. Все команды, выданные операционной системой Android (например, onClick, onCreate и т. Д.), Отправляются и обрабатываются этим основным потоком.

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

Чтобы предотвратить эту проблему, рекомендуется выполнять все задачи, которые могут занимать много времени в отдельном фоновом потоке, а не в основном потоке. Это оставило бы основной поток свободным, чтобы оставаться чувствительным к пользовательскому вводу, посланному операционной системой Android.

Одним существенным недостатком этого метода является то, что пользовательский интерфейс не может быть обновлен из фонового потока, он должен обновляться из основного потока. Но с небольшим планированием вперед, эта проблема может быть легко исправлена.

Создание программы для отображения индикатора выполнения с использованием потоков

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

Добавление индикатора выполнения

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

Мы собираемся добавить индикатор выполнения, добавив тег <ProgressBar> в стандартный файл layout.xml.

Ниже приведен файл layout.xml:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello"

/>

<ProgressBar android:id="@+id/progress"

style="?android:attr/progressBarStyleHorizontal"

android:layout_width="fill_parent"

android:layout_height="wrap_content" />

</LinearLayout>

В приведенном выше примере мы добавили «ProgressBar» с идентификатором как «progress» и стилем как «горизонтальный» для отображения горизонтального индикатора выполнения.

Ниже приведен метод действия onCreate:

 @Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

PrgBar=(ProgressBar)findViewById(R.id.progress);

}

В приведенном выше примере мы создаем экземпляр индикатора выполнения и сохраняем его в переменной «PrgBar».

Если мы запустим программу сейчас, мы должны увидеть следующее:

Добавление обработчика

Как упоминалось ранее, мы не можем обновить пользовательский интерфейс из фонового потока, поэтому нам нужно будет создать обработчик, который будет работать в основном потоке, но получать сообщения из фонового потока.

Чтобы создать обработчик, нам нужно создать экземпляр класса «Обработчик» и переопределить функцию handleMessage следующим образом:

 Handler handler=new Handler() {

@Override

public void handleMessage(Message msg) {

PrgBar.incrementProgressBy(5);

}

};

Как видите, мы решили увеличить значение нашего индикатора выполнения на 5.

Создание темы

Затем мы объявляем «AtomicBoolean» для запуска и завершения потока в действии.

 AtomicBoolean ContinueThread = new AtomicBoolean(false);

Затем мы создаем поток, используя метод onStart. Метод будет выглядеть так:

 void onStart() {

super.onStart();

PrgBar.setProgress(0);

Thread background=new Thread(new Runnable() {

public void run() {

try {

while(ContinueThread.get()) {

Thread.sleep(1000);

handler.sendMessage(handler.obtainMessage());

}

}

catch (Throwable t) {

}

}

});

ContinueThread.set(true);

background.start();

}

Используя этот метод, мы устанавливаем значение индикатора выполнения равным 0. Затем мы создаем новый поток, передавая ему экземпляр класса, который реализует метод run (). В методе run у нас есть цикл while, который будет продолжаться до тех пор, пока AtomicBoolean ContinueThread не станет true.

Внутри цикла while мы заставили поток спать в течение 1 секунды, а затем отправили сообщение обработчику, которое увеличит значение ProgressBar.

В конце onStart мы устанавливаем значение ContinueThread равным true, а затем запускаем фоновый поток.

Остановка потока, когда действие прекращается

Чтобы остановить поток, нам просто нужно установить для ContinueThread значение «false» в методе onStop действия.

 public void onStop() {

super.onStop();

ContinueThread.set(false);

}

Если мы запустим программу, вы увидите обновление индикатора выполнения, как показано на рисунке ниже.

Создание программы для цифровых часов

Теперь, для дальнейшего развития нашего использования потоков, мы собираемся создать небольшую программу для цифровых часов.

Создание Деятельности и TextView

Чтобы создать цифровые часы, сначала нам нужно создать активность. В основном макете действий объявите TextView. Мы собираемся увеличить размер шрифта TextView, чтобы цифры на часах были четкими.

Ниже приведен основной файл layout.xml:

 <?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<TextView

android:id="@+id/txtWatch"

android:textSize="50dp"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:text="@string/hello"

/>

</LinearLayout>

Создание функции для отображения текущего времени

Теперь мы собираемся написать функцию для отображения текущего времени в TextView. Функция выглядит следующим образом:

 public void displayCurrentTime()

{

Date dt = new Date();

int hours = dt.getHours();

int minutes = dt.getMinutes();

int seconds = dt.getSeconds();

String curTime = hours + ":" + minutes + ":" + seconds;

Watch.setText(curTime);

}

В приведенном выше примере мы создаем новый объект «Date», который инициализируется с текущим временем.

Затем мы читаем часы, минуты и секунды с этого объекта и передаем его в текстовый раздел.

Мы будем использовать нижеприведенную функцию для вызова функции onCreate и отображения текущего времени:

 @Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

Watch=(TextView)findViewById(R.id.txtWatch);

displayCurrentTime();

}

А в функции handleMessage нашего ранее созданного обработчика мы добавим следующее:

 Handler handler=new Handler() {

@Override

public void handleMessage(Message msg) {

displayCurrentTime();

}

};

Создание темы

Теперь в функции onStart мы создадим поток, который каждую секунду отправляет сообщение в основной поток. Затем основной поток обновит TextView, чтобы отобразить текущее время.

Функция onStart выглядит следующим образом:

 public void onStart() {

super.onStart();

Thread background=new Thread(new Runnable() {

public void run() {

try {

while(ContinueThread.get()) {

Thread.sleep(1000);

handler.sendMessage(handler.obtainMessage());

}

}

catch (Throwable t) {

}

}

});

ContinueThread.set(true);

background.start();

}

Асинхронные задачи

Android предоставляет удобный способ выполнять сложные и трудоемкие задачи в фоновом режиме, скрывая процесс создания фонового потока от разработчика. Это делается классом AsyncTask.

Чтобы создать AsyncTask, вы просто создадите дочерний класс, унаследованный от AsyncTask, а затем просто переопределите некоторые его методы по умолчанию. Класс AsyncTask позаботится о создании фонового потока, а также освободит и уничтожит поток, когда он больше не нужен. Нам просто нужно вызвать метод execute для подкласса AsyncTask.

Создание асинхронной задачи для чтения файла в фоновом режиме

Теперь мы собираемся создать простую программу для чтения файла в фоновом потоке, используя AsyncTask.

Код выглядит следующим образом:

 package asyncTaskDemo.com;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.widget.TextView;

public class AsyncTaskDemo extends Activity {

private TextView txtContent;

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

txtContent = (TextView) findViewById(R.id.txtContent);

ReadFileTask task = new ReadFileTask();

task.execute();

}

public InputStream openResourceFile(int id)

{

return getResources().openRawResource(id);

}

private class ReadFileTask extends AsyncTask<Void, Void, String> {

@Override

protected String doInBackground(Void... UnUsed) {

String response = "";

String str="";

StringBuffer buf = new StringBuffer();

InputStream is = openResourceFile(R.drawable.textfile);

BufferedReader reader = new BufferedReader(new InputStreamReader(is));

if (is!=null) {

try {

while ((str = reader.readLine()) != null) {

buf.append(str + "n" );

}

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}

response = buf.toString();

return response;

}

@Override

protected void onPostExecute(String result) {

txtContent.setText(result);

}

}

}

Здесь у нас есть TextView в пользовательском интерфейсе, на котором мы собираемся отобразить содержимое файла. Этот файл будет читаться в фоновом режиме, используя отдельный поток. В методе onCreate мы просто получаем TextView и сохраняем его в переменной-члене класса. Наконец, мы создаем экземпляр класса AsyncTask и выполняем его.

Аргументы асинхронных задач

Объявляя подкласс AsyncTask, мы также можем объявить типы аргументов, которые мы передадим различным методам в нашем подклассе. Эти аргументы переопределяют значения по умолчанию.

В нашем классе ReadFileTask мы использовали void, void и string, поэтому void будет аргументом, передаваемым в функцию doInBackground, void будет аргументом, передаваемым в функцию onProgressUpdate, а string будет аргументом, передаваемым в функцию onPostExecute.

 private class ReadFileTask extends AsyncTask<Void, Void, String>

Методы AsyncTask

Теперь мы переопределим метод doInBackground AsyncTask — код, который будет выполняться в фоновом потоке. Здесь мы просто открываем файл в папке drawable, читаем его построчно и сохраняем его в строковом буфере.

Затем мы переопределяем метод onPostExecute, который будет вызываться, когда фоновый поток завершит выполнение задачи отображения файла на экране устройства.

Если мы запустим программу сейчас, результат будет следующим:

Вывод

Разработчики всегда должны выполнять сложные и трудоемкие задачи в наших приложениях для Android. Если эти задачи не сегментированы в отдельных потоках, они могут привести к зависанию, зависанию, падению нашего приложения или просто быть медленным и неэффективным. Преимущества использования отдельных потоков перевешивают недостатки, поэтому вы должны часто использовать их для создания быстрых, отзывчивых, защищенных от сбоев приложений Android.