Андроид студио игровой поток нажатие домой. Процессы и потоки

Когда компонент приложения запускается при отсутствии других работающих компонентов, система Android запускает новый процесс Linux для приложения с одним потоком выполнения. По умолчанию все компоненты одного приложения работают в одном процессе и потоке (называется «главным потоком»). Если компонент приложения запускается при наличии процесса для этого приложения (так как существует другой компонент из приложения), тогда компонент запускается в этом процессе и использует тот же поток выполнения. Однако можно организовать выполнение других компонентов приложения в отдельных процессах и создавать дополнительный поток для любого процесса.

В этом документе обсуждается работа процессов и потоков в приложении Android.

Процессы

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

Запись манифеста для каждого типа элементов компонента — , и — поддерживает атрибут android:process , позволяющий задавать процесс, в котором следует выполнять этот компонент. Можно установить этот атрибут так, чтобы каждый компонент выполнялся в собственном процессе, или так, чтобы только некоторые компоненты совместно использовали один процесс. Можно также настроить процесс android:process так, чтобы компоненты разных приложений выполнялись в одном процессе, при условии что приложения совместно используют один идентификатор пользователя Linux и выполняют вход с одним сертификатом.

  • Пустой процесс

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

  • Система Android относит процесс к максимально высокому уровню на основе важности компонентов, активных в процессе в текущее время. Например, если процесс содержит служебное и видимое действие, процесс считается видимым, а не служебным процессом.

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

    Так как процесс, выполняющий службу, оценивается выше процесса с фоновыми действиям, действие, запускающее долговременную операцию, может запустить для этой операции, а не просто создать рабочий поток, особенно в случае, если операция продлится дольше действия. Например, действие, которое загружает изображение на веб-сайт, должно запустить службу для выполнения загрузки, так что загрузка может продолжаться в фоновом режиме даже после выхода пользователя из действия. Использование службы гарантирует, что операция будет иметь приоритет не ниже «служебного процесса», независимо от того, что происходит с действием. По этой же причине ресиверы должны использовать службы, а не просто ставить в поток операции, требующие много времени для выполнения.

    Потоки

    При запуске приложения система создает поток выполнения для приложения, который называется «главным». Этот поток очень важен, так как он отвечает за диспетчеризацию событий на виджеты соответствующего интерфейса пользователя, включая события графического представления. Он также является потоком, в котором приложение взаимодействует с компонентами из набора инструментов пользовательского интерфейса Android (компонентами из пакетов и ). По существу, главный поток - это то, что иногда называют потоком пользовательского интерфейса.

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

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

    Когда приложение выполняет интенсивную работу в ответ на действия пользователя, эта одиночная модель потока может показывать плохую производительность, если приложение реализовано неправильно. То есть, если все происходит в потоке пользовательского интерфейса, выполнение долговременных операций, таких как сетевой доступ или запросы к базе данных, будет блокировать весь пользовательский интерфейс. Когда поток заблокирован, не могут обрабатываться никакие события, включая события изменения отображения. С точки зрения пользователя приложение выглядит зависшим. Хуже того, если поток пользовательского интерфейса заблокирован более нескольких секунд (в настоящее время около 5 секунд), отображается печально известное диалоговое окно ». После этого недовольный пользователь может выйти из вашего приложения и удалить его.

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

    1. Не блокируйте поток пользовательского интерфейса
    2. Не обращайтесь к набору инструментов пользовательского интерфейса Android снаружи потока пользовательского интерфейса

    Рабочие потоки

    Вследствие описанной выше однопоточной модели для динамичности пользовательского интерфейса ваших приложений очень важно не блокировать поток пользовательского интерфейса. Если требуется выполнять операции, занимающие некоторое время, обязательно выполняйте их в отдельных потоках (»фоновых» или «рабочих» потоках).

    Например, ниже приведен код контроля нажатий, который загружает изображение из отдельного потока и отображает их в виджете :

    Public void onClick(View v) { new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork("http://example.com/image.png"); mImageView.setImageBitmap(b); } }).start(); }

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

    Для устранения этой проблемы Android предлагает несколько путей доступа к потоку пользовательского интерфейса из других потоков. Ниже приведен список полезных методов:

    Например, можно исправить приведенный выше код с помощью метода :

    Public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); }

    Теперь реализация является потокобезопасной: сетевая операция выполняется из отдельного потока, тогда как работает из потока пользовательского интерфейса.

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

    Использование AsyncTask

    Потокобезопасные методы

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

    В первую очередь это относится к методам, которые можно вызывать удаленно, например, к методам в . Когда вызов метода реализуется в классе , происходящем из того же процесса, в котором выполняется , метод выполняется в потоке вызывающего метода. Однако, когда вызов происходит из другого процесса, метод выполняется в потоке, выбранном из пула потоков, которые система поддерживает в том же процессе, что и (он не выполняется в потоке пользовательского интерфейса процесса). Например, поскольку метод службы будет вызываться из потока пользовательского интерфейса процесса службы, методы, реализованные в объекте, который возвращает (например, подкласс, который реализует методы RPC), будут вызываться из потоков в пуле. Так как служба может иметь несколько клиентов, несколько потоков из пула могут одновременно использовать один и тот же метод . Поэтому методы должны быть реализованы с сохранением потокобезопасности.

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

    Взаимодействие процессов

    Система Android предлагает механизм взаимодействия процессов (IPC) с помощью удаленного вызова процедуры (RPC), при котором метод вызывается действием или другим компонентом приложения, но выполняется удаленно (в другом процессе) с возвратом всех результатов вызывающему компоненту. Это влечет разложение вызова метода и его данных до уровня, понятного операционной системе, передачу его из локального процесса и адресного пространства удаленному процессу и адресному пространству, а затем повторную сборку и восстановление вызова. После этого возвращенные значения передаются в обратном направлении. Система Android содержит все коды для выполнения этих механизмов IPC, так что вы можете сосредоточиться на определении и реализации программного интерфейса RPC.

    Для выполнения IPC приложение должно быть привязано к службе с помощью метода . Дополнительные сведения представлены в разделе руководства для разработчиков.


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

    Использование фоновых потоков

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

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

    Применение фоновых потоков - необходимое условие, если вы хотите избежать появления диалогового окна для принудительного закрытия приложения. Когда активность в Android на протяжении 5 секунд не отвечает на события пользовательского ввода (например, нажатие кнопки) или приёмник широковещательных намерений не завершает работу обработчика onReceive() в течение 10 секунд, считается, что приложение зависло. Подобные ситуации следует избегать любой ценой. Используйте фоновые потоки для всех трудоёмких операций, включая работу с файлами, сетевые запросы, транзакции в базах данных и сложные вычисления.

    Android предоставляет несколько механизмов перемещения функциональности в фоновый режим.

    • Activity.runOnUiThread(Runnable)
    • View.post(Runnable)
    • View.postDelayed(Runnable, long)
    • Handlers
    • AsyncTask

    Хотя использование AsyncTask - хорошее решение, случается, что для работы в фоновом режиме приходится создавать собственные потоки и управлять ими.

    В Java есть стандартный класс , который вы можете использовать следующим образом:

    New Thread(new Runnable() { public void run() { //do time consuming operations }); } }).start();

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

    Если вам нужно обновлять интерфейс программы, то нужно использовать AsyncTask , о котором говорилось выше, или вы можете реализовать ваш собственный класс, наследованный от , используя объект Handler из пакета android.os для синхронизации с потоком GUI перед обновлением пользовательского интерфейса.

    Вы можете создавать дочерние потоки и управлять ими с помощью класса Handler , а также классов, доступных в пространстве имён java.lang.Thread . Ниже показан простой каркас для переноса операций в дочерний поток.

    // Этот метод вызывается из главного потока GUI. private void mainProcessing() { // Здесь трудоемкие задачи переносятся в дочерний поток. Thread thread = new Thread(null, doBackgroundThreadProcessing, "Background"); thread.start(); } // Объект Runnable, который запускает метод для выполнения задач // в фоновом режиме. private Runnable doBackgroundThreadProcessing = new Runnable() { public void run() { backgroundThreadProcessing(); } }; // Метод, который выполняет какие-то действия в фоновом режиме. private void backgroundThreadProcessing() { [ ... Трудоемкие операции... ] }

    Плохое приложение

    Напишем "плохое" приложение, неправильно использующее основной поток. Однажды мы писали программу для подсчёта ворон. На этот раз будем считать чёрных котов, которые перебегают нам дорогу. Зачем они это делают - молчит наука. Может быть собранная статистика поможет разгадать тайну. Добавим на экран активности кнопки и текстовую метку. Код для щелчка кнопки.

    Public void onClick(View view) { long endTime = System.currentTimeMillis() + 20 * 1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } TextView infoTextView = (TextView) findViewById(R.id.textViewInfo); infoTextView.setText("Сегодня коты перебегали дорогу: " + mCounter++ + " раз"); }

    Для имитации тяжёлой работы программа делает паузу на двадцать секунд, а потом выводит текст с подсчётом котов. Если нажать на кнопку один раз и подождать двадцать секунд, то программа отработает как положено. Но представьте себе, что вы нажали на кнопку один раз. Программа запустила паузу. Вы, не дожидаясь окончания паузы, снова нажали на кнопку. Программа должна выполнить вашу команду, но предыдущая команда ещё не отработала и наступает конфликт. Попробуйте нажать на кнопку несколько раз с небольшими перерывами. В какой-то момент приложение зависнет и выведет системное диалоговое окно:

    В реальных приложениях такое окно может разозлить пользователя и он поставит низкую оценку вашему приложению.

    В данном случае ошибку вызывает не сам вывод текста в текстовой метке, который, к слову, тоже выполняется в основном потоке, а сам щелчок кнопки. Если вы закомментируете последние две строчки кода, связанные с TextView , то ошибка сохранится.

    Вам необходимо перенести трудоёмкую задачу в отдельный поток. Для этого создаётся экземпляр класса Runnable , у которого есть метод run() . Далее создаётся объект , в конструкторе у которого указывается созданный Runnable . После этого можно запускать новый поток с помощью метода start() . Перепишем пример.

    Public void onClick(View view) { Runnable runnable = new Runnable() { public void run() { // Переносим сюда старый код long endTime = System.currentTimeMillis() + 20 * 1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } Log.i("Thread", "Сегодня коты перебегали дорогу: " + mCounter++ + " раз"); // Нельзя! // TextView infoTextView = // (TextView) findViewById(R.id.textViewInfo); // infoTextView.setText("Сегодня коты перебегали дорогу: " + mCounter++ + " раз"); } }; Thread thread = new Thread(runnable); thread.start(); }

    Весь код мы перенесли в метод run() . Теперь вы можете безостановочно щёлкать по кнопке. На этот раз приложение сохранит свою работоспособность. Чтобы в этом убедиться, в код добавлено протоколирование логов Log.i() . При каждом нажатии создаётся новый поток, в котором выполняется код. Потоки друг другу не мешают и дожидаются своей очереди, когда система позволит им отработать.

    Основной поток также называют UI-потоком. Имено в главном потоке можно обновить текст у текстовой метки. В создаваемых нами потоках это делать нельзя. Если вы уберёте комментарии с последнего примера и запустите проект, то получите сообщение об ошибке.

    Нужен некий посредник между создаваемыми потоками и основным UI-потоком. В роли такого посредника служит класс Handler (полное название класса android.os.Handler , не перепутайте). Вам нужно создать экземпляр класса и указать код, который нужно выполнить.

    Handler handler = new Handler() { @Override public void handleMessage(Message msg) { TextView infoTextView = (TextView) findViewById(R.id.textViewInfo); infoTextView.setText("Сегодня коты перебегали дорогу: " + mCounter++ + " раз"); } };

    После строчки кода с Log.i() добавьте вызов метода посредника.

    Log.i("Thread", "Сегодня коты перебегали дорогу: " + mCounter++ + " раз"); handler.sendEmptyMessage(0); // Нельзя! // TextView infoTextView = // (TextView) findViewById(R.id.textViewInfo); // infoTextView.setText("Сегодня коты перебегали дорогу: " + mCounter++ + " раз");

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

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

    Public void onClick(View view) { Runnable runnable = new Runnable() { public void run() { Message msg = handler.obtainMessage(); Bundle bundle = new Bundle(); SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss MM/dd/yyyy", Locale.US); String dateString = dateFormat.format(new Date()); bundle.putString("Key", dateString); msg.setData(bundle); handler.sendMessage(msg); } }; Thread thread = new Thread(runnable); thread.start(); } Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { Bundle bundle = msg.getData(); String date = bundle.getString("Key"); TextView infoTextView = (TextView) findViewById(R.id.textViewInfo); infoTextView.setText(date); } };

    Запуск потока

    Предположим, мы разрабатываем собственный проигрыватель. У нас есть кнопка Play , которая вызывает метод play() для воспроизведения музыки:

    Button playButton = (Button) findViewById(R.id.play); playButton.setOnClickListener(new View.OnClickListener()) { public void onClick(View v) { play(); // вызываем воспроизведение } }

    Теперь запустим метод в другом потоке. Сначала создаётся новый поток. Далее описывается объект Runnable в конструкторе потока. А внутри созданного потока вызываем наш метод play() . И, наконец, запускаем поток.

    Thread myThread = new Thread(// создаём новый поток new Runnable() { // описываем объект Runnable в конструкторе public void run() { play(); // вызываем метод воспроизведения } });

    Усыпить поток

    Иногда требуется временно приостановить поток ("усыпить"):

    // засыпаем на 2 секунды (в миллисекундах) myThread.sleep(2000);

    Приоритет потоков

    Для установки приоритета процесса используется метод setPriority() , который вызывается до запуска потока. Значение приоритета может варьироваться от Thread.MIN_PRIORITY (1) до Thread.MAX_PRIORITY (10):

    MyThread.setPriority(10); myThread.start();

    Отмена выполнения потока

    У потока есть метод stop() , но использовать его не рекомендуется, поскольку он оставляет приложение в неопределённом состоянии. Обычно используют такой подход:

    If (myThread != null) { Thread dummy = myThread; myThread = null; dummy.interrupt(); }

    Существует и другой способ, когда все запускаемые потоки объявляются демонами. В этом случае все запущенные потоки будут автоматически завершены при завершении основного потока приложения:

    MyThread.setDaemon(true); myThread.start();

    Я следую этому руководству, чтобы иметь экран загрузки в моей программе. В учебном пособии говорится, что моя активность должна Sleep () с помощью команды Sleep (), однако она не распознает функцию Sleep () как функцию и предоставляет мне ошибку, спрашивая, хочу ли я создать метод Sleep ().

    http://androidcookbook.com/Recipe.seam;jsessionid=4DBCC1688B51DB16A2A40A86E135D361?recipeId=1599

    Вот пример кода:

    Public class LoadingScreenActivity extends Activity { //Introduce an delay private final int WAIT_TIME = 2500; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); System.out.println("LoadingScreenActivity screen started"); setContentView(R.layout.loading_screen); findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE); new Handler().postDelayed(new Runnable(){ @Override public void run() { //Simulating a long running task this.Sleep(1000); System.out.println("Going to Profile Data"); /* Create an Intent that will start the ProfileData-Activity. */ Intent mainIntent = new Intent(LoadingScreenActivity.this,ProfileData.class); LoadingScreenActivity.this.startActivity(mainIntent); LoadingScreenActivity.this.finish(); } }, WAIT_TIME); } }

    Вы можете использовать один из следующих методов:

    Thread.sleep(timeInMills);

    Или

    SystemClock.sleep(timeInMills);

    SystemClock.sleep(milliseconds) – это функция утилиты, очень похожая на Thread.sleep(milliseconds) , но игнорирует InterruptedException . Используйте эту функцию для задержек, если вы не используете Thread.interrupt() , так как она сохранит прерванное состояние потока.

    Функция – Thread.sleep(long) .

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

    Если вы используете Handlers, имейте в виду, что обработчик создается в потоке, где он выполняется . Поэтому вызов new Handler().post(... в потоке пользовательского интерфейса будет выполнять runnable в потоке пользовательского интерфейса, включая эту «длительную операцию». Преимущество заключается в том, что вы можете создать обработчик для потока пользовательского интерфейса, который вы можете использовать Позже, как показано ниже.

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

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

    Public class LoadingScreenActivity extends Activity { //Introduce a delay private final int WAIT_TIME = 2500; private Handler uiHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); uiHandler = new Handler(); // anything posted to this handler will run on the UI Thread System.out.println("LoadingScreenActivity screen started"); setContentView(R.layout.loading_screen); findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE); Runnable onUi = new Runnable() { @Override public void run() { // this will run on the main UI thread Intent mainIntent = new Intent(LoadingScreenActivity.this,ProfileData.class); LoadingScreenActivity.this.startActivity(mainIntent); LoadingScreenActivity.this.finish(); } }; Runnable background = new Runnable() { @Override public void run() { // This is the delay Thread.Sleep(WAIT_TIME); // This will run on a background thread //Simulating a long running task Thread.Sleep(1000); System.out.println("Going to Profile Data"); uiHandler.post(onUi); } }; new Thread(background).start(); }

    Метод Thread.sleep() можно использовать для приостановки выполнения текущего потока на указанное время в миллисекундах. Значение аргумента в миллисекундах не может быть отрицательным, иначе оно выдает исключение IllegalArgumentException.

    Существует еще один метод sleep(long millis, int nanos), который можно использовать для приостановки выполнения текущего потока на указанные миллисекунды и наносекунды. Допустимое значение nano second составляет от 0 до 999999.

    InterruptedException возникает, если какой-либо поток прервал текущий поток. Прерванное состояние текущего потока очищается при возникновении этого исключения.

    Объявление:

    Public static void sleep(long millis) throws InterruptedException

    Как работает Thread Sleep?

    Thread.sleep() взаимодействует с планировщиком потока, чтобы перевести текущий поток в состояние ожидания в течение указанного периода времени. По истечении времени, ожидания состояние потока изменяется на работоспособное состояние и ожидает ЦП для дальнейшего выполнения.

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

    Пример thread sleep на Java

    Вот , в которой Thread.sleep() используется для задержки по времени основного потока на 2 секунды.

    Package com.journaldev.threads; public class ThreadSleep { public static void main(String args) throws InterruptedException { long start = System.currentTimeMillis(); Thread.sleep(2000); System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start)); } }

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

    Важно знать, что

    • Всегда приостанавливает выполнение текущего потока.
    • Фактический поток «спит» до пробуждения, и время выполнения зависит от системных таймеров и планировщиков.
    • Спящий поток не блокирует текущий поток.
    • Любой другой поток может прервать текущий поток в спящем режиме, в этом случае создается исключение InterruptedException.

    Еще пример с использованием метода java.lang.Thread.sleep().

    Package com.tutorial; import java.lang.*; public class ThreadDemo implements Runnable { Thread t; public void run() { for (int i = 10; i

    Давайте скомпилируем и запустим программу, это даст следующий результат:
    Thread-0 10
    Thread-1 10
    Thread-0 11
    Thread-1 11
    Thread-0 12
    Thread-1 12

    Оцени статью

    Оценить

    При создании мобильного приложения чуть сложнее «Hello, world» почти наверняка требуется скачать что-то из Сети или считать файл с диска. Для стабильной работы программы в целом эти действия должны совершаться в отдельных потоках. Зачем, когда и как генерировать новые потоки в Android - об этом ты узнаешь в этой статье.

    Процессы и потоки

    Прежде чем разбираться с Android API, вспомним, какой структурой обладает эта ОС. В ее основе лежит Linux-ядро, в котором реализованы базовые механизмы, присущие всем *nix-системам. В ядре собраны модули, предназначенные для низкоуровневой работы: взаимодействия с железом, организации памяти, файловой системы и так далее.

    В мире Linux каждая запущенная программа - это отдельный процесс . Каждый процесс обладает уникальным номером и собственной «территорией» - виртуальным адресным пространством, в рамках которого содержатся все данные процесса. Поток же - это набор инструкций внутри запущенной программы (процесса), который может быть выполнен отдельно. У потока нет своего уникального идентификатора и адресного пространства - все это он наследует от родительского процесса и делит с другими потоками.

    Массовое распространение в Google Play приложений, имеющих проблемы с утечкой памяти, резонно вызовет у пользователей ощущение, что «Android тормозит»

    Такое положение дел приводит к тому, что со стороны неизвестно, как протекает жизнь внутри процесса, есть ли там потоки и сколько их, - для ОС и других процессов это атомарная структура с уникальным идентификатором. Поэтому ОС может манипулировать лишь процессом, а управляет потоками только породивший их процесс. Вообще, внутренний мир операционных систем очень интересен, поэтому советую читателям почитать что-нибудь из классической литературы по Linux.

    Когда в компьютерах (а вслед за ними - в планшетах и телефонах) появились процессоры с несколькими ядрами, программисты внедрили в ОС планировщик задач. Такой планировщик самостоятельно распределяет нагрузку по всем ядрам процессора, исполняя блоки кода параллельно или асинхронно, и тем самым повышает производительность. Поначалу маркетологи даже продавали компьютеры с лозунгом «Два ядра - в два раза быстрее», но, к сожалению, действительности он не соответствует.

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

    Вообще, суть многопоточного программирования в том, чтобы максимально задействовать все ресурсы устройства, при этом синхронизируя результаты вычислений. Это не так легко, как может показаться на первый взгляд, но создатели Android добавили в API несколько полезных классов, которые сильно упростили жизнь Java-разработчику.

    Потоки в Android

    Запущенное в Android приложение имеет собственный процесс и как минимум один поток - так называемый главный поток (main thread). Если в приложении есть какие-либо визуальные элементы, то в этом потоке запускается объект класса Activity, отвечающий за отрисовку на дисплее интерфейса (user interface, UI).

    В главном Activity должно быть как можно меньше вычислений, единственная его задача - отображать UI. Если главный поток будет занят подсчетом числа пи, то он потеряет связь с пользователем - пока число не досчиталось, Activity не сможет обрабатывать запросы пользователя и со стороны будет казаться, что приложение зависло. Если ожидание продлится чуть больше пары секунд, ОС Android это заметит и пользователь увидит сообщение ANR (application not responding - «приложение не отвечает») с предложением принудительно завершить приложение.


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

    Thread и Runnable

    Базовым классом для потоков в Android является класс Thread, в котором уже все готово для создания потока. Но для того, чтобы что-то выполнить внутри нового потока, нужно завернуть данные в объект класса Runnable. Thread, получив объект этого класса, сразу же выполнит метод run .

    Public class MyRunnable implements Runnable { String goal; public MyRunnable(String goal) { this.goal=goal; } @Override public void run() { getData(goal); } } ... MyRunnable runnable = new MyRunnable("do_smth"); new Thread(runnable).start();

    Но при такой организации сложно использовать всю силу дополнительных потоков - нельзя ни поменять задачу, ни поcмотреть результат вычислений. Хотя все это происходит в едином адресном пространстве, у Java-разработчика нет возможности просто так получить ресурсы соседних потоков.

    Looper

    Было бы классно уметь перекидывать данные из одного потока в другой. В Android, как и любой Linux-системе, это возможно. Один из доступных в Android способов - это создать очередь сообщений (MessageQueue) внутри потока. В такую очередь можно добавлять задания из других потоков, заданиями могут быть переменные, объекты или кусок кода для исполнения (Runnable).

    Message msg = new Message(); Bundle mBundle = new Bundle(); mBundle.putString("KEY", "textMessage"); msg.setData(mBundle);

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

    Public class MyLooper extends Thread { Integer number; public Handler mHandler; @Override public void run() { Looper.prepare(); mHandler = new Handler() { @Override public void handleMessage(Message msg) { // process incoming messages here Log.e("Thread", "#"+number + ": "+msg.getData().getString("KEY")); } }; Looper.loop(); } }

    Запуск такого потока устроен по похожей схеме - создание нового объекта и вызов метода start .

    MyLooper myLooper = new MyLooper(); myLooper.start();

    После выполнения этого метода создастся новый поток, который заживет своей жизнью. А это значит, что инициализация переменных и создание объектов будут уже идти параллельно с теми вызовами, которые забиты в следующих строчках после команды myLooper.start() . Поэтому перед обращением к очереди в новом потоке нужно немного подождать - объект handler может еще не существовать.

    If (myLooper.hanlder!=null) { myLooper.mHandler.sendMessage(msg); }

    AsynkTask

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

    Продолжение доступно только участникам

    Вариант 1. Присоединись к сообществу «сайт», чтобы читать все материалы на сайте

    Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», увеличит личную накопительную скидку и позволит накапливать профессиональный рейтинг Xakep Score!

    airsoft-unity.ru - Портал майнингов - Виды бизнеса. Инструкции. Компании. Маркетинг. Налоги