Bu eğitimde açıklayacağız Android, bir hizmeti çalıştırırken nasıl davranır?, yürütme iş parçacıklarının nelerden oluştuğunu ve işlemlerin ne hakkında olduğunu açıklayacağız. Bu, uygulamalarımızın nasıl çalıştığını anlamamızı sağlayacak ve bize, kurulacakları mobil cihazlarda daha fazla kontrol ve kararlılık sağlayacaktır.
Konu
Kullanıcı bir uygulamayı çalıştırdığında, Android, ana (ana) adlı bir iş parçacığı oluşturur. Bu iş parçacığı çok önemlidir, çünkü kullanıcının uygun bileşenlere tetiklediği olayları yönetmekten sorumludur ve aynı zamanda ekranı çizen olayları da içerir. Bir işletim sisteminde, bu durumda Android'de (Linux çekirdeği ile) bir zamanlayıcı tarafından işlenebilen en küçük parça olan yürütme dizisi.
NS birden çok iş parçacığının uygulanması aynı uygulamada aynı anda işlenen, (yürütme eşzamanlılığını ifade eden eşzamanlılık olarak adlandırın), çok iş parçacığı olarak bilinir. Bu iş parçacıklarının kaynakları paylaşması için çoklu iş parçacığı uygulanır Ve bir işlemin içerdiği şey budur, bunun aynı uygulamanın kodu içinde programlı olarak uygulanabileceğini unutmayın, işletim sistemi düzeyinde çoklu okumanın uygulanması bize bağlı değildir.
Bir uygulamanın yaşam döngüsünün başlangıcı, bir Ana iş parçacığı veya UI iş parçacığı (uygulamanın grafik işleminden sorumlu iş parçacığı, İngilizce kullanıcı arabirimi iş parçacığı) atanan yeni bir Linux işleminin oluşturulmasını içerir.
NotYaşam döngüsü, şu yöntemlerin yürütülmesini içerir: onCreate (), onStart () ve onResume (); başlangıcında ve kapanışında: onPause (), onStop () ve onDestroy ().
Bellek yetersizliği nedeniyle Android tarafından bir işlem kapanmaya zorlanabilir, bu tür durumlar teknolojik ilerleme nedeniyle nadirdir ancak yine de olur.
Soru: Android hangi süreçleri kapatmaya karar veriyor?
Bunlar önem dereceleri karşılaştırılarak kapatılır, şöyle özetlenir:
En önemlisi: ön plan süreçleriKullanıcı söz konusu süreçle etkileşim halindedir (söz konusu sürecin onResume () yöntemi şu anda çalışmaktadır). Yaşam döngüsü yöntemlerini çalıştıran bir hizmet var. ya da var mı Yayın alıcı onun koşması onReceive () yöntemi.
İkinci en önemli: Görünür süreçlerÇağrı ile etkinlik onPause () yöntemi. Görünür bir etkinliğe bağlı hizmet (bağlı hizmet).
Üçüncü en önemlisi: Bir hizmetle süreçKullanıcı doğrudan süreçle etkileşime girmiyor.Sürecin arka planda çalışan bir hizmeti var.
İkinci en az önemli: Arka plan süreciKullanıcı ile herhangi bir etkileşim türü yoktur. Kullanıcı tarafından en son görüntülenen süreç, yok edilecek son süreç olacaktır.
En az önemli: Boş süreçAktif bileşeni yoktur. İşlem, önbelleğe alma amaçları için hala canlı ve kullanıcının bu işlemin kullanımına geri dönmesini engelliyor.
İkincisi, boş süreç, hafıza eksikliği durumunda sonlandırılacak ilk işlemdir. Bu nedenle, internetten içerik indirmek için bir iş parçacığının oluşturulduğu bir hizmeti uygulayan bir uygulama, bir hizmet uygulamadan iş parçacığı oluşturan bir uygulamadan daha önemli olacaktır, bu nedenle indirmeyi tamamlamadan sonlandırılma olasılığı daha yüksektir. , çünkü bunlar uzun süreli süreçlerdir.
anlamak için çoklu okuma, Android'in ana iş parçacığını nasıl ele aldığını görelim.
SÜREÇ A'nın bir kullanıcı arayüzü var veya ANA iplik, bu iş parçacığı bir mesaj kuyruğu veya iş parçacığı boşta kaldığında çalışan mesaj kuyruğu, bunu kim hallediyor? NS ilmek yapıcı.
Looper, bir kullanıcı arabirimi sınıfıdır. Android Java ile birlikte olduğunu işleyici sınıfı, düğmeye basma, yeniden çizilen ekranlar ve yönlendirme anahtarları gibi kullanıcı arabirimi olaylarını işler. Olaylar, bir HTTP hizmetine içerik yüklemek, görüntüleri yeniden boyutlandırmak ve uzak istekleri yürütmek için de kullanılabilir. Bu sınıfların temel özelliği, bir eşzamanlılık modeli uygulayabilmeleridir.
NS Android Döngü sınıfı içerir MessageQueue (mesaj kuyruğu) ve yalnızca oluşturulduğu konu ile ilişkilendirilir. Lütfen bu bağlantının kopamayacağını ve ldöngü başka bir ipliğe eklenemez. Ayrıca, Looper yerel depolamadadır ve yalnızca statik bir yöntemden çağrılabilir. Bir evreleme yöntemi, bir Looper'ın bir iş parçacığıyla zaten ilişkili olup olmadığını kontrol eder ve ardından statik yöntem, Looper'ı oluşturur. Daha sonra, kuyruktaki mesajları kontrol etmek için bir döngü kullanılabilir.
Şimdiye kadar birkaç kavramı anlıyoruz: süreç, iş parçacığı, UI iş parçacığı, ilmek yapıcı, ancak hala neden olduğunu bilmiyoruz. çoklu kullanım.
Uzun vadeli operasyonlar
Yürütülmesi 5 saniyeyi aşan herhangi bir yöntem için uzun süre olarak kabul edilir, bu da tipik bir mesajı tetikler: "Uygulama yanıt vermiyor. kapatmak istiyor musun?
Bu işlemler neler olabilir?: İnternet erişimi, SQL sorguları, XML/HTML/JSON ayrıştırma, karmaşık grafik işleme. Ana iş parçacığında çalıştırılan bu işlemlerden herhangi biri onu engeller ve grafiksel kullanıcı arabirimini yöneten bu olduğundan, android'in kapatmaya karar verdiği bir donma olarak yorumlanır.
Bu işlemlerden herhangi birinin 7 saniye sürdüğünü ve kullanıcının bazı metin girişlerinde bir şeyler yazmaya karar verdiğini düşünelim, bu nedenle bu 7 saniye geçmemişken, UI iş parçacığı, kullanıcının yazdığını takdir etmesi için görünümü güncelleyemez ve bu nedenle bir donma oluşturur, iki seçeneğiniz olan "yanıt yok" mesajı tetiklenir, bekle veya yok et, ne kadar bekleyeceğinizi asla bilemeseniz de, mesaj kuyruğuna bağlı olarak birkaç saniye veya hatta dakika olabilir Ana iş parçacığına sahip olan.
Donmayı nasıl önleriz?
Görevin görünümü değiştirmeyi gerektirip gerektirmediğine bağlı olarak, iş parçacıkları veya hizmetler kullanılması, bu durumda bir uygulamanın görünümü UI iş parçacığı dışında değiştirilemeyeceğinden bir hizmet uygulanır. Donmayı önlemenin en iyi yolu Asenkron Görevleri AsyncTask sınıfıyla kullanmaktır, bu eğitimde Android mimarisinin davranışını anlamak için birden çok iş parçacığı uygulayacağız.
Kod ve geliştirme
Bundan sonra oluşturacağımız proje bir görüntü indirmeye dayalı olacaktır bununla internet üzerinden erişimi ve indirmeyi yönetmemize izin veren bir iş parçacığı oluşturmamız gerekiyor çünkü ANA veya Kullanıcı Arabirimi Konusu bu işleme izin vermez.
Boş bir aktivite ile yeni bir proje oluşturarak başlayacağız, bu projeye isim verdik. "Çoklu İş parçacığı Örneği", tek bir basit aktivite ile XML dosyasının yapısını oluşturacağız bu aktiviteye ait olan.
Bir metin alanımız, bir düğmemiz, daha sonra kullanacağımız belirsiz bir yükleme çubuğuna karşılık gelen bir Doğrusal düzenimiz ve internette barındırılan resimlerin bir dizi URL'sini içeren bir liste görünümümüz var. (Benzersiz) etkinliğimiz için Java sınıfını içeren dosyada aşağıdaki kodla yazılmıştır:
paket com.omglabs.multithreaexample; android.support.v7.app.AppCompatActivity'yi içe aktarın; android.os.Bundle'ı içe aktar; android.view.View'i içe aktar; android.widget.AdapterView'ı içe aktarın; android.widget.EditText'i içe aktar; android.widget.LinearLayout'u içe aktarın; android.widget.ListView'ı içe aktarın; android.widget.ProgressBar'ı içe aktar; public class MainActivity, AppCompatActivity'yi genişletir AdapterView.OnItemClickListener {private EditText editText; özel ListView listView; özel Dize [] url'leri; özel ProgressBar ilerleme Çubuğu; özel LinearLayout progressLayout; @Override protected void onCreate (Paket saveInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); editText = (EditText) findViewById (R.id.downloadURL); listView = (ListView) findViewById (R.id.listurls); listView.setOnItemClickListener (bu); urls = getResources().getStringArray (R.array.URLs); progressBar = (ProgressBar) findViewById (R.id.progressbar); progressLayout = (LinearLayout) findViewById (R.id.progresslayout); } public void indirme (Görünüm görünümü) {} @Override public void onItemClick (AdapterView AdapterView, Görünüm görünümü, int i, uzun l) {editText.setText (urls [i]); }}Şimdiye kadar uygulama sorunsuz bir şekilde derlenebilir, bu sınıfta değişkenleri bildiririz:
- metni düzenle
- liste görünümü
- url'ler
- ilerleme çubuğu
- ilerleme Düzeni
Bir metin alanı, bir liste, bir dize düzenlemesi, bir ilerleme çubuğu ve bir Doğrusal Düzen.
İçinde onCreate yöntemi Dize dosyasındaki değerler klasöründen değerlerini atayan ve düzenlemesi bildirilen url'ler hariç, kendilerine ait olan ve etkinliğin XML dosyasında oluşturulan ilgili görünümü bunlara atarız. aşağıdaki gibi:
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.webpBoş yöntem indirmesi (Görünüm görünümü), indirmeyi yapacak olan kodla doldurulacaktır. İndir düğmesi Bot onclick özelliği aracılığıyla. Sonunda onitemclick yöntemi hangisine ait liste görünümü, listede bulunan url'lerden herhangi birine tıkladığınızda metin alanını doldurur. Bu kod derlendikten sonra şöyle görünecektir:
Bir sonraki adımda, aşağıdaki adımları izleyerek indirmeye devam edecek yöntemleri oluşturacağız:
- İndirilecek url'yi temsil edecek bir URL sınıfı (java.net) nesnesi oluşturun.
- Bu nesneyi kullanarak bağlantıyı açın.
- Bir bayt dizisindeki giriş akışı sınıfını kullanarak verileri (web aracılığıyla) okuyun.
- URL verilerinin SD karta kaydedileceği bir çıktı akışı dosyası açın / oluşturun.
- Verileri o dosyaya yazın.
- Ve son olarak bağlantıyı kapatın.
Şimdilik şöyle görünecek:
genel boolean indirme kullanarakThreads (String link) {booleanonay = false; URL indirmeLink = null; HttpURLConnection bağlantı = boş; InputStream inputStream = null; {downloadLink = yeni URL'yi (bağlantı) deneyin; bağlantı = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream(); } yakalama (MalformedURLException e) {e.printStackTrace (); } yakalama (IOException e) {e.printStackTrace (); } nihayet {if (bağ! = boş) {bağ.disconnect (); } if (inputStream! = null) {deneyin {inputStream.close(); } yakalama (IOException e) {e.printStackTrace (); }}} iade onayı; }Oluşturduğumuz bu yöntem sadece bir Sicim indirilecek URL olacak, boole İndirmeyi onaylamak için downloadLink, URL nesnesidir, bağlantı, nesneye erişmek için yapılacak bağlantıdır ve inputStream, bu yöntemi düğmede kullanmaya çalışırsak verileri okumaya devam edecek olandır. indirBot üzerinde çalışamadığı için uygulama duracaktı. ana konu.
Burada thread kullanımı ile başlıyoruz, bunu bir sınıfla yapmanın iki yolu vardır ve bu, o sınıfı Thread'e genişleterek veya Runnable sınıfını uygulayarak, bu sınıf bir thread değil, sadece sizin kullanabileceğiniz bir yöntem oluşturmanıza izin verir. belirli bir anda çalışabilir ve ayrı bir iş parçacığı oluşturursanız, içinde çalıştırın.
İndir butonunun içine bu kodu yazacağız ve şöyle görünecek:
public void indirme (Görünüm görünümü) {Thread mThread = new Thread (yeni mRunn ()); mThread.start(); }Burada, aşağıdaki gibi özel bir sınıfta oluşturduğumuz Runnable nesnesine ihtiyaç duyan yeni bir iş parçacığı oluşturuyoruz:
özel sınıf mRunn, Runnable {@Override public void run () { kullanarakThreads indir (urls [0]); }}Özel sınıf oluştur
NotBunların hepsinin tek etkinliğimizin Java sınıfında olduğunu unutmayın.
Hat ile:
kullanarak Konuları indirin (url'ler [0]);Bağlantıyı açtığımız yerde oluşturduğumuz işlevi çağırıyoruz, URL dizisinin bir öğesi ona o adresten veri okuyabilmesi için geçiriliyor. Daha sonra değiştirilecektir.
Butona basarak bu uygulamayı çalıştırmayı deneseydik uygulama dururdu, çünkü uygulamamızın manifestosu aracılığıyla istenen internete erişmek için özel bir izne ihtiyacımız var. Etiketten önce satırı ekleme:
Şimdi, uygulamanın indirmeyi gerçekten gerçekleştirdiğini doğrulamak için, dosyaya birkaç satır kod ekleyeceğiz. Threads kullanarak indirme yöntemi, Bunun gibi görünecek:
genel boolean indirme kullanarakThreads (String link) {booleanonay = false; URL indirmeLink = null; HttpURLConnection bağlantı = boş; InputStream inputStream = null; FileOutputStream archOutputStream = boş; Dosya dosyası = boş; {downloadLink = yeni URL'yi (bağlantı) deneyin; bağlantı = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream(); dosya = yeni Dosya (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (bağlantı) .getLastPathSegment ()); archOutputStream = yeni FileOutputStream (dosya); int Okuma = -1; bayt [] arabellek = yeni bayt [1024]; while ((Oku = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } onay = doğru; } yakalama (MalformedURLException e) {e.printStackTrace (); } yakalama (IOException e) {e.printStackTrace (); } nihayet {if (bağ! = boş) {bağ.disconnect (); } if (inputStream! = null) {deneyin {inputStream.close(); } yakalama (IOException e) {e.printStackTrace (); }} if (archOutputStream! = null) {deneyin {archOutputStream.close (); } yakalama (IOException e) {e.printStackTrace (); }}} iade onayı; } FileOutputStream archOutputStream = boş; Dosya dosyası = boş;Bu nesnelerin bildirimleri, okunmakta olan dosyanın yazılmasını ve okumanın kaydedileceği boş dosyayı temsil eder.
dosya = yeni Dosya (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = yeni FileOutputStream (dosya); int Okuma = -1; bayt [] arabellek = yeni bayt [1024]; while ((Oku = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } onay = doğru;"Dosya", adresi "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" SD kartına erişilerek ve bir eğik çizgi "/" eklenerek ve URL'nin genellikle dosyanın adını temsil eden son bölümü eklenerek oluşturulan boş Dosya nesnesidir. indir, bunu getLastPathSegment() yöntemiyle elde ediyoruz.
Uygulamayı test etmeden önce bildirime son bir izin ekleyeceğiz:
Uygulamayı emülatörde veya Android cihazda çalıştırdıktan sonra, düğmeye bastığımızda görünüşte hiçbir şey olmadığını göreceğiz ancak İndirme klasörünü bir dosya gezgini ile kontrol edersek listedeki ilk öğenin indirildiğini fark edeceğiz; 1.jpg.webp adlı bir fotoğraf.
Yapmak için dinamik uygulama ve liste görünümünün URL'lerini uygulayın, güncelleyeceğiz indirme yöntemi (Görünümü görüntüle) ve bunu ilk satır olarak ekleyeceğiz:
String link = editText.getText().ToString();Ve içinde mRunn sınıfı bunu run() yönteminden önce ekleyeceğiz:
özel sınıf mRunn, Runnable {private String link; public mRunn (String link) {this.link = link; } @Override public void run () { kullanarakThreads indirin (bağlantı); }}Ve içinde mRunn sınıfı bunu run() yönteminden önce ekleyeceğiz:
Böylece metin alanından link değişkenini indirme işlemini gerçekleştiren metoda geçirebiliriz. Bu noktada uygulama, biraz kullanıcı dostu olmamasına rağmen tamamen işlevseldir, bu yüzden başlangıçta bildirdiğimiz ilerleme çubuğunu kullanarak bunu düzeltmeye çalışacağız.
run () yöntemindeki mRunn sınıfına şunları dahil edeceğiz:
MainActivity.this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}});downloadusandoThreads çağrısından önce. Bu, butona bastığımızda yükleme çubuğunun görünmesini sağlayacaktır, nihayet yan tümcesinde Threads kullanarak indirme yöntemi.
Ekleyeceğiz:
this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}});Böylece indirme tamamlandığında çubuk tekrar kaybolur. Bu, indirme başarılı olsun veya olmasın gerçekleşecektir.
Ve hepsi bu oldu, bir birden çok iş parçacığının kısa uygulanmasıBu biraz sıkıcıdır ve daha karmaşık uygulamalar için bazı komplikasyonlar getirir.Bu görevi gerçekleştirmenin en etkili yolu, bizim durumumuzda bazı görüntüleri indirmektir. zaman uyumsuz görevler.