• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Xử lý Thay đổi Thời gian chạy
2page.tags=hoạt động,vòng đời
3@jd:body
4
5<div id="qv-wrapper">
6<div id="qv">
7
8  <h2>Trong tài liệu này</h2>
9  <ol>
10    <li><a href="#RetainingAnObject">Giữ lại một Đối tượng trong khi Thay đổi Cấu hình</a></li>
11    <li><a href="#HandlingTheChange">Tự mình Xử lý Thay đổi Cấu hình</a>
12  </ol>
13
14  <h2>Xem thêm</h2>
15  <ol>
16    <li><a href="providing-resources.html">Cung cấp Tài nguyên</a></li>
17    <li><a href="accessing-resources.html">Truy cập Tài nguyên</a></li>
18    <li><a href="http://android-developers.blogspot.com/2009/02/faster-screen-orientation-change.html">Thay đổi
19        Hướng Màn hình Nhanh hơn</a></li>
20  </ol>
21</div>
22</div>
23
24<p>Một số cấu hình thiết bị có thể thay đổi trong thời gian chạy
25(chẳng hạn như hướng màn hình, sự sẵn có của bàn phím, và ngôn ngữ). Khi sự thay đổi như vậy diễn ra,
26Android sẽ khởi động lại việc chạy
27{@link android.app.Activity} ({@link android.app.Activity#onDestroy()} sẽ được gọi, sau đó là {@link
28android.app.Activity#onCreate(Bundle) onCreate()}). Hành vi khởi động lại được thiết kế để giúp
29ứng dụng điều chỉnh phù hợp với cấu hình mới bằng cách tự động tải lại ứng dụng của bạn bằng
30các tài nguyên thay thế khớp với cấu hình thiết bị mới.</p>
31
32<p>Để xử lý khởi động lại cho đúng, điều quan trọng là hoạt động của bạn khôi phục lại trạng thái trước đó
33của nó thông qua vòng đời <a href="{@docRoot}guide/components/activities.html#Lifecycle">Hoạt động
34thông thường</a>, trong đó Android sẽ gọi
35{@link android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()} trước khi nó hủy
36hoạt động của bạn sao cho bạn có thể lưu dữ liệu về trạng thái của ứng dụng. Khi đó, bạn có thể khôi phục trạng thái
37trong khi {@link android.app.Activity#onCreate(Bundle) onCreate()} hoặc {@link
38android.app.Activity#onRestoreInstanceState(Bundle) onRestoreInstanceState()}.</p>
39
40<p>Để kiểm tra xem ứng dụng của bạn có tự khởi động lại mà giữ nguyên trạng thái ứng dụng hay không,
41bạn cần gọi ra các thay đổi cấu hình (chẳng hạn như thay đổi hướng màn hình) trong khi thực hiện các
42tác vụ khác nhau trong ứng dụng của bạn. Ứng dụng của bạn sẽ có thể khởi động lại vào bất cứ lúc nào mà không bị mất
43dữ liệu của người dùng hay trạng thái để xử lý các sự kiện như thay đổi cấu hình hoặc khi người dùng nhận được
44một cuộc gọi đến rồi quay lại ứng dụng của bạn muộn hơn nhiều sau khi tiến trình
45ứng dụng của bạn có thể đã bị hủy. Để tìm hiểu về cách bạn có thể khôi phục trạng thái hoạt động của mình, hãy đọc về <a href="{@docRoot}guide/components/activities.html#Lifecycle">Vòng đời của hoạt động</a>.</p>
46
47<p>Tuy nhiên, bạn có thể gặp phải một tình huống trong đó việc khởi động lại ứng dụng của bạn và
48khôi phục phần lớn dữ liệu có thể tốn kém và tạo nên trải nghiệm người dùng kém. Trong
49tình huống như vậy, bạn có hai tùy chọn:</p>
50
51<ol type="a">
52  <li><a href="#RetainingAnObject">Giữ lại một đối tượng trong khi thay đổi cấu hình</a>
53  <p>Cho phép hoạt động của bạn khởi động lại khi cấu hình thay đổi, nhưng mang theo một
54đối tượng có trạng thái tới thực thể mới của hoạt động của bạn.</p>
55
56  </li>
57  <li><a href="#HandlingTheChange">Tự mình xử lý thay đổi cấu hình</a>
58  <p>Ngăn không cho hệ thống khởi động lại hoạt động của bạn trong những thay đổi
59cấu hình nhất định, nhưng nhận một lệnh gọi lại khi cấu hình thay đổi, sao cho bạn có thể cập nhật thủ công
60hoạt động của mình nếu cần.</p>
61  </li>
62</ol>
63
64
65<h2 id="RetainingAnObject">Giữ lại một Đối tượng trong khi Thay đổi Cấu hình</h2>
66
67<p>Nếu việc khởi động lại hoạt động của bạn yêu cầu bạn phải khôi phục nhiều tập hợp dữ liệu lớn, hãy thiết lập lại kết nối
68mạng, hoặc thực hiện các thao tác tăng cường khác, khi đó khởi động lại hoàn toàn do thay đổi cấu hình
69có thể gây ra trải nghiệm người dùng chậm chạp. Đồng thời, có thể bạn sẽ không thể hoàn toàn khôi phục được
70trạng thái hoạt động của mình với {@link android.os.Bundle} mà hệ thống lưu cho bạn bằng phương pháp gọi lại {@link
71android.app.Activity#onSaveInstanceState(Bundle) onSaveInstanceState()}&mdash;nó không
72được thiết kế để mang các đối tượng lớn (chẳng hạn như bitmap) và dữ liệu trong nó phải được nối tiếp hóa rồi
73bỏ nối tiếp hóa, điều này có thể tiêu tốn nhiều bộ nhớ và khiến việc thay đổi cấu hình diễn ra chậm. Trong một
74tình huống như vậy, bạn có thể gỡ bỏ gánh nặng khởi tạo lại hoạt động của mình bằng cách giữ lại {@link
75android.app.Fragment} khi hoạt động của bạn được khởi động lại do thay đổi cấu hình. Phân đoạn này
76có thể chứa các tham chiếu tới đối tượng có trạng thái mà bạn muốn giữ lại.</p>
77
78<p>Khi hệ thống Android tắt hoạt động của bạn do một thay đổi cấu hình, các phân đoạn
79của hoạt động mà bạn đã đánh dấu để giữ lại sẽ không bị hủy. Bạn có thể thêm các phân đoạn này vào
80hoạt động của mình để giữ lại các đối tượng có trạng thái.</p>
81
82<p>Để giữ lại các đối tượng có trạng thái trong một phân đoạn trong khi thay đổi cấu hình thời gian chạy:</p>
83
84<ol>
85  <li>Mở rộng lớp {@link android.app.Fragment} và khai báo các tham chiếu tới đối tượng
86      có trạng thái của bạn.</li>
87  <li>Gọi {@link android.app.Fragment#setRetainInstance(boolean)} khi phân đoạn được tạo.
88      </li>
89  <li>Thêm phân đoạn vào hoạt động của bạn.</li>
90  <li>Sử dụng {@link android.app.FragmentManager} để truy xuất phân đoạn khi hoạt động
91      được khởi động lại.</li>
92</ol>
93
94<p>Ví dụ, định nghĩa phân đoạn của bạn như sau:</p>
95
96<pre>
97public class RetainedFragment extends Fragment {
98
99    // data object we want to retain
100    private MyDataObject data;
101
102    // this method is only called once for this fragment
103    &#64;Override
104    public void onCreate(Bundle savedInstanceState) {
105        super.onCreate(savedInstanceState);
106        // retain this fragment
107        setRetainInstance(true);
108    }
109
110    public void setData(MyDataObject data) {
111        this.data = data;
112    }
113
114    public MyDataObject getData() {
115        return data;
116    }
117}
118</pre>
119
120<p class="caution"><strong>Chú ý:</strong> Trong khi bạn có thể lưu trữ bất kỳ đối tượng nào, bạn
121không nên chuyển một đối tượng được gắn với {@link android.app.Activity}, chẳng hạn như {@link
122android.graphics.drawable.Drawable}, {@link android.widget.Adapter}, {@link android.view.View}
123hay bất kỳ đối tượng nào khác đi kèm với một {@link android.content.Context}. Nếu bạn làm vậy, nó sẽ
124rò rỉ tất cả dạng xem và tài nguyên của thực thể hoạt động gốc. (Rò rỉ tài nguyên
125có nghĩa là ứng dụng của bạn duy trì việc lưu giữ tài nguyên và chúng không thể được thu dọn bộ nhớ rác, vì thế
126rất nhiều bộ nhớ có thể bị mất.)</p>
127
128<p>Khi đó, hãy sử dụng {@link android.app.FragmentManager} để thêm phân đoạn vào hoạt động.
129Bạn có thể thu được đối tượng dữ liệu từ phân đoạn khi hoạt động bắt đầu lại trong khi
130thay đổi cấu hình thời gian chạy. Ví dụ, định nghĩa hoạt động của bạn như sau:</p>
131
132<pre>
133public class MyActivity extends Activity {
134
135    private RetainedFragment dataFragment;
136
137    &#64;Override
138    public void onCreate(Bundle savedInstanceState) {
139        super.onCreate(savedInstanceState);
140        setContentView(R.layout.main);
141
142        // find the retained fragment on activity restarts
143        FragmentManager fm = getFragmentManager();
144        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);
145
146        // create the fragment and data the first time
147        if (dataFragment == null) {
148            // add the fragment
149            dataFragment = new DataFragment();
150            fm.beginTransaction().add(dataFragment, “data”).commit();
151            // load the data from the web
152            dataFragment.setData(loadMyData());
153        }
154
155        // the data is available in dataFragment.getData()
156        ...
157    }
158
159    &#64;Override
160    public void onDestroy() {
161        super.onDestroy();
162        // store the data in the fragment
163        dataFragment.setData(collectMyLoadedData());
164    }
165}
166</pre>
167
168<p>Trong ví dụ này, {@link android.app.Activity#onCreate(Bundle) onCreate()} thêm một phân đoạn
169hoặc khôi phục một tham chiếu đến nó. {@link android.app.Activity#onCreate(Bundle) onCreate()} cũng
170lưu trữ đối tượng có trạng thái bên trong thực thể phân đoạn đó.
171{@link android.app.Activity#onDestroy() onDestroy()} cập nhật đối tượng có trạng thái bên trong
172thực thể phân đoạn được giữ lại.</p>
173
174
175
176
177
178<h2 id="HandlingTheChange">Tự mình Xử lý Thay đổi Cấu hình</h2>
179
180<p>Nếu ứng dụng của bạn không cần cập nhật các tài nguyên trong một thay đổi
181cấu hình cụ thể <em>và</em> bạn có giới hạn về hiệu năng yêu cầu bạn phải
182tránh khởi động lại hoạt động, khi đó bạn có thể khai báo rằng hoạt động của bạn tự mình xử lý thay đổi cấu hình
183, làm vậy sẽ tránh cho hệ thống khởi động lại hoạt động của bạn.</p>
184
185<p class="note"><strong>Lưu ý:</strong> Việc tự mình xử lý thay đổi cấu hình có thể khiến việc
186sử dụng các tài nguyên thay thế khó khăn hơn nhiều, vì hệ thống không tự động áp dụng chúng
187cho bạn. Kỹ thuật này nên được coi là giải pháp cuối cùng khi bạn phải tránh khởi động lại do một
188thay đổi cấu hình và không được khuyến cáo đối với hầu hết ứng dụng.</p>
189
190<p>Để khai báo rằng hoạt động của bạn xử lý một thay đổi cấu hình, hãy chỉnh sửa phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity&gt;}</a> phù hợp trong
191tệp bản kê khai của bạn để bao gồm thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
192android:configChanges}</a> với một giá trị có chức năng biểu diễn cấu hình mà bạn muốn
193xử lý. Các giá trị có thể được liệt kê trong tài liệu dành cho thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
194android:configChanges}</a> (các giá trị thường được sử dụng nhất là {@code "orientation"} để
195ngăn khởi động lại khi hướng màn hình thay đổi và {@code "keyboardHidden"} để ngăn
196khởi động lại khi tính sẵn có của bàn phím thay đổi).  Bạn có thể khai báo nhiều giá trị cấu hình trong
197thuộc tính bằng cách tách chúng bằng một ký tự {@code |} đường dẫn nối.</p>
198
199<p>Ví dụ, đoạn mã bản kê khai sau khai báo một hoạt động có chức năng xử lý cả
200thay đổi về hướng màn hình và thay đổi về tính sẵn có của bàn phím:</p>
201
202<pre>
203&lt;activity android:name=".MyActivity"
204          android:configChanges="orientation|keyboardHidden"
205          android:label="@string/app_name">
206</pre>
207
208<p>Lúc này, khi một trong những cấu hình này thay đổi, {@code MyActivity} không khởi động lại.
209Thay vào đó, {@code MyActivity} nhận được một lệnh gọi tới {@link
210android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Phương pháp này
211được chuyển bởi một đối tượng {@link android.content.res.Configuration} mà quy định
212cấu hình thiết bị mới. Bằng cách đọc các trường trong {@link android.content.res.Configuration},
213bạn có thể xác định cấu hình mới và thực hiện những thay đổi phù hợp bằng cách cập nhật
214tài nguyên được sử dụng trong giao diện của bạn. Tại
215thời điểm phương pháp này được gọi, đối tượng {@link android.content.res.Resources} của hoạt động của bạn được cập nhật
216để trả về các tài nguyên dựa trên cấu hình mới, sao cho bạn có thể dễ dàng
217đặt lại các phần tử trong UI của mình mà không để hệ thống khởi động lại hoạt động của bạn.</p>
218
219<p class="caution"><strong>Chú ý:</strong> Bắt đầu với Android 3.2 (API mức 13), <strong>
220"kích cỡ màn hình" cũng thay đổi</strong> khi thiết bị chuyển giữa hướng dọc và khổ ngang
221. Vì thế, nếu bạn muốn ngăn cản việc khởi động lại vào thời gian chạy do thay đổi hướng khi phát triển
222cho API mức 13 hoặc cao hơn (như được khai báo bởi các thuộc tính <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> và <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
223), bạn phải bao gồm giá trị {@code "screenSize"} bên cạnh giá trị {@code
224"orientation"}. Cụ thể, bạn phải khai báo {@code
225android:configChanges="orientation|screenSize"}. Tuy nhiên, nếu ứng dụng của bạn nhắm tới API mức
22612 hoặc thấp hơn, khi đó hoạt động của bạn luôn tự mình xử lý thay đổi cấu hình này (thay đổi
227cấu hình này không khởi động lại hoạt động của bạn, ngay cả khi đang chạy trên một thiết bị phiên bản Android 3.2 hoặc cao hơn).</p>
228
229<p>Ví dụ, việc triển khai {@link
230android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()} sau
231sẽ kiểm tra hướng thiết bị hiện tại:</p>
232
233<pre>
234&#64;Override
235public void onConfigurationChanged(Configuration newConfig) {
236    super.onConfigurationChanged(newConfig);
237
238    // Checks the orientation of the screen
239    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
240        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
241    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
242        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
243    }
244}
245</pre>
246
247<p>Đối tượng {@link android.content.res.Configuration} biểu diễn tất cả cấu hình
248hiện tại, không chỉ những cấu hình đã thay đổi. Trong phần lớn thời gian, bạn sẽ không quan tâm chính xác xem
249cấu hình đã thay đổi như thế nào và có thể đơn giản gán lại tất cả tài nguyên của mình với chức năng cung cấp nội dung thay thế
250cho cấu hình mà bạn đang xử lý. Ví dụ, do đối tượng {@link
251android.content.res.Resources} nay đã được cập nhật, bạn có thể đặt lại
252bất kỳ{@link android.widget.ImageView} nào với {@link android.widget.ImageView#setImageResource(int)
253setImageResource()}
254và tài nguyên phù hợp cho cấu hình mới được sử dụng (như được mô tả trong phần <a href="providing-resources.html#AlternateResources">Cung cấp Tài nguyên</a>).</p>
255
256<p>Lưu ý rằng giá trị từ các trường {@link
257android.content.res.Configuration} là những số nguyên khớp với hằng số cụ thể
258từ lớp {@link android.content.res.Configuration}. Đối với tài liệu về những hằng số
259cần sử dụng với mỗi trường, hãy tham khảo trường phù hợp trong tham chiếu {@link
260android.content.res.Configuration}.</p>
261
262<p class="note"><strong>Hãy ghi nhớ:</strong> Khi bạn khai báo hoạt động của mình để xử lý một
263thay đổi cấu hình, bạn có trách nhiệm đặt lại bất kỳ phần tử nào mà bạn cung cấp nội dung thay thế cho. Nếu bạn
264khai báo hoạt động của mình để xử lý thay đổi hướng và có những hình ảnh nên thay đổi
265giữa khổ ngang và hướng dọc, bạn phải gán lại từng tài nguyên cho từng phần tử trong khi {@link
266android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}.</p>
267
268<p>Nếu bạn không cần cập nhật ứng dụng của mình dựa trên những thay đổi
269cấu hình này, thay vào đó bạn có thể <em>không</em> triển khai {@link
270android.app.Activity#onConfigurationChanged(Configuration) onConfigurationChanged()}. Trong
271trường hợp đó, tất cả tài nguyên được sử dụng trước khi thay đổi cấu hình sẽ vẫn được sử dụng
272và bạn chỉ mới tránh được việc khởi động lại hoạt động của mình. Tuy nhiên, ứng dụng của bạn cần luôn có khả năng
273tắt và khởi động lại với trạng thái trước đó của nó được giữ nguyên, vì thế bạn không nên coi
274kỹ thuật này như một cách để thoát khỏi việc giữ lại trạng thái của mình trong vòng đời của hoạt động bình thường. Không chỉ bởi
275có những thay đổi cấu hình khác mà bạn không thể ngăn không cho khởi động lại ứng dụng của mình, mà
276cả bởi vì bạn nên xử lý những sự kiện như là khi người dùng rời khỏi ứng dụng của bạn và nó bị
277hủy trước khi người dùng quay lại.</p>
278
279<p>Để biết thêm về những thay đổi cấu hình nào mà bạn có thể xử lý trong hoạt động của mình, hãy xem tài liệu <a href="{@docRoot}guide/topics/manifest/activity-element.html#config">{@code
280android:configChanges}</a> và lớp {@link android.content.res.Configuration}
281.</p>
282