https://medium.com/@programmerr47/singletons-in-android-63ddf972a7e7
Đã có rất nhiều bài thảo luận về Singleton Pattern.Trong thực tế có rất nhiều bài viết nói về tác hại của Singleton trong android.
Nhưng mọi bài viết đó chỉ nói về một vấn đề riêng. Một vài chúng nói về memory leaking, một vài nói về việc mất trạng thái, một vài lại nói về xử lý đa luồng.
Nhưng tôi không thể tìm được một bài viết tổng hợp tất cả những điều trên.Vi tôi muốn nâng cấp sự hiểu biết và các kĩ năng lập trình nên tôi nghĩ bài viết này sẽ rất hữu ích không chỉ cho bạn mà còn cho bản thân mình.
So, sẽ ra sao nếu chúng ta có một Singleton đơn giản
Chúng ta hãy xem đoạn code dưới đây:
public class CarelessSingleton {
public static final CarelessSingleton instance = new CarelessSingleton();
private Object someState;
public void setState(Object state) {
this.someState = state;
}
public Object getState() {
return someState;
}
}
Đoạn code trên có 2 vấn đề chính:
Chúng ta sẽ xem một ví dụ đơn giản sử dụng Singleton. Đây là layout cho activity:
Đấy là tất cả. Bây giờ chúng ta có thể chạy ứng dụng của chúng ta. Và bạn sẽ thấy 0 xuất hiện ở giữa màn hình. Sau khi chúng ta click vào button chúng ta sẽ thấy số 5 xuất hiện ở giữa màn hình. Chúng ta có thể nhấn bút Home và sau đó nhấn application icon, mọi thứ vẫn bình thường. Chúng ta vẫn có số 5 ở giữa màn hình. Nhưng khi chúng ta chạy đồng thời với các ứng dụng khác: chơi game, đọc email và e.t.c Sau đó quay lại ứng dụng, chúng ta sẽ thấy số 0 đã thay thế cho số 5. What? What happend ?
Vấn đề đã xảy ra là gi?
Trong quá trình chúng ta sử dụng điện thoại, hệ thống cần nhiều bộ nhớ và nếu nó thấy ứng dụng của chúng ta không hoạt động trong một khoảng thời gian, nó sẽ giết ứng dụng chúng ta. Và sau đó khi chúng ta vào lại ứng dụng hệ thống sẽ tạo lại nó, khôi phục lại trạng thái. Chắc chắn rằng singleton của chúng ta đã không được lưu và khôi phục lại bởi vì nó rất ngây thơ và không nghĩ rằng mình đã bị giết.
Giải pháp
Không có giải pháp nào rõ ràng cho vấn để này, bởi vì Singleton có thể thay đổi ở khắp mọi nơi. Trong mọi Activity, mọi Fragment và trong service - mọi nơi trong xử lý của nó. Chắc chắn rằng, tất cả những gì chúng ta biết chính xác phải làm là chúng ta cần cung cấp một vài cơ chế lưu trữ. Có 2 giải pháp nhanh để thực hiện điều này, nhưng cả hai đều không hoàn hảo. Vì vậy, tôi sẽ nói rằng sử dụng singleton là không phải giải pháp hoàn hảo cho cấu trúc.
Giải pháp 1: Chúng ta có thể tạo hai phương thức saveState(Bundle outState)/ restoreState(Bundle state) tương tự như onSaveInstanceState(Bundle outState)/ onRestoreInstanceState(Bundle outState) cho Singleton của chúng ta và sau đó gọi chúng bất cứ khi nào chúng ta muốn.
Nhưng cách tiếp cận này có một lỗ hổng lớn. Bạn cần phải quyết định nơi lưu trữ trạng thái. Trong thực tế, bạn có thể chọn duy nhất giữa activities, views và fragments. Cho ví dụ, nếu bạn thay đổi state của Singleton trong activity, sau đó đóng nó, nhưng bạn có background service, cái mà thay đổi state một lần nữa, sau khi app bị kill và khởi tạo lại bởi hệ thống, bạn sẽ mất updating cuối, cái đã thực hiện bởi service. Và để chắc chắn, bãn sẽ phải gọi những hàm đó (saveState, restoreState) cho tất cả các Activities, Views và Fragments.
Cải tiến nhỏ cho phương pháp này: Thay vì lưu trữ trong Bundle chúng ta có thể lưu trữ trong SharedPreferencem Stream hoặc trong Class/Interface của riêng bạn.
Đã có rất nhiều bài thảo luận về Singleton Pattern.Trong thực tế có rất nhiều bài viết nói về tác hại của Singleton trong android.
Nhưng mọi bài viết đó chỉ nói về một vấn đề riêng. Một vài chúng nói về memory leaking, một vài nói về việc mất trạng thái, một vài lại nói về xử lý đa luồng.
Nhưng tôi không thể tìm được một bài viết tổng hợp tất cả những điều trên.Vi tôi muốn nâng cấp sự hiểu biết và các kĩ năng lập trình nên tôi nghĩ bài viết này sẽ rất hữu ích không chỉ cho bạn mà còn cho bản thân mình.
So, sẽ ra sao nếu chúng ta có một Singleton đơn giản
Chúng ta hãy xem đoạn code dưới đây:
public class CarelessSingleton {
public static final CarelessSingleton instance = new CarelessSingleton();
private Object someState;
public void setState(Object state) {
this.someState = state;
}
public Object getState() {
return someState;
}
}
Đoạn code trên có 2 vấn đề chính:
Chúng ta sẽ xem một ví dụ đơn giản sử dụng Singleton. Đây là layout cho activity:
<?xml version="1.0" encoding="utf-8"?><FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/test_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="0"/>
<Button
android:id="@+id/test_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="16dp"
android:text="Set global 5" />
</FrameLayout>
Và activity:public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.test_text);
displayState();
findViewById(R.id.test_button)
.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view) {
CarelessSingleton.instance.setState(5);
displayState();
}
});
}
private void displayState() {
Object state = CarelessSingleton.instance.getState();
textView.setText(String.valueOf(state));
}
}
Đấy là tất cả. Bây giờ chúng ta có thể chạy ứng dụng của chúng ta. Và bạn sẽ thấy 0 xuất hiện ở giữa màn hình. Sau khi chúng ta click vào button chúng ta sẽ thấy số 5 xuất hiện ở giữa màn hình. Chúng ta có thể nhấn bút Home và sau đó nhấn application icon, mọi thứ vẫn bình thường. Chúng ta vẫn có số 5 ở giữa màn hình. Nhưng khi chúng ta chạy đồng thời với các ứng dụng khác: chơi game, đọc email và e.t.c Sau đó quay lại ứng dụng, chúng ta sẽ thấy số 0 đã thay thế cho số 5. What? What happend ?
Vấn đề đã xảy ra là gi?
Trong quá trình chúng ta sử dụng điện thoại, hệ thống cần nhiều bộ nhớ và nếu nó thấy ứng dụng của chúng ta không hoạt động trong một khoảng thời gian, nó sẽ giết ứng dụng chúng ta. Và sau đó khi chúng ta vào lại ứng dụng hệ thống sẽ tạo lại nó, khôi phục lại trạng thái. Chắc chắn rằng singleton của chúng ta đã không được lưu và khôi phục lại bởi vì nó rất ngây thơ và không nghĩ rằng mình đã bị giết.
Giải pháp
Không có giải pháp nào rõ ràng cho vấn để này, bởi vì Singleton có thể thay đổi ở khắp mọi nơi. Trong mọi Activity, mọi Fragment và trong service - mọi nơi trong xử lý của nó. Chắc chắn rằng, tất cả những gì chúng ta biết chính xác phải làm là chúng ta cần cung cấp một vài cơ chế lưu trữ. Có 2 giải pháp nhanh để thực hiện điều này, nhưng cả hai đều không hoàn hảo. Vì vậy, tôi sẽ nói rằng sử dụng singleton là không phải giải pháp hoàn hảo cho cấu trúc.
Giải pháp 1: Chúng ta có thể tạo hai phương thức saveState(Bundle outState)/ restoreState(Bundle state) tương tự như onSaveInstanceState(Bundle outState)/ onRestoreInstanceState(Bundle outState) cho Singleton của chúng ta và sau đó gọi chúng bất cứ khi nào chúng ta muốn.
public void saveState(Bundle state) {
state.putInt(SINGLETON_STATE, getState());
}
public void restoreState(Bundle state) {
setState(state.getInt(SINGLETON_STATE));
}
Nhưng cách tiếp cận này có một lỗ hổng lớn. Bạn cần phải quyết định nơi lưu trữ trạng thái. Trong thực tế, bạn có thể chọn duy nhất giữa activities, views và fragments. Cho ví dụ, nếu bạn thay đổi state của Singleton trong activity, sau đó đóng nó, nhưng bạn có background service, cái mà thay đổi state một lần nữa, sau khi app bị kill và khởi tạo lại bởi hệ thống, bạn sẽ mất updating cuối, cái đã thực hiện bởi service. Và để chắc chắn, bãn sẽ phải gọi những hàm đó (saveState, restoreState) cho tất cả các Activities, Views và Fragments.
Cải tiến nhỏ cho phương pháp này: Thay vì lưu trữ trong Bundle chúng ta có thể lưu trữ trong SharedPreferencem Stream hoặc trong Class/Interface của riêng bạn.
Comments
Post a Comment