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()}—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 @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 @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 @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 <activity>}</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<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@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