Skip to content

Commit 8eb2713

Browse files
committed
dynamic links published and service post added
1 parent d056192 commit 8eb2713

File tree

8 files changed

+257
-4
lines changed

8 files changed

+257
-4
lines changed

_drafts/2019-07-08-service.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
---
2+
layout: post
3+
title: "Service"
4+
date: 2019-07-08
5+
categories: ["Background"]
6+
image: background/service
7+
github: background/tree/master/service
8+
description: "Background threading"
9+
keywords: "service, thread, task, foreground, background, bound, start, bind, onstartcommand, threading, android, programowanie, programming"
10+
---
11+
12+
## Wstęp
13+
`Service` jest usługą dziąłającą w tle na głównym wątku aplikacji. Nie posiada interfejsu użytkownika i jest niezależny względem cyklu życia aktywności (`Activity`). Komponenty mogą związać się do serwisu w celu interakacji z nim. Wykorzystywany przede wszystkim do natychmiastowego wykonywania długich, powtarzalnych lub ciągłych zadań w tle implementowanych na wątku roboczym (np. pobieranie, przetwarzanie danych, odtwarzanie muzyki). Może zostać uruchomiony przez różne komponenty oraz przez inne aplikacje. Jest zdolny do kontynuowania pracy w tle nawet po wyjściu z aplikacji. Może rozpocząć działanie zarówno poprzez wystartowanie jak i podpięcie. Wyróżnia się trzy tryby działania serwisu: `background`, `foreground` i `bound`.
14+
## Ograniczenia
15+
Ze względu na wykonywanie pracy całego serwisu na wątku głównym należy zaimplementować kosztowne zadania usługi w oparciu o wątki robocze lub jako alternatywę rozważyć wykorzystanie `IntentService`. W zależności od wybranego trybu działania serwisu trzeba uwzględnić także konieczność dostarczenia mechanizmu odpowiedzi zwrotnej do klienta oraz restrykcję systemu dla procesów działających w tle. Jeśli wykonywanie zadania ma sens tylko w ramach aktywnego danego ekranu czy też powiązane jest z cyklem życia komponentu należy zrezygnować z wykorzystania `Service` i użyć innych mechanizmów takich jak np. `AsyncTask` czy `HandlerThread`.
16+
17+
## Implementacja
18+
Aby stworzyć klasyczny serwis należy rozszerzyć klasę `Service` oraz dostarczyć implementację podpięcia serwisu i opcjonalnie nadpisać metody cyklu życia. Metoda `onStartCommand` wywoływana jest w wyniku żądania wystartowania serwisu przez `startService` lub `startForegroundService`, a `onBind` w sytuacji podpięcia serwisu przez `bindService`. Obie metody mogą zostać wykonane wielokrotnie w ramach działania serwisu. Metody cyklu życia `onCreate` i `onDestroy` wywoływane są jeden raz w momencie inicjalizacji i niszczenia serwisu niezależnie od tego czy został on wystartowany (`startService`) czy podpięty (`bindService`). Ponadto należy zadeklarować komponent serwisu w pliku `AndroidManifest`.
19+
20+
{% highlight kotlin %}
21+
class CustomService : Service() {
22+
23+
//use worker thread if long running operation to not block main UI thread
24+
//or provide some multithreading if needed
25+
private val handlerThread = HandlerThread("HandlerThread")
26+
private lateinit var handler : Handler
27+
28+
override fun onBind(intent: Intent?): IBinder? {
29+
//invokes when bindService called, retrieve intent and decide what to do
30+
//if service is created by bindService and onStartCommand wasn't called then runs only as components are bound
31+
32+
return null //provide communication interface or return null when no bind needed
33+
}
34+
35+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
36+
//invokes when startService or startForegroundService called, retrieve intent and decide what to do
37+
//continues to run until stops itself or by another component
38+
39+
return START_STICKY //restart service strategy after destroyed by the system
40+
}
41+
42+
override fun onCreate() {
43+
//invokes one time setup before onStartCommand or onBind
44+
//not called when service already running
45+
super.onCreate()
46+
handlerThread.start()
47+
handler = Handler(handlerThread.looper)
48+
}
49+
50+
override fun onDestroy() {
51+
//invokes when stopSelf or stopService is called
52+
//service is no longer used or is being destroyed by system or client
53+
super.onDestroy()
54+
handler.removeCallbacksAndMessages(null)
55+
handlerThread.quitSafely()
56+
handlerThread.interrupt()
57+
}
58+
59+
//more lifecycle callbacks
60+
}
61+
62+
//run startService or bindService and pass Intent to run Service
63+
{% endhighlight %}
64+
65+
{% highlight xml %}
66+
<!-- don't declare intent filters, use explicit intent to start -->
67+
<!-- enabled - can be instantiated by the system, exported - is available for other apps -->
68+
<service
69+
android:name=".CustomService"
70+
android:enabled="true"
71+
android:exported="false"/>
72+
{% endhighlight %}
73+
74+
## Background
75+
Serwis w trybie `background` przeznaczony jest do wykonywania operacji, których wynik nie jest odnotowany przez użytkownika (np. krótkie zapytanie sieciowe). Wykorzystywany do zadań typu `fire and forget`, startowany za pomocą metody `startService`. Ze względu na nałożone ograniczenia systemowe (które nie pozwalają na działanie serwisu w tle bez wiedzy użytkownika) od wersji `Android O` serwis może działać tylko gdy aplikacja jest w trybie `foreground`. W przypadku wyjścia z aplikacji lub przeniesienia jej do tła działanie serwisu zostanie po pewnym czasie zatrzymane, chyba że zostanie on przeniesiony do trybu `foreground`.
76+
77+
{% highlight kotlin %}
78+
class BackgroundService : Service() {
79+
80+
override fun onBind(intent: Intent?): IBinder? {
81+
return null
82+
}
83+
84+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
85+
if(intent != null && intent.hasExtra("PARAM")) {
86+
val data = intent.getStringExtra("PARAM")
87+
//do something based on param
88+
action()
89+
}
90+
return START_STICKY
91+
}
92+
93+
override fun onDestroy() {
94+
//will destroy on Android Oreo and above if app close
95+
super.onDestroy()
96+
}
97+
98+
private fun action() {
99+
//some work
100+
101+
//inform about finish by Toast
102+
Toast.makeText(this, "Some message", Toast.LENGTH_LONG).show()
103+
104+
//after that service is no longer need, so destroy manual
105+
stopSelf()
106+
}
107+
}
108+
109+
class BackgroundActivity : AppCompatActivity() {
110+
111+
override fun onCreate(savedInstanceState: Bundle?) {
112+
super.onCreate(savedInstanceState)
113+
114+
val intent = Intent(this, BackgroundService::class.java)
115+
intent.putExtra("PARAM", "value")
116+
startService(intent)
117+
}
118+
119+
override fun onStop() {
120+
super.onStop()
121+
stopService(Intent(this, BackgroundService::class.java))
122+
}
123+
}
124+
{% endhighlight %}
125+
126+
## Foreground
127+
Tryb `foreground` wykorzystywany jest do wykonywania zadań zauważalnych przez użytkownika (np. odtwarzanie muzyki) i startowany jest za pomocą `startForegroundService`. Serwis działający jako `foreground` musi wyświetlać notyfikację (`Notification`) za pomocą metody `startForeground`, która informuje użytkownika o działaniu jakiegoś zadania w tle spełniając tym samym ograniczenia systemów od wersji `Android O` dla usług działających w tle (bez wiedzy użytkownika). Kontynuuje pracę także po wyjściu z aplikacji czy pomimo braku interakacji.
128+
129+
{% highlight kotlin %}
130+
class ForegroundService : Service() {
131+
132+
override fun onBind(intent: Intent?): IBinder? {
133+
return null
134+
}
135+
136+
override fun onCreate() {
137+
super.onCreate()
138+
139+
//must run service in foreground immediately by show notification
140+
showNotification()
141+
//notification can't be dismissed unless the service is stopped or removed from foreground
142+
}
143+
144+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
145+
//do some action
146+
return START_STICKY
147+
}
148+
149+
private fun showNotification() {
150+
//create NotificationChannel if not exists for Android Oreo and above
151+
val channelId = "channel_id"
152+
153+
//customize notification
154+
val notification = NotificationCompat.Builder(this, channelId)
155+
.setOngoing(true)
156+
.setSmallIcon(R.mipmap.ic_launcher)
157+
.setContentTitle("title")
158+
.build()
159+
160+
//start service in foreground
161+
startForeground(100, notification)
162+
}
163+
}
164+
165+
class ForegroundActivity : AppCompatActivity() {
166+
167+
override fun onCreate(savedInstanceState: Bundle?) {
168+
super.onCreate(savedInstanceState)
169+
170+
val intent = Intent(this, ForegroundService::class.java)
171+
intent.putExtra("PARAM", "value")
172+
173+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
174+
//background restriction for Android Oreo and above
175+
startForegroundService(intent)
176+
}
177+
else {
178+
//just start as normal
179+
startService(intent)
180+
}
181+
182+
//remove from foreground by calling stopForeground
183+
}
184+
}
185+
{% endhighlight %}
186+
187+
## Bound
188+
W momemencie podpięcia komponentu aplikacji do serwisu za pomocą metody `bindService` przechodzi on w tryb `bound`. Taki serwis dostarcza interfejs komunikacji między komponentem, a nim samym oraz umożliwia wysyłanie żądanie i odbieranie wyników. Jeśli nie został wcześniej wystartowany w standardowy sposób przez `startService` wówczas działa tak długo dopóki jest podpięty przynajmniej przez jeden komponent.
189+
190+
{% highlight kotlin %}
191+
class BoundService : Service() {
192+
193+
inner class CustomBinder : Binder() {
194+
fun getService() : CustomService = this@CustomService
195+
//allow to call public methods
196+
}
197+
198+
private val binder = CustomBinder()
199+
200+
override fun onBind(intent: Intent?): IBinder? {
201+
return binder
202+
}
203+
204+
override fun onUnbind(intent: Intent?): Boolean {
205+
//called when all clients disconnect, return true to allow call onRebind
206+
return super.onUnbind(intent)
207+
}
208+
209+
override fun onRebind(intent: Intent?) {
210+
//called when new client connect after all had disconnected
211+
super.onRebind(intent)
212+
}
213+
214+
//can be used by Binder
215+
fun action() {
216+
//some work
217+
}
218+
}
219+
220+
class BoundActivity : AppCompatActivity() {
221+
222+
private var service : CustomService? = null
223+
private var isBound = false
224+
225+
private val connection = object: ServiceConnection {
226+
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
227+
val binder = service as CustomService.CustomBinder
228+
this@MainActivity.service = binder.getService()
229+
isBound = true
230+
}
231+
override fun onServiceDisconnected(name: ComponentName?) {
232+
isBound = false
233+
}
234+
}
235+
236+
override fun onCreate(savedInstanceState: Bundle?) {
237+
super.onCreate(savedInstanceState)
238+
239+
val intent = Intent(this, CustomService::class.java)
240+
intent.putExtra("PARAM", "value")
241+
bindService(intent, connection, Context.BIND_AUTO_CREATE)
242+
243+
//use service binder action to communicate
244+
button.setOnClickListener { service?.action() }
245+
}
246+
247+
override fun onStop() {
248+
super.onStop()
249+
unbindService(connection)
250+
isBound = false
251+
}
252+
}
253+
{% endhighlight %}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
layout: post
33
title: "IntentService"
4-
date: 2019-07-08
4+
date: 2019-07-15
55
categories: ["Background"]
66
image: background/intentservice
77
github: background/tree/master/intentservice
@@ -10,7 +10,7 @@ keywords: "intentservice, jobintentservice, broadcastreceiver, async, task, back
1010
---
1111

1212
## Wstęp
13-
`IntentService` jest rozszerzeniem klasy `Service` o obsługę zadań przetwarzanych na w tle na dedykowanym wątku roboczym przy pomocy klasy Handler. Umożliwia wykonanie pracy w sposób asynchroniczny niezależnie od wątku głównego, jednakże obsługuje tylko jedno zadanie w danej chwili. Wysyłane żądania w postaci obiektu `Intent` zostają natychmiast obsłużone lub trafiają do kolejki oczekujących. Gdy cała kolejka zadań zostanie zrealizowana wówczas usługa automatycznie kończy pracę. Większość zdarzeń cyklu życia interfejsu użytkownika nie ma wpływu na działanie `IntentService` (w przeciwieństwie do `AsyncTask`). Przeznaczony jest przede wszystkim do prostych operacji w tle, które niekoniecznie wymagają reakcji interfejsu użytkownika na otrzymany rezultat.
13+
`IntentService` jest rozszerzeniem klasy `Service` o obsługę zadań przetwarzanych na w tle na dedykowanym wątku roboczym przy pomocy klasy `Handler`. Umożliwia wykonanie pracy w sposób asynchroniczny niezależnie od wątku głównego, jednakże obsługuje tylko jedno zadanie w danej chwili. Wysyłane żądania w postaci obiektu `Intent` zostają natychmiast obsłużone lub trafiają do kolejki oczekujących. Gdy cała kolejka zadań zostanie zrealizowana wówczas usługa automatycznie kończy pracę. Większość zdarzeń cyklu życia interfejsu użytkownika nie ma wpływu na działanie `IntentService` (w przeciwieństwie do `AsyncTask`). Przeznaczony jest przede wszystkim do prostych operacji w tle, które niekoniecznie wymagają reakcji interfejsu użytkownika na otrzymany rezultat.
1414

1515
## Implementacja
1616
Aby stworzyć komponent `IntentService` należy zdefiniować klasę rozszerzającą klasę `IntentService`, nadpisać metodę `onHandleIntent` odpowiedzialną za przechwytywanie żądań i podobnie jak przy klasycznej usłudze (`Service`) dodać do pliku manifestu (`AndroidManifest`).
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
layout: post
33
title: "WorkManager"
4-
date: 2019-07-15
4+
date: 2019-07-22
55
categories: ["Background"]
66
image: background/workmanager
77
github: background/tree/master/workmanager
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
layout: post
33
title: "Coroutines"
4-
date: 2019-07-22
4+
date: 2019-07-29
55
categories: ["Kotlin"]
66
image: kotlin/coroutines
77
github: kotlin/blob/master/coroutines
File renamed without changes.
453 KB
Loading
16.6 KB
Loading
46.6 KB
Loading

0 commit comments

Comments
 (0)