Service in Android

Service là một trong 4 thành phần chính của android (Activity, Service, BroadcastReceiver, ContentProvider). Service không có giao diện chuyên dùng để thực hiện một nhiệm vụ nào đó được thực hiện ngầm dưới background mà không cần tương tác đến giao diện ví dụ như: chơi nhạc nền, download file, xử lý tính toán…
Vòng đời của Service
Một serivce là một thành phần chạy dưới nền mà không trực tiếp tương tác với người dùng. Service không có giao diện người dùng, nó không có bao quanh bởi lifecycle của một activity.
Service dùng cho các tác vụ chạy lâu dài, ví dụ download, checking new data, data processing, update content…
Service chạy với độ ưu tiên cao hơn so với activiy invisible vì vậy nó ít bị khả năng hệ thống Android kết thúc. Service có thể được cấu hình để khởi động lại nếu nó bị kết thúc bởi hệ thống android môt khi các tài nguyên lại có sẵn lần nữa.
Có khả năng chỉ định service có ngang độ ưu tiên với activity đang active. Trong trường hợp này nó được yêu cầu để có một hành động notification nhìn thấy được cho các dịch vụ liên quan. Nó được thường xuyên sử dụng để chơi nhạc hoặc video.
Lưu ý: Khi một context nào đó gọi startService() để start service mong muốn. Nếu service đó chưa được khởi tạo sẽ gọi onCreate() rồi gọi tiếp onStart().
Nếu sau đó lại có context gọi service này mà service đang chạy thì chi mỗi phương thức onStart() được gọi lại, không gọi lại onCreate().
Như vậy cho dù bạn có start bao nhiều lần thì chỉ có duy nhất mất một instance của service và chỉ cần gọi stopService() một lần để kết thúc service.

Có 2 loại service tương ứng với 2 kiểu start Service:
1. Unbound Service sử dụng phương thức startService(), thường được sử dụng để thực thi một hành động đơn và có thể không trả về kết quả (ví dụ chơi nhac). Để kết thúc Service sẽ phải gọi hàm stopService().
Ví dụ:Khi chơi nhạc thì gọi startService, muốn nhạc dừng chơi thì stopService.

2. Bound Service sử dụng phương thức blindService() cung cấp một interface dạng client-server cho phép app có thể tương tác với service.

Bound Service đóng vai trò như môt server trong mô hình client-server. Bound service cho phép các thành phần của app (Activity) có thể liên kết với service để gửi các request- nhận response.
Bound Service chỉ tồn tại khi nó phục vụ một thành phần của app mà nó không tồn tại vô hạn dưới background.
Có 3 cách để blindService từ các thành phần của app:
– IBlinder
– Messenger
– AIDL

Activity vs Fragment vs FragmentActivity, what is difference point?

Activity như đã biết một trong những thành phần chính của Android, Activity chính là xương sống, giao diện người dùng được xây dựng trên nền của Activity. – Giới thiệu Fragment: Fragment được đưa vào Android từ API 11, là một phần của activity, đóng góp vào xây dựng giao diện cho Activity. Fragment cũng được coi là sub-activity. Fragment life-cycle phụ thuộc vào Activity chứa nó, có nghĩa là Activity destroy thì fragment cũng không còn. – Giới thiệu về FragmentActivity: Thực chất là Activity Plus, bạn có thể gọi mọi function của Activity và có khả năng sử dụng Fragment, hữu dụng trong nhiều trường hợp. Bạn sử dụng FragmentActivity để dễ dàng xây dựng tab và swap. Chú ý: Nếu bạn sử dụng Activity thì phải đi với android.app.Fragment, còn nếu sử dụng FragmentActivity thì phải đi với android.support.v4.app.Fragment. Nếu làm ngược lại thì sẽ xảy ra exception. Bonus thêm Fragment Fragment Manager xử lý “Back” không giống Activity. Theo Activity thì trở về Activity trước đó, còn trong Fragment là trở về trạng thái trước đó. – fragmentTransaction.add(int containerViewId, Fragment fragment, String tag): add fragment mới vào container. – fragmentTransaction.remove( Fragment fragment): remove old fragment ra khỏi container. – fragmentTransaction.replace(int containerViewId, Fragment fragment, String tag): có nghĩa là nó gọi remove fragment cũ-> sau đó sẽ gọi add mới. Nó gộp quá trình remove và add. – fragmentTransaction.addToBackStack(str): Có nghĩa là quá trình transaction được lưu vào stack, option là name hoặc null. Khi replace nhiều fragment vào một stack thì nó sẽ xử lí nút Back. Function này thường sử dụng với Replace – fragmentManager.popBackStack(TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE): pop fragment có TAG, ngoài ra có xóa stack như sau transaction.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE);

getBackStackEntryAt(int index): get at index

getBackStackEntryCount(): get count in the back stack

Handling Runtime Change

Một số cấu hình có thể thay đổi trong khi runtime (ví dụ xoay màn hình, có cuộc gọi đến…). Khi đó Android restart Activity đang chạy (liên tiếp gọi onDestroy()-> onRestart).
Vấn đề xảy ra là sẽ làm mất các thiết lập và trạng thái đang có của Avtivity, ví dụ form đang nhập sau khi restar thì các dữ liệu trên form bị mất hoàn toàn, do đó tùy một số trường hợp cần phải lưu các thông số này trước khi Activity bị destroy. Một bài trước đã giới thiệu việc lưu trữ trong Bundle trong function onSaveInstanceState(), để khôi phục lại dữ liệu thì có thể get Bundle trong function onCreate() hoặc onRestoreInstanceState().
Việc gọi các function trên nhằm mục đích tránh việc restart bất kì thời điểm nào mà không mất dữ liệu người dùng hoặc trạng thái. Tuy nhiên có thể làm giảm trải nghiệm của người dùng vì tốn các chi phí lưu trữ và hồi phục đối với một số lượng lớn data. Có 2 option để giải quyết:
– Giữ lại object trong khi cấu hình thay đổi : cho phép activity có thể restart khi cấu hình thay đôi, nhưng mang theo đối tượng đến new instance.
– Xử lí cấu hình thay đổi: Ngăn chặn việc restart activity trong khi cấu hình thay đổi, nhưng nhận các callback khi cấu hình thay đổi, vì vậy có thể tự cập nhật nếu cần thiết.

Giữ lại object trong khi cấu hình thay đổi
Nếu khi restart activity cần một lượng lớn data thì có thể làm giảm chất lượng giao diện người dùng. Mặc dù bạn có thể restore trạng thái với Bundle với function onSaveInstanceState() – Tuy nhiên nó không được thiết kế để mang theo đối tượng lớn (ví dụ bitmap) và data phải được serialized và sau đó deserialized. Một giải pháp khác được đưa ra là sử dụng một Fragment. Fragment này có thế chưa các liên kết đển các objetc mà bạn có thể giữ lại.
Vì sao fragment có thể làm được, khi hệ thống shut down activity khi cấu hình thay đổi, fragment được giữ lại mà không bị destroy. Do đó có thể lưu lại trong fragment.
Để lưu giữ object trong một fragment có thể làm theo các bước sau:
1. Extend Fragment và khai báo tham chiếu đến đối tượng.
2. Gọi setRetainInstance(boolean) khi fragment được tạo ra.
3. Add fragment vào activity.
4. Sử dụng Fragment để lấy fragment khi activitu được restart.
Ví dụ code đơn giản:

public class RetainedFragment extends Fragment {

// data object we want to retain
private MyDataObject data;

// this method is only called once for this fragment
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}

public void setData(MyDataObject data) {
this.data = data;
}

public MyDataObject getData() {
return data;
}
}

Chú ý: Không thế lưu trữ bất kì đổi tượng nào như Drawable, Adapter, View hoặc bất kì object nào liền quan đến Context. Nếu bạn làm vậy thì các đối tượng trên sẽ bị ứng dụng giữ và không thể garbage-collected, có nghĩa là không thể giải phóng bộ nhớ, vì vậy có rất nhiều bộ nhớ bị mất không thể thu hồi.
Khi sử dụng FragmentManager để thêm fragment vào activity. Bạn có thể chứa các object data từ fragment khi activity khởi động lại. Ví dụ:

public class MyActivity extends Activity {

private RetainedFragment dataFragment;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
dataFragment = (DataFragment) fm.findFragmentByTag(“data”);

// create the fragment and data the first time
if (dataFragment == null) {
// add the fragment
dataFragment = new DataFragment();
fm.beginTransaction().add(dataFragment, “data”).commit();
// load the data from the web
dataFragment.setData(loadMyData());
}

// the data is available in dataFragment.getData()
...
}

@Override
public void onDestroy() {
super.onDestroy();
// store the data in the fragment
dataFragment.setData(collectMyLoadedData());
}
}

Như trên, khi gọi function onCreate() thêm tham chiếu đối tượng và lấy data từ trong fragment. Và onDestroy để lưu trữ data bên trong fragment.

Xử lí cấu hình thay đổi
Nếu Activity không cần thay đổi khi cấu hình thay đổi, cần ngăn chặn việc restart activity.
Bạn cần khai báo bên trong activity để xử lí khi cấu hình thay đổi. Bên trong file manifest, bên trong các bao gồm thẻ android:configChanges. Nếu tôi khai khai báo như sau:


Có nghĩa là Activity có tên là MyActivity ngăn chặn việc restart activity khi xoay màn hình và hiện bàn phím. Thay vì đó, MyActivity sẽ nhận được một callback đến function onConfigurationChanges(). Sự thay đối cấu hình lúc này sẽ được xử lí.