Програма Android: Процеси, теми и услуги

В този урок ще обясним как действа Android при стартиране на услуга, ще опишем от какво се състоят нишките за изпълнение и какви са процесите. Това ще ни позволи да разберем начина, по който се изпълняват нашите приложения, като ни дава по -голям контрол и стабилност на мобилните устройства, където те ще бъдат инсталирани.

Конец


Когато потребителят стартира приложение, Android създава нишка, наречена main (main). Тази нишка е много важна, тъй като отговаря за управлението на събитията, които потребителят задейства в съответните компоненти, а също така включва събитията, които нарисуват екрана. Нишка на изпълнение, най -малката част, която може да бъде обработена от планировчик в операционна система, в този случай Android (с ядро ​​на Linux).

The внедряване на множество нишки които се обработват по едно и също време в едно и също приложение (наричаме го едновременно, което се отнася до едновременността на изпълнение), той е известен като многонишково. Прилага се многонишково, така че тези нишки споделят ресурси И това е, което включва един процес, не забравяйте, че това може да се приложи програмно в кода на същото приложение, внедряването на многопоточност на ниво операционна система не зависи от нас.

Началото на жизнения цикъл на приложението включва генерирането на нов процес на Linux, на който е присвоена основна нишка или нишка на потребителския интерфейс (нишката, която отговаря за графичната обработка на приложението, нишка на потребителския интерфейс на английски).

ЗабележкаЖизненият цикъл включва изпълнението на методите: onCreate (), onStart () и onResume (); в началото и в края му: onPause (), onStop () и onDestroy ().

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

Въпросът е: Кои процеси Android решава да затвори?

Те се затварят чрез сравняване на нивото им на важност, обобщава се по следния начин:

Най -важното: процеси на преден планПотребителят взаимодейства със споменатия процес (методът onResume () на споменатия процес в момента се изпълнява). Има услуга, която изпълнява методите на своя жизнен цикъл. Или има а Излъчващ приемник тичане неговото метод onReceive ().

Второто най -важно: Видими процесиДейност с повикване до метод onPause (). Услуга, свързана с видима дейност (свързана услуга).

Третият най -важен: Процес с услугаПотребителят не взаимодейства директно с процеса. Процесът има услуга, работеща във фонов режим.

Второто най -малко важно: Фонов процесНяма тип взаимодействие с потребителя. Процесът, последно разгледан от потребителя, ще бъде унищожен последен.

Най -малко важното: Празен процесТой няма активни компоненти. Процесът все още е жив за целите на кеширането, като не позволява на потребителя да се върне към използването на този процес.

Последният, празният процес, е първият, който се прекратява в случай на липса на памет. По този начин, приложение, което реализира услуга, в която се създава нишка за изтегляне на съдържание от интернет, ще бъде по -важно от приложение, което създава нишката без внедряване на услуга, така че е по -вероятно да бъде прекратено преди завършване на изтеглянето. , тъй като те са дълготрайни процеси.

За да се разбере многопоточни, нека видим как Android се справя с основната си нишка.

ПРОЦЕС А има потребителски интерфейс или ОСНОВНА нишка, тази нишка обработва a опашка за съобщения или опашка за съобщения, която се изпълнява, когато нишката става неактивна, кой обработва това? The Looper.

Looper е клас на потребителски интерфейс на Android Java че заедно с Клас манипулатор, обработва събития от потребителския интерфейс като натискане на бутони, прекроени екрани и превключватели за ориентация. Събитията могат да се използват и за зареждане на съдържание в HTTP услуга, преоразмеряване на изображенията и изпълнение на отдалечени заявки. Ключовата характеристика на тези класове е, че те могат да прилагат модел на едновременност.

The Клас Android Looper съдържа a MessageQueue (опашка за съобщения) и е свързана само с темата, от която е създадена. Моля, обърнете внимание, че тази връзка не може да бъде прекъсната и че lLooper не може да бъде прикрепен към друга нишка. Също така Looper е на локално хранилище и може да бъде извикан само от статичен метод. Етапният метод проверява дали Looper вече е свързан с нишка и след това статичният метод създава Looper. След това може да се използва цикъл за проверка на съобщенията в опашката.

Досега разбираме няколко понятия: процес, нишка, нишка на потребителския интерфейс, петлител, но все още не знаем защо многонишково.

Дългосрочни операции


Счита се за продължителна за всеки метод, чието изпълнение надвишава 5 секунди, което задейства типичното съобщение „приложението не реагира. Искате ли да го затворите?

Какви могат да бъдат тези операции?: Достъп до интернет, SQL заявки, XML / HTML / JSON анализ, сложна графична обработка. Всяка от тези операции, които се изпълняват в основната нишка, ще я блокира и тъй като тя е тази, която обработва графичния потребителски интерфейс, тя се интерпретира като замразяване, което android решава да затвори.

Нека си представим, че всяка от тези операции продължава 7 секунди и потребителят решава да напише нещо при въвеждане на текст, така че докато тези 7 секунди не са изтекли, нишката на потребителския интерфейс не може да актуализира изгледа, така че потребителят да оцени, че пише, и така че генерира замразяване, се задейства съобщението „без отговор“, с което имате две възможности, изчакайте или унищожете, въпреки че никога не можете да знаете колко дълго да чакате, може да отнеме няколко секунди или дори минути в зависимост от опашката за съобщения които имат основната нишка.

Как да избегнем замръзване?


Използване на нишки или услуги, в зависимост от това дали задачата изисква промяна на изгледа, в този случай се реализира услуга, тъй като изгледът на приложение не може да бъде променен извън нишката на потребителския интерфейс. Най -добрият начин да избегнете замразяване е да използвате асинхронни задачи с класа AsyncTask, в този урок ще внедрим множество нишки, за да разберем поведението на архитектурата на Android.

Код и развитие


Проектът, който ще създадем по -нататък ще се основава на изтегляне на изображение с която трябва да създадем нишка, която ни позволява да управляваме достъпа и да изтегляме през интернет, защото ОСНОВНО или UI нишка не позволява това действие.

Ще започнем със създаването на нов проект с празна дейност, ние сме озаглавили този проект "MultiThreadExample", с една проста дейност ще създадем структурата на XML файла което принадлежи към тази дейност.

 
Имаме текстово поле, бутон, линейно оформление, което съответства на неопределена лента за зареждане, която ще използваме по -късно, и изглед на списък, който съдържа масив от URL адреси на изображения, хоствани в интернет. Във файла, който съдържа класа Java за нашата (уникална) дейност, той е написан със следния код:
 пакет com.omglabs.multithreaexample; импортиране на android.support.v7.app.AppCompatActivity; импортиране на android.os.Bundle; импортиране на android.view.View; импортиране на android.widget.AdapterView; импортиране на android.widget.EditText; импортиране на android.widget.LinearLayout; импортиране на android.widget.ListView; импортиране на android.widget.ProgressBar; публичен клас MainActivity разширява AppCompatActivity реализира AdapterView.OnItemClickListener {private EditText editText; частен ListView listView; частни URL адреси на String []; частен ProgressBar progressBar; частен LinearLayout progressLayout; @Override защитена void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); editText = (EditText) findViewById (R.id.downloadURL); listView = (ListView) findViewById (R.id.listurls); listView.setOnItemClickListener (това); urls = getResources (). getStringArray (R.array.URLs); progressBar = (ProgressBar) findViewById (R.id.progressbar); progressLayout = (LinearLayout) findViewById (R.id.progresslayout); } public void изтегляне (View view) {} @Override public void onItemClick (AdapterView adapterView, View view, int i, long l) {editText.setText (urls [i]); }} 
Досега приложението може да се компилира без проблем, в този клас декларираме променливите:
  • editText
  • listView
  • URL адреси
  • progressBar
  • progressLayout

Текстово поле, списък, подреждане на низ, лента за напредъка и линейно оформление.

В метод onCreate Ние присвояваме на тях съответния изглед, който им принадлежи и който е създаден в XML файла на дейността, с изключение на URL адресите, които присвояват стойностите му от папката ценности в низния файл и чиято подредба е декларирана както следва:

 http://www.fmdos.cl/wp-content/uploads/2016/03/1.jpg.webp http://vignette3.wikia.nocookie.net/teenwolf/images/9/90/Crystal_Reed_003.jpeg.webp https: // pbs.twimg.com/profile_images/699667844129107968/EvhTFBHN.jpg.webp http://vignette1.wikia.nocookie.net/teen-wolf-pack/images/0/0b/Holland-holland-roden-31699868-500-600.png.webp 
Празният метод за изтегляне (View view) ще бъде попълнен с кода, който ще извърши изтеглянето и който е свързан с Бутон за изтегляне Bot чрез атрибута onclick. Най -накрая метод onitemclick който принадлежи на listview, той запълва текстовото поле, когато щракнете върху някой от URL адресите, съдържащи се в списъка. След като бъде компилиран, този код ще изглежда така:

В следващата стъпка ще създадем методите, които ще продължат към изтеглянето, като следваме тези стъпки:

  • Създайте обект от класа URL (java.net), който ще представлява URL адреса за изтегляне.
  • Отворете връзката с помощта на този обект.
  • Прочетете данните (чрез мрежата), като използвате класа на входния поток в байтов масив.
  • Отваряне / създаване на изходен поток файл, където URL адресите ще бъдат записани на SD картата.
  • Запишете данните в този файл.
  • И накрая затворете връзката.

Засега ще изглежда така:

 публично логическо изтегляне usingThreads (връзка към низ) {boolean confirmation = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; опитайте {downloadLink = нов URL адрес (връзка); връзка = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } накрая {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {опитайте {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} потвърждение за връщане; } 
Този метод, който сме изградили, ще се нуждае само от a Низ който ще бъде URL адресът за изтегляне, е булева За да потвърдите изтеглянето, downloadLink е URL обектът, връзката е връзката, която ще бъде направена за достъп до обекта и inputStream е тази, която ще продължи да чете данните, ако се опитаме да използваме този метод на бутона downloadBot приложението би спряло поради невъзможността да работи на основна нишка.

Тук отиваме с използването на нишки, има два начина да направите това с клас и това е чрез разширяване на този клас до Thread или прилагане на Runnable класа, този клас не е нишка, той просто ви позволява да създадете метод, който вие може да стартира в определен момент и ако създадете отделна нишка, стартирайте я в нея.

Вътре в бутона за изтегляне ще напишем този код и той ще изглежда така:

 публично изтегляне на празнотата (Преглед на изгледа) {Thread mThread = нова нишка (нова mRunn ()); mThread.start (); } 
Тук създаваме нова нишка, която се нуждае от Runnable обект, който създаваме в частен клас като този:
 частен клас mRunn изпълнява Runnable {@Override public void run () {изтегляне usingThreads (urls [0]); }} 
Създайте частен клас

ЗабележкаНе забравяйте, че всичко това е в класа Java на единствената ни дейност.

С реда:

 изтегляне с помощта на Threads (urls [0]);
Извикваме функцията, която създадохме, когато отворихме връзката, към нея се предава елемент от URL масива, за да може да чете данните от този адрес. По -късно той ще бъде променен.

Ако се опитаме да стартираме това приложение с натискане на бутона, приложението ще спре, тъй като се нуждаем от специално разрешение за достъп до интернет, което се изисква чрез манифеста на нашето приложение. Добавяне на реда преди етикета:

 
Сега, за да проверим дали приложението действително извършва изтеглянето, ще добавим няколко реда код в метод за изтегляне с помощта на Threads, ще изглежда така:
 публично логическо изтегляне usingThreads (връзка към низ) {boolean confirmation = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; FileOutputStream archOutputStream = null; Файлов файл = null; опитайте {downloadLink = нов URL адрес (връзка); връзка = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); файл = нов файл (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (връзка) .getLastPathSegment ()); archOutputStream = нов FileOutputStream (файл); int Прочетете = -1; байт [] буфер = нов байт [1024]; while ((Read = inputStream.read (буфер))! = -1) {archOutputStream.write (буфер, 0, Read); } потвърждение = вярно; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } накрая {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {опитайте {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }} ако (archOutputStream! = null) {опитайте {archOutputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} потвърждение за връщане; } FileOutputStream archOutputStream = null; Файлов файл = null; 
Декларациите на тези обекти представляват писането на файла, който се чете, и празния файл, където ще бъде записано четенето.
 файл = нов файл (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = нов FileOutputStream (файл); int Прочетете = -1; байт [] буфер = нов байт [1024]; while ((Read = inputStream.read (буфер))! = -1) {archOutputStream.write (буфер, 0, Read); } потвърждение = вярно; 
"File" е празният File обект, чийто адрес е конструиран чрез достъп до SD картата "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" и добавяне на наклонена черта "/" и последния сегмент от URL адреса, който обикновено представлява името на файла към изтегляне, постигаме това с метода getLastPathSegment ().

Преди да тестваме приложението, ще добавим последно разрешение в манифеста:

 
След като стартираме приложението на емулатора или устройството с Android, при натискане на бутона ще видим, че очевидно нищо не се случва, но ако проверим папката Download с файловия изследовател, ще разберем, че първият елемент от списъка е изтеглен; снимка, наречена 1.jpg.webp.

Да го направя динамично приложение и внедряване на URL адресите на viewview, ще актуализираме метод на изтегляне (Преглед на изгледа) и ще добавим това като първи ред:

 Низова връзка = editText.getText (). ToString ();
И в клас mRunn ще добавим това, преди метода run ():
 частен клас mRunn изпълнява Runnable {private String link; обществен mRunn (низова връзка) {this.link = връзка; } @Override public void run () {изтегляне usingThreads (връзка); }}
И в клас mRunn ще добавим това, преди метода run ():

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

В класа mRunn в метода run () ще включим:

 MainActivity.this.runOnUiThread (нов Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}}); 
Преди обаждането към downloadusandoThreads. Това ще накара лентата за зареждане да се появи, когато натиснем бутона, в крайната клауза на метод за изтегляне с помощта на Threads.

Ще добавим:

 this.runOnUiThread (нов Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}}); 
Така че, когато изтеглянето завърши, лентата отново изчезва. Това ще се случи независимо дали изтеглянето е успешно или не.
И това е всичко, едно кратко изпълнение на множество нишкиТова е малко досадно и носи някои усложнения за по -сложни приложения.Най -ефективният начин за изпълнение на тази задача, в нашия случай изтегляне на някои изображения, е използването на AsyncTasks.

wave wave wave wave wave