[Android] Service in Android — P1: Started Service & Intent Service

NH. Thanh
14 min readDec 11, 2019

--

Khái niệm

Vào thẳng vấn đề luôn nhen <’: Service là một thành phần rất quan trọng trong lập trình android, đôi khi chúng ta cần làm một việc gì đó mà khi ứng dụng đã đóng, mà việc xử lý vẫn còn tồn tại, thì khi đó chính là lúc Service thể hiện vai trò to lớn của mình. Nó chính là thứ quyền lực có thể chạy ngầm dưới background và tất nhiên chạy background thì không có giao diện người dùng, nó vẫn sẽ chạy kể cả khi ứng dụng đã đóng(à nó sẽ được chạy trên MainThread nhé).

Theo Android thì hiện tại Service được chia làm 3 loại là Foreground, Background Bound (trước kia được chia thành 2 là Started ServiceBound Service). Tuy nhiên trong bài viết này mình sẽ chia làm 2 loại là Started ServiceBound Service.

  • Started Service: Service được gọi và hủy bằng startService()/stopService() và chạy ở background một thời gian dài(có thể nói là vĩnh viễn nếu không có tác động làm nó buộc dừng). Và cũng có thể hủy bằng cách tự gọi stopSelf() ngay trong nó.
  • Bound Service: Service được gọi và hủy bằng bindService()/unbindService(). Giao tiếp thông qua một IBinder Tuy nhiên Bound Service sẽ liên kết với chính component gọi nó(activity, fragment,…) và đến khi component cuối cùng còn liên kết với Bound Service cũng bị hủy thì lúc này Bound Service cũng bị hủy.

Mỗi service có vòng đời riêng của nó, và đây là vòng đời của Started ServiceBound Service

1. Vòng đời của Service (theo Android)

Ở trên là khái niệm cơ bản về 2 loại service hiện có, và trong phần này mình sẽ nghiên cứu vào kiểu đầu tiên là Started Service.

Khai báo Service trong Manifest

Việc đầu tiên khi sử dụng Service là chúng ta phải khai báo nó trong Manifest.

Thẻ Service trong Manifest có nhiều thuộc tính, tuy nhiên thuộc tính bắt buộc phải có là android:name=”String”. Có thể xem thêm các thuộc tính khác tại ĐÂY.

VD: Tạo một lớp kế thừa lớp Service có tên là BackgroundService như sau

public class BackgroundService extends Service {
CountDownTimer countDownTimer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
...

Trong Manifest sẽ khai báo

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.thanh.service">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".Background.BackgroundService"/>
...

Tạo một Started Service như thế nào?

Để cài đặt Background ServiceForeground Service ta có thể kế thừa lớp Service và gọi đến nó bằng startService() từ một component bất kỳ. Và bên cạnh đó, Service có 1 lớp con là IntentService, bằng việc kế thừa lớp này, việc tạo ra một luồng mới chạy background sẽ đơn giản hơn rất nhiều, một chút nữa mình sẽ trình bày về IntentService rõ hơn hén, còn bây giờ ta sẽ đến với từng loại của Started Service.

Background Service

Một Background Service sẽ thực hiện các công việc chạy ngầm mà người dùng không nhận thấy, ví dụ như gọi dịch vụ để lấy dữ liệu từ internet chẳng hạn. Để rõ ràng hơn mình sẽ show đoạn code mẫu về Background Service ngay sau đây.

Với lớp BackgroundService lúc nãy khi extends lớp Service, sẽ được override sẵn hàm onBind() tuy nhiên ở đây, khi cài đặt Background Service thì chúng ta sẽ không cần thiết sử dụng đến onBind(), hãy override thêm các phương thức quan trọng sau onCreate(), onStartCommand(), onDestroy().

  • onCreate(): Hàm này sẽ chỉ được gọi duy nhất 1 lần cho tới khi nó bị hủy, nên việc khai báo các biến để tránh hao tài nguyên thì nên khai báo ở đây nhé.
  • onStartCommand(): Hàm này sẽ được gọi đến sau onCreate() vào lần đầu, nhưng sẽ là hàm được gọi trực tiếp khi có component gọi startService() thông qua intent. Ở đây thường sẽ thực hiện các tasks chúng ta đưa ra, và khi task hoàn thành nếu muốn hủy service có thể gọi stopSelf() để hủy nó. Hàm này trả về kiểu giá trị int ở đây nó là cờ để để báo rằng service sẽ có những ràng buộc gì khi bị kill, giá trị mặc định của nó sẽ là START_STICKY. Bạn cũng có thể xem thêm về cờ tại ĐÂY
  • onDestroy(): Hàm này được gọi sau khi có lệnh stopSelf() từ bản thân nó, stopService() component khác gọi, hoặc bị buộc hủy. Là hàm hủy nên chúng ta nên giải phóng các tài nguyên không còn sử dụng tại đây để tránh leak.

Một lớp kế thừa service sẽ như sau

public class BackgroundService extends Service {

@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}

Ở bên phía component gọi Service, ở đây mình sẽ gọi từ Activity.

Để tránh bài viết dài hơn thì các bạn giúp mình tự tạo 2 Button trong Activity nhé. Giả sử 2 Button có id là btn_startService là Butotn để start Service btn_stopService là Butotn để stop Service.

onCreate() của Activity khai báo và tạo một Intent như sau

Intent backgroundServiceIntent=new Intent(this, BackgroundService.class);

btn_startService (Button start):

Button btn_startService=findViewById(R.id.btn_startService);

btn_startService.setOnClickListener(v->{

startService(backgroundServiceIntent);

});

btn_stopService (Button stop):

Button btn_stopBoundService=findViewById(R.id.btn_stopService);

btn_stopBoundService.setOnClickListener(v->{

stopService(backgroundServiceIntent);

});

Sau khi cài đặt xong như vậy thì bạn có thể gắn thêm Log hoặc Toast để kiểm tra nhé.

Foreground Service

Foreground Service sẽ thực hiện hiện những công việc chạy ngầm, tuy nhiên service này sẽ thông báo cho người dùng biết. Ví dụ có thể dễ thấy nhất là định vị GPS khi sử dụng Google Map tìm đường

Foreground Service sẽ được gọi bằng startForegroundService() và hủy bằng stopService(). Khi kế thừa lớp service, trong hàm onStartCommand() cần gọi setForeground(ID,Notifitication) để gọi Notification, Notification này sẽ không thể tắt đến khi ForegroundService liên kết với nó bị hủy.

Để phục vụ cho việc này, các bạn giúp mình tạo thêm 2 Button để startForegroundService() và stopService() nhé, giả sử 2 button này sẽ có id là btn_startForegroundService btn_stopForegroundService, và tiếp tục tạo 1 lớp ForegroundService và 1 lớp riêng để hỗ trợ việc tạo Notification có tên là NotificationHelper để tránh làm dài class ForegroundService.

Tạo intent cho service như sau:

Intent foregroundServiceIntent=new Intent(this, ForegroundService.class);

btn_startForegroundService:

Button btn_startForegroundService=findViewById(R.id.btn_startForegroundService);

btn_startForegroundService.setOnClickListener(v->{

startForegroundService(foregroundServiceIntent);

});

btn_stopForegroundService:

Button btn_stopForegroundService=findViewById(R.id.btn_stopForegroundService);

btn_startForegroundService.setOnClickListener(v->{

stopService(foregroundServiceIntent);

});

Lớp NotificationHelper sẽ như sau:

public class NotificationHelper {

private static final String CHANNEL_ID = "Foreground";

private final Context context;

private final String message;



public NotificationHelper(Context context, String message){

this.context = context;

this.message = message;

}



public Notification builder(){

createNotificationChannel();

PendingIntent pendingIntent=PendingIntent.getActivity(context,0,

new Intent(context, BackgroundServiceActivity.class)

.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),0);

NotificationCompat.Builder builder = new NotificationCompat

.Builder(context, CHANNEL_ID)

.setContentIntent(pendingIntent)

.setSmallIcon(R.drawable.ic_launcher_background)

.setContentTitle("Foreground Service")

.setContentText(message)

.setColor(Color.RED)

.setPriority(NotificationCompat.PRIORITY_DEFAULT);

synchronized (builder){



}

return builder.build();

}



private void createNotificationChannel () {

// Create the NotificationChannel, but only on API 26+ because

// the NotificationChannel class is new and not in the support library

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

Log.e("OKK", CHANNEL_ID);

CharSequence name = context.getString(R.string.channel_name);

String description = context.getString(R.string.channel_description);

int importance = NotificationManager.IMPORTANCE_DEFAULT;

NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);

channel.setDescription(description);



NotificationManager notificationManager = context.getSystemService(NotificationManager.class);

notificationManager.createNotificationChannel(channel);

}

}

}

Trong class trên mình có đề cập đến Notification Channels, từ API 26 (Android O) thì khi tạo Notification cần phải định nghĩa thêm Channel riêng cho nó. Chỉ giới thiệu thôi vì phần này dành cho Service. Thông tin thêm xem ở ĐÂY nhé.

Lớp ForegroundService như sau

public class ForegroundService extends Service {

NotificationHelper notificationHelper;

@Override

public void onCreate() {

super.onCreate();

notificationHelper =new NotificationHelper(this,"Bấm vô xem chúng ta có gì");

}



@Nullable

@Override

public IBinder onBind(Intent intent) {

return null;

}



@Override

public int onStartCommand(Intent intent, int flags, int startId) {

startForeground(101, notificationHelper.builder());

return START_STICKY;

}



@Override

public void onDestroy() {

super.onDestroy();

}

}

Đây là lớp ForegroundService onStartCommand gọi hàm startForeground(ID,Notification); với id dùng để phân biệt notification và Notification là notification do chúng ta tự build, Notification này sẽ tồn tại cùng với ForegroundService đến khi nó vào onDestroy(). Và đương nhiên Notification này cũng có thể custom layout hay để mở các activity hay thông báo các thông tin như thường.

Chỉ cần như vậy, build ứng dụng và test thử một chút, kết quả thu được sẽ như sau:

Notification kết quả của ForegroundService
Notification chạy trê ForegroundService

Tạo new Thread trong Service hay tạo Service trong new Thread ?

Theo như tài liệu từ Android, Service khi được gọi sẽ chạy trên Main Thread của component gọi nó, và khuyên chúng ta nên xử lý Service ở trong 1 luồng khác, vậy vấn đề ở đây là ta sẽ tạo 1 thread mới rồi gọi service trong đó hay gọi service và sau đó gọi new Thread ở trong service.

Kết quả ở đây là ta gọi service và tạo 1 new Thread trong service. Vì sao vậy nhỉ?

Vì trên lý thuyết đã nói, Service dù gọi ở luồng nào thì cuối cùng nó sẽ chạy trên Main Thread của component gọi nó. Vậy nên, việc tạo 1 Worker Thread mới và gọi Service thì khi Service được tạo, nó sẽ chạy trên Main Thread. Tuy nhiên, khi đã tạo Service rồi, thì bên trong Service khi gọi new Thread, lúc này 1 Worker Thread mới sẽ được tạo ra, và chịu trách nhiệm chạy các task chúng ta sẽ yêu cầu xử lý và Worker Thread này tự hủy khi thực hiện xong.

Mình sẽ thử như sau:

Tạo 1 thread để gọi Service trong đó:

btn_startService.setOnClickListener(v->{

new Thread(()->{

Log.e("Thread call service:",Thread.currentThread().getName());

startService(backgroundServiceIntent);

}).start();

//startService(backgroundServiceIntent);

});

Trong Service lại gọi tạo mới Thread:

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

Log.e("Thread name tại Command",Thread.currentThread().getName()+" "+Thread.currentThread().getThreadGroup());



new Thread(()->{

Log.e("Thread name tại Command khi gọi new Thread",Thread.currentThread().getName()+" "+Thread.currentThread().getThreadGroup());

}).start();

return START_STICKY;

}

Và đây là kết quả:

Kết quả cho thử nghiệm trên

Có thể thấy rằng: Khi tạo thread mới ở Activity, thread tạo 1 worker có tên là Thread-2, nhưng ghi gọi startService(), thì service lại chạy trong Main Thread, tạo mới 1 thread trong service ta có 1 thread mới tên là Thread-3. Đây là ví dụ chứng minh cho những điều nói ở trên ^^.

IntentService

Vấn đề về việc tạo ra 1 Worker Thread trên Main Thread của service ở trên sẽ được xử lý 1 cách gọn gàng êm đẹp hơn khi chúng ta biết đến IntentService.

IntentService là một lớp con kế thừa từ Service. Cũng có thể nói nó là một Custom Service.

IntentService cũng như Service, khi được gọi startService() từ một component nó vẫn được chạy trên Main Thread, có thể kiểm tra bằng cách đặt 1 Log ở hàm onStartCommand() để kiểm tra điều này. Tuy nhiên, thay vì xử lý ở onStartCommand() như service thông thường, thì khứa này lại lấy intent từ onStartCommand() này gửi tiếp vào onHandleIntent() lúc này một Worker Thread mới được tạo ra và chịu trách nhiệm xử lý các request gửi đến onHandleIntent() những mỗi lần chỉ xử lý 1 request nhận được. Và nó có một cái gọi là Message Queue, tất cả intent chuyển từ onStartCommand() đến onHandleIntent() không thực hiện một lúc mà nó sẽ đưa vào Message Queue để thực hiện tuần tự theo quy tắc của hàng đợi là “First in first out” cho đến khi không còn task nào thì Worker Thread này bị hủy.

Trong khi đang thực thi 1 task, IntentService ko thể dừng, dù là gọi stopSelf() hay stopService(), khi gọi các hàm hủy nó sẽ chỉ dừng khi thực hiện xong task đang thực thi.(*)

Để test IntentService, các bạn giúp mình tạo tiếp thêm 2 Button nữa nhé, lại là 1 start và 1 stop. id btn_startIntentService btn_stopIntentService.

Lớp MyIntentService như sau, các Log trong class để phục vụ cho việc test trường hợp (*):

public class MyIntentService extends IntentService {
public MyIntentService() {
super("Thanh");
setIntentRedelivery(true); }

@Override
public void onCreate() {
super.onCreate(); Log.e("on create","create");
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
Log.e("onHandleIntent","Thread name: "+Thread.currentThread().getName()); try {
Thread.sleep(1000); } catch (InterruptedException e) {
e.printStackTrace(); }
Log.e("End HandleIntent","Stop");
}

@Override
public void onDestroy() {
super.onDestroy(); Log.e("onDestroy","destroy"); }
}

Ở lớp MyIntentService này, khi sử dụng IntentService bắt buộc phải có 1 constructor để cung cấp name (name này để đặt tên cho Thread ý mà). Và có thể thấy ở constructor có 1 hàm là setIntentRedelivery(Boolean);

  • Với giá trị TRUE: Nếu trong quá trình thực thi, onStartCommand() chết trước onHandleIntent() thì khi Process khởi động lại thì Intent cuối cùng sẽ được gửi đến cho onHandleIntent().
  • Với giá trị FALSE: Nếu process die, thì intent sẽ chết theo luôn.

*(Đây là lý thuyết về setIntentRedelivery(), mình chưa biết phải test nó như nào @@, giúp mình test với nha :’( …)

Với 2 Button ta set như sau:

Intent intentService = new Intent(this, MyIntentService.class);

Button btn_startIntentService=findViewById(R.id.btn_startIntentService);

btn_startIntentService.setOnClickListener(v->{

startService(intentService);

});

Button btn_stopIntentService=findViewById(R.id.btn_stopIntentService);

btn_stopIntentService.setOnClickListener(v->{

stopService(intentService);

});

Chạy chương trình và test thử bằng cách bấm nút gọi startService(), ta sẽ thấy được IntentService chạy trong 1 luồng mới có tên là IntentService[Thanh]. Và ở ví dụ (*) ta sẽ test như sau:

Click vào nút gọi startService(), trước khi hoàn tất 1s dừng của thread, ta bấm luôn hàm gọi stopService(), và đây là kết quả:

Kết quả chứng minh IntentService sẽ ko dừng ngay lập tức

➤ Với kết quả nhận được ta thấy rằng, khi gọi stopService() trước khi hoàn thành task thì service đã thực hiện hàm onDestroy, nhưng IntentService vẫn chạy bằng thread riêng của nó đến khi hoàn thành mới thôi.

Sẽ có một thắc mắc là, vậy 1 IntentService sẽ chỉ làm 1 việc 1 thời điểm, vậy gọi mấy chục cái IntentService đó cho nó làm mấy chục việc, vậy ko được hả ta ???

✎ Câu trả lời ở đây là không đó các bạn. Vì mỗi IntentService nó chỉ khởi tạo 1 lần duy nhất và các request khác ko khởi tạo lại, mà add vào queue để xử lý trên cùng 1 luồng.

VD để chứng minh như sau:

Tạo mới 4 Intent gọi vào MyIntentService

Intent intentService = new Intent(this, MyIntentService.class);

Intent intentService2 = new Intent(this, MyIntentService.class);

Intent intentService3 = new Intent(this, MyIntentService.class);

Intent intentService4 = new Intent(this, MyIntentService.class);

Button btn_startIntentService=findViewById(R.id.btn_startIntentService);

btn_startIntentService.setOnClickListener(v->{

startService(intentService);

startService(intentService2);

startService(intentService3);

startService(intentService4);

});

Và nhận được kết quả như sau:

Tạo nhiều Intent thì IntentService vẫn chạy trong 1 luồng

Có thể thấy onCreate chỉ được gọi đến 1 lần, và cả 4 request đều đưa vào xử lý ở 1 luồng riêng, khi kết thúc cả 4 thì gọi đến Destroy.

Ơ vậy nó làm việc dưới Worker Thread , vậy muốn giao tiếp với Main Thread thì sao nhỉ? Đừng lo, nó có thể giao tiếp thông qua BroadcastReceiver hoặc sử dụng Handler, Đây không phải bài nói về BroadcastReceiver nhưng chút xíu nữa mình cũng sẽ làm một chút về BroadcastReceiver để ví dụ cho việc giao tiếp giữa Worker ThreadMain Thread, ráng đọc chút nữa nhé ^^.

Giao tiếp IntentService với MainThread thông qua BroadcastReceiver

Để thực hiện việc này, mình sẽ đưa ra một ví dụ tạo ứng dụng đếm số đơn giản.

Layout sẽ đơn giản thôi activity_count_number.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".Activity.CountNumberActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="50dp"
android:textColor="#F44336"
android:id="@+id/tv_count"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Count"
android:id="@+id/btn_count"/>

</LinearLayout>

Tạo một BroadcastReceiver để lắng nghe giá trị, nhớ đăng ký ở Manifest nữa nhen

Tạo một lớp kế thừa BroadcastReceiver có tên CountNumberBroadcastReceiver như sau, BroadcastReceiver sẽ chạy trên MainThread luôn nha các bạn

public class CountNumberBroadcastReceiver extends BroadcastReceiver {



@Override

public void onReceive(Context context, Intent intent) {

String s=intent.getExtras().getString("value");

CountNumberActivity.ChangeValueAsyncTask changeValueAsyncTask=new CountNumberActivity.ChangeValueAsyncTask();

changeValueAsyncTask.execute(s);

}

}

Ở đây có đề cập đến AsyncTask, vì nó ko nằm trong phạm vi bài này nên sẽ kèm link xem thêm ở ĐÂY nhé.

Thêm đoạn sau vào manifest để khai báo BroadcastReceiver:

<receiver android:name=".CountNumberBroadcastReceiver">
</receiver>

Tiếp tục tạo Một IntentService để chạy ngầm và đăng ký BroadcastReceiver trong đó. IntentService này ta dùng lại lớp MyIntentService lúc nãy cho đỡ code lại hén, sửa lại đổi ba chỗ như sau:

public class MyIntentService extends IntentService {
boolean checkCounting=false;
CountNumberBroadcastReceiver countNumberBroadcastReceiver=new CountNumberBroadcastReceiver();
public MyIntentService() {
super("Thanh");
setIntentRedelivery(false); }

@Override
public void onCreate() {
super.onCreate(); registerReceiver(countNumberBroadcastReceiver, new IntentFilter("CountNumber"));
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (checkCounting==false) {
checkCounting=true;
for (int i = 0; i < 100; ++i) {
sendBroadcast(new Intent("CountNumber").putExtra("value", String.valueOf(i)));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace(); }
}
}
}

@Override
public void onDestroy() {
super.onDestroy();
checkCounting=false;
}
}

Ở đây chúng ta tạo 1 biến checkCounting để trong quá trình chạy chỉ nhận chạy 1 intent vào onHandleIntent thôi.

Ta thấy ở chỗ đăng ký BroadcastReceiver new IntentFilter(“CountNumber”) thấy quen quen phới hông, thường IntentFilter sẽ được đăng ký trong Manifest, tuy nhiên, mình đã đọc ở đâu đó trên trang Android Developer thì Android đã ko khuyến khích đăng ký bằng cách này ở Manifest nữa rồi. Vậy nên mình sẽ đăng ký ở đây luôn ahehe.

onHandleIntent thì chúng ta sẽ gửi đi BroadcastReceiver mang theo value tương ứng

Cuối cùng đễ hoạt động thì component chính là Activity , tạo một Activity tên là CountNumberActivity

public class CountNumberActivity extends AppCompatActivity {

Button btn_count;

CountNumberBroadcastReceiver countNumberBroadcastReceiver=new CountNumberBroadcastReceiver();

static TextView tv_count;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_count_number);

btn_count=findViewById(R.id.btn_count);

tv_count=findViewById(R.id.tv_count);

btn_count.setOnClickListener(v->{

startService(new Intent(this, MyIntentService.class));

});

}

public static class ChangeValueAsyncTask extends AsyncTask<String,String,String>{



@Override

public void onPostExecute(String s) {

super.onPostExecute(s);

tv_count.setText(s);

}



@Override

protected String doInBackground(String... strings) {

return strings[0];

}

}

}

Vẫn là tại event click của btn_count gọi service thông qua startService() , à quên nói về chỗ setOnClickListener có thể sẽ có một vài bạn chưa biết, nó là dạng viết rút gọn với Lambda Expressions(Từ Java 8 trở đi) có thể tìm hiểu để viết code gọn gàng hơn nhen :D .

Ok. Vậy thội, build app sẽ được kết quả như dưới đây

Tổng kết

Tùy vào mục đích mà việc sử dụng các loại service sẽ phù hợp vào từng trường hợp khác nhau.

Background Service thực hiện những việc mà người dùng không cần chú ý tới, chạy trên Main Thread.

Foreground Service dùng để thực hiện hay thông báo những hành vi mà người dùng có thể nhận biết.

IntentService là lớp kế thừa từ Service, nó tạo ra thêm một Worker Thread riêng để thực hiện những Intent được truyền từ onStartCommand(). Tại một thời điểm chỉ được thực hiện duy nhất 1 công việc, việc gọi nhiều lần IntentService nó vẫn chỉ đưa request vào hàng đợi và thực hiện tuần tự, IntentService không thể tắt khi có 1 task đang thực thi, nếu được yêu cầu hủy thì nó sẽ hủy khi hoàn thành xong task đang làm dỡ. hoặc tự hủy khi hoàn thành tất cả task được giao.

Phần sau mình sẽ trình bày về phần còn lại là Bound Service. Ở bài viết này nếu có sai sót gì mong được nhận phản hồi/góp ý từ phía mọi người để cải thiện bài viết tốt hơn. Cám ơn tất cả mọi người.

Tham khảo:

https://developer.android.com/guide/components/service

--

--