diff options
Diffstat (limited to 'docs/html-intl/intl/vi/guide/components')
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/activities.jd | 756 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/bound-services.jd | 658 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/fragments.jd | 812 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/fundamentals.jd | 480 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/index.jd | 57 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/intents-filters.jd | 899 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/loaders.jd | 494 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/processes-and-threads.jd | 411 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/recents.jd | 256 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/services.jd | 813 | ||||
-rw-r--r-- | docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd | 578 |
11 files changed, 6214 insertions, 0 deletions
diff --git a/docs/html-intl/intl/vi/guide/components/activities.jd b/docs/html-intl/intl/vi/guide/components/activities.jd new file mode 100644 index 000000000000..83e7669a7f76 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/activities.jd @@ -0,0 +1,756 @@ +page.title=Các hoạt động +page.tags=hoạt động,ý định +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>Trong tài liệu này</h2> +<ol> + <li><a href="#Creating">Tạo một Hoạt động</a> + <ol> + <li><a href="#UI">Triển khai một giao diện người dùng</a></li> + <li><a href="#Declaring">Khai báo hoạt động trong bản kê khai</a></li> + </ol> + </li> + <li><a href="#StartingAnActivity">Bắt đầu một Hoạt động</a> + <ol> + <li><a href="#StartingAnActivityForResult">Bắt đầu một hoạt động cho một kết quả</a></li> + </ol> + </li> + <li><a href="#ShuttingDown">Tắt một Hoạt động</a></li> + <li><a href="#Lifecycle">Quản lý Vòng đời của Hoạt động</a> + <ol> + <li><a href="#ImplementingLifecycleCallbacks">Triển khai gọi lại vòng đời</a></li> + <li><a href="#SavingActivityState">Lưu trạng thái của hoạt động</a></li> + <li><a href="#ConfigurationChanges">Xử lý thay đổi về cấu hình</a></li> + <li><a href="#CoordinatingActivities">Điều phối hoạt động</a></li> + </ol> + </li> +</ol> + +<h2>Lớp khóa</h2> +<ol> + <li>{@link android.app.Activity}</li> +</ol> + +<h2>Xem thêm</h2> +<ol> + <li><a href="{@docRoot}guide/components/tasks-and-back-stack.html">Tác vụ và Ngăn +Xếp</a></li> +</ol> + +</div> +</div> + + + +<p>{@link android.app.Activity} là một thành phần ứng dụng cung cấp một màn hình mà với nó +người dùng có thể tương tác để thực hiện một điều gì đó, chẳng hạn như quay số điện thoại, chụp ảnh, gửi e-mail hoặc +xem bản đồ. Mỗi hoạt động được cho trong một cửa sổ là nơi để vẽ giao diện người dùng của nó. Cửa sổ này +thường lấp đầy màn hình, nhưng có thể nhỏ hơn màn hình và nổi bên trên các cửa sổ +khác.</p> + +<p> Ứng dụng thường bao gồm nhiều hoạt động được liên kết lỏng lẻo +với nhau. Thường thì một hoạt động trong một ứng dụng sẽ được quy định là hoạt động "chính", nó được +trình bày trước người dùng khi khởi chạy ứng dụng lần đầu. Sau đó, mỗi +hoạt động có thể bắt đầu một hoạt động khác để thực hiện các hành động khác nhau. Mỗi khi một hoạt động +mới bắt đầu, hoạt động trước đó sẽ bị dừng lại, nhưng hệ thống vẫn giữ nguyên hoạt động +trong một ngăn xếp ("back stack"). Khi một hoạt động mới bắt đầu, nó được đẩy lên ngăn xếp và +chiếm lấy tiêu điểm của người dùng. Ngăn xếp sẽ tuân theo cơ chế xếp chồng cơ bản "vào cuối, ra đầu", +vì thế, khi người dùng kết thúc hoạt động hiện tại và nhấn nút <em>Quay lại</em>, nó +sẽ được đẩy ra khỏi ngăn xếp (và bị hủy) và hoạt động trước đó sẽ tiếp tục. (Ngăn xếp được +đề cập kỹ hơn trong tài liệu <a href="{@docRoot}guide/components/tasks-and-back-stack.html">Tác vụ +và Ngăn Xếp</a>.)</p> + +<p>Khi một hoạt động bị dừng vì một hoạt động mới bắt đầu, nó được thông báo về sự thay đổi trạng thái này +qua các phương pháp gọi lại vòng đời của hoạt động. +Có một vài phương pháp gọi lại vòng đời mà một hoạt động có thể nhận, do một thay đổi về +trạng thái của nó—dù hệ thống đang tạo, dừng hay tiếp tục nó, hay hủy nó—và +mỗi lần gọi lại cho bạn cơ hội thực hiện công việc cụ thể +phù hợp với sự thay đổi trạng thái đó. Ví dụ, khi bị dừng, hoạt động của bạn sẽ giải phóng mọi +đối tượng lớn, chẳng hạn như các kết nối mạng hoặc cơ sở dữ liệu. Khi hoạt động tiếp tục, bạn có thể +thu lại những tài nguyên cần thiết và tiếp tục những hành động bị gián đoạn. Những chuyển tiếp trạng thái này +đều là một phần của vòng đời hoạt động.</p> + +<p>Phần còn lại của tài liệu này bàn đến những nội dung cơ bản về cách xây dựng và sử dụng một hoạt động, +bao gồm một nội dung đề cập đầy đủ về cách vận hành của vòng đời hoạt động, để bạn có thể quản lý tốt +sự chuyển tiếp giữa các trạng thái hoạt động khác nhau.</p> + + + +<h2 id="Creating">Tạo một Hoạt động</h2> + +<p>Để tạo một hoạt động, bạn phải tạo một lớp con của {@link android.app.Activity} (hoặc +một lớp con hiện tại của nó). Trong lớp con của mình, bạn cần triển khai các phương pháp gọi lại mà hệ thống +gọi khi hoạt động chuyển tiếp giữa các trạng thái khác nhau trong vòng đời, chẳng hạn như khi +hoạt động đang được tạo, dừng, tiếp tục, hoặc hủy. Hai phương pháp gọi lại quan trọng nhất +là:</p> + +<dl> + <dt>{@link android.app.Activity#onCreate onCreate()}</dt> + <dd>Bạn phải triển khai phương pháp này. Hệ thống gọi phương pháp này khi tạo hoạt động +của bạn. Trong quá trình thực hiện của mình, bạn nên khởi chạy những thành phần thiết yếu cho hoạt động +của mình. + Quan trọng nhất, đây là lúc bạn phải gọi {@link android.app.Activity#setContentView + setContentView()} để định nghĩa bố trí cho giao diện người dùng của hoạt động.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>Hệ thống gọi phương pháp này là dấu hiệu đầu tiên về việc người dùng đang rời khỏi hoạt động +của bạn (mặc dù không phải lúc nào cũng có nghĩa rằng hoạt động đang bị hủy). Trường hợp này thường là khi bạn +định thực hiện bất kỳ thay đổi nào vẫn cần có hiệu lực ngoài phiên của người dùng hiện thời (vì +người dùng có thể không quay lại).</dd> +</dl> + +<p>Có một vài phương pháp gọi lại vòng đời khác mà bạn nên sử dụng để đem đến +một trải nghiệm người dùng mượt mà giữa các hoạt động và xử lý những gián đoạn bất ngờ khiến hoạt động của bạn +bị dừng và thậm chí bị hủy. Tất cả phương pháp gọi lại vòng đời được bàn sau trong phần +nói về <a href="#Lifecycle">Quản lý Vòng đời của Hoạt động</a>.</p> + + + +<h3 id="UI">Triển khai một giao diện người dùng</h3> + +<p> Giao diện người dùng cho một hoạt động sẽ được cung cấp theo phân cấp dạng xem—đối tượng được suy ra +từ lớp {@link android.view.View}. Mỗi chế độ xem kiểm soát một không gian chữ nhật riêng +trong cửa sổ của hoạt động và có thể phản hồi trước tương tác của người dùng. Ví dụ, chế độ xem có thể là +một nút khởi xướng một hành động khi người dùng chạm vào nó.</p> + +<p>Android cung cấp nhiều chế độ xem sẵn có mà bạn có thể sử dụng để thiết kế và tổ chức cho bố trí +của mình. "Widget" là những chế độ xem cung cấp những phần tử trực quan (và tương tác) cho màn hình, chẳng hạn như +nút, trường văn bản, hộp kiểm, hay chỉ là một hình ảnh. "Bố trí" là những chế độ xem được suy ra từ {@link +android.view.ViewGroup} cung cấp một mô hình bố trí duy nhất cho các chế độ xem con của nó, chẳng hạn như bố trí +tuyến tính, bố trí lưới, hoặc bố trí tương đối. Bạn cũng có thể chia thành lớp con {@link android.view.View} và các lớp +{@link android.view.ViewGroup} (hoặc các lớp con hiện tại) để tạo widget và +bố trí của chính mình và áp dụng chúng vào bố trí hoạt động của bạn.</p> + +<p>Cách phổ biến nhất để định nghĩa một bố trí bằng cách sử dụng các chế độ xem là dùng một tệp bố trí XML được lưu trong tài nguyên ứng dụng +của bạn. Bằng cách này, bạn có thể duy trì thiết kế giao diện người dùng của mình độc lập với +mã nguồn định nghĩa hành vi của hoạt động. Bạn có thể đặt bố trí làm UI cho hoạt động +của mình bằng {@link android.app.Activity#setContentView(int) setContentView()}, chuyển +ID tài nguyên cho bố trí. Tuy nhiên, bạn cũng có thể tạo {@link android.view.View} mới trong mã hoạt động +của mình và xây dựng một cấp bậc chế độ xem bằng cách chèn các {@link +android.view.View} mới vào một {@link android.view.ViewGroup}, sau đó sử dụng bố trí đó bằng cách chuyển root +{@link android.view.ViewGroup} sang {@link android.app.Activity#setContentView(View) +setContentView()}.</p> + +<p>Để biết thông tin về việc tạo một giao diện người dùng, hãy xem tài liệu <a href="{@docRoot}guide/topics/ui/index.html">Giao diện Người dùng</a>.</p> + + + +<h3 id="Declaring">Khai báo hoạt động trong bản kê khai</h3> + +<p>Bạn phải khai báo hoạt động của mình trong tệp bản kê khai để hoạt động +có thể truy cập được vào hệ thống. Để khai báo hoạt động của mình, hãy mở tệp bản kê khai của bạn và thêm một phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +làm con của phần tử <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +. Ví dụ:</p> + +<pre> +<manifest ... > + <application ... > + <activity android:name=".ExampleActivity" /> + ... + </application ... > + ... +</manifest > +</pre> + +<p>Có vài thuộc tính khác mà bạn có thể nêu trong phần tử này, để định nghĩa các thuộc tính +như nhãn cho hoạt động, biểu tượng cho hoạt động, hoặc chủ đề mô tả kiểu UI của +hoạt động. Thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#nm">{@code android:name}</a> +là thuộc tính bắt buộc duy nhất—nó quy định tên lớp của hoạt động. Một khi +bạn phát hành ứng dụng của mình, bạn không nên thay đổi tên này, vì nếu bạn làm vậy, bạn có thể làm hỏng +một số tính năng, chẳng hạn như các lối tắt của ứng dụng (hãy đọc bài đăng trên blog, <a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Những Điều +Không Thay Đổi Được</a>).</p> + +<p>Xem tài liệu tham khảo phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +để biết thêm thông tin về việc khai báo hoạt động của bạn trong bản kê khai.</p> + + +<h4>Sử dụng các bộ lọc ý định</h4> + +<p>Một phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> cũng có thể quy định các bộ lọc ý định khác nhau—bằng cách sử dụng phần tử <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> —để khai báo cách thức mà các thành phần khác của ứng dụng có thể +kích hoạt nó.</p> + +<p>Khi bạn tạo một ứng dụng mới bằng cách sử dụng các công cụ SDK của Android, hoạt động chương trình nhỏ +được tạo cho bạn sẽ tự động bao gồm một bộ lọc ý định khai báo hoạt động +phản hồi lại hành động "chính" và nên được đặt trong thể loại "trình khởi chạy". Bộ lọc ý định +trông như thế này:</p> + +<pre> +<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> +</pre> + +<p>Phần tử <a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> quy định rằng đây là điểm mục nhập "chính" đối với ứng dụng. Phần tử <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> quy định rằng hoạt động này nên được liệt kê trong trình khởi chạy ứng dụng của hệ thống +(để cho phép người dùng khởi chạy hoạt động này).</p> + +<p>Nếu bạn có ý định cho ứng dụng của mình được độc lập và không cho phép các ứng dụng khác +kích hoạt các hoạt động của nó, vậy bạn không cần bất kỳ bộ lọc ý định nào khác. Chỉ một hoạt động nên có +hành động "chính" và thể loại "trình khởi chạy" như trong ví dụ trước. Những hoạt động mà +bạn không muốn cung cấp sẵn cho các ứng dụng khác không nên có bộ lọc ý định và bạn có thể +tự mình bắt đầu chúng bằng cách sử dụng các ý định rõ ràng (như được đề cập trong phần sau).</p> + +<p>Tuy nhiên, nếu bạn muốn hoạt động của mình phản hồi lại những ý định ngầm mà được chuyển giao từ +các ứng dụng khác (và chính bạn), thì bạn phải định nghĩa các bộ lọc ý định bổ sung cho hoạt động +của mình. Với mỗi loại ý định mà bạn muốn phản hồi, bạn phải nêu một <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> bao gồm một phần tử +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> và, không bắt buộc, một phần tử <a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> và/hoặc một phần tử <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a>. Những phần tử này quy định loại ý định mà hoạt động của bạn có thể +phản hồi.</p> + +<p>Để biết thêm thông tin về cách thức các hoạt động của bạn có thể phản hồi lại ý định, hãy xem tài liệu <a href="{@docRoot}guide/components/intents-filters.html">Ý định và Bộ lọc Ý định</a> +.</p> + + + +<h2 id="StartingAnActivity">Bắt đầu một Hoạt động</h2> + +<p>Bạn có thể bắt đầu một hoạt động khác bằng cách gọi {@link android.app.Activity#startActivity + startActivity()}, chuyển cho nó một {@link android.content.Intent} mà mô tả hoạt động bạn +muốn bắt đầu. Ý định này sẽ quy định hoặc hoạt động chính xác mà bạn muốn bắt đầu hoặc mô tả + loại hành động mà bạn muốn thực hiện (và hệ thống lựa chọn hoạt động phù hợp cho bạn, +thậm chí +có thể từ một ứng dụng khác). Một ý định cũng có thể mang theo lượng nhỏ dữ liệu sẽ được + sử dụng bởi hoạt động được bắt đầu.</p> + +<p>Khi đang làm việc trong ứng dụng của chính mình, bạn thường sẽ cần khởi chạy một hoạt động đã biết. + Bạn có thể làm vậy bằng cách tạo một ý định trong đó quy định rõ hoạt động bạn muốn bắt đầu, +sử dụng tên lớp đó. Ví dụ, sau đây là cách một hoạt động bắt đầu một hoạt động khác có tên {@code +SignInActivity}:</p> + +<pre> +Intent intent = new Intent(this, SignInActivity.class); +startActivity(intent); +</pre> + +<p>Tuy nhiên, ứng dụng của bạn cũng có thể muốn thực hiện một số hành động, chẳng hạn như gửi một e-mail, tin nhắn + văn bản, hoặc cập nhật trạng thái, bằng cách sử dụng dữ liệu từ hoạt động của bạn. Trong trường hợp này, ứng dụng của bạn có thể + không có các hoạt động của chính nó để thực hiện những hành động đó, vì vậy, thay vào đó, bạn có thể tận dụng những hoạt động + được cung cấp bởi các ứng dụng khác trên thiết bị mà có thể thực hiện hành động cho bạn. Đây là lúc +ý định thực sự có giá trị—bạn có thể tạo một ý định mô tả một hành động bạn muốn +thực hiện và hệ thống + sẽ khởi chạy hoạt động phù hợp đó từ một ứng dụng khác. Nếu có + nhiều hoạt động mà có thể xử lý ý định, vậy người dùng có thể chọn hoạt động nào sẽ sử dụng. Ví + dụ, nếu bạn muốn cho phép người dùng gửi e-mail, bạn có thể tạo + ý định sau:</p> + +<pre> +Intent intent = new Intent(Intent.ACTION_SEND); +intent.putExtra(Intent.EXTRA_EMAIL, recipientArray); +startActivity(intent); +</pre> + +<p>{@link android.content.Intent#EXTRA_EMAIL} phụ được thêm vào ý định là một mảng xâu của + các địa chỉ e-mail mà e-mail sẽ được gửi tới. Khi một ứng dụng e-mail phản hồi + ý định này, nó đọc mảng xâu được cung cấp trong phần phụ và đặt nó vào trường "đến" của mẫu soạn thảo + e-mail. Trong trường hợp này, hoạt động của ứng dụng e-mail bắt đầu và khi người dùng + làm xong, hoạt động của bạn sẽ tiếp tục.</p> + + + + +<h3 id="StartingAnActivityForResult">Bắt đầu một hoạt động cho một kết quả</h3> + +<p>Đôi khi bạn có thể muốn nhận được một kết quả từ hoạt động mà bạn bắt đầu. Trong trường hợp đó, +hãy bắt đầu hoạt động bằng cách gọi {@link android.app.Activity#startActivityForResult + startActivityForResult()} (thay vì {@link android.app.Activity#startActivity + startActivity()}). Rồi để nhận được kết quả từ hoạt động +sau đó, hãy triển khai phương pháp gọi lại {@link android.app.Activity#onActivityResult onActivityResult()} +. Khi hoạt động sau đó diễn ra xong, nó trả về một kết quả trong một {@link +android.content.Intent} cho phương pháp {@link android.app.Activity#onActivityResult onActivityResult()} +của bạn.</p> + +<p>Ví dụ, bạn có thể muốn người dùng chọn một trong các liên lạc của họ, vì vậy hoạt động của bạn có thể +làm gì đó với thông tin trong liên lạc đó. Đây là cách bạn có thể tạo một ý định như vậy và +xử lý kết quả:</p> + +<pre> +private void pickContact() { + // Create an intent to "pick" a contact, as defined by the content provider URI + Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI); + startActivityForResult(intent, PICK_CONTACT_REQUEST); +} + +@Override +protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // If the request went well (OK) and the request was PICK_CONTACT_REQUEST + if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) { + // Perform a query to the contact's content provider for the contact's name + Cursor cursor = getContentResolver().query(data.getData(), + new String[] {Contacts.DISPLAY_NAME}, null, null, null); + if (cursor.moveToFirst()) { // True if the cursor is not empty + int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME); + String name = cursor.getString(columnIndex); + // Do something with the selected contact's name... + } + } +} +</pre> + +<p>Ví dụ này thể hiện lô-gic cơ bản mà bạn sẽ sử dụng trong phương pháp {@link +android.app.Activity#onActivityResult onActivityResult()} của mình để xử lý một +kết quả hoạt động. Điều kiện đầu tiên kiểm tra xem yêu cầu có thành công không—nếu có thì +{@code resultCode} sẽ là {@link android.app.Activity#RESULT_OK}—và liệu yêu cầu +mà kiểm tra này đang phản hồi có được biết hay không—trong trường hợp này, {@code requestCode} phù hợp với +tham số thứ hai được gửi bằng {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Từ đó, mã xử lý kết quả hoạt động bằng cách truy vấn +dữ liệu được trả về trong {@link android.content.Intent} (tham số {@code data}).</p> + +<p>Điều xảy ra đó là, {@link +android.content.ContentResolver} sẽ thực hiện một truy vấn đối với nhà cung cấp nội dung, truy vấn này trả về một +{@link android.database.Cursor} cho phép đọc dữ liệu được truy vấn. Để biết thêm thông tin, hãy xem tài liệu +<a href="{@docRoot}guide/topics/providers/content-providers.html">Trình cung cấp Nội dung</a>.</p> + +<p>Để biết thêm thông tin về việc sử dụng ý định, hãy xem tài liệu <a href="{@docRoot}guide/components/intents-filters.html">Ý định và Bộ lọc +Ý định</a>.</p> + + +<h2 id="ShuttingDown">Tắt một Hoạt động</h2> + +<p>Bạn có thể tắt một hoạt động bằng cách gọi phương pháp {@link android.app.Activity#finish +finish()} của nó. Bạn cũng có thể tắt một hoạt động riêng mà trước đó bạn đã bắt đầu bằng cách gọi +{@link android.app.Activity#finishActivity finishActivity()}.</p> + +<p class="note"><strong>Lưu ý:</strong> Trong hầu hết trường hợp, bạn không nên kết thúc một hoạt động một cách rõ ràng +bằng cách sử dụng những phương pháp này. Như đề cập trong phần sau về vòng đời của hoạt động, hệ thống +Android quản lý tuổi thọ của một hoạt động cho bạn, vì vậy bạn không cần kết thúc các hoạt động +của chính mình. Việc gọi những phương pháp này có thể ảnh hưởng tiêu cực tới trải nghiệm người dùng +kỳ vọng và chỉ nên được sử dụng khi bạn tuyệt đối không muốn người dùng quay lại thực thể này của +hoạt động.</p> + + +<h2 id="Lifecycle">Quản lý Vòng đời của Hoạt động</h2> + +<p>Việc quản lý vòng đời các hoạt động của bạn bằng cách triển khai các phương pháp gọi lại +rất quan trọng đối với việc xây dựng một ứng dụng mạnh +và linh hoạt. Vòng đời của một hoạt động trực tiếp bị ảnh hưởng bởi sự liên kết giữa nó với +các hoạt động khác, tác vụ của nó và ngăn xếp (back stack).</p> + +<p>Về cơ bản, một hoạt động có thể tồn tại ở ba trạng thái:</p> + +<dl> + <dt><i>Tiếp tục</i></dt> + <dd>Hoạt động ở tiền cảnh của màn hình và có tiêu điểm của người dùng. (Trạng thái này +đôi khi cũng được gọi là "đang chạy".)</dd> + + <dt><i>Tạm dừng</i></dt> + <dd>Một hoạt động khác ở tiền cảnh và có tiêu điểm, nhưng hoạt động này vẫn hiển thị. Cụ thể, +một hoạt động khác hiển thị ở trên hoạt động này và hoạt động đó trong suốt một phần hoặc không +che toàn bộ màn hình. Trạng thái tạm dừng hoàn toàn đang hoạt động (đối tượng {@link android.app.Activity} +được giữ lại trong bộ nhớ, nó duy trì tất cả thông tin về trạng thái và thành viên, và vẫn gắn với +trình quản lý cửa sổ), nhưng có thể bị hệ thống tắt bỏ trong trường hợp bộ nhớ cực kỳ thấp.</dd> + + <dt><i>Dừng</i></dt> + <dd>Hoạt động bị che khuất hoàn toàn bởi một hoạt động khác (hoạt động hiện đang +“dưới nền"). Hoạt động dừng cũng vẫn đang hoạt động ({@link android.app.Activity} +đối tượng được giữ lại trong bộ nhớ, nó duy trì tất cả thông tin về trạng thái và thành viên, nhưng <em>không</em> +gắn với trình quản lý cửa sổ). Tuy nhiên, hoạt động không còn hiển thị với người dùng nữa và hệ thống +có thể tắt bỏ hoạt động này khi cần bộ nhớ ở nơi khác.</dd> +</dl> + +<p>Nếu một hoạt động bị tạm dừng hoặc dừng, hệ thống có thể bỏ nó khỏi bộ nhớ hoặc bằng cách yêu cầu nó +kết thúc (gọi phương pháp {@link android.app.Activity#finish finish()} của nó), hoặc đơn giản là tắt bỏ tiến trình +của hoạt động. Khi hoạt động được mở lại (sau khi bị kết thúc hoặc tắt bỏ), nó phải được tạo +lại hoàn toàn.</p> + + + +<h3 id="ImplementingLifecycleCallbacks">Triển khai gọi lại vòng đời</h3> + +<p>Khi một hoạt động chuyển tiếp vào ra các trạng thái khác nhau nêu trên, nó được thông báo +thông qua các phương pháp gọi lại. Tất cả phương pháp gọi lại đều là những móc (hook) mà bạn +có thể khống chế để làm công việc phù hợp khi trạng thái hoạt động của bạn thay đổi. Hoạt động khung sau +bao gồm từng phương pháp trong các phương pháp vòng đời cơ bản:</p> + + +<pre> +public class ExampleActivity extends Activity { + @Override + public void {@link android.app.Activity#onCreate onCreate}(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // The activity is being created. + } + @Override + protected void {@link android.app.Activity#onStart onStart()} { + super.onStart(); + // The activity is about to become visible. + } + @Override + protected void {@link android.app.Activity#onResume onResume()} { + super.onResume(); + // The activity has become visible (it is now "resumed"). + } + @Override + protected void {@link android.app.Activity#onPause onPause()} { + super.onPause(); + // Another activity is taking focus (this activity is about to be "paused"). + } + @Override + protected void {@link android.app.Activity#onStop onStop()} { + super.onStop(); + // The activity is no longer visible (it is now "stopped") + } + @Override + protected void {@link android.app.Activity#onDestroy onDestroy()} { + super.onDestroy(); + // The activity is about to be destroyed. + } +} +</pre> + +<p class="note"><strong>Lưu ý:</strong> Việc bạn triển khai những phương pháp vòng đời này phải luôn +gọi triển khai siêu lớp trước khi làm bất kỳ công việc nào, như minh họa trong các ví dụ bên trên.</p> + +<p>Cùng nhau, những phương pháp này định nghĩa toàn bộ vòng đời của một hoạt động. Bằng việc triển khai những phương pháp +này, bạn có thể theo dõi ba vòng lặp lồng nhau trong vòng đời của hoạt động: </p> + +<ul> +<li><b>Toàn bộ vòng đời</b> của một hoạt động sẽ xảy ra từ thời điểm lệnh gọi đến {@link +android.app.Activity#onCreate onCreate()} cho tới thời điểm lệnh gọi đến {@link +android.app.Activity#onDestroy}. Hoạt động của bạn nên thực hiện thiết lập +trạng thái "chung" (chẳng hạn như định nghĩa bố trí) trong {@link android.app.Activity#onCreate onCreate()}, và +giải phóng tất cả tài nguyên còn lại trong {@link android.app.Activity#onDestroy}. Ví dụ, nếu hoạt động của bạn +có một luồng đang chạy ngầm để tải xuống dữ liệu từ mạng, nó có thể tạo +luồng đó trong {@link android.app.Activity#onCreate onCreate()} rồi dừng luồng trong {@link +android.app.Activity#onDestroy}.</li> + +<li><p><b>Vòng đời hiển thị</b> của một hoạt động xảy ra từ thời điểm lệnh gọi đến {@link +android.app.Activity#onStart onStart()} cho tới lệnh gọi đến {@link +android.app.Activity#onStop onStop()}. Trong thời gian này, người dùng có thể thấy hoạt động +trên màn hình và tương tác với nó. Ví dụ, {@link android.app.Activity#onStop onStop()} được gọi +khi một hoạt động mới bắt đầu và không còn hiển thị nữa. Giữa hai phương pháp này, bạn có thể +duy trì các tài nguyên cần để cho người dùng thấy hoạt động. Ví dụ, bạn có thể đăng ký một +{@link android.content.BroadcastReceiver} trong {@link +android.app.Activity#onStart onStart()} để theo dõi các thay đổi tác động tới UI của mình, và bỏ đăng ký +nó trong {@link android.app.Activity#onStop onStop()} khi người dùng không còn thấy thứ bạn đang +hiển thị nữa. Hệ thống có thể gọi {@link android.app.Activity#onStart onStart()} và {@link +android.app.Activity#onStop onStop()} nhiều lần trong suốt vòng đời của hoạt động, khi đó +hoạt động luân chuyển giữa trạng thái hiển thị và ẩn với người dùng.</p></li> + +<li><p><b>Vòng đời ở tiền cảnh</b> của một hoạt động xảy ra từ thời điểm lệnh gọi đến {@link +android.app.Activity#onResume onResume()} cho tới thời điểm lệnh gọi đến {@link android.app.Activity#onPause +onPause()}. Trong thời gian này, hoạt động sẽ ở phía trước tất cả hoạt động khác trên màn hình và có +tiêu điểm đầu vào của người dùng. Hoạt động có thể thường xuyên chuyển tiếp vào và ra tiền cảnh—ví +dụ, {@link android.app.Activity#onPause onPause()} được gọi khi thiết bị vào trạng thái ngủ hoặc +khi một hộp thoại xuất hiện. Vì trạng thái này có thể chuyển tiếp thường xuyên, mã trong hai phương pháp này nên +tương đối nhẹ để tránh chuyển tiếp chậm khiến người dùng phải đợi.</p></li> +</ul> + +<p>Hình 1 minh họa những vòng lặp này và các đường dẫn mà một hoạt động có thể diễn ra giữa các trạng thái. +Hình chữ nhật đại diện cho các phương pháp gọi lại bạn có thể triển khai để thực hiện thao tác khi +hoạt động chuyển tiếp giữa những trạng thái này. <p> + +<img src="{@docRoot}images/activity_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Hình 1.</strong> Vòng đời của hoạt động.</p> + +<p>Những phương pháp gọi lại vòng đời này cũng được liệt kê trong bảng 1, trong đó mô tả từng phương pháp +gọi lại một cách chi tiết hơn và xác định từng phương pháp +trong vòng đời tổng thể của hoạt động, bao gồm việc hệ thống có thể tắt bỏ hoạt động hay không sau khi +phương pháp gọi lại hoàn tất.</p> + +<p class="table-caption"><strong>Bảng 1.</strong> Tóm tắt các phương pháp gọi lại +trong vòng đời của hoạt động.</p> + +<table border="2" width="85%" frame="hsides" rules="rows"> +<colgroup align="left" span="3"></colgroup> +<colgroup align="left"></colgroup> +<colgroup align="center"></colgroup> +<colgroup align="center"></colgroup> + +<thead> +<tr><th colspan="3">Phương pháp</th> <th>Mô tả</th> <th>Có thể tắt bỏ sau?</th> <th>Tiếp theo</th></tr> +</thead> + +<tbody> +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onCreate onCreate()}</code></td> + <td>Được gọi khi hoạt động mới được tạo. + Đây là lúc bạn nên thực hiện tất cả thiết lập cố định thông thường của mình — + tạo chế độ xem, kết ghép dữ liệu với danh sách, v.v. Phương pháp này được chuyển cho + một đối tượng Gói chứa trạng thái trước đây của hoạt động, nếu trạng thái + đó được thu lại (xem phần <a href="#actstate">Lưu Trạng thái Hoạt động</a>, + ở đoạn sau). + <p>Luôn được theo sau bởi {@code onStart()}.</p></td> + <td align="center">Không</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td rowspan="5" style="border-left: none; border-right: none;"> </td> + <td colspan="2" align="left"><code>{@link android.app.Activity#onRestart +onRestart()}</code></td> + <td>Được gọi sau khi hoạt động đã được dừng, ngay trước khi hoạt động được + bắt đầu lại. + <p>Luôn được theo sau bởi {@code onStart()}</p></td> + <td align="center">Không</td> + <td align="center">{@code onStart()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStart onStart()}</code></td> + <td>Được gọi ngay trước khi hoạt động hiển thị trước người dùng. + <p>Được theo sau bởi {@code onResume()} nếu hoạt động vào + tiền cảnh, hoặc {@code onStop()} nếu hoạt động bị ẩn.</p></td> + <td align="center">Không</td> + <td align="center">{@code onResume()} <br/>hoặc<br/> {@code onStop()}</td> +</tr> + +<tr> + <td rowspan="2" style="border-left: none;"> </td> + <td align="left"><code>{@link android.app.Activity#onResume onResume()}</code></td> + <td>Được gọi ngay trước khi hoạt động bắt đầu + tương tác với người dùng. Tại điểm này, hoạt động nằm ở + trên cùng của chồng hoạt động, trong đó mục nhập của người dùng sẽ đến hoạt động này. + <p>Luôn được theo sau bởi {@code onPause()}.</p></td> + <td align="center">Không</td> + <td align="center">{@code onPause()}</td> +</tr> + +<tr> + <td align="left"><code>{@link android.app.Activity#onPause onPause()}</code></td> + <td>Được gọi khi hệ thống sắp bắt đầu tiếp tục một hoạt động + khác. Phương pháp này thường được sử dụng để thực hiện các thay đổi chưa lưu cho + dữ liệu liên tục, dừng các hoạt ảnh và những việc khác mà có thể tiêu tốn công suất + CPU, v.v. Nó sẽ thực hiện rất nhanh, vì + hoạt động tiếp theo sẽ không được tiếp tục tới khi nó trở lại. + <p>Được theo sau hoặc bởi {@code onResume()} nếu hoạt động + trở lại phía trước, hoặc bởi {@code onStop()} nếu nó + không hiển thị với người dùng.</td> + <td align="center"><strong style="color:#800000">Có</strong></td> + <td align="center">{@code onResume()} <br/>hoặc<br/> {@code onStop()}</td> +</tr> + +<tr> + <td colspan="2" align="left"><code>{@link android.app.Activity#onStop onStop()}</code></td> + <td>Được gọi khi hoạt động không còn hiển thị với người dùng. Điều này + có thể xảy ra vì nó đang bị hủy, hoặc vì một hoạt động khác + (đang tồn tại hoặc mới) đã được tiếp tục và đang che khuất nó. + <p>Được theo sau hoặc bởi {@code onRestart()} nếu + hoạt động đang quay lại để tương tác với người dùng, hoặc bởi + {@code onDestroy()} nếu hoạt động này sẽ đi mất.</p></td> + <td align="center"><strong style="color:#800000">Có</strong></td> + <td align="center">{@code onRestart()} <br/>hoặc<br/> {@code onDestroy()}</td> +</tr> + +<tr> + <td colspan="3" align="left"><code>{@link android.app.Activity#onDestroy +onDestroy()}</code></td> + <td>Được gọi trước khi hoạt động bị hủy. Đây là lần gọi cuối cùng + mà hoạt động sẽ nhận được. Nên gọi nó hoặc vì + hoạt động đang kết thúc (ai đó đã gọi <code>{@link android.app.Activity#finish + finish()}</code> trên nó), hoặc vì hệ thống đang tạm thời hủy thực thể này của + hoạt động để tiết kiệm bộ nhớ trống. Bạn có thể phân biệt + những những kịch bản này bằng phương pháp <code>{@link + android.app.Activity#isFinishing isFinishing()}</code>.</td> + <td align="center"><strong style="color:#800000">Có</strong></td> + <td align="center"><em>không có gì</em></td> +</tr> +</tbody> +</table> + +<p>Cột ghi "Có thể tắt bỏ sau?" cho biết liệu hệ thống có thể +tắt bỏ tiến trình đang lưu trữ hoạt động vào bất cứ lúc nào <em>sau khi phương pháp trả về</em>, mà không +thực hiện một dòng mã khác của hoạt động hay không. Ba phương pháp được ghi là "có": ({@link +android.app.Activity#onPause +onPause()}, {@link android.app.Activity#onStop onStop()}, và {@link android.app.Activity#onDestroy +onDestroy()}). Vì {@link android.app.Activity#onPause onPause()} là phương pháp đầu tiên +trong ba phương pháp, sau khi hoạt động được tạo, {@link android.app.Activity#onPause onPause()} là +phương pháp cuối cùng được bảo đảm sẽ được gọi trước khi tiến trình <em>có thể</em> bị tắt bỏ—nếu +hệ thống phải khôi phục bộ nhớ trong một tình huống khẩn cấp, khi đó {@link +android.app.Activity#onStop onStop()} và {@link android.app.Activity#onDestroy onDestroy()} có thể +không được gọi. Vì thế, bạn nên sử dụng {@link android.app.Activity#onPause onPause()} để ghi +dữ liệu cố định quan trọng (chẳng hạn như những chỉnh sửa của người dùng) vào thiết bị lưu trữ. Tuy nhiên, bạn nên chọn lọc +thông tin nào phải được giữ lại trong {@link android.app.Activity#onPause onPause()}, vì bất kỳ +thủ tục chặn nào trong phương pháp này cũng chặn chuyển tiếp sang hoạt động kế tiếp và làm chậm trải nghiệm +của người dùng.</p> + +<p> Những phương pháp được ghi "Không" trong cột <b>Có thể tắt bỏ</b> sẽ bảo vệ tiến trình đang lưu trữ +hoạt động khỏi bị tắt bỏ từ thời điểm chúng được gọi. Vì thế, một hoạt động có thể tắt bỏ được +từ thời điểm {@link android.app.Activity#onPause onPause()} trở về tới thời điểm +{@link android.app.Activity#onResume onResume()} sẽ được gọi. Nó sẽ không thể lại tắt bỏ được tới khi +{@link android.app.Activity#onPause onPause()} lại được gọi và trả về. </p> + +<p class="note"><strong>Lưu ý:</strong> Một hoạt động mà không thể "tắt bỏ được" về mặt kỹ thuật bởi +định nghĩa này trong bảng 1 vẫn có thể bị hệ thống tắt bỏ—nhưng điều đó chỉ xảy ra trong +những hoàn cảnh cực đoan khi không còn giải pháp nào khác. Thời điểm một hoạt động có thể bị tắt bỏ được +đề cập kỹ hơn trong tài liệu <a href="{@docRoot}guide/components/processes-and-threads.html">Tiến trình và +Luồng</a>.</p> + + +<h3 id="SavingActivityState">Lưu trạng thái của hoạt động</h3> + +<p>Phần giới thiệu về <a href="#Lifecycle">Quản lý Vòng đời của Hoạt động</a> có đề cập sơ qua +rằng +khi một hoạt động bị tạm dừng hoặc dừng, trạng thái của hoạt động đó sẽ được giữ lại. Điều này đúng vì +đối tượng {@link android.app.Activity} vẫn được giữ trong bộ nhớ khi nó bị tạm dừng hoặc +dừng—tất cả thông tin về các thành viên và trạng thái hiện tại của nó vẫn hoạt động. Vì thế, bất kỳ thay đổi nào +mà người dùng đã thực hiện trong hoạt động đều được giữ lại sao cho khi hoạt động trở về +tiền cảnh (khi nó "tiếp tục"), thì những thay đổi này vẫn còn đó.</p> + +<p>Tuy nhiên, khi hệ thống hủy một hoạt động để khôi phục bộ nhớ, đối tượng {@link +android.app.Activity} bị hủy, vì vậy hệ thống không thể đơn thuần tiếp tục hoạt động với trạng thái +không bị ảnh hưởng. Thay vào đó, hệ thống phải tạo lại đối tượng {@link android.app.Activity} nếu người dùng +điều hướng trở lại nó. Tuy vậy, người dùng không biết +rằng hệ thống đã hủy hoạt động và tạo lại nó và, vì thế, có thể +cho rằng hoạt động sẽ vẫn nguyên như cũ. Trong tình huống này, bạn có thể đảm bảo rằng +thông tin quan trọng về trạng thái của hoạt động được giữ nguyên bằng cách triển khai một phương pháp gọi lại +bổ sung cho phép bạn lưu thông tin về trạng thái của hoạt động của mình: {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}.</p> + +<p>Hệ thống gọi {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +trước khi khiến hoạt động dễ bị hủy. Hệ thống chuyển cho phương pháp này +một {@link android.os.Bundle} trong đó bạn có thể lưu +thông tin trạng thái về hoạt động như cặp tên giá trị, bằng cách sử dụng các phương pháp như {@link +android.os.Bundle#putString putString()} và {@link +android.os.Bundle#putInt putInt()}. Sau đó, nếu hệ thống tắt bỏ tiến trình ứng dụng của bạn +và người dùng điều hướng trở lại hoạt động của bạn, hệ thống sẽ tạo lại hoạt động đó và +chuyển {@link android.os.Bundle} cho cả {@link android.app.Activity#onCreate onCreate()} và {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()}. Sử dụng một trong +hai phương pháp này, bạn có thể trích xuất trạng thái đã lưu của mình từ {@link android.os.Bundle} và khôi phục +trạng thái của hoạt động. Nếu không có thông tin trạng thái để khôi phục, khi đó {@link +android.os.Bundle} được chuyển cho bạn sẽ rỗng (là trường hợp khi hoạt động được tạo +lần đầu).</p> + +<img src="{@docRoot}images/fundamentals/restore_instance.png" alt="" /> +<p class="img-caption"><strong>Hình 2.</strong> Hai cách mà theo đó một hoạt động trở về tiêu điểm +của người dùng với trạng thái không thay đổi: hoặc hoạt động bị hủy, rồi tạo lại và hoạt động phải khôi phục +trạng thái đã lưu trước đó, hoặc hoạt động bị dừng, rồi tiếp tục và trạng thái của hoạt động +giữ nguyên không đổi.</p> + +<p class="note"><strong>Lưu ý:</strong> Không có gì bảo đảm rằng {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} sẽ được gọi trước khi hoạt động +của bạn bị hủy, vì có những trường hợp mà sẽ không cần lưu trạng thái +(chẳng hạn như khi người dùng rời bỏ hoạt động của bạn bằng cách sử dụng nút <em>Quay lại</em>, vì người dùng +rõ ràng +đang đóng hoạt động). Nếu hệ thống gọi {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()}, nó làm vậy trước {@link +android.app.Activity#onStop onStop()} và có thể trước cả {@link android.app.Activity#onPause +onPause()}.</p> + +<p>Tuy nhiên, ngay cả khi bạn không làm gì và không triển khai {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}, một phần trạng thái của hoạt động được khôi phục +bởi việc lớp {@link android.app.Activity} triển khai mặc định {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()}. Cụ thể, triển khai +mặc định sẽ gọi phương pháp {@link +android.view.View#onSaveInstanceState onSaveInstanceState()} tương ứng cho mọi {@link +android.view.View} trong bố trí, nó cho phép mỗi chế độ xem cung cấp thông tin về chính nó +mà sẽ được lưu. Gần như mọi widget trong khuôn khổ Android đều triển khai phương pháp này nếu +phù hợp, sao cho mọi thay đổi hiển thị đối với UI đều tự động được lưu và khôi phục khi hoạt động +của bạn được tạo lại. Ví dụ, widget {@link android.widget.EditText} lưu mọi văn bản +do người dùng điền vào và widget {@link android.widget.CheckBox} lưu sẽ thông tin cho dù đã được kiểm tra +hay chưa. Việc duy nhất bạn cần làm đó là cung cấp một ID duy nhất (với thuộc tính <a href="{@docRoot}guide/topics/resources/layout-resource.html#idvalue">{@code android:id}</a> +) cho mỗi widget bạn muốn lưu trạng thái của nó. Nếu một widget không có ID thì hệ thống +không thể lưu trạng thái của nó.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<p>Bạn cũng có thể rõ ràng dừng một chế độ xem trong bố trí của mình khỏi việc lưu trạng thái của nó bằng cách đặt thuộc tính +{@link android.R.attr#saveEnabled android:saveEnabled} thành {@code "false"} hoặc bằng cách gọi +phương pháp {@link android.view.View#setSaveEnabled setSaveEnabled()}. Thường thì bạn không nên +vô hiệu hóa điều này, nhưng có thể làm nếu bạn muốn khôi phục trạng thái của UI hoạt động khác đi.</p> +</div> +</div> + +<p>Mặc dù việc triển khai mặc định {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} lưu thông tin hữu ích về +UI hoạt động của bạn, bạn có thể vẫn cần khống chế nó để lưu thêm thông tin. +Ví dụ, bạn có thể cần lưu các giá trị thành viên đã thay đổi trong vòng đời của hoạt động (mà +có thể tương quan với các giá trị được khôi phục trong UI, nhưng các thành viên nắm giữ giá trị UI đó không được +khôi phục theo mặc định).</p> + +<p>Vì việc triển khai mặc định {@link +android.app.Activity#onSaveInstanceState onSaveInstanceState()} giúp lưu trạng thái của UI, nếu +bạn khống chế phương pháp để lưu thêm thông tin trạng thái, bạn nên luôn luôn gọi +triển khai siêu lớp của {@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} +trước khi thực hiện bất kỳ công việc nào. Tương tự, bạn cũng nên gọi triển khai siêu lớp {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} nếu bạn khống chế nó, để +triển khai mặc định có thể khôi phục các trạng thái xem.</p> + +<p class="note"><strong>Lưu ý:</strong> Vì {@link android.app.Activity#onSaveInstanceState +onSaveInstanceState()} không đảm bảo +sẽ được gọi, bạn chỉ nên sử dụng nó để ghi trạng thái giao thời của hoạt động (trạng thái của +UI)—bạn không nên sử dụng nó để lưu giữ dữ liệu liên tục. Thay vào đó, bạn nên sử dụng {@link +android.app.Activity#onPause onPause()} để lưu giữ dữ liệu liên tục (chẳng hạn như dữ liệu mà nên được lưu +vào một cơ sở dữ liệu) khi người dùng rời bỏ hoạt động.</p> + +<p>Một cách hay để kiểm tra khả năng khôi phục trạng thái của ứng dụng của bạn đó là chỉ cần xoay +thiết bị sao cho hướng màn hình thay đổi. Khi hướng màn hình thay đổi, hệ thống +hủy và tạo lại hoạt động để áp dụng các tài nguyên thay thế mà có thể có sẵn +cho cấu hình màn hình mới. Chỉ với lý do này mà một điều rất quan trọng đó là hoạt động của bạn +hoàn toàn khôi phục trạng thái của mình khi nó được tạo lại, vì người dùng thường xoay màn hình trong khi +sử dụng ứng dụng.</p> + + +<h3 id="ConfigurationChanges">Xử lý thay đổi về cấu hình</h3> + +<p>Một số cấu hình thiết bị có thể thay đổi trong thời gian chạy (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 đó diễn ra, Android tạo lại hoạt động đang chạy +(hệ thống gọi {@link android.app.Activity#onDestroy}, rồi ngay lập tức gọi {@link +android.app.Activity#onCreate onCreate()}). Hành vi này +được thiết kế để giúp ứng dụng của bạn điều chỉnh theo những 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 các tài nguyên thay thế mà bạn đã cung cấp (chẳng hạn như bố trí khác cho +các hướng và kích cỡ màn hình khác).</p> + +<p>Nếu bạn thiết kế hoạt động của mình một cách phù hợp để xử lý khởi động lại do thay đổi hướng màn hình và +khôi phục trạng thái hoạt động như nêu trên, ứng dụng của bạn sẽ linh hoạt hơn trước +những sự kiện bất ngờ khác trong vòng đời của hoạt động.</p> + +<p>Cách tốt nhất để xử lý khởi động lại đó là + lưu và khôi phục trạng thái hoạt động của bạn bằng cách sử dụng {@link + android.app.Activity#onSaveInstanceState onSaveInstanceState()} và {@link +android.app.Activity#onRestoreInstanceState onRestoreInstanceState()} (hoặc {@link +android.app.Activity#onCreate onCreate()}), như đã đề cập trong phần trước.</p> + +<p>Để biết thêm thông tin về những thay đổi cấu hình xảy ra tại thời điểm chạy và cách bạn có thể xử lý +chúng, hãy đọc hướng dẫn <a href="{@docRoot}guide/topics/resources/runtime-changes.html">Xử lý +Thay đổi trong Thời gian chạy</a>.</p> + + + +<h3 id="CoordinatingActivities">Điều phối hoạt động</h3> + + <p>Khi một hoạt động bắt đầu một hoạt động khác, cả hai đều trải qua những chuyển tiếp vòng đời. Hoạt động thứ nhất +tạm dừng và dừng (tuy nhiên, nó sẽ không dừng nếu vẫn hiển thị được dưới nền), trong khi hoạt động kia +được tạo. Trong trường hợp những hoạt động này chia sẻ dữ liệu được lưu vào đĩa hoặc nơi khác, điều quan trọng là +phải hiểu rằng hoạt động thứ nhất không bị dừng hoàn toàn trước khi hoạt động thứ hai được tạo. +Thay vào đó, tiến trình bắt đầu hoạt động thứ hai chồng lấp với tiến trình dừng hoạt động +thứ nhất.</p> + +<p>Thứ tự gọi lại vòng đời được định nghĩa rõ, cụ thể là khi hai hoạt động trong cùng tiến trình +và hoạt động này bắt đầu hoạt động kia. Sau đây là thứ tự thao tác diễn ra khi Hoạt động +A bắt đầu Hoạt động B: </p> + +<ol> +<li>Phương pháp {@link android.app.Activity#onPause onPause()} của Hoạt động A thực thi.</li> + +<li>{@link android.app.Activity#onCreate onCreate()} của Hoạt động B, {@link +android.app.Activity#onStart onStart()}, và các phương pháp {@link android.app.Activity#onResume onResume()} +thực thi theo trình tự. (Hoạt động B lúc này có tiêu điểm của người dùng.)</li> + +<li>Sau đó, nếu Hoạt động A không còn hiển thị trên màn hình, phương pháp {@link +android.app.Activity#onStop onStop()} của nó sẽ thực thi.</li> +</ol> + + <p>Trình tự gọi lại vòng đời có thể dự đoán này cho phép bạn quản lý chuyển tiếp +thông tin từ hoạt động này sang hoạt động khác. Ví dụ, nếu bạn phải ghi vào một cơ sở dữ liệu khi +hoạt động thứ nhất dừng sao cho hoạt động theo sau có thể đọc nó, khi đó bạn nên ghi vào +cơ sở dữ liệu trong khi {@link android.app.Activity#onPause onPause()} thay vì trong khi {@link +android.app.Activity#onStop onStop()}.</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how Android maintains a history of activities and +enables user multitasking, continue with the <b><a +href="{@docRoot}guide/components/tasks-and-back-stack.html">Tasks and Back +Stack</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/vi/guide/components/bound-services.jd b/docs/html-intl/intl/vi/guide/components/bound-services.jd new file mode 100644 index 000000000000..7a2ddbaf6321 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/bound-services.jd @@ -0,0 +1,658 @@ +page.title=Dịch vụ Gắn kết +parent.title=Dịch vụ +parent.link=services.html +@jd:body + + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>Trong tài liệu này</h2> +<ol> + <li><a href="#Basics">Nội dung Cơ bản</a></li> + <li><a href="#Creating">Tạo một Dịch vụ Gắn kết</a> + <ol> + <li><a href="#Binder">Mở rộng lớp Trình gắn kết</a></li> + <li><a href="#Messenger">Sử dụng một Hàm nhắn tin</a></li> + </ol> + </li> + <li><a href="#Binding">Gắn kết với một Dịch vụ</a></li> + <li><a href="#Lifecycle">Quản lý Vòng đời của một Dịch vụ Gắn kết</a></li> +</ol> + +<h2>Lớp khóa</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.content.ServiceConnection}</li> + <li>{@link android.os.IBinder}</li> +</ol> + +<h2>Mẫu</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code + RemoteService}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>Xem thêm</h2> +<ol> + <li><a href="{@docRoot}guide/components/services.html">Dịch vụ</a></li> +</ol> +</div> + + +<p>Dịch vụ gắn kết là máy chủ trong một giao diện máy khách-máy chủ. Dịch vụ gắn kết cho phép các thành phần +(chẳng hạn như các hoạt động) gắn kết với dịch vụ, gửi yêu cầu, nhận phản hồi, và thậm chí thực hiện +truyền thông liên tiến trình (IPC). Dịch vụ gắn kết thường chỉ hoạt động khi nó phục vụ một thành phần +ứng dụng khác và không chạy ngầm mãi liên tục.</p> + +<p>Tài liệu này cho bạn biết cách tạo một dịch vụ gắn kết, bao gồm cách gắn kết +với dịch vụ từ các thành phần ứng dụng khác. Tuy nhiên, bạn cũng nên tham khảo tài liệu <a href="{@docRoot}guide/components/services.html">Dịch vụ</a> để biết thêm thông tin +về các dịch vụ nói chung, chẳng hạn như cách gửi thông báo từ một dịch vụ, đặt +dịch vụ để chạy trong tiền cảnh, và nhiều nội dung khác.</p> + + +<h2 id="Basics">Nội dung Cơ bản</h2> + +<p>Dịch vụ gắn kết là một sự triển khai lớp {@link android.app.Service} cho phép +các ứng dụng khác gắn kết và tương tác với nó. Để thực hiện gắn kết cho một +dịch vụ, bạn phải triển khai phương pháp gọi lại {@link android.app.Service#onBind onBind()}. Phương pháp này +trả về một đối tượng {@link android.os.IBinder} định nghĩa giao diện lập trình mà +các máy khách có thể sử dụng để tương tác với dịch vụ.</p> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Gắn kết với một Dịch vụ được Bắt đầu</h3> + +<p>Như đã đề cập trong tài liệu <a href="{@docRoot}guide/components/services.html">Dịch vụ</a> +, bạn có thể tạo một dịch vụ được bắt đầu và gắn kết. Cụ thể, dịch vụ có thể được +bắt đầu bằng cách gọi {@link android.content.Context#startService startService()}, nó cho phép dịch vụ +chạy mãi, và cũng cho phép một máy khách gắn kết với dịch vụ bằng cách gọi {@link +android.content.Context#bindService bindService()}. + <p>Nếu bạn có cho phép dịch vụ của mình được bắt đầu và gắn kết, thì khi dịch vụ đã được +bắt đầu, hệ thống sẽ <em>không</em> hủy dịch vụ đó khi tất cả máy khách bỏ gắn kết. Thay vào đó, bạn phải +dừng dịch vụ một cách rõ ràng bằng cách gọi {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}.</p> + +<p>Mặc dù bạn nên thường xuyên triển khai hoặc {@link android.app.Service#onBind onBind()} +<em>hoặc</em> {@link android.app.Service#onStartCommand onStartCommand()}, đôi khi cần phải +triển khai cả hai. Ví dụ, một trình chơi nhạc có thể cho rằng nên cho phép dịch vụ của nó chạy +mãi và cũng thực hiện gắn kết. Bằng cách này, một hoạt động có thể bắt đầu dịch vụ để chơi vài +bản nhạc và nhạc tiếp tục chơi ngay cả khi người dùng rời khỏi ứng dụng. Lúc đó, khi người dùng +trở lại ứng dụng, hoạt động có thể gắn kết với dịch vụ để giành lại quyền kiểm soát phát lại.</p> + +<p>Đảm bảo đọc phần về <a href="#Lifecycle">Quản lý Vòng đời của một Dịch vụ +Gắn kết</a> để biết thêm thông tin về vòng đời của dịch vụ khi thêm gắn kết vào một +dịch vụ được bắt đầu.</p> +</div> +</div> + +<p>Một máy khách có thể gắn kết với dịch vụ bằng cách gọi {@link android.content.Context#bindService +bindService()}. Khi làm vậy, nó phải cung cấp việc triển khai {@link +android.content.ServiceConnection}, có chức năng theo dõi kết nối với dịch vụ. Phương pháp {@link +android.content.Context#bindService bindService()} trả về ngay lập tức mà không có giá trị, nhưng +khi hệ thống Android tạo kết nối giữa +máy khách và dịch vụ, nó gọi {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} trên {@link +android.content.ServiceConnection}, để giao {@link android.os.IBinder} mà +máy khách có thể sử dụng để giao tiếp với dịch vụ.</p> + +<p>Nhiều máy khách có thể kết nối với dịch vụ đồng thời. Tuy nhiên, hệ thống sẽ gọi phương pháp +{@link android.app.Service#onBind onBind()} của dịch vụ của bạn để truy xuất {@link android.os.IBinder} chỉ +khi máy khách đầu tiên gắn kết. Sau đó, hệ thống sẽ giao cùng một {@link android.os.IBinder} đó cho bất kỳ +máy khách bổ sung nào có gắn kết mà không gọi lại {@link android.app.Service#onBind onBind()}.</p> + +<p>Khi máy khách cuối cùng bỏ gắn kết với dịch vụ, hệ thống sẽ hủy dịch vụ (trừ khi dịch vụ +cũng được bắt đầu bởi {@link android.content.Context#startService startService()}).</p> + +<p>Khi bạn triển khai dịch vụ gắn kết của mình, phần quan trọng nhất là định nghĩa giao diện +mà phương pháp gọi lại {@link android.app.Service#onBind onBind()} của bạn sẽ trả về. Có một vài +cách khác nhau mà bạn có thể định nghĩa giao diện {@link android.os.IBinder} của dịch vụ của mình và phần +sau đây sẽ bàn về từng kỹ thuật.</p> + + + +<h2 id="Creating">Tạo một Dịch vụ Gắn kết</h2> + +<p>Khi tạo một dịch vụ thực hiện gắn kết, bạn phải nêu một {@link android.os.IBinder} +cung cấp giao diện lập trình mà các máy khách có thể sử dụng để tương tác với dịch vụ. Có +ba cách bạn có thể định nghĩa giao diện:</p> + +<dl> + <dt><a href="#Binder">Mở rộng lớp Trình gắn kết</a></dt> + <dd>Nếu dịch vụ của bạn chỉ riêng cho ứng dụng của chính bạn và chạy trong cùng tiến trình như máy khách +(điều này thường hay gặp), bạn nên tạo giao diện của mình bằng cách mở rộng lớp {@link android.os.Binder} +và trả về một thực thể của nó từ +{@link android.app.Service#onBind onBind()}. Máy khách nhận được {@link android.os.Binder} và +có thể sử dụng nó để trực tiếp truy cập các phương pháp công khai có sẵn trong triển khai {@link android.os.Binder} +hoặc thậm chí trong {@link android.app.Service}. + <p>Nên áp dụng kỹ thuật này khi dịch vụ của bạn chỉ là một trình thực hiện chạy ngầm cho ứng dụng +của chính bạn. Lý do duy nhất bạn không nên tạo giao diện của mình bằng cách này đó là +dịch vụ của bạn được sử dụng bởi các ứng dụng khác hoặc giữa những tiến trình khác nhau.</dd> + + <dt><a href="#Messenger">Sử dụng một Hàm nhắn tin</a></dt> + <dd>Nếu bạn cần giao diện của mình thực hiện các tiến trình khác nhau, bạn có thể tạo +một giao diện cho dịch vụ bằng {@link android.os.Messenger}. Bằng cách này, dịch vụ +định nghĩa một {@link android.os.Handler} phản hồi các loại đối tượng {@link +android.os.Message} khác nhau. {@link android.os.Handler} +này là cơ sở cho một {@link android.os.Messenger} mà sau đó có thể chia sẻ một {@link android.os.IBinder} +với máy khách, cho phép máy khách gửi lệnh tới dịch vụ bằng cách sử dụng các đối tượng {@link +android.os.Message}. Ngoài ra, máy khách có thể định nghĩa {@link android.os.Messenger} của +chính nó để dịch vụ có thể gửi lại thông báo. + <p>Đây là cách đơn giản nhất để thực hiện truyền thông liên tiến trình (IPC), vì {@link +android.os.Messenger} xếp hàng tất cả yêu cầu thành một luồng duy nhất sao cho bạn không phải thiết kế +dịch vụ của mình an toàn với luồng.</p> + </dd> + + <dt>Sử dụng AIDL</dt> + <dd>AIDL (Ngôn ngữ Định nghĩa Giao diện Android) thực hiện tất cả công việc để phân tách đối tượng thành +các phần tử mà hệ điều hành có thể hiểu được và ghép nối chúng qua các tiến trình để thực hiện +IPC. Bằng cách sử dụng {@link android.os.Messenger}, kỹ thuật trước đó thực tế được dựa trên AIDL như là +cấu trúc cơ bản của nó. Như đã đề cập bên trên, {@link android.os.Messenger} tạo một hàng chờ +gồm tất cả yêu cầu của máy khách trong một luồng duy nhất, vì thế dịch vụ nhận được từng yêu cầu một. Tuy nhiên, nếu +bạn muốn dịch vụ xử lý nhiều yêu cầu đồng thời, bạn có thể sử dụng AIDL +trực tiếp. Trong trường hợp này, dịch vụ của bạn phải có khả năng tạo đa luồng và được xây dựng an toàn với luồng. + <p>Để sử dụng AIDL trực tiếp, bạn phải +tạo một tệp {@code .aidl} định nghĩa giao diện lập trình. Các công cụ SDK Android sử dụng tệp +này để khởi tạo một lớp tóm tắt (abstract class) nhằm triển khai giao diện và xử lý IPC, mà sau đó +bạn có thể mở rộng trong dịch vụ của mình.</p> + </dd> +</dl> + + <p class="note"><strong>Lưu ý:</strong> Hầu hết ứng dụng <strong>không nên</strong> sử dụng AIDL để +tạo một dịch vụ gắn kết, vì nó có thể yêu cầu khả năng tạo đa luồng và +có thể dẫn đến việc triển khai phức tạp hơn. Như vậy, AIDL không phù hợp với hầu hết ứng dụng +và tài liệu này không bàn về cách sử dụng nó cho dịch vụ của bạn. Nếu bạn chắc chắn rằng mình cần +sử dụng AIDL trực tiếp, hãy xem tài liệu <a href="{@docRoot}guide/components/aidl.html">AIDL</a> +.</p> + + + + +<h3 id="Binder">Mở rộng lớp Trình gắn kết</h3> + +<p>Nếu dịch vụ của bạn chỉ được sử dụng bởi ứng dụng cục bộ và không cần làm việc qua nhiều tiến trình, +khi đó bạn có thể triển khai lớp {@link android.os.Binder} của chính mình để cung cấp quyền truy cập +trực tiếp cho máy khách của bạn để truy nhập các phương pháp công khai trong dịch vụ.</p> + +<p class="note"><strong>Lưu ý:</strong> Cách này chỉ có tác dụng nếu máy khách và dịch vụ nằm trong cùng +ứng dụng và tiến trình, là trường hợp phổ biến nhất. Ví dụ, cách này sẽ hoạt động tốt đối với một ứng dụng +nhạc cần gắn kết một hoạt động với dịch vụ của chính nó đang phát nhạc +chạy ngầm.</p> + +<p>Sau đây là cách thiết lập:</p> +<ol> + <li>Trong dịch vụ của bạn, hãy tạo một thực thể {@link android.os.Binder} mà hoặc: + <ul> + <li>chứa các phương pháp công khai mà máy khách có thể gọi</li> + <li>trả về thực thể {@link android.app.Service} hiện tại, trong đó có các phương pháp công khai mà +máy khách có thể gọi</li> + <li>hoặc, trả về một thực thể của một lớp khác được lưu trữ bởi dịch vụ bằng các phương pháp công khai mà +máy khách có thể gọi</li> + </ul> + <li>Trả về thực thể {@link android.os.Binder} này từ phương pháp gọi lại {@link +android.app.Service#onBind onBind()}.</li> + <li>Trong máy khách, nhận {@link android.os.Binder} từ phương pháp gọi lại {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()} và +thực hiện gọi tới dịch vụ gắn kết bằng cách sử dụng các phương pháp đã nêu.</li> +</ol> + +<p class="note"><strong>Lưu ý:</strong> Lý do dịch vụ và máy khách phải ở trong cùng +ứng dụng đó là máy khách có thể đổi kiểu đối tượng được trả về và gọi các API của nó một cách phù hợp. Dịch vụ +và máy khách cũng phải ở trong cùng tiến trình, vì kỹ thuật này không thực hiện bất kỳ thao tác +ghép nối qua các tiến trình nào.</p> + +<p>Ví dụ, sau đây là một dịch vụ cung cấp cho máy khách quyền truy cập các phương pháp trong dịch vụ thông qua +việc triển khai {@link android.os.Binder}:</p> + +<pre> +public class LocalService extends Service { + // Binder given to clients + private final IBinder mBinder = new LocalBinder(); + // Random number generator + private final Random mGenerator = new Random(); + + /** + * Class used for the client Binder. Because we know this service always + * runs in the same process as its clients, we don't need to deal with IPC. + */ + public class LocalBinder extends Binder { + LocalService getService() { + // Return this instance of LocalService so clients can call public methods + return LocalService.this; + } + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** method for clients */ + public int getRandomNumber() { + return mGenerator.nextInt(100); + } +} +</pre> + +<p>{@code LocalBinder} cung cấp phương pháp {@code getService()} cho máy khách để truy xuất +thực thể hiện tại của {@code LocalService}. Điều này cho phép máy khách gọi các phương pháp công khai trong +dịch vụ. Ví dụ, máy khách có thể gọi {@code getRandomNumber()} từ dịch vụ.</p> + +<p>Sau đây là một hoạt động gắn kết với {@code LocalService} và sẽ gọi {@code getRandomNumber()} +khi nhấp vào nút:</p> + +<pre> +public class BindingActivity extends Activity { + LocalService mService; + boolean mBound = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to LocalService + Intent intent = new Intent(this, LocalService.class); + bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } + + /** Called when a button is clicked (the button in the layout file attaches to + * this method with the android:onClick attribute) */ + public void onButtonClick(View v) { + if (mBound) { + // Call a method from the LocalService. + // However, if this call were something that might hang, then this request should + // occur in a separate thread to avoid slowing down the activity performance. + int num = mService.getRandomNumber(); + Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show(); + } + } + + /** Defines callbacks for service binding, passed to bindService() */ + private ServiceConnection mConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, + IBinder service) { + // We've bound to LocalService, cast the IBinder and get LocalService instance + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mBound = false; + } + }; +} +</pre> + +<p>Mẫu trên cho thấy cách mà máy khách gắn kết với dịch vụ bằng cách sử dụng triển khai +{@link android.content.ServiceConnection} và gọi lại {@link +android.content.ServiceConnection#onServiceConnected onServiceConnected()}. Phần tiếp theo +cung cấp thêm thông tin về tiến trình gắn kết này với dịch vụ.</p> + +<p class="note"><strong>Lưu ý:</strong> Ví dụ trên không công khai bỏ gắn kết khỏi dịch vụ, +nhưng tất cả máy khách cần bỏ gắn kết tại một thời điểm phù hợp (chẳng hạn như khi hoạt động tạm dừng).</p> + +<p>Để biết thêm mã ví dụ, hãy xem lớp <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code +LocalService.java}</a> và lớp <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalServiceActivities.html">{@code +LocalServiceActivities.java}</a> trong <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + +<h3 id="Messenger">Sử dụng một Hàm nhắn tin</h3> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h4>So sánh với AIDL</h4> + <p>Khi bạn cần thực hiện IPC, việc sử dụng một {@link android.os.Messenger} cho giao diện của bạn +sẽ đơn giản hơn so với việc triển khai nó bằng AIDL, vì {@link android.os.Messenger} xếp hàng +tất cả lệnh gọi đối với dịch vụ, trong khi đó, giao diện AIDL thuần túy sẽ gửi các yêu cầu đồng thời tới +dịch vụ, sau đó dịch vụ phải xử lý tạo đa luồng.</p> + <p>Đối với hầu hết ứng dụng, dịch vụ không cần thực hiện tạo đa luồng, vì vậy sử dụng một {@link +android.os.Messenger} sẽ cho phép dịch vụ xử lý từng lệnh gọi tại một thời điểm. Nếu quan trọng là +dịch vụ của bạn phải được tạo đa luồng, khi đó bạn nên sử dụng <a href="{@docRoot}guide/components/aidl.html">AIDL</a> để định nghĩa giao diện của mình.</p> +</div> +</div> + +<p>Nếu bạn cần dịch vụ của mình giao tiếp với các tiến trình từ xa, khi đó bạn có thể sử dụng một +{@link android.os.Messenger} để cung cấp giao diện cho dịch vụ của mình. Kỹ thuật này cho phép +bạn thực hiện truyền thông liên tiến trình (IPC) mà không cần sử dụng AIDL.</p> + +<p>Sau đây là tóm tắt cách sử dụng {@link android.os.Messenger}:</p> + +<ul> + <li>Dịch vụ triển khai {@link android.os.Handler} để nhận lệnh gọi lại cho mỗi +lệnh gọi từ một máy khách.</li> + <li>{@link android.os.Handler} được sử dụng để tạo một đối tượng {@link android.os.Messenger} +(là một tham chiếu tới {@link android.os.Handler}).</li> + <li>{@link android.os.Messenger} tạo một {@link android.os.IBinder} mà dịch vụ +trả về máy khách từ {@link android.app.Service#onBind onBind()}.</li> + <li>Máy khách sử dụng {@link android.os.IBinder} để khởi tạo {@link android.os.Messenger} +(tham chiếu tới {@link android.os.Handler} của dịch vụ), mà máy khách sử dụng để gửi các đối tượng +{@link android.os.Message} tới dịch vụ.</li> + <li>Dịch vụ nhận được từng {@link android.os.Message} trong {@link +android.os.Handler} của mình—cụ thể là theo phương pháp {@link android.os.Handler#handleMessage +handleMessage()}.</li> +</ul> + + +<p>Theo cách này, không có "phương pháp" nào để máy khách gọi đối với dịch vụ. Thay vào đó, máy khách +gửi “thông báo” (đối tượng {@link android.os.Message}) mà dịch vụ nhận được trong +{@link android.os.Handler} của mình.</p> + +<p>Sau đây là một dịch vụ ví dụ đơn giản sử dụng một giao diện {@link android.os.Messenger}:</p> + +<pre> +public class MessengerService extends Service { + /** Command to the service to display a message */ + static final int MSG_SAY_HELLO = 1; + + /** + * Handler of incoming messages from clients. + */ + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SAY_HELLO: + Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show(); + break; + default: + super.handleMessage(msg); + } + } + } + + /** + * Target we publish for clients to send messages to IncomingHandler. + */ + final Messenger mMessenger = new Messenger(new IncomingHandler()); + + /** + * When binding to the service, we return an interface to our messenger + * for sending messages to the service. + */ + @Override + public IBinder onBind(Intent intent) { + Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); + return mMessenger.getBinder(); + } +} +</pre> + +<p>Để ý rằng phương pháp {@link android.os.Handler#handleMessage handleMessage()} trong +{@link android.os.Handler} là nơi dịch vụ nhận được {@link android.os.Message} +đến và quyết định việc cần làm dựa trên thành viên {@link android.os.Message#what}.</p> + +<p>Tất cả việc mà một máy khách cần làm đó là tạo một {@link android.os.Messenger} dựa trên {@link +android.os.IBinder} được dịch vụ trả về và gửi một thông báo bằng cách sử dụng {@link +android.os.Messenger#send send()}. Ví dụ, sau đây là một hoạt động đơn giản gắn kết với dịch vụ +và gửi tin nhắn {@code MSG_SAY_HELLO} cho dịch vụ:</p> + +<pre> +public class ActivityMessenger extends Activity { + /** Messenger for communicating with the service. */ + Messenger mService = null; + + /** Flag indicating whether we have called bind on the service. */ + boolean mBound; + + /** + * Class for interacting with the main interface of the service. + */ + private ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + // This is called when the connection with the service has been + // established, giving us the object we can use to + // interact with the service. We are communicating with the + // service using a Messenger, so here we get a client-side + // representation of that from the raw IBinder object. + mService = new Messenger(service); + mBound = true; + } + + public void onServiceDisconnected(ComponentName className) { + // This is called when the connection with the service has been + // unexpectedly disconnected -- that is, its process crashed. + mService = null; + mBound = false; + } + }; + + public void sayHello(View v) { + if (!mBound) return; + // Create and send a message to the service, using a supported 'what' value + Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0); + try { + mService.send(msg); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } + + @Override + protected void onStart() { + super.onStart(); + // Bind to the service + bindService(new Intent(this, MessengerService.class), mConnection, + Context.BIND_AUTO_CREATE); + } + + @Override + protected void onStop() { + super.onStop(); + // Unbind from the service + if (mBound) { + unbindService(mConnection); + mBound = false; + } + } +} +</pre> + +<p>Để ý rằng ví dụ này không cho biết cách mà dịch vụ có thể phản hồi máy khách. Nếu bạn muốn dịch vụ +phản hồi, khi đó bạn cũng cần tạo một {@link android.os.Messenger} trong máy khách. Sau đó +khi máy khách nhận được lệnh gọi lại {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()}, nó sẽ gửi một {@link android.os.Message} tới dịch vụ, trong đó bao gồm +{@link android.os.Messenger} của máy khách trong tham số {@link android.os.Message#replyTo} +của phương pháp {@link android.os.Messenger#send send()}.</p> + +<p>Bạn có thể xem một ví dụ về cách cung cấp tính năng nhắn tin hai chiều trong <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerService.html">{@code +MessengerService.java}</a> (dịch vụ) và các mẫu <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/MessengerServiceActivities.html">{@code +MessengerServiceActivities.java}</a> (máy khách).</p> + + + + + +<h2 id="Binding">Gắn kết với một Dịch vụ</h2> + +<p>Các thành phần ứng dụng (máy khách) có thể gắn kết với một dịch vụ bằng cách gọi +{@link android.content.Context#bindService bindService()}. Hệ thống Android +khi đó sẽ gọi phương pháp {@link android.app.Service#onBind +onBind()} của dịch vụ, nó trả về một {@link android.os.IBinder} để tương tác với dịch vụ.</p> + +<p>Việc gắn kết diễn ra không đồng bộ. {@link android.content.Context#bindService +bindService()} trả về ngay lập tức và <em>không</em> trả {@link android.os.IBinder} về +máy khách. Để nhận một {@link android.os.IBinder}, máy khách phải tạo một thực thể của {@link +android.content.ServiceConnection} và chuyển nó cho {@link android.content.Context#bindService +bindService()}. {@link android.content.ServiceConnection} bao gồm một phương pháp gọi lại mà hệ thống +gọi để gửi {@link android.os.IBinder}.</p> + +<p class="note"><strong>Lưu ý:</strong> Chỉ các hoạt động, dịch vụ, và trình cung cấp nội dung mới có thể gắn kết +với một dịch vụ—bạn <strong>không thể</strong> gắn kết với một dịch vụ từ một hàm nhận quảng bá (broadcast receiver).</p> + +<p>Vì vậy, để gắn kết với một dịch vụ từ máy khách của mình, bạn phải: </p> +<ol> + <li>Triển khai {@link android.content.ServiceConnection}. + <p>Việc triển khai của bạn phải khống chế hai phương pháp gọi lại:</p> + <dl> + <dt>{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}</dt> + <dd>Hệ thống gọi phương pháp này để gửi {@link android.os.IBinder} được trả về bởi +phương pháp {@link android.app.Service#onBind onBind()} của dịch vụ.</dd> + <dt>{@link android.content.ServiceConnection#onServiceDisconnected +onServiceDisconnected()}</dt> + <dd>Hệ thống Android gọi phương pháp này khi kết nối với dịch vụ bị mất +đột ngột, chẳng hạn như khi dịch vụ bị lỗi hoặc bị tắt bỏ. Phương pháp này <em>không</em> được gọi khi +máy khách bỏ gắn kết.</dd> + </dl> + </li> + <li>Gọi {@link +android.content.Context#bindService bindService()}, chuyển việc triển khai {@link +android.content.ServiceConnection}. </li> + <li>Khi hệ thống gọi phương pháp gọi lại {@link android.content.ServiceConnection#onServiceConnected +onServiceConnected()} của bạn, bạn có thể bắt đầu thực hiện các lệnh gọi tới dịch vụ bằng các phương pháp +được định nghĩa bởi giao diện.</li> + <li>Để ngắt kết nối khỏi dịch vụ, hãy gọi {@link +android.content.Context#unbindService unbindService()}. + <p>Khi máy khách của bạn bị hủy, nó sẽ bỏ gắn kết khỏi dịch vụ, nhưng bạn nên luôn bỏ gắn kết +khi bạn đã tương tác xong với dịch vụ hoặc khi hoạt động của bạn tạm dừng sao cho dịch vụ có thể +tắt khi không dùng đến. (Thời điểm phù hợp để gắn kết và bỏ gắn kết được đề cập +kỹ hơn ở bên dưới.)</p> + </li> +</ol> + +<p>Ví dụ, đoạn mã HTML sau sẽ kết nối máy khách với dịch vụ được tạo bên trên bằng cách +<a href="#Binder">mở rộng lớp Trình gắn kết</a>, vì vậy tất cả những việc mà nó phải làm là đổi kiểu +{@link android.os.IBinder} được trả về thành lớp {@code LocalService} và yêu cầu thực thể {@code +LocalService}:</p> + +<pre> +LocalService mService; +private ServiceConnection mConnection = new ServiceConnection() { + // Called when the connection with the service is established + public void onServiceConnected(ComponentName className, IBinder service) { + // Because we have bound to an explicit + // service that is running in our own process, we can + // cast its IBinder to a concrete class and directly access it. + LocalBinder binder = (LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + // Called when the connection with the service disconnects unexpectedly + public void onServiceDisconnected(ComponentName className) { + Log.e(TAG, "onServiceDisconnected"); + mBound = false; + } +}; +</pre> + +<p>Với {@link android.content.ServiceConnection} này, máy khách có thể gắn kết với một dịch vụ bằng cách chuyển +nó cho {@link android.content.Context#bindService bindService()}. Ví dụ:</p> + +<pre> +Intent intent = new Intent(this, LocalService.class); +bindService(intent, mConnection, Context.BIND_AUTO_CREATE); +</pre> + +<ul> + <li>Tham số đầu tiên của {@link android.content.Context#bindService bindService()} là một +{@link android.content.Intent} trong đó nêu rõ tên của các dịch vụ sẽ gắn kết (mặc dù ý định +có thể ngầm hiểu).</li> +<li>Tham số thứ hai là đối tượng {@link android.content.ServiceConnection}.</li> +<li>Tham số thứ ba là một cờ cho biết các tùy chọn cho gắn kết. Nên luôn luôn là {@link +android.content.Context#BIND_AUTO_CREATE} để tạo dịch vụ nếu nó chưa hoạt động. +Các giá trị có thể khác là {@link android.content.Context#BIND_DEBUG_UNBIND} +và {@link android.content.Context#BIND_NOT_FOREGROUND}, hoặc {@code 0} trong trường hợp không có.</li> +</ul> + + +<h3>Lưu ý bổ sung</h3> + +<p>Sau đây là một số lưu ý quan trọng về việc gắn kết với một dịch vụ:</p> +<ul> + <li>Bạn nên luôn bẫy các lỗi ngoại lệ {@link android.os.DeadObjectException} phát sinh khi +kết nối bị đứt. Đây là lỗi ngoại lệ duy nhất phát sinh bởi các phương pháp từ xa.</li> + <li>Các đối tượng được xem là tham chiếu khắp các tiến trình. </li> + <li>Bạn nên luôn ghép đôi gắn kết và bỏ gắn kết trong khi +khớp những khoảnh khắc kết nối và đứt kết nối trong vòng đời của máy khách. Ví dụ: + <ul> + <li>Nếu bạn chỉ cần tương tác với dịch vụ trong khi hoạt động của bạn hiển thị, bạn +nên gắn kết trong khi {@link android.app.Activity#onStart onStart()} và bỏ gắn kết trong khi {@link +android.app.Activity#onStop onStop()}.</li> + <li>Nếu bạn muốn hoạt động của mình nhận được phản hồi ngay cả trong khi bị dừng khi đang +dưới nền, khi đó bạn có thể gắn kết trong khi {@link android.app.Activity#onCreate onCreate()} và bỏ gắn kết +trong khi {@link android.app.Activity#onDestroy onDestroy()}. Chú ý rằng điều này hàm ý rằng hoạt động +của bạn cần sử dụng dịch vụ trong toàn bộ thời gian khi nó đang chạy (ngay cả khi chạy ngầm), do đó nếu +dịch vụ ở trong một tiến trình khác thì bạn hãy tăng trọng số của tiến trình và khả năng hệ thống +tắt bỏ tiến trình đó sẽ cao hơn.</li> + </ul> + <p class="note"><strong>Lưu ý:</strong> Thông thường bạn <strong>không</strong> nên gắn kết và bỏ gắn kết +trong khi {@link android.app.Activity#onResume onResume()} và {@link +android.app.Activity#onPause onPause()} cho hoạt động của mình, vì những lệnh gọi lại này diễn ra tại mọi thời điểm chuyển tiếp vòng đời +và bạn nên duy trì xử lý tại những thời điểm chuyển tiếp này ở mức tối thiểu. Đồng thời, nếu +nhiều hoạt động trong ứng dụng của bạn gắn kết với cùng dịch vụ và có sự chuyển tiếp giữa +hai trong số những hoạt động đó, dịch vụ có thể bị hủy và tạo lại khi hoạt động hiện tại bỏ gắn kết +(trong khi tạm dừng) trước khi hoạt động tiếp theo gắn kết (trong khi tiếp tục). (Sự chuyển tiếp hoạt động này đối với cách mà các hoạt động +phối hợp vòng đời của chúng được mô tả trong tài liệu <a href="{@docRoot}guide/components/activities.html#CoordinatingActivities">Hoạt động</a> +.)</p> +</ul> + +<p>Để biết thêm mã ví dụ, thể hiện cách gắn kết với một dịch vụ, hãy xem lớp <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html">{@code +RemoteService.java}</a> trong <a href="{@docRoot}resources/samples/ApiDemos/index.html">ApiDemos</a>.</p> + + + + + +<h2 id="Lifecycle">Quản lý Vòng đời của một Dịch vụ Gắn kết</h2> + +<p>Khi một dịch vụ bị bỏ gắn kết khỏi tất cả máy khách, hệ thống Android sẽ hủy nó (trừ khi nó cũng +được bắt đầu bằng {@link android.app.Service#onStartCommand onStartCommand()}). Như vậy, bạn không phải + quản lý vòng đời dịch vụ của mình nếu nó thuần túy là một +dịch vụ gắn kết—hệ thống Android sẽ quản lý nó cho bạn dựa trên việc nó có gắn kết với bất kỳ máy khách nào không.</p> + +<p>Tuy nhiên, nếu bạn chọn triển khai phương pháp gọi lại {@link android.app.Service#onStartCommand +onStartCommand()}, vậy thì bạn phải dừng dịch vụ một cách tường minh, vì dịch vụ +lúc này đang được coi là <em>được bắt đầu</em>. Trong trường hợp này, dịch vụ sẽ chạy cho tới khi dịch vụ +tự dừng bằng {@link android.app.Service#stopSelf()} hoặc một thành phần khác sẽ gọi {@link +android.content.Context#stopService stopService()}, bất kể nó có gắn kết với bất kỳ máy khách +nào không.</p> + +<p>Ngoài ra, nếu dịch vụ của bạn được bắt đầu và chấp nhận gắn kết, lúc đó khi hệ thống gọi +phương pháp {@link android.app.Service#onUnbind onUnbind()} của bạn, bạn có thể tùy chọn trả về +{@code true} nếu bạn muốn nhận một lệnh gọi tới {@link android.app.Service#onRebind +onRebind()} vào lần tới khi một máy khách gắn kết với dịch vụ (thay vì nhận một lệnh gọi tới {@link +android.app.Service#onBind onBind()}). {@link android.app.Service#onRebind +onRebind()} sẽ trả về rỗng, nhưng máy khách vẫn nhận được {@link android.os.IBinder} trong gọi lại +{@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} của mình. +Hình 1 bên dưới minh họa lô-gic cho loại vòng đời này.</p> + + +<img src="{@docRoot}images/fundamentals/service_binding_tree_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Hình 1.</strong> Vòng đời của một dịch vụ được bắt đầu +và cũng cho phép gắn kết.</p> + + +<p>Để biết thêm thông tin về vòng đời của một dịch vụ được bắt đầu, hãy xem tài liệu <a href="{@docRoot}guide/components/services.html#Lifecycle">Dịch vụ</a>.</p> + + + + diff --git a/docs/html-intl/intl/vi/guide/components/fragments.jd b/docs/html-intl/intl/vi/guide/components/fragments.jd new file mode 100644 index 000000000000..95d9c76337fc --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/fragments.jd @@ -0,0 +1,812 @@ +page.title=Phân đoạn +parent.title=Hoạt động +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + <h2>Trong tài liệu này</h2> + <ol> + <li><a href="#Design">Triết lý Thiết kế</a></li> + <li><a href="#Creating">Tạo một Phân đoạn</a> + <ol> + <li><a href="#UI">Thêm một giao diện người dùng</a></li> + <li><a href="#Adding">Thêm một phân đoạn vào một hoạt động</a></li> + </ol> + </li> + <li><a href="#Managing">Quản lý Phân đoạn</a></li> + <li><a href="#Transactions">Thực hiện Giao tác Phân đoạn</a></li> + <li><a href="#CommunicatingWithActivity">Giao tiếp với Hoạt động</a> + <ol> + <li><a href="#EventCallbacks">Tạo gọi lại sự kiện cho hoạt động</a></li> + <li><a href="#ActionBar">Thêm mục vào Thanh Hành động</a></li> + </ol> + </li> + <li><a href="#Lifecycle">Xử lý Vòng đời của Phân đoạn</a> + <ol> + <li><a href="#CoordinatingWithActivity">Phối hợp với vòng đời của hoạt động</a></li> + </ol> + </li> + <li><a href="#Example">Ví dụ</a></li> + </ol> + + <h2>Lớp khóa</h2> + <ol> + <li>{@link android.app.Fragment}</li> + <li>{@link android.app.FragmentManager}</li> + <li>{@link android.app.FragmentTransaction}</li> + </ol> + + <h2>Xem thêm</h2> + <ol> + <li><a href="{@docRoot}training/basics/fragments/index.html">Xây dựng một UI Động bằng các Phân đoạn</a></li> + <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Hỗ trợ Máy tính bảng +và Thiết bị cầm tay</a></li> + </ol> +</div> +</div> + +<p>{@link android.app.Fragment} biểu diễn một hành vi hay một phần giao diện người dùng trong một +{@link android.app.Activity}. Bạn có thể kết hợp nhiều phân đoạn trong một hoạt động duy nhất để xây dựng một +UI nhiều bảng và sử dụng lại phân đoạn trong nhiều hoạt động. Bạn có thể coi phân đoạn như là một +phần mô-đun của một hoạt động, có vòng đời của chính nó, nhận các sự kiện đầu vào của chính nó, và +bạn có thể thêm hoặc gỡ bỏ trong khi hoạt động đang chạy (kiểu như một "hoạt động con" mà +bạn có thể sử dụng lại trong các hoạt động khác nhau).</p> + +<p>Phân đoạn phải luôn được nhúng trong một hoạt động và vòng đời của phân đoạn bị ảnh hưởng trực tiếp bởi +vòng đời của hoạt động chủ. Ví dụ, khi hoạt động bị tạm dừng, tất cả +phân đoạn trong nó cũng vậy, và khi hoạt động bị hủy, tất cả phân đoạn cũng vậy. Tuy nhiên, trong khi một +hoạt động đang chạy (nó ở trong trạng thái vòng đời <em>được tiếp tục</em><a href="{@docRoot}guide/components/activities.html#Lifecycle"></a>), bạn có thể +thao tác từng phân đoạn độc lập, chẳng hạn như thêm hay xóa chúng. Khi bạn thực hiện một +giao tác phân đoạn, bạn cũng có thể thêm nó vào một ngăn xếp được quản lý bởi +hoạt động đó—từng mục nhập vào ngăn xếp trong hoạt động là một bản ghi giao tác phân đoạn +đã xảy ra. Ngăn xếp cho phép người dùng đảo ngược một giao tác phân đoạn (điều hướng ngược lại), +bằng cách nhấn nút <em>Quay lại</em>.</p> + +<p>Khi bạn thêm một phân đoạn như một phần trong bố trí hoạt động của mình, nó sẽ ở trong một {@link +android.view.ViewGroup} bên trong phân cấp dạng xem của hoạt động đó và phân đoạn này sẽ định nghĩa bố trí +dạng xem của chính nó. +Bạn có thể chèn một phân đoạn vào bố trí hoạt động của mình bằng cách khai báo phân đoạn trong tệp +bố trí của hoạt động, dưới dạng một phần tử {@code <fragment>}, hoặc từ mã ứng dụng của bạn bằng cách thêm nó vào một +{@link android.view.ViewGroup} hiện hữu. Tuy nhiên, không bắt buộc phải có một phân đoạn là một bộ phận của bố trí hoạt động +; bạn cũng có thể sử dụng một phân đoạn mà không cần UI của chính nó như một trình thực hiện vô hình cho hoạt động +.</p> + +<p>Tài liệu này mô tả cách xây dựng ứng dụng của bạn để sử dụng phân đoạn, bao gồm +cách các phân đoạn có thể duy trì trạng thái của chúng khi được thêm vào ngăn xếp của hoạt động, chia sẻ +các sự kiện với hoạt động và các phân đoạn khác trong hoạt động, đóng góp vào thanh hành động của hoạt động +và nhiều thông tin khác.</p> + + +<h2 id="Design">Triết lý Thiết kế</h2> + +<p>Android giới thiệu phân đoạn trong phiên bản Android 3.0 (API mức 11), chủ yếu nhằm hỗ trợ +các thiết kế UI động và linh hoạt hơn trên màn hình lớn, chẳng hạn như máy tính bảng. Vì +màn hình của máy tính bảng lớn hơn nhiều màn hình của thiết bị cầm tay, có nhiều khoảng trống hơn để kết hợp và +trao đổi các thành phần UI. Phân đoạn cho phép những thiết kế như vậy mà không cần bạn phải quản lý những thay đổi +phức tạp về phân cấp dạng xem. Bằng cách chia bố trí của một hoạt động thành các phân đoạn, bạn có thể +sửa đổi diện mạo của hoạt động vào thời gian chạy và giữ những thay đổi đó trong một ngăn xếp +được quản lý bởi hoạt động.</p> + +<p>Ví dụ, một ứng dụng tin tức có thể sử dụng một phân đoạn để hiển thị một danh sách bài viết ở +bên trái và một phân đoạn khác để hiển thị một bài viết ở bên phải—cả hai phân đoạn đều xuất hiện trong một +hoạt động, bên cạnh nhau, và từng phân đoạn có tập phương pháp gọi lại vòng đời riêng và xử lý +các sự kiện nhập liệu người dùng riêng của mình. Vì thế, thay vì sử dụng một hoạt động để chọn một bài viết và một +hoạt động khác để đọc bài viết, người dùng có thể chọn một bài viết và đọc nó trong cùng +hoạt động, như được minh họa trong bố trí máy tính bảng trong hình 1.</p> + +<p>Bạn nên thiết kế từng phân đoạn như một thành phần hoạt động dạng mô-đun và có thể sử dụng lại. Đó là bởi +mỗi phân đoạn sẽ định nghĩa bố trí và hành vi của chính nó với các phương pháp gọi lại vòng đời của chính nó, bạn có thể +bao gồm một phân đoạn trong nhiều hoạt động, vì thế bạn nên thiết kế để tái sử dụng và tránh trực tiếp +thao tác một phân đoạn từ một phân đoạn khác. Điều này đặc biệt quan trọng vì một phân đoạn +mô-đun cho phép bạn thay đổi kết hợp phân đoạn của mình cho các kích cỡ màn hình khác nhau. Khi thiết kế +ứng dụng của bạn để hỗ trợ cả máy tính bảng và thiết bị cầm tay, bạn có thể sử dụng lại phân đoạn của mình trong các cấu hình +bố trí khác nhau nhằm tối ưu hóa trải nghiệm người dùng dựa trên không gian màn hình có sẵn. Ví +dụ, trên một thiết bị cầm tay, có thể cần phải tách riêng các phân đoạn để cung cấp một UI đơn bảng khi mà +không thể làm vừa khít nhiều hơn một phân đoạn trong cùng hoạt động.</p> + +<img src="{@docRoot}images/fundamentals/fragments.png" alt="" /> +<p class="img-caption"><strong>Hình 1.</strong> Ví dụ về cách hai mô-đun UI được định nghĩa +bởi các phân đoạn có thể được kết hợp thành một hoạt động đối với thiết kế máy tính bảng, nhưng được tách riêng đối với +thiết kế thiết bị cầm tay.</p> + +<p>Ví dụ—để tiếp tục với ví dụ về ứng dụng tin tức—ứng dụng có thể nhúng +hai phân đoạn trong <em>Hoạt động A</em>, khi đang chạy trên một thiết bị có kích cỡ máy tính bảng. Tuy nhiên, trên một +màn hình kích cỡ thiết bị cầm tay, không có đủ khoảng trống cho cả hai phân đoạn, vì thế <em>Hoạt động A</em> chỉ +bao gồm phân đoạn cho danh sách bài viết, và khi người dùng chọn một bài viết, nó sẽ khởi động +<em>Hoạt động B</em>, hoạt động này chứa phân đoạn thứ hai là đọc bài viết. Vì thế, ứng dụng +hỗ trợ cả máy tính bảng và thiết bị cầm tay bằng cách sử dụng lại các phân đoạn theo các cách kết hợp khác nhau như được minh họa trong +hình 1.</p> + +<p>Để biết thêm thông tin về việc thiết kế ứng dụng của bạn bằng các cách kết hợp phân đoạn khác nhau cho +cấu hình màn hình khác nhau, hãy xem hướng dẫn <a href="{@docRoot}guide/practices/tablets-and-handsets.html">Hỗ trợ Máy tính bảng và Thiết bị cầm tay</a>.</p> + + + +<h2 id="Creating">Tạo một Phân đoạn</h2> + +<div class="figure" style="width:327px"> +<img src="{@docRoot}images/fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Hình 2.</strong> Vòng đời của một phân đoạn (trong khi hoạt động +của nó đang chạy).</p> +</div> + +<p>Để tạo một phân đoạn, bạn phải tạo một lớp con của {@link android.app.Fragment} (hoặc +một lớp con hiện tại của nó). Lớp {@link android.app.Fragment} có mã trông rất giống +một {@link android.app.Activity}. Nó chứa các phương pháp gọi lại tương tự như hoạt động, chẳng +hạn như {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, +{@link android.app.Fragment#onPause onPause()}, và {@link android.app.Fragment#onStop onStop()}. Trên +thực tế, nếu bạn đang chuyển đổi một ứng dụng Android hiện tại để sử dụng các phân đoạn, bạn có thể chỉ cần di chuyển +mã khỏi các phương pháp gọi lại của hoạt động của bạn vào các phương pháp gọi lại tương ứng của phân đoạn +của bạn.</p> + +<p>Thường thì ít nhất bạn nên triển khai các phương pháp vòng đời sau:</p> + +<dl> + <dt>{@link android.app.Fragment#onCreate onCreate()}</dt> + <dd>Hệ thống sẽ gọi phương pháp này khi tạo phân đoạn. Trong triển khai của mình, bạn nên +khởi tạo các thành phần thiết yếu của phân đoạn mà bạn muốn giữ lại khi phân đoạn +bị tạm dừng hoặc dừng hẳn, sau đó tiếp tục.</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>Hệ thống sẽ gọi phương pháp này khi đến lúc phân đoạn vẽ giao diện người dùng của nó +lần đầu tiên. Để vẽ một UI cho phân đoạn của mình, bạn phải trả về một {@link android.view.View} từ phương pháp +này, đây là gốc của bố trí phân đoạn của bạn. Bạn có thể trả về giá trị rỗng nếu phân đoạn không +cung cấp UI.</dd> + <dt>{@link android.app.Activity#onPause onPause()}</dt> + <dd>Hệ thống gọi phương pháp này là dấu hiệu đầu tiên về việc người dùng đang rời khỏi +phân đoạn (mặc dù không phải lúc nào cũng có nghĩa rằng phân đoạn đang bị hủy). Trường hợp này thường là khi bạn +định thực hiện bất kỳ thay đổi nào vẫn cần có hiệu lực ngoài phiên của người dùng hiện thời (vì +người dùng có thể không quay lại).</dd> +</dl> + +<p>Phần lớn ứng dụng nên triển khai ít nhất ba phương pháp sau đối với mọi phân đoạn, nhưng có một vài +phương pháp gọi lại khác mà bạn cũng nên sử dụng để xử lý các giai đoạn khác nhau trong +vòng đời của phân đoạn. Tất cả phương pháp gọi lại vòng đời được đề cập chi tiết hơn trong phần +về <a href="#Lifecycle">Xử lý Vòng đời của Phân đoạn</a>.</p> + + +<p>Cũng có một vài lớp con mà bạn có thể muốn mở rộng thay vì lớp cơ bản {@link +android.app.Fragment}:</p> + +<dl> + <dt>{@link android.app.DialogFragment}</dt> + <dd>Hiển thị một hộp thoại trôi nổi. Sử dụng lớp này để tạo một hộp thoại là một phương án hay cho việc sử dụng các phương pháp trình trợ giúp +hộp thoại trong lớp {@link android.app.Activity}, vì bạn có thể +kết hợp một hộp thoại phân đoạn vào ngăn xếp của các phân đoạn được quản lý bởi hoạt động, +cho phép người dùng trả về một phân đoạn bị bỏ.</dd> + + <dt>{@link android.app.ListFragment}</dt> + <dd>Hiển thị một danh sách các mục được quản lý bởi một trình điều hợp (chẳng hạn như một {@link +android.widget.SimpleCursorAdapter}), tương tự như {@link android.app.ListActivity}. Nó cung cấp +một vài phương pháp để quản lý một dạng xem danh sách, chẳng hạn như phương pháp gọi lại {@link +android.app.ListFragment#onListItemClick(ListView,View,int,long) onListItemClick()} để +xử lý các sự kiện nhấp.</dd> + + <dt>{@link android.preference.PreferenceFragment}</dt> + <dd>Hiển thị một phân cấp các đối tượng {@link android.preference.Preference} dưới dạng một danh sách, tương tự như +{@link android.preference.PreferenceActivity}. Điều này hữu ích khi tạo một hoạt động "thiết đặt" +cho ứng dụng của bạn.</dd> +</dl> + + +<h3 id="UI">Thêm một giao diện người dùng</h3> + +<p>Phân đoạn thường được sử dụng như một phần giao diện người dùng của hoạt động và đóng góp bố trí của +chính nó cho hoạt động.</p> + +<p>Để cung cấp một bố trí cho một phân đoạn, bạn phải triển khai phương pháp gọi lại {@link +android.app.Fragment#onCreateView onCreateView()}, phương pháp này được hệ thống Android gọi +khi đến lúc phân đoạn vẽ bố trí của nó. Việc bạn triển khai phương pháp này phải trả về một +{@link android.view.View} là phần gốc cho bố trí phân đoạn của bạn.</p> + +<p class="note"><strong>Lưu ý:</strong> Nếu phân đoạn của bạn là một lớp con của {@link +android.app.ListFragment}, triển khai mặc định sẽ trả về một {@link android.widget.ListView} từ +{@link android.app.Fragment#onCreateView onCreateView()}, vì thế bạn không cần triển khai nó.</p> + +<p>Để trả về một bố trí từ {@link +android.app.Fragment#onCreateView onCreateView()}, bạn có thể bung nó từ một <a href="{@docRoot}guide/topics/resources/layout-resource.html">tài nguyên bố trí</a> được định nghĩa trong XML. Để +giúp bạn làm vậy, {@link android.app.Fragment#onCreateView onCreateView()} cung cấp một đối tượng +{@link android.view.LayoutInflater}.</p> + +<p>Ví dụ, sau đây là một lớp con của {@link android.app.Fragment} với chức năng nạp một bố trí từ tệp +{@code example_fragment.xml}:</p> + +<pre> +public static class ExampleFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.example_fragment, container, false); + } +} +</pre> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Tạo một bố trí</h3> + <p>Trong ví dụ trên, {@code R.layout.example_fragment} là một tham chiếu tới tài nguyên bố trí +có tên {@code example_fragment.xml} được lưu trong tài nguyên ứng dụng. Để biết thông tin về cách +tạo một bố trí trong XML, hãy xem tài liệu <a href="{@docRoot}guide/topics/ui/index.html">Giao diện Người dùng</a> +.</p> +</div> +</div> + +<p>Tham số {@code container} được chuyển tới {@link android.app.Fragment#onCreateView +onCreateView()} là {@link android.view.ViewGroup} mẹ (tức bố trí của hoạt động), trong đó +bố trí phân đoạn của bạn +sẽ được chèn vào. Tham số {@code savedInstanceState} là một {@link android.os.Bundle} có chức năng +cung cấp dữ liệu về thực thể trước đó của phân đoạn, nếu phân đoạn đang được tiếp tục +(việc khôi phục trạng thái được bàn kỹ hơn trong phần về <a href="#Lifecycle">Xử lý +Vòng đời của Phân đoạn</a>).</p> + +<p>Phương pháp {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} có +ba tham đối:</p> +<ul> + <li>ID tài nguyên của bố trí mà bạn muốn bung.</li> + <li>{@link android.view.ViewGroup} là mẹ của bố trí được bung. Việc chuyển {@code +container} có vai trò quan trọng để hệ thống áp dụng các tham số bố trí cho dạng xem gốc của bố trí +được bung, được quy định bởi dạng xem mẹ là nơi mà nó diễn ra trong đó.</li> + <li>Một boolean cho biết bố trí được bung có nên được gắn với {@link +android.view.ViewGroup} (tham số thứ hai) trong khi bung hay không. (Trong trường hợp này, điều này là +sai vì hệ thống đã đang chèn bố trí được bung vào {@code +container}—việc chuyển đúng sẽ tạo ra một nhóm dạng xem thừa trong bố trí cuối cùng.)</li> +</ul> + +<p>Giờ bạn đã thấy cách tạo một phân đoạn nhằm cung cấp một bố trí. Tiếp theo, bạn cần thêm +phân đoạn vào hoạt động của mình.</p> + + + +<h3 id="Adding">Thêm một phân đoạn vào một hoạt động</h3> + +<p>Thường thì một phân đoạn đóng góp một phần UI vào hoạt động chủ, nó được nhúng như một phần +trong phân cấp dạng xem tổng thể của hoạt động. Có hai cách mà bạn có thể thêm một phân đoạn vào bố trí +của hoạt động:</p> + +<ul> + <li><b>Khai báo phân đoạn bên trong tệp bố trí của hoạt động.</b> +<p>Trong trường hợp này, bạn có thể +chỉ định các tính chất bố trí cho phân đoạn như thể nó là một dạng xem. Ví dụ, sau đây là tệp bố trí +cho một hoạt động có hai phân đoạn:</p> +<pre> +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <fragment android:name="com.example.news.ArticleListFragment" + android:id="@+id/list" + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent" /> + <fragment android:name="com.example.news.ArticleReaderFragment" + android:id="@+id/viewer" + android:layout_weight="2" + android:layout_width="0dp" + android:layout_height="match_parent" /> +</LinearLayout> +</pre> + <p>Thuộc tính {@code android:name} trong {@code <fragment>} sẽ chỉ định lớp {@link +android.app.Fragment} để khởi tạo trong bố trí.</p> + +<p>Khi hệ thống tạo bố trí hoạt động này, nó sẽ khởi tạo từng phân đoạn được chỉ định trong bố trí +và gọi ra phương pháp {@link android.app.Fragment#onCreateView onCreateView()} cho từng phân đoạn, +để truy xuất bố trí của từng phân đoạn. Hệ thống sẽ chèn {@link android.view.View} được trả về bởi phân đoạn +trực tiếp thế chỗ phần tử {@code <fragment>}.</p> + +<div class="note"> + <p><strong>Lưu ý:</strong> Mỗi phân đoạn yêu cầu một mã định danh duy nhất +mà hệ thống có thể sử dụng để khôi phục phân đoạn nếu hoạt động bị khởi động lại (và bạn có thể sử dụng để +nắm bắt phân đoạn sẽ thực hiện giao tác, chẳng hạn như gỡ bỏ nó). Có ba cách để cung cấp ID cho một +phân đoạn:</p> + <ul> + <li>Cung cấp thuộc tính {@code android:id} với một ID duy nhất.</li> + <li>Cung cấp thuộc tính {@code android:tag} với một xâu duy nhất.</li> + <li>Nếu bạn không cung cấp được thuộc tính nào, hệ thống sẽ sử dụng ID của dạng xem +của bộ chứa.</li> + </ul> +</div> + </li> + + <li><b>Hoặc, bằng cách lập trình, thêm phân đoạn vào một {@link android.view.ViewGroup} hiện hữu.</b> +<p>Vào bất cứ lúc nào trong khi hoạt động của bạn đang chạy, bạn có thể thêm phân đoạn vào bố trí hoạt động của mình. Bạn +chỉ cần chỉ định một {@link +android.view.ViewGroup} là nơi mà bạn sẽ đặt phân đoạn vào.</p> + <p>Để thực hiện giao tác phân đoạn trong hoạt động của mình (chẳng hạn như thêm, gỡ bỏ, hay thay thế một +phân đoạn), bạn phải sử dụng các API từ {@link android.app.FragmentTransaction}. Bạn có thể nhận một thực thể +của {@link android.app.FragmentTransaction} từ {@link android.app.Activity} của mình như sau:</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()} +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>Sau đó, bạn có thể thêm một phân đoạn bằng cách sử dụng phương pháp {@link +android.app.FragmentTransaction#add(int,Fragment) add()}, chỉ định phân đoạn sẽ thêm và +dạng xem mà bạn sẽ chèn nó vào. Ví dụ:</p> + +<pre> +ExampleFragment fragment = new ExampleFragment(); +fragmentTransaction.add(R.id.fragment_container, fragment); +fragmentTransaction.commit(); +</pre> + + <p>Tham đối đầu tiên được chuyển cho {@link android.app.FragmentTransaction#add(int,Fragment) add()} +là {@link android.view.ViewGroup}, là nơi mà phân đoạn sẽ được đặt vào, được chỉ định bởi +ID tài nguyên, và tham đối thứ hai là phân đoạn cần thêm.</p> + <p>Sau khi bạn đã thực hiện các thay đổi của mình bằng +{@link android.app.FragmentTransaction}, bạn phải +gọi {@link android.app.FragmentTransaction#commit} để các thay đổi có hiệu lực.</p> + </li> +</ul> + + +<h4 id="AddingWithoutUI">Thêm một phân đoạn không có UI</h4> + +<p>Các ví dụ nêu trên cho biết cách thêm một phân đoạn vào hoạt động của bạn để cung cấp một UI. Tuy nhiên, +bạn cũng có thể sử dụng một phân đoạn để cung cấp một hành vi chạy ngầm cho hoạt động mà không cần đưa +UI bổ sung.</p> + +<p>Để thêm một phân đoạn không có UI, hãy thêm phân đoạn từ hoạt động đang bằng cách sử dụng {@link +android.app.FragmentTransaction#add(Fragment,String)} (cung cấp một "tag" xâu duy nhất cho phân đoạn +, thay vì một ID dạng xem). Làm vậy sẽ thêm phân đoạn, nhưng vì không liên kết với một dạng xem +trong bố trí hoạt động, nó sẽ không nhận được lệnh gọi tới {@link +android.app.Fragment#onCreateView onCreateView()}. Vì thế, bạn không cần triển khai phương pháp đó.</p> + +<p>Việc cung cấp tag xâu cho phân đoạn không chỉ áp dụng cho các phân đoạn không có UI—bạn cũng có thể +cung cấp tag xâu cho phân đoạn có UI—nhưng nếu phân đoạn không có +UI, khi đó, tag xâu là cách duy nhất để nhận biết nó. Nếu sau này bạn muốn nhận phân đoạn từ +hoạt động, bạn cần sử dụng {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()}.</p> + +<p>Để biết ví dụ về hoạt động sử dụng phân đoạn như một trình thực hiện nền, không có UI, hãy xem mẫu {@code +FragmentRetainInstance.java}, mẫu này có trong các mẫu SDK (có sẵn thông qua +Trình quản lý SDK Android) và nằm trên hệ thống của bạn như là +<code><sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java</code>.</p> + + + +<h2 id="Managing">Quản lý Phân đoạn</h2> + +<p>Để quản lý các phân đoạn trong hoạt động của mình, bạn cần sử dụng {@link android.app.FragmentManager}. Để +có nó, hãy gọi {@link android.app.Activity#getFragmentManager()} từ hoạt động của bạn.</p> + +<p>Một số việc bạn có thể làm với {@link android.app.FragmentManager} bao gồm:</p> + +<ul> + <li>Nhận các phân đoạn tồn tại trong hoạt động, bằng {@link +android.app.FragmentManager#findFragmentById findFragmentById()} (đối với các phân đoạn cung cấp UI trong +bố trí hoạt động) hoặc {@link android.app.FragmentManager#findFragmentByTag +findFragmentByTag()} (đối với các phân đoạn có hoặc không cung cấp UI).</li> + <li>Lấy phân đoạn ra khỏi ngăn xếp, bằng {@link +android.app.FragmentManager#popBackStack()} (mô phỏng một câu lệnh <em>Quay lại</em> của người dùng).</li> + <li>Đăng ký một đối tượng theo dõi cho những thay đổi đối với ngăn xếp, bằng {@link +android.app.FragmentManager#addOnBackStackChangedListener addOnBackStackChangedListener()}.</li> +</ul> + +<p>Để biết thêm thông tin về những phương pháp này và phương pháp khác, hãy tham khảo tài liệu lớp {@link +android.app.FragmentManager}.</p> + +<p>Như minh họa trong phần trước, bạn cũng có thể sử dụng {@link android.app.FragmentManager} +để mở một {@link android.app.FragmentTransaction}, nó cho phép bạn thực hiện các giao tác, ví dụ như +thêm hoặc gỡ bỏ phân đoạn.</p> + + +<h2 id="Transactions">Thực hiện Giao tác Phân đoạn</h2> + +<p>Một tính năng tuyệt vời khi sử dụng phân đoạn trong hoạt động của bạn đó là khả năng thêm, gỡ bỏ, thay thế, +và thực hiện các hành động khác với chúng, để hồi đáp lại tương tác của người dùng. Mỗi tập hợp thay đổi mà bạn +thực thi cho hoạt động được gọi là một giao tác và bạn có thể thực hiện một giao tác bằng cách sử dụng các API trong {@link +android.app.FragmentTransaction}. Bạn cũng có thể lưu từng giao tác vào một ngăn xếp được quản lý bởi +hoạt động, cho phép người dùng điều hướng ngược lại thông qua những thay đổi phân đoạn (tương tự như điều hướng +ngược lại thông qua hoạt động).</p> + +<p>Bạn có thể thu được một thực thể của {@link android.app.FragmentTransaction} từ {@link +android.app.FragmentManager} như sau:</p> + +<pre> +FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}; +FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()}; +</pre> + +<p>Mỗi giao tác là một tập hợp những thay đổi mà bạn muốn thực hiện tại cùng thời điểm. Bạn có thể thiết lập +tất cả thay đổi mà mình muốn thực hiện đối với một giao tác cho trước bằng cách sử dụng các phương pháp như {@link +android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()}, +và {@link android.app.FragmentTransaction#replace replace()}. Sau đó, để áp dụng giao tác +cho hoạt động, bạn phải gọi {@link android.app.FragmentTransaction#commit()}.</p> +</dl> + +<p>Trước khi bạn gọi {@link +android.app.FragmentTransaction#commit()}, tuy nhiên, bạn có thể muốn gọi {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, để thêm giao tác +vào một ngăn xếp của các giao tác phân đoạn. Ngăn xếp này được quản lý bởi hoạt động và cho phép +người dùng trở về trạng thái phân đoạn trước đó, bằng cách nhấp nút <em>Quay lại</em>.</p> + +<p>Ví dụ, sau đây là cách bạn có thể thay thế phân đoạn này bằng phân đoạn khác, và giữ nguyên +trạng thái trước đó của ngăn xếp:</p> + +<pre> +// Create new fragment and transaction +Fragment newFragment = new ExampleFragment(); +FragmentTransaction transaction = getFragmentManager().beginTransaction(); + +// Replace whatever is in the fragment_container view with this fragment, +// and add the transaction to the back stack +transaction.replace(R.id.fragment_container, newFragment); +transaction.addToBackStack(null); + +// Commit the transaction +transaction.commit(); +</pre> + +<p>Trong ví dụ này, {@code newFragment} thay thế mọi phân đoạn (nếu có) hiện đang ở trong +bộ chứa bố trí được nhận biết bởi ID {@code R.id.fragment_container}. Bằng cách gọi {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, giao tác thay thế +được lưu vào ngăn xếp, vì thế người dùng có thể đảo ngược giao tác và mang +giao tác trước đó trở lại bằng cách nhấn nút <em>Quay lại</em>.</p> + +<p>Nếu bạn thêm nhiều thay đổi vào giao tác (chẳng hạn như một {@link +android.app.FragmentTransaction#add add()} khác hoặc {@link android.app.FragmentTransaction#remove +remove()}) và gọi {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()}, khi đó, tất cả thay đổi được áp dụng +trước khi bạn gọi {@link android.app.FragmentTransaction#commit commit()} đều được thêm vào +ngăn xếp như một giao tác riêng lẻ và nút <em>Quay lại</em> sẽ đảo ngược tất cả cùng nhau.</p> + +<p>Thứ tự mà bạn thêm thay đổi vào một {@link android.app.FragmentTransaction} không quan trọng, +ngoại trừ:</p> +<ul> + <li>Bạn phải gọi {@link android.app.FragmentTransaction#commit()} cuối cùng</li> + <li>Nếu bạn thêm nhiều phân đoạn vào cùng bộ chứa, khi đó thứ tự mà +bạn thêm chúng sẽ xác định thứ tự chúng xuất hiện trong phân cấp dạng xem</li> +</ul> + +<p>Nếu bạn không gọi {@link android.app.FragmentTransaction#addToBackStack(String) +addToBackStack()} khi thực hiện một giao tác để xóa một phân đoạn, khi đó, phân đoạn đó sẽ bị +hủy khi giao tác được thực hiện và người dùng không thể điều hướng trở lại nó. Trong khi đó, nếu bạn +gọi {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} khi +gỡ bỏ một phân đoạn, khi đó phân đoạn bị <em>dừng</em> và sẽ được khôi phục nếu người dùng điều hướng +trở lại.</p> + +<p class="note"><strong>Mẹo:</strong> Với mỗi giao tác phân đoạn, bạn có thể áp dụng một hoạt ảnh +chuyển tiếp bằng cách gọi {@link android.app.FragmentTransaction#setTransition setTransition()} trước khi +thực thi.</p> + +<p>Việc gọi {@link android.app.FragmentTransaction#commit()} không thực hiện giao tác +ngay lập tức. Thay vào đó, nó lập lịch biểu để chạy trên luồng UI của hoạt động (luồng "chính") ngay khi +luồng có thể làm vậy. Tuy nhiên, nếu cần, bạn có thể gọi {@link +android.app.FragmentManager#executePendingTransactions()} từ luồng UI của mình để ngay lập tức thực hiện +các giao tác được gửi bởi {@link android.app.FragmentTransaction#commit()}. Làm vậy +thường không cần thiết trừ khi giao tác đó là phụ thuộc cho các tác vụ ở những luồng khác.</p> + +<p class="caution"><strong>Chú ý:</strong> Bạn có thể thực thi một giao tác bằng cách sử dụng {@link +android.app.FragmentTransaction#commit commit()} chỉ trước khi hoạt động <a href="{@docRoot}guide/components/activities.html#SavingActivityState">lưu +trạng thái</a> của nó (khi người dùng rời khỏi hoạt động). Nếu bạn định thực thi sau thời điểm đó sẽ phát sinh một lỗi +ngoại lệ. Nguyên nhân là vì trạng thái sau khi thực thi có thể bị mất nếu hoạt động +cần được khôi phục. Đối với những trường hợp mà bạn có thể mất thực thi, hãy sử dụng {@link +android.app.FragmentTransaction#commitAllowingStateLoss()}.</p> + + + + +<h2 id="CommunicatingWithActivity">Giao tiếp với Hoạt động</h2> + +<p>Mặc dù {@link android.app.Fragment} được triển khai như một đối tượng độc lập với +{@link android.app.Activity} và có thể được sử dụng bên trong nhiều hoạt động, một thực thể đã cho của +phân đoạn sẽ được gắn kết trực tiếp với hoạt động chứa nó.</p> + +<p>Cụ thể, phân đoạn có thể truy cập thực thể {@link android.app.Activity} bằng {@link +android.app.Fragment#getActivity()} và dễ dàng thực hiện các tác vụ như tìm một dạng xem trong bố trí +hoạt động:</p> + +<pre> +View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list); +</pre> + +<p>Tương tự, hoạt động của bạn có thể gọi ra các phương pháp trong phân đoạn bằng cách thu được một tham chiếu tới +{@link android.app.Fragment} từ {@link android.app.FragmentManager}, bằng cách sử dụng {@link +android.app.FragmentManager#findFragmentById findFragmentById()} hoặc {@link +android.app.FragmentManager#findFragmentByTag findFragmentByTag()}. Ví dụ:</p> + +<pre> +ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment); +</pre> + + +<h3 id="EventCallbacks">Tạo gọi lại sự kiện cho hoạt động</h3> + +<p>Trong một số trường hợp, bạn có thể cần một phân đoạn để chia sẻ sự kiện với hoạt động. Một cách hay để làm điều này +đó là định nghĩa một giao diện gọi lại bên trong phân đoạn và yêu cầu hoạt động chủ triển khai +nó. Khi hoạt động nhận được một lệnh gọi lại thông qua giao diện, nó có thể chia sẻ thông tin với +các phân đoạn khác trong bố trí nếu cần.</p> + +<p>Ví dụ, nếu một ứng dụng tin tức có hai phân đoạn trong một hoạt động—một để hiển thị danh sách +bài viết (phân đoạn A) và một để hiển thị một bài viết (phân đoạn B)—khi đó, phân đoạn A phải thông báo với +hoạt động khi nào thì một mục danh sách được chọn để nó có thể yêu cầu phân đoạn B hiển thị bài viết đó. Trong +trường hợp này, giao diện {@code OnArticleSelectedListener} sẽ được khai báo bên trong phân đoạn A:</p> + +<pre> +public static class FragmentA extends ListFragment { + ... + // Container Activity must implement this interface + public interface OnArticleSelectedListener { + public void onArticleSelected(Uri articleUri); + } + ... +} +</pre> + +<p>Khi đó, hoạt động lưu trữ phân đoạn sẽ triển khai giao diện {@code OnArticleSelectedListener} +và +khống chế {@code onArticleSelected()} để thông báo với phân đoạn B về sự kiện từ phân đoạn A. Để đảm bảo +rằng hoạt động chủ triển khai giao diện này, phương pháp gọi lại {@link +android.app.Fragment#onAttach onAttach()} của phân đoạn A (mà hệ thống gọi khi thêm +phân đoạn vào hoạt động) sẽ khởi tạo một thực thể của {@code OnArticleSelectedListener} bằng cách +đổi kiểu {@link android.app.Activity} mà được chuyển vào {@link android.app.Fragment#onAttach +onAttach()}:</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mListener = (OnArticleSelectedListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); + } + } + ... +} +</pre> + +<p>Nếu hoạt động chưa triển khai giao diện, khi đó phân đoạn sẽ đưa ra lỗi +{@link java.lang.ClassCastException}. +Nếu thành công, thành viên {@code mListener} giữ một tham chiếu tới triển khai +{@code OnArticleSelectedListener}của hoạt động, sao cho phân đoạn A có thể chia sẻ sự kiện với hoạt động bằng cách gọi các phương pháp +được định nghĩa bởi giao diện {@code OnArticleSelectedListener}. Ví dụ, nếu phân đoạn A là một +phần mở rộng của {@link android.app.ListFragment}, mỗi lần +người dùng nhấp vào một mục danh sách, hệ thống sẽ gọi ra {@link android.app.ListFragment#onListItemClick +onListItemClick()} trong phân đoạn, và nó lại gọi {@code onArticleSelected()} để chia sẻ +sự kiện với hoạt động:</p> + +<pre> +public static class FragmentA extends ListFragment { + OnArticleSelectedListener mListener; + ... + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + // Append the clicked item's row ID with the content provider Uri + Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id); + // Send the event and Uri to the host activity + mListener.onArticleSelected(noteUri); + } + ... +} +</pre> + +<p>Tham số {@code id} được chuyển vào {@link +android.app.ListFragment#onListItemClick onListItemClick()} là ID hàng của mục được nhấp, +nó được sử dụng bởi hoạt động (hoặc phân đoạn kia) để tải bài viết từ {@link +android.content.ContentProvider} của ứng dụng.</p> + +<p><!--To see a complete implementation of this kind of callback interface, see the <a +href="{@docRoot}resources/samples/NotePad/index.html">NotePad sample</a>. -->Bạn có thể xem thêm thông tin về +cách sử dụng một trình cung cấp nội dung trong tài liệu <a href="{@docRoot}guide/topics/providers/content-providers.html">Trình cung cấp Nội dung</a>.</p> + + + +<h3 id="ActionBar">Thêm mục vào Thanh Hành động</h3> + +<p>Phân đoạn của bạn có thể đóng góp các mục menu vào <a href="{@docRoot}guide/topics/ui/menus.html#options-menu">Menu Tùy chọn</a> của hoạt động (và tiếp đó là cả <a href="{@docRoot}guide/topics/ui/actionbar.html">Thanh Hành động</a>) bằng cách triển khai +{@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. Tuy nhiên, để +phương pháp này nhận lệnh gọi, bạn phải gọi {@link +android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} trong khi {@link +android.app.Fragment#onCreate(Bundle) onCreate()}, để cho biết rằng phân đoạn +sẽ muốn thêm mục vào Menu Tùy chọn (nếu không, phân đoạn sẽ không nhận được lệnh gọi tới +{@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}).</p> + +<p>Bất kỳ mục nào mà bạn thêm vào Menu Tùy chọn sau đó từ phân đoạn đều được nối với các mục menu +hiện tại. Phân đoạn cũng nhận các lệnh gọi lại tới {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} khi một mục menu +được chọn.</p> + +<p>Bạn cũng có thể đăng ký một dạng xem trong bố trí phân đoạn của mình để cung cấp một menu ngữ cảnh bằng cách gọi {@link +android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. Khi người dùng mở +menu ngữ cảnh, phân đoạn nhận một lệnh gọi tới {@link +android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) +onCreateContextMenu()}. Khi người dùng chọn một mục, phân đoạn nhận được một lệnh gọi tới {@link +android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.</p> + +<p class="note"><strong>Lưu ý:</strong> Mặc dù phân đoạn của bạn nhận được một lệnh gọi khi chọn mục +đối với từng mục menu mà nó thêm, trước tiên hoạt động sẽ nhận phương pháp gọi lại tương ứng khi người dùng +chọn một mục menu. Nếu việc triển khai gọi lại khi chọn mục của hoạt động không +xử lý mục được chọn, khi đó sự kiện được chuyển sang phương pháp gọi lại của phân đoạn. Điều này đúng đối với +Menu Tùy chọn và các menu ngữ cảnh.</p> + +<p>Để biết thêm thông tin về các menu, xem các hướng dẫn cho nhà phát triển <a href="{@docRoot}guide/topics/ui/menus.html">Menu</a> và <a href="{@docRoot}guide/topics/ui/actionbar.html">Thanh Hành động</a>.</p> + + + + +<h2 id="Lifecycle">Xử lý Vòng đời của Phân đoạn</h2> + +<div class="figure" style="width:350px"> +<img src="{@docRoot}images/activity_fragment_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Hình 3.</strong> Ảnh hưởng của vòng đời hoạt động tới vòng đời +của phân đoạn.</p> +</div> + +<p>Việc quản lý vòng đời của một phân đoạn rất giống với quản lý vòng đời của một hoạt động. Giống như +hoạt động, phân đoạn có thể tồn tại ở ba trạng thái:</p> + +<dl> + <dt><i>Tiếp tục</i></dt> + <dd>Phân đoạn hiển thị trong hoạt động đang chạy.</dd> + + <dt><i>Tạm dừng</i></dt> + <dd>Một hoạt động khác ở trong tiền cảnh và có tiêu điểm, nhưng hoạt động mà phân đoạn +này nằm trong vẫn hiển thị (hoạt động tiền cảnh mờ một phần hoặc không +che phủ toàn bộ màn hình).</dd> + + <dt><i>Dừng</i></dt> + <dd>Phân đoạn không hiển thị. Hoặc là hoạt động chủ đã bị dừng hoặc +phân đoạn đã được gỡ bỏ khỏi hoạt động, nhưng được thêm vào ngăn xếp. Phân đoạn dừng +vẫn còn hoạt động (tất cả thông tin về trạng thái và thành viên đều được hệ thống giữ lại). Tuy nhiên, nó không còn +hiển thị với người dùng nữa và sẽ bị tắt bỏ nếu hoạt động bị tắt bỏ.</dd> +</dl> + +<p>Cũng như một hoạt động, bạn có thể giữ lại trạng thái của một phân đoạn bằng cách sử dụng {@link +android.os.Bundle}, trong trường hợp tiến trình của hoạt động bị tắt bỏ và bạn cần khôi phục +trạng thái của phân đoạn khi hoạt động được tạo lại. Bạn có thể lưu trạng thái trong phương pháp gọi lại {@link +android.app.Fragment#onSaveInstanceState onSaveInstanceState()} của phân đoạn và khôi phục nó trong +hoặc {@link android.app.Fragment#onCreate onCreate()}, {@link +android.app.Fragment#onCreateView onCreateView()}, hoặc {@link +android.app.Fragment#onActivityCreated onActivityCreated()}. Để biết thêm thông tin về việc lưu +trạng thái, xem tài liệu <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Hoạt động</a> +.</p> + +<p>Sự khác nhau quan trọng nhất trong vòng đời giữa một hoạt động và một phân đoạn đó là cách chúng +được lưu trữ trong ngăn xếp tương ứng. Hoạt động được đặt vào một ngăn xếp gồm nhiều hoạt động +, được quản lý bởi hệ thống theo mặc định khi bị dừng (sao cho người dùng có thể điều hướng lại +nó bằng nút <em>Quay lại</em> như được đề cập trong <a href="{@docRoot}guide/components/tasks-and-back-stack.html">Tác vụ và Ngăn xếp</a>). +Tuy nhiên, phân đoạn chỉ được đặt vào một ngăn xếp do hoạt động chủ quản lý khi bạn +yêu cầu rõ ràng rằng trường hợp đó phải được lưu bằng cách gọi {@link +android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} trong một giao tác +gỡ bỏ phân đoạn.</p> + +<p>Nếu không thì việc quản lý vòng đời của phân đoạn rất giống với việc quản lý vòng đời +của hoạt động. Vì thế, những nội dung áp dụng cho <a href="{@docRoot}guide/components/activities.html#Lifecycle">quản lý vòng đời của +hoạt động</a> cũng áp dụng cho phân đoạn. Tuy nhiên, việc mà bạn cũng cần phải hiểu đó là cách +vòng đời của hoạt động ảnh hưởng tới vòng đời của phân đoạn.</p> + +<p class="caution"><strong>Chú ý:</strong> Nếu bạn cần một đối tượng {@link android.content.Context} trong +{@link android.app.Fragment}của mình, bạn có thể gọi {@link android.app.Fragment#getActivity()}. +Tuy nhiên, nhớ chỉ được gọi {@link android.app.Fragment#getActivity()} khi phân đoạn được gắn với +một hoạt động. Khi phân đoạn chưa được gắn, hoặc bị gỡ trong khi kết thúc +vòng đời của nó, {@link android.app.Fragment#getActivity()} sẽ trả về rỗng.</p> + + +<h3 id="CoordinatingWithActivity">Phối hợp với vòng đời của hoạt động</h3> + +<p>Vòng đời của hoạt động mà phân đoạn có ở trong đó sẽ trực tiếp ảnh hưởng tới vòng đời của phân đoạn +, sao cho mỗi lệnh gọi lại vòng đời cho hoạt động đó sẽ dẫn tới một lệnh gọi lại tương tự cho từng +phân đoạn. Ví dụ, khi hoạt động nhận được {@link android.app.Activity#onPause}, mỗi +phân đoạn trong hoạt động sẽ nhận được {@link android.app.Fragment#onPause}.</p> + +<p>Tuy nhiên, các phân đoạn có thêm một vài lệnh gọi lại vòng đời nhằm xử lý tương tác duy nhất với +hoạt động để thực hiện các hành động như xây dựng và hủy UI của phân đoạn. Những phương pháp gọi lại +bổ sung này là:</p> + +<dl> + <dt>{@link android.app.Fragment#onAttach onAttach()}</dt> + <dd>Được gọi khi phân đoạn đã được liên kết với hoạt động {@link +android.app.Activity} được chuyển ở đây).</dd> + <dt>{@link android.app.Fragment#onCreateView onCreateView()}</dt> + <dd>Được gọi khi tạo phân cấp dạng xem được liên kết với phân đoạn.</dd> + <dt>{@link android.app.Fragment#onActivityCreated onActivityCreated()}</dt> + <dd>Được gọi khi phương pháp {@link android.app.Activity#onCreate +onCreate()} của hoạt động đã trả về.</dd> + <dt>{@link android.app.Fragment#onDestroyView onDestroyView()}</dt> + <dd>Được gọi khi phân cấp dạng xem được liên kết với phân đoạn đang được gỡ bỏ.</dd> + <dt>{@link android.app.Fragment#onDetach onDetach()}</dt> + <dd>Được gọi khi phân đoạn đang được bỏ liên kết khỏi hoạt động.</dd> +</dl> + +<p>Tiến trình vòng đời của một phân đoạn, do bị ảnh hưởng bởi hoạt động chủ của nó, được minh họa +bởi hình 3. Trong hình này, bạn có thể thấy cách thức mỗi trạng thái nối tiếp nhau của hoạt động sẽ xác định +các phương pháp gọi lại nào mà một phân đoạn có thể nhận được. Ví dụ, khi hoạt động đã nhận được lệnh gọi lại {@link +android.app.Activity#onCreate onCreate()} của nó, phân đoạn trong hoạt động sẽ nhận được không quá +lệnh gọi lại {@link android.app.Fragment#onActivityCreated onActivityCreated()}.</p> + +<p>Sau khi hoạt động đạt trạng thái tiếp tục, bạn có thể tự do thêm và gỡ bỏ phân đoạn vào +hoạt động. Vì thế, chỉ trong khi hoạt động ở trạng thái tiếp tục thì vòng đời của một phân đoạn +mới có thể thay đổi độc lập.</p> + +<p>Tuy nhiên, khi hoạt động rời khỏi trạng thái tiếp tục, phân đoạn lại bị hoạt động đẩy qua vòng đời +của mình.</p> + + + + +<h2 id="Example">Ví dụ</h2> + +<p>Để kết hợp mọi nội dung được đề cập trong tài liệu này, sau đây là một ví dụ về hoạt động +sử dụng hai phân đoạn để tạo một bố trí hai bảng. Hoạt động bên dưới bao gồm một phân đoạn để +hiển thị danh sách các vở kịch của Shakespeare và một phân đoạn khác để hiển thị tóm tắt về vở kịch khi được chọn +từ danh sách. Nó cũng minh họa cách cung cấp các cấu hình phân đoạn khác nhau, +dựa trên cấu hình màn hình.</p> + +<p class="note"><strong>Lưu ý:</strong> Mã nguồn hoàn chỉnh cho hoạt động này có sẵn trong +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.html">{@code +FragmentLayout.java}</a>.</p> + +<p>Hoạt động chính áp dụng một bố trí theo cách thông thường, trong {@link +android.app.Activity#onCreate onCreate()}:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main} + +<p>Bố trí được áp dụng là {@code fragment_layout.xml}:</p> + +{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout} + +<p>Khi sử dụng bố trí này, hệ thống sẽ khởi tạo {@code TitlesFragment} (liệt kê tên +các vở kịch) ngay khi hoạt động nạp bố trí, trong khi {@link android.widget.FrameLayout} +(nơi sẽ xuất hiện phân đoạn hiển thị tóm tắt về vở kịch) chiếm khoảng trống phía bên phải của +màn hình, nhưng ban đầu vẫn trống. Như bạn sẽ thấy bên dưới, mãi tới khi người dùng chọn một mục +từ danh sách thì một phân đoạn mới được đặt vào {@link android.widget.FrameLayout}.</p> + +<p>Tuy nhiên, không phải tất cả cấu hình màn hình đều đủ rộng để hiển thị cả danh sách +các vở kịch và tóm tắt bên cạnh nhau. Vì thế, bố trí trên chỉ được sử dụng cho cấu hình +màn hình khổ ngang bằng cách lưu nó dưới dạng {@code res/layout-land/fragment_layout.xml}.</p> + +<p>Vì thế, khi màn hình hướng đứng, hệ thống sẽ áp dụng bố trí sau, nó +được lưu tại {@code res/layout/fragment_layout.xml}:</p> + +{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout} + +<p>Bố trí này chỉ bao gồm {@code TitlesFragment}. Điều này có nghĩa là, khi thiết bị ở +hướng đứng, chỉ danh sách tên vở kịch được hiển thị. Vì thế, khi người dùng nhấp vào một +mục danh sách trong cấu hình này, ứng dụng sẽ bắt đầu một hoạt động mới để hiển thị tóm tắt, +thay vì tải một phân đoạn thứ hai.</p> + +<p>Tiếp theo, bạn có thể thấy cách hoàn thành điều này trong các lớp phân đoạn. Đầu tiên là {@code +TitlesFragment}, hiển thị danh sách tên các vở kịch của Shakespeare. Phân đoạn này sẽ mở rộng {@link +android.app.ListFragment} và dựa vào nó để xử lý hầu hết công việc về dạng xem danh sách.</p> + +<p>Khi bạn kiểm tra đoạn mã này, hãy để ý rằng có hai hành vi có thể khi người dùng nhấp vào một +mục danh sách: phụ thuộc vào bố trí nào trong hai bố trí đang hiện hoạt, nó có thể hoặc tạo và hiển thị một phân đoạn +mới để hiển thị chi tiết trong cùng hoạt động (thêm phân đoạn vào {@link +android.widget.FrameLayout}), hoặc bắt đầu một hoạt động mới (tại đó phân đoạn có thể được hiển thị).</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles} + +<p>Phân đoạn thứ hai, {@code DetailsFragment} sẽ hiển thị tóm tắt vở kịch cho mục được chọn từ +danh sách trong {@code TitlesFragment}:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details} + +<p>Nhớ lại ở lớp {@code TitlesFragment} rằng, nếu người dùng nhấp vào một mục danh sách và bố trí +hiện tại <em>không</em> có dạng xem {@code R.id.details} (là nơi mà +{@code DetailsFragment} thuộc về), khi đó, ứng dụng sẽ bắt đầu hoạt động {@code DetailsActivity} +để hiển thị nội dung của mục đó.</p> + +<p>Sau đây là {@code DetailsActivity}, nó chỉ đơn thuần nhúng {@code DetailsFragment} để hiển thị +tóm tắt vở kịch được chọn khi màn hình ở hướng đứng:</p> + +{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java +details_activity} + +<p>Lưu ý rằng hoạt động này tự kết thúc nếu cấu hình là khổ ngang, sao cho hoạt động +chính có thể chiếm lấy và hiển thị {@code DetailsFragment} bên cạnh {@code TitlesFragment}. +Điều này có thể xảy ra nếu người dùng bắt đầu {@code DetailsActivity} ở dạng hướng đứng, nhưng +sau đó xoay thành khổ ngang (làm vậy sẽ bắt đầu lại hoạt động hiện tại).</p> + + +<p>Để biết thêm mẫu sử dụng phân đoạn (và toàn bộ tệp nguồn cho ví dụ này), +hãy xem ứng dụng mẫu API Demos có sẵn trong <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#Fragment"> +ApiDemos</a> (có thể tải xuống từ <a href="{@docRoot}resources/samples/get.html">Thành phần SDK Mẫu</a>).</p> + + diff --git a/docs/html-intl/intl/vi/guide/components/fundamentals.jd b/docs/html-intl/intl/vi/guide/components/fundamentals.jd new file mode 100644 index 000000000000..4b70140723d9 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/fundamentals.jd @@ -0,0 +1,480 @@ +page.title=Đại cương về Ứng dụng +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Trong tài liệu này</h2> +<ol> +<li><a href="#Components">Thành phần của Ứng dụng</a> + <ol> + <li><a href="#ActivatingComponents">Kích hoạt các thành phần</a></li> + </ol> +</li> +<li><a href="#Manifest">Tệp Bản kê khai</a> + <ol> + <li><a href="#DeclaringComponents">Khai báo các thành phần</a></li> + <li><a href="#DeclaringRequirements">Khai báo các yêu cầu của ứng dụng</a></li> + </ol> +</li> +<li><a href="#Resources">Tài nguyên Ứng dụng</a></li> +</ol> +</div> +</div> + +<p>Ứng dụng Android được viết bằng ngôn ngữ lập trình Java. Bộ công cụ SDK Android sẽ biên dịch +mã của bạn—cùng với bất kỳ tệp dữ liệu và tài nguyên nào—vào một APK: một <i>gói Android</i>, +đó là một tệp lưu trữ có hậu tố {@code .apk}. Một tệp APK chứa tất cả nội dung +của một ứng dụng Android và là tệp mà các thiết bị dựa trên nền tảng Android sử dụng để cài đặt ứng dụng.</p> + +<p>Sau khi được cài đặt lên một thiết bị, từng ứng dụng Android sẽ ở bên trong hộp cát bảo mật của chính nó: </p> + +<ul> + <li>Hệ điều hành Android là một hệ thống Linux đa người dùng trong đó mỗi ứng dụng là một +người dùng khác nhau.</li> + +<li>Theo mặc định, hệ thống gán cho từng ứng dụng một ID người dùng Linux duy nhất (ID chỉ được sử dụng bởi +hệ thống và không xác định đối với ứng dụng). Hệ thống sẽ đặt quyền cho tất cả tệp trong một ứng dụng +sao cho chỉ ID người dùng được gán cho ứng dụng đó mới có thể truy cập chúng. </li> + +<li>Mỗi tiến trình có máy ảo (VM) riêng của mình, vì thế mã của một ứng dụng sẽ chạy độc lập với +các ứng dụng khác.</li> + +<li>Theo mặc định, mọi ứng dụng chạy trong tiến trình Linux của chính nó. Android khởi động tiến trình khi bất kỳ +thành phần nào của ứng dụng cần được thực thi, sau đó tắt tiến trình khi không còn +cần nữa hoặc khi hệ thống phải khôi phục bộ nhớ cho các ứng dụng khác.</li> +</ul> + +<p>Bằng cách này, hệ thống Android triển khai <em>nguyên tắc đặc quyền ít nhất</em>. Cụ thể, +theo mặc định, mỗi ứng dụng chỉ có thể truy cập vào các thành phần mà nó cần để thực hiện công việc của mình và +không hơn. Điều này tạo ra một môi trường rất bảo mật mà trong đó một ứng dụng không thể truy cập các bộ phận của +hệ thống mà nó không được cấp quyền.</p> + +<p>Tuy nhiên, có nhiều cách để một ứng dụng chia sẻ dữ liệu với các ứng dụng khác và để một +ứng dụng truy cập vào các dịch vụ của hệ thống:</p> + +<ul> + <li>Có thể sắp xếp để hai ứng dụng chia sẻ cùng ID người dùng Linux, trong trường hợp đó +chúng có thể truy cập các tệp của nhau. Để tiết kiệm tài nguyên của hệ thống, các ứng dụng có +cùng ID người dùng cũng có thể sắp xếp để chạy trong cùng tiến trình Linux và chia sẻ cùng VM (các +ứng dụng cũng phải được ký bằng cùng chứng chỉ).</li> + <li>Một ứng dụng có thể yêu cầu quyền truy cập dữ liệu của thiết bị chẳng hạn như +danh bạ của người dùng, tin nhắn SMS, thiết bị lưu trữ gắn được (thẻ SD), máy ảnh, Bluetooth và nhiều nữa. Tất cả +quyền ứng dụng đều phải được cấp bởi người dùng tại thời điểm cài đặt.</li> +</ul> + +<p>Đó là nội dung cơ bản về cách mà một ứng dụng Android tồn tại trong hệ thống. Phần còn lại của +tài liệu này giới thiệu với bạn về:</p> +<ul> + <li>Các thành phần khuôn khổ cốt lõi định nghĩa ứng dụng của bạn.</li> + <li>Tệp bản kê khai mà trong đó bạn khai báo các thành phần và tính năng yêu cầu của thiết bị cho ứng dụng +của bạn.</li> + <li>Các tài nguyên tách riêng với mã ứng dụng và cho phép ứng dụng của bạn +tối ưu hóa hành vi của nó cho nhiều loại cấu hình thiết bị đa dạng.</li> +</ul> + + + +<h2 id="Components">Thành phần của Ứng dụng</h2> + +<p>Thành phần của ứng dụng là những khối dựng thiết yếu của một ứng dụng Android. Mỗi +thành phần là một điểm khác nhau mà qua đó hệ thống có thể vào ứng dụng của bạn. Không phải tất cả +thành phần đều là các điểm nhập thực tế cho người dùng và một số phụ thuộc vào nhau, nhưng mỗi thành phần tồn tại +như một thực thể riêng và đóng một vai trò riêng—mỗi thành phần là một khối dựng duy nhất +giúp định nghĩa hành vi chung của ứng dụng của bạn.</p> + +<p>Có bốn loại thành phần ứng dụng khác nhau. Mỗi loại có một mục đích riêng +và có một vòng đời riêng, xác định cách thành phần được tạo lập và hủy.</p> + +<p>Sau đây là bốn loại thành phần ứng dụng:</p> + +<dl> + +<dt><b>Hoạt động</b></dt> + +<dd>Một <i>hoạt động</i> biểu diễn một màn hình đơn với một giao diện người dùng. Ví dụ, +một ứng dụng e-mail có thể có một hoạt động với chức năng hiển thị một danh sách +e-mail mới, một hoạt động khác để soạn e-mail, và một hoạt động khác để đọc e-mail. Mặc dù +các hoạt động cùng nhau tạo thành một trải nghiệm người dùng gắn kết trong ứng dụng e-mail, mỗi hoạt động +lại độc lập với nhau. Như vậy, một ứng dụng khác có thể khởi động bất kỳ hoạt động nào +trong số này (nếu ứng dụng e-mail cho phép nó). Ví dụ, một ứng dụng máy ảnh có thể khởi động +hoạt động trong ứng dụng e-mail có chức năng soạn thư mới, để người dùng chia sẻ một bức ảnh. + +<p>Hoạt động được triển khai như một lớp con của {@link android.app.Activity} và bạn có thể tìm hiểu thêm +về nó trong hướng dẫn dành cho nhà phát triển <a href="{@docRoot}guide/components/activities.html">Hoạt động</a> +.</p> +</dd> + + +<dt><b>Dịch vụ</b></dt> + +<dd>Một <i>dịch vụ</i> là một thành phần chạy ngầm để thực hiện các thao tác +chạy lâu hoặc để thực hiện công việc cho các tiến trình từ xa. Dịch vụ +không cung cấp giao diện người dùng. Ví dụ, một dịch vụ có thể phát nhạc dưới nền trong khi +người dùng đang ở một ứng dụng khác, hoặc nó có thể tải dữ liệu qua mạng mà không +chặn người dùng tương tác với hoạt động. Một thành phần khác, chẳng hạn như một hoạt động, có thể khởi động +dịch vụ và để nó chạy hoặc gắn kết với nó để tương tác với nó. + +<p>Dịch vụ được triển khai như một lớp con của {@link android.app.Service} và bạn có thể tìm hiểu +thêm về nó trong hướng dẫn cho nhà phát triển <a href="{@docRoot}guide/components/services.html">Dịch vụ</a> +.</p> +</dd> + + +<dt><b>Trình cung cấp Nội dung</b></dt> + +<dd>Một <i>trình cung cấp nội dung</i> sẽ quản lý một tập dữ liệu ứng dụng được chia sẻ. Bạn có thể lưu trữ dữ liệu trong +hệ thống tệp, một cơ sở dữ liệu SQLite, trên web, hay bất kỳ vị trí lưu trữ liên tục nào khác mà +ứng dụng của bạn có thể truy cập. Thông qua trình cung cấp nội dung, các ứng dụng khác có thể truy vấn hay thậm chí sửa đổi +dữ liệu (nếu trình cung cấp nội dung cho phép). Ví dụ, hệ thống Android cung cấp một trình cung cấp +nội dung có chức năng quản lý thông tin danh bạ của người dùng. Như vậy, bất kỳ ứng dụng nào có các quyền +phù hợp đều có thể truy vấn bất kỳ phần nào của trình cung cấp nội dung (chẳng hạn như {@link +android.provider.ContactsContract.Data}) để đọc và ghi thông tin về một người cụ thể. + +<p>Trình cung cấp nội dung cũng hữu ích với việc đọc và ghi dữ liệu riêng tư đối với +ứng dụng của bạn và không được chia sẻ. Ví dụ, ứng dụng mẫu <a href="{@docRoot}resources/samples/NotePad/index.html">Note Pad</a> sử dụng một +trình cung cấp nội dung để lưu các ghi chú.</p> + +<p>Trình cung cấp nội dung được triển khai như một lớp con của {@link android.content.ContentProvider} +và phải triển khai một tập các API tiêu chuẩn cho phép các ứng dụng khác thực hiện +giao tác. Để biết thêm thông tin, xem hướng dẫn cho nhà phát triển <a href="{@docRoot}guide/topics/providers/content-providers.html">Trình cung cấp Nội dung</a> +.</p> +</dd> + + +<dt><b>Hàm nhận quảng bá</b></dt> + +<dd>Một <i>hàm nhận quảng bá</i> (broadcast receiver) là một thành phần có chức năng hồi đáp lại các thông báo +quảng bá trên toàn hệ thống. Nhiều quảng bá khởi nguồn từ hệ thống—ví dụ, một quảng bá thông báo +rằng màn hình đã tắt, pin yếu, hoặc một bức ảnh được chụp. +Các ứng dụng cũng có thể khởi tạo quảng bá—ví dụ như để các ứng dụng khác biết rằng +một phần dữ liệu đã được tải xuống thiết bị và có sẵn để họ sử dụng. Mặc dù các hàm nhận quảng bá +không hiển thị giao diện người dùng, chúng có thể <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">tạo một thông báo thanh trạng thái</a> +để cảnh báo người tiếp nhận khi xảy ra một sự kiện quảng bá. Tuy nhiên trường hợp phổ biến hơn đó là hàm nhận quảng bá chỉ +là một "cổng kết nối" tới các thành phần khác và nhằm mục đích thực hiện lượng công việc rất ít. Ví +dụ, nó có thể khởi tạo một dịch vụ để thực hiện một số công việc dựa trên sự kiện. + +<p>Hàm nhận quảng bá được triển khai như một lớp con của {@link android.content.BroadcastReceiver} +và mỗi quảng bá được chuyển giao như một đối tượng {@link android.content.Intent}. Để biết thêm thông tin, +hãy xem lớp {@link android.content.BroadcastReceiver}.</p> +</dd> + +</dl> + + + +<p>Một khía cạnh độc đáo trong thiết kế hệ thống Android đó là bất kỳ ứng dụng nào cũng có thể khởi động một thành phần của +ứng dụng khác. Ví dụ, nếu bạn muốn người dùng chụp +ảnh bằng máy ảnh của thiết bị, có thể có một ứng dụng khác có chức năng đó và +ứng dụng của bạn có thể sử dụng nó thay vì phát triển một hoạt động để tự chụp ảnh. Bạn không +cần tích hợp hay thậm chí là liên kết với mã từ ứng dụng của máy ảnh. +Thay vào đó, bạn đơn giản có thể khởi động hoạt động đó trong ứng dụng máy ảnh có chức năng +chụp ảnh. Khi hoàn thành, ảnh thậm chí được trả về ứng dụng của bạn để bạn có thể sử dụng nó. Đối với người dùng, +có vẻ như máy ảnh là một bộ phận thực sự trong ứng dụng của bạn.</p> + +<p>Khi hệ thống khởi động một thành phần, nó sẽ khởi động tiến trình cho ứng dụng đó (nếu tiến trình không +đang chạy) và khởi tạo các lớp cần thiết cho thành phần. Ví dụ, nếu ứng dụng +của bạn khởi động hoạt động trong ứng dụng máy ảnh có chức năng chụp ảnh, hoạt động đó +sẽ chạy trong tiến trình thuộc về ứng dụng máy ảnh chứ không chạy trong tiến trình của ứng dụng của bạn. +Vì thế, không như ứng dụng trên hầu hết các hệ thống khác, ứng dụng Android không có một điểm nhập +duy nhất (ví dụ, không có chức năng {@code main()}).</p> + +<p>Vì hệ thống chạy từng ứng dụng trong một tiến trình riêng với các quyền của tệp mà +hạn chế truy cập vào các ứng dụng khác, ứng dụng của bạn không thể trực tiếp kích hoạt một thành phần từ +một ứng dụng khác. Tuy nhiên, hệ thống Android có thể. Vì thế, để kích hoạt một thành phần trong +một ứng dụng khác, bạn phải chuyển giao một thông báo tới hệ thống trong đó nêu rõ <em>ý định</em> của bạn để +khởi động một thành phần cụ thể. Sau đó, hệ thống sẽ kích hoạt thành phần cho bạn.</p> + + +<h3 id="ActivatingComponents">Kích hoạt Thành phần</h3> + +<p>Ba trong bốn loại thành phần—hoạt động, dịch vụ và +hàm nhận quảng bá—sẽ được kích hoạt bằng một thông báo không đồng bộ gọi là <em>ý định</em>. +Ý định sẽ gắn kết từng thành phần với nhau vào thời gian chạy (bạn có thể nghĩ chúng như là +các hàm nhắn tin có chức năng yêu cầu một hành động từ các thành phần khác), dù thành phần đó thuộc +về ứng dụng của bạn hay ứng dụng khác.</p> + +<p>Một ý định được tạo thành bằng một đối tượng {@link android.content.Intent}, nó định nghĩa một thông báo để +kích hoạt một thành phần cụ thể hoặc một <em>loại</em> thành phần cụ thể—tương ứng, một ý định +có thể biểu thị hoặc không biểu thị.</p> + +<p>Đối với các hoạt động và dịch vụ, ý định có chức năng định nghĩa một hành động sẽ thực hiện (ví dụ, "xem" hoặc +"gửi" gì đó) và có thể chỉ định URI của dữ liệu để hành động dựa trên đó (ngoài những điều khác mà +thành phần được khởi động có thể cần biết). Ví dụ, một ý định có thể truyền tải một yêu cầu +để một hoạt động hiển thị một hình ảnh hay mở một trang web. Trong một số trường hợp, bạn có thể khởi động một +hoạt động để nhận kết quả, trong trường hợp đó, hoạt động cũng trả về +kết quả trong một {@link android.content.Intent} (ví dụ, bạn có thể phát hành một ý định để cho phép +người dùng chọn một liên lạc cá nhân và yêu cầu trả nó về cho bạn—ý định trả về bao gồm một +URI chỉ đến liên lạc được chọn).</p> + +<p>Đối với hàm nhận quảng bá, ý định chỉ định nghĩa +thông báo đang được quảng bá (ví dụ, một quảng bá để báo rằng pin của thiết bị yếu +sẽ chỉ bao gồm một xâu hành động chỉ báo rằng "pin yếu").</p> + +<p>Loại thành phần còn lại, trình cung cấp nội dung, không được kích hoạt bởi ý định. Thay vào đó, nó được +kích hoạt khi được nhằm tới bởi một yêu cầu từ một {@link android.content.ContentResolver}. Bộ giải quyết +nội dung xử lý tất cả giao tác trực tiếp với trình cung cấp nội dung sao cho thành phần mà +đang thực hiện giao tác với trình cung cấp sẽ không cần mà thay vào đó gọi các phương pháp trên đối tượng {@link +android.content.ContentResolver}. Điều này để lại một lớp tóm tắt giữa trình cung cấp +nội dung và thành phần yêu cầu thông tin (để bảo mật).</p> + +<p>Có các phương pháp riêng để kích hoạt từng loại thành phần:</p> +<ul> + <li>Bạn có thể khởi động một hoạt động (hoặc giao cho nó việc gì mới để làm) bằng cách +chuyển một {@link android.content.Intent} đến {@link android.content.Context#startActivity +startActivity()} hoặc {@link android.app.Activity#startActivityForResult startActivityForResult()} +(khi bạn muốn hoạt động trả về một kết quả).</li> + <li>Bạn có thể khởi động một dịch vụ (hoặc gửi chỉ dẫn mới tới một dịch vụ đang diễn ra) bằng cách +chuyển một {@link android.content.Intent} đến {@link android.content.Context#startService +startService()}. Hoặc bạn có thể gắn kết với dịch vụ bằng cách chuyển một {@link android.content.Intent} đến +{@link android.content.Context#bindService bindService()}.</li> + <li>Bạn có thể khởi tạo một quảng bá bằng cách chuyển {@link android.content.Intent} tới các phương pháp như +{@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, {@link +android.content.Context#sendOrderedBroadcast(Intent, String) sendOrderedBroadcast()}, hoặc {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.</li> + <li>Bạn có thể thực hiện một truy vấn tới một trình cung cấp nội dung bằng cách gọi {@link +android.content.ContentProvider#query query()} trên một {@link android.content.ContentResolver}.</li> +</ul> + +<p>Để biết thêm thông tin về việc sử dụng ý định, hãy xem tài liệu <a href="{@docRoot}guide/components/intents-filters.html">Ý định và Bộ lọc +Ý định</a>. Bạn cũng có thể xem thêm thông tin về việc kích hoạt các thành phần cụ thể +trong những tài liệu sau: <a href="{@docRoot}guide/components/activities.html">Hoạt động</a>, <a href="{@docRoot}guide/components/services.html">Dịch vụ</a>, {@link +android.content.BroadcastReceiver} và <a href="{@docRoot}guide/topics/providers/content-providers.html">Trình cung cấp Nội dung</a>.</p> + + +<h2 id="Manifest">Tệp Bản kê khai</h2> + +<p>Trước khi hệ thống Android có thể khởi động một thành phần ứng dụng, hệ thống phải biết rằng +thành phần đó tồn tại bằng cách đọc tệp {@code AndroidManifest.xml} của ứng dụng (tệp +"bản kê khai"). Ứng dụng của bạn phải khai báo tất cả thành phần của nó trong tệp này, nó phải nằm ở gốc của +thư mục dự án của ứng dụng.</p> + +<p>Bản kê khai làm nhiều việc bên cạnh việc khai báo các thành phần của ứng dụng, +chẳng hạn như:</p> +<ul> + <li>Xác định bất kỳ quyền của người dùng nào mà ứng dụng yêu cầu, chẳng hạn như truy cập Internet hay +truy cập đọc vào danh bạ của người dùng.</li> + <li>Khai báo <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">Mức API</a> +tối thiểu mà ứng dụng yêu cầu dựa trên những API mà ứng dụng sử dụng.</li> + <li>Khai báo các tính năng phần cứng và phần mềm được sử dụng hoặc yêu cầu bởi ứng dụng, chẳng hạn như máy ảnh, +dịch vụ Bluetooth, hoặc màn hình cảm ứng đa điểm.</li> + <li>Các thư viện API mà ứng dụng cần được liên kết với (ngoài các API khuôn khổ +Android), chẳng hạn như <a href="http://code.google.com/android/add-ons/google-apis/maps-overview.html">thư viện Google Maps +</a>.</li> + <li>Và hơn thế nữa</li> +</ul> + + +<h3 id="DeclaringComponents">Khai báo các thành phần</h3> + +<p>Nhiệm vụ chính của bản kê khai là thông báo cho hệ thống về các thành phần của ứng dụng. Ví +dụ, một tệp bản kê khai có thể khai báo một hoạt động như sau: </p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<manifest ... > + <application android:icon="@drawable/app_icon.png" ... > + <activity android:name="com.example.project.ExampleActivity" + android:label="@string/example_label" ... > + </activity> + ... + </application> +</manifest></pre> + +<p>Trong phần tử <code><a +href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> +, thuộc tính {@code android:icon} sẽ trỏ đến các tài nguyên cho một biểu tượng có chức năng nhận biết +ứng dụng.</p> + +<p>Trong phần tử <code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code>, +thuộc tính {@code android:name} quy định tên lớp hoàn toàn đủ tiêu chuẩn của lớp con {@link +android.app.Activity} và các thuộc tính {@code android:label} quy định một xâu +để sử dụng làm nhãn hiển thị với người dùng đối với hoạt động.</p> + +<p>Bạn phải khai báo tất cả thành phần của ứng dụng như sau:</p> +<ul> + <li>Các phần tử <code><a +href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +cho hoạt động</li> + <li>Các phần tử <code><a +href="{@docRoot}guide/topics/manifest/service-element.html"><service></a></code> cho +dịch vụ</li> + <li>Các phần tử <code><a +href="{@docRoot}guide/topics/manifest/receiver-element.html"><receiver></a></code> +cho hàm nhận quảng bá</li> + <li>Các phần tử <code><a +href="{@docRoot}guide/topics/manifest/provider-element.html"><provider></a></code> +cho trình cung cấp nội dung</li> +</ul> + +<p>Các hoạt động, dịch vụ và trình cung cấp nội dung mà bạn bao gồm trong nguồn của mình nhưng không khai báo +trong bản kê khai sẽ không hiển thị với hệ thống và hệ quả là không bao giờ chạy được. Tuy nhiên, +hàm nhận +quảng bá có thể hoặc được khai báo trong bản kê khai hoặc được tạo linh hoạt trong mã (dạng đối tượng +{@link android.content.BroadcastReceiver}) và được đăng ký với hệ thống bằng cách gọi +{@link android.content.Context#registerReceiver registerReceiver()}.</p> + +<p>Để tìm hiểu thêm về cách cấu trúc tệp bản kê khai cho ứng dụng của mình, hãy xem tài liệu <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">Tệp AndroidManifest.xml</a> +. </p> + + + +<h3 id="DeclaringComponentCapabilities">Khai báo các khả năng của thành phần</h3> + +<p>Như đã nêu bên trên trong phần <a href="#ActivatingComponents">Kích hoạt các Thành phần</a>, bạn có thể sử dụng một +{@link android.content.Intent} để khởi động các hoạt động, dịch vụ và hàm nhận quảng bá. Bạn có thể làm vậy bằng cách +công khai chỉ định thành phần đích (sử dụng tên lớp thành phần) trong ý định. Tuy nhiên, +sức mạnh thực sự của ý định nằm trong khái niệm <em>ý định không biểu thị</em>. Ý định không biểu thị +đơn thuần mô tả kiểu hành động cần thực hiện (và có thể có cả dữ liệu mà bạn muốn +thực hiện hành động) và cho phép hệ thống tìm một thành phần trên thiết bị có khả năng thực hiện +hành động và khởi động nó. Nếu có nhiều thành phần có thể thực hiện hành động được mô tả bởi +ý định, khi đó người dùng chọn ý định sẽ sử dụng.</p> + +<p>Cách hệ thống nhận biết các thành phần có khả năng hồi đáp lại một ý định là bằng cách so sánh +ý định nhận được với <i>các bộ lọc ý định</i> được cung cấp trong tệp bản kê khai của các ứng dụng khác trên +thiết bị.</p> + +<p>Khi bạn khai báo một hoạt động trong bản kê khai ứng dụng của mình, bạn có thể tùy chọn bao gồm +các bộ lọc ý định có chức năng khai báo các khả năng của hoạt động sao cho nó có thể hồi đáp lại ý định +từ các ứng dụng khác. Bạn có thể khai báo một bộ lọc ý định cho thành phần của mình bằng cách +thêm một phần tử <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code +<intent-filter>}</a> làm con của phần tử công khai của thành phần đó.</p> + +<p>Ví dụ, nếu bạn đã xây dựng một ứng dụng e-mail có một hoạt động soạn e-mail mới, bạn có thể +khai báo bộ lọc ý định đó để trả lời các ý định "gửi" (để gửi một e-mail mới) như sau:</p> +<pre> +<manifest ... > + ... + <application ... > + <activity android:name="com.example.project.ComposeEmailActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND" /> + <data android:type="*/*" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> +</manifest> +</pre> + +<p>Sau đó, nếu một ứng dụng khác tạo một ý định với hành động {@link +android.content.Intent#ACTION_SEND} và chuyển nó cho {@link android.app.Activity#startActivity +startActivity()}, hệ thống có thể khởi động hoạt động của bạn để người dùng có thể soạn thảo và gửi một +e-mail.</p> + +<p>Để tìm hiểu thêm về việc tạo các bộ lọc ý định, hãy xem tài liệu <a href="{@docRoot}guide/components/intents-filters.html">Ý định và Bộ lọc Ý định</a>. +</p> + + + +<h3 id="DeclaringRequirements">Khai báo các yêu cầu của ứng dụng</h3> + +<p>Có nhiều loại thiết bị dựa trên nền tảng Android và không phải tất cả chúng đều cung cấp +các tính năng và khả năng như nhau. Để tránh việc ứng dụng của bạn bị cài đặt trên các thiết bị +thiếu những tính năng mà ứng dụng của bạn cần, điều quan trọng là bạn phải định nghĩa rõ ràng một hồ sơ cho +các kiểu thiết bị mà ứng dụng của bạn hỗ trợ bằng cách khai báo các yêu cầu về thiết bị và phần mềm trong tệp +bản kê khai của mình. Hầu hết những khai báo này đều chỉ mang tính chất thông báo và hệ thống không đọc +chúng, nhưng các dịch vụ bên ngoài như Google Play thì có đọc để cung cấp tính năng lọc +cho người dùng khi họ tìm kiếm ứng dụng từ thiết bị của mình.</p> + +<p>Ví dụ, nếu ứng dụng của bạn yêu cầu máy ảnh và sử dụng các API được giới thiệu trong Android 2.1 (<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">API Mức</a> 7), +bạn cần khai báo những điều này như yêu cầu trong tệp bản kê khai của mình như sau:</p> + +<pre> +<manifest ... > + <uses-feature android:name="android.hardware.camera.any" + android:required="true" /> + <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="19" /> + ... +</manifest> +</pre> + +<p>Lúc này, những thiết bị mà <em>không</em> có máy ảnh và có một phiên bản +Android <em>thấp</em> hơn 2.1 sẽ không thể cài đặt ứng dụng của bạn từ Google Play.</p> + +<p>Tuy nhiên, bạn cũng có thể khai báo rằng ứng dụng của bạn sử dụng máy ảnh, nhưng không +<em>yêu cầu</em> nó. Trong trường hợp đó, ứng dụng của bạn phải đặt thuộc tính <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#required">{@code required}</a> +thành {@code "false"} và kiểm tra tại thời gian chạy xem +thiết bị có máy ảnh không và vô hiệu hóa bất kỳ tính năng máy ảnh nào cho phù hợp.</p> + +<p>Bạn có thể tìm hiểu thêm thông tin về cách bạn có thể quản lý tính tương thích của ứng dụng của bạn với các thiết bị khác nhau +trong tài liệu <a href="{@docRoot}guide/practices/compatibility.html">Tính tương thích với Thiết bị</a> +.</p> + + + +<h2 id="Resources">Tài nguyên Ứng dụng</h2> + +<p>Một ứng dụng Android được soạn không chỉ có mã—nó còn yêu cầu các tài nguyên +tách riêng với mã nguồn, chẳng hạn như hình ảnh, tệp âm thanh và bất kỳ thứ gì liên quan tới trình chiếu +trực quan của ứng dụng. Ví dụ, bạn nên định nghĩa các hoạt cảnh, menu, kiểu, màu sắc, +và bố trí của giao diện người dùng của hoạt động bằng các tệp XML. Việc sử dụng các tài nguyên ứng dụng giúp dễ dàng +cập nhật các đặc điểm khác nhau trong ứng dụng của bạn mà không sửa đổi mã và—bằng cách cung cấp +các tập hợp tài nguyên thay thế—cho phép bạn tối ưu hóa ứng dụng của mình cho nhiều loại +cấu hình thiết bị (chẳng hạn như ngôn ngữ và kích cỡ màn hình khác nhau).</p> + +<p>Đối với mọi tài nguyên mà bạn bao gồm trong dự án Android của mình, bộ công cụ xây dựng SDK định nghĩa một ID số nguyên +duy nhất mà bạn có thể sử dụng để tham chiếu tài nguyên từ mã ứng dụng của mình hoặc từ +các tài nguyên khác được định nghĩa trong XML. Ví dụ, nếu ứng dụng của bạn chứa một tệp hình ảnh có tên {@code +logo.png} (được lưu trong thư mục {@code res/drawable/}), bộ công cụ SDK sẽ khởi tạo một ID tài nguyên +đặt tên là {@code R.drawable.logo} mà bạn có thể sử dụng để tham chiếu hình ảnh và chèn nó vào trong giao diện người dùng +của mình.</p> + +<p>Một trong những khía cạnh quan trọng nhất của việc cung cấp tài nguyên tách riêng với mã nguồn của bạn +là khả năng cho phép bạn cung cấp các tài nguyên thay thế cho các +cấu hình thiết bị khác nhau. Ví dụ, bằng cách định nghĩa các xâu UI trong XML, bạn có thể biên dịch xâu sang +các ngôn ngữ khác và lưu các xâu đó vào tệp riêng. Sau đó, dựa vào một <em>hạn định</em> ngôn ngữ +mà bạn nối với tên của thư mục tài nguyên (chẳng hạn như {@code res/values-fr/} đối với các giá trị xâu +tiếng Pháp) và thiết đặt ngôn ngữ của người dùng, hệ thống Android sẽ áp dụng các xâu ngôn ngữ phù hợp +cho UI của bạn.</p> + +<p>Android hỗ trợ nhiều <em>hạn định</em> khác nhau cho các tài nguyên thay thế của bạn. Hạn định +là một xâu ngắn mà bạn bao gồm trong tên của các thư mục tài nguyên của mình nhằm +định nghĩa cấu hình thiết bị cho những tài nguyên đó nên được sử dụng. Lấy một +ví dụ khác, bạn nên thường xuyên tạo các bố trí khác nhau cho hoạt động của mình, tùy vào hướng và kích cỡ +màn hình của thiết bị. Ví dụ, khi màn hình thiết bị ở hướng +đứng (cao), bạn có thể muốn một bố trí có các nút thẳng đứng, nhưng khi màn hình ở hướng +khổ ngang (rộng), các nút nên được căn ngang. Để thay đổi bố trí +tùy vào hướng, bạn có thể định nghĩa hai bố trí khác nhau và áp dụng hạn định +phù hợp cho tên thư mục của từng bố trí. Sau đó, hệ thống sẽ tự động áp dụng bố trí +phù hợp tùy thuộc vào hướng hiện tại của thiết bị.</p> + +<p>Để biết thêm thông tin về các loại tài nguyên khác nhau mà bạn có thể bao gồm trong ứng dụng của mình và cách +tạo các tài nguyên thay thế cho những cấu hình thiết bị khác nhau, hãy đọc <a href="{@docRoot}guide/topics/resources/providing-resources.html">Cung cấp Tài nguyên</a>.</p> + + + +<div class="next-docs"> +<div class="col-6"> + <h2 class="norule">Tiếp tục đọc về:</h2> + <dl> + <dt><a href="{@docRoot}guide/components/intents-filters.html">Ý định và Bộ lọc Ý định</a> + </dt> + <dd>Thông tin về cách sử dụng các API {@link android.content.Intent} để + kích hoạt các thành phần của ứng dụng, chẳng hạn như hoạt động và dịch vụ, và cách tạo các thành phần cho ứng dụng của bạn + có sẵn để cho các ứng dụng khác sử dụng.</dd> + <dt><a href="{@docRoot}guide/components/activities.html">Hoạt động</a></dt> + <dd>Thông tin về cách tạo một thực thể của lớp {@link android.app.Activity}, + có chức năng cung cấp một màn hình riêng trong ứng dụng của bạn với một giao diện người dùng.</dd> + <dt><a href="{@docRoot}guide/topics/resources/providing-resources.html">Cung cấp Tài nguyên</a></dt> + <dd>Thông tin về cách các ứng dụng Android được cấu trúc để tách riêng các tài nguyên ứng dụng khỏi + mã ứng dụng, bao gồm cách bạn có thể cung cấp các tài nguyên thay thế cho những + cấu hình thiết bị cụ thể. + </dd> + </dl> +</div> +<div class="col-6"> + <h2 class="norule">Bạn cũng có thể quan tâm tới:</h2> + <dl> + <dt><a href="{@docRoot}guide/practices/compatibility.html">Tính tương thích của Thiết bị</a></dt> + <dd>Thông tin về Android hoạt động trên các loại thiết bị khác nhau và giới thiệu + về cách bạn có thể tối ưu hóa ứng dụng của mình cho từng thiết bị hoặc hạn chế tính sẵn có của ứng dụng của bạn + đối với các thiết bị khác nhau.</dd> + <dt><a href="{@docRoot}guide/topics/security/permissions.html">Quyền của Hệ thống</a></dt> + <dd>Thông tin về cách Android hạn chế truy cập của ứng dụng vào một số API nhất định bằng một hệ thống + quyền cần có sự đồng ý của người dùng cho phép ứng dụng của bạn có thể sử dụng các API đó.</dd> + </dl> +</div> +</div> + diff --git a/docs/html-intl/intl/vi/guide/components/index.jd b/docs/html-intl/intl/vi/guide/components/index.jd new file mode 100644 index 000000000000..966597d999c2 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/index.jd @@ -0,0 +1,57 @@ +page.title=Thành phần Ứng dụng +page.landing=true +page.landing.intro=Khuôn khổ ứng dụng của Android cho phép bạn tạo lập nhiều ứng dụng đa dạng và sáng tạo bằng cách sử dụng một tập hợp các thành phần có thể tái sử dụng. Phần này giải thích cách bạn có thể xây dựng các thành phần định nghĩa các khối dựng cho ứng dụng của mình và cách kết nối chúng với nhau bằng cách sử dụng ý định. +page.metaDescription=Khuôn khổ ứng dụng của Android cho phép bạn tạo lập nhiều ứng dụng đa dạng và sáng tạo bằng cách sử dụng một tập hợp các thành phần có thể tái sử dụng. Phần này giải thích cách bạn có thể xây dựng các thành phần định nghĩa các khối dựng cho ứng dụng của mình và cách kết nối chúng với nhau bằng cách sử dụng ý định. +page.landing.image=images/develop/app_components.png +page.image=images/develop/app_components.png + +@jd:body + +<div class="landing-docs"> + + <div class="col-6"> + <h3>Bài viết Blog</h3> + + <a href="http://android-developers.blogspot.com/2012/05/using-dialogfragments.html"> + <h4>Sử dụng DialogFragments</h4> + <p>Trong bài viết này, tôi sẽ trình bày cách sử dụng DialogFragments bằng thư viện hỗ trợ v4 (cho khả năng tương thích ngược trên các thiết bị chạy phiên bản trước Honeycomb) để hiển thị một hộp thoại chỉnh sửa đơn giản và trả về một kết quả cho lệnh gọi Hoạt động bằng cách sử dụng một giao diện.</p> + </a> + + <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> + <h4>Phân đoạn cho Tất cả</h4> + <p>Hôm nay, chúng tôi đã phát hành một thư viện tĩnh giới thiệu API Phân đoạn (cũng như LoaderManager mới và một vài lớp khác) tương tự sao cho các ứng dụng tương thích với phiên bản Android 1.6 hoặc mới hơn có thể sử dụng phân đoạn để tạo các giao diện người dùng tương thích với máy tính bảng. </p> + </a> + + <a href="http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html"> + <h4>Tạo đa luồng cho Hiệu năng</h4> + <p>Một cách làm hay trong khi tạo các ứng dụng hồi đáp đó là đảm bảo luồng UI chính của bạn +thực hiện lượng công việc tối thiểu. Bất kỳ tác vụ dài tiềm ẩn nào mà có thể làm treo ứng dụng của bạn đều cần được +xử lý trong một luồng khác.</p> + </a> + </div> + + <div class="col-6"> + <h3>Đào tạo</h3> + + <a href="http://developer.android.com/training/basics/activity-lifecycle/index.html"> + <h4>Quản lý Vòng đời của Hoạt động</h4> + <p>Lớp này giải thích các phương pháp gọi lại vòng đời quan trọng mà mỗi thực thể + Hoạt động nhận được và cách bạn có thể sử dụng chúng sao cho hoạt động của bạn thực hiện như người dùng kỳ vọng và không tiêu tốn tài nguyên + của hệ thống khi hoạt động của bạn không cần chúng.</p> + </a> + + <a href="http://developer.android.com/training/basics/fragments/index.html"> + <h4>Xây dựng một UI Động bằng các Phân đoạn</h4> + <p>Lớp này trình bày với bạn cách tạo một trải nghiệm người dùng động bằng các phân đoạn và tối ưu hóa +trải nghiệm người dùng của ứng dụng của bạn đối với các thiết bị có kích cỡ màn hình khác nhau trong khi vẫn tiếp tục hỗ trợ +các thiết bị chạy phiên bản cũ như Android 1.6.</p> + </a> + + <a href="http://developer.android.com/training/sharing/index.html"> + <h4>Chia sẻ Nội dung</h4> + <p>Lớp này trình bày một số cách thông dụng mà bạn có thể gửi và nhận nội dung giữa + các ứng dụng bằng cách sử dụng các API Ý định và đối tượng ActionProvider.</p> + </a> + </div> + +</div> diff --git a/docs/html-intl/intl/vi/guide/components/intents-filters.jd b/docs/html-intl/intl/vi/guide/components/intents-filters.jd new file mode 100644 index 000000000000..cdc623f8e8ce --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/intents-filters.jd @@ -0,0 +1,899 @@ +page.title=Ý định và Bộ lọc Ý định +page.tags="IntentFilter" +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Trong tài liệu này</h2> +<ol> + <li><a href="#Types">Các Loại Ý định</a></li> + <li><a href="#Building">Xây dựng một Ý định</a> + <ol> + <li><a href="#ExampleExplicit">Ví dụ về ý định biểu thị</a></li> + <li><a href="#ExampleSend">Ví dụ về ý định không biểu thị</a></li> + <li><a href="#ForceChooser">Bắt buộc một bộ chọn ứng dụng</a></li> + </ol> + </li> + <li><a href="#Receiving">Nhận một Ý định Không biểu thị</a> + <ol> + <li><a href="#ExampleFilters">Ví dụ về bộ lọc</a></li> + </ol> + </li> + <li><a href="#PendingIntent">Sử dụng một Ý định Chờ</a></li> + <li><a href="#Resolution">Giải quyết Ý định</a> + <ol> + <li><a href="#ActionTest">Kiểm tra hành động</a></li> + <li><a href="#CategoryTest">Kiểm tra thể loại</a></li> + <li><a href="#DataTest">Kiểm tra dữ liệu</a></li> + <li><a href="#imatch">So khớp ý định</a></li> + </ol> + </li> +</ol> + +<h2>Xem thêm</h2> +<ol> +<li><a href="{@docRoot}training/basics/intents/index.html">Tương tác với các Ứng dụng khác</a></li> +<li><a href="{@docRoot}training/sharing/index.html">Chia sẻ Nội dung</a></li> +</ol> + +</div> +</div> + + + + +<p>{@link android.content.Intent} là một đối tượng nhắn tin mà bạn có thể sử dụng để yêu cầu một hành động +từ một <a href="{@docRoot}guide/components/fundamentals.html#Components">thành phần ứng dụng</a> khác. +Mặc dù các ý định sẽ tạo điều kiện cho giao tiếp giữa các thành phần bằng một vài cách, có ba +trường hợp sử dụng cơ bản:</p> + +<ul> +<li><b>Để bắt đầu một hoạt động:</b> +<p>{@link android.app.Activity} biểu diễn một màn hình đơn trong một ứng dụng. Bạn có thể bắt đầu một thực thể +mới của một {@link android.app.Activity} bằng cách chuyển {@link android.content.Intent} +sang {@link android.content.Context#startActivity startActivity()}. {@link android.content.Intent} +mô tả hoạt động cần bắt đầu và mang theo mọi dữ liệu cần thiết.</p> + +<p>Nếu bạn muốn nhận một kết quả từ hoạt động khi nó hoàn thành, +hãy gọi {@link android.app.Activity#startActivityForResult +startActivityForResult()}. Hoạt động của bạn nhận được kết quả +dưới dạng một đối tượng {@link android.content.Intent} riêng biệt trong lệnh gọi lại {@link +android.app.Activity#onActivityResult onActivityResult()} của hoạt động của bạn. +Để biết thêm thông tin, hãy xem hướng dẫn <a href="{@docRoot}guide/components/activities.html">Hoạt động</a>.</p></li> + +<li><b>Để bắt đầu một dịch vụ:</b> +<p>{@link android.app.Service} là một thành phần có chức năng thực hiện các thao tác dưới nền +mà không cần giao diện người dùng. Bạn có thể bắt đầu một dịch vụ để thực hiện một thao tác một lần +(chẳng hạn như tải xuống một tệp) bằng cách chuyển {@link android.content.Intent} +tới {@link android.content.Context#startService startService()}. {@link android.content.Intent} +mô tả dịch vụ cần bắt đầu và mang theo mọi dữ liệu cần thiết.</p> + +<p>Nếu dịch vụ được thiết kế với một giao diện máy khách-máy chủ, bạn có thể gắn kết với dịch vụ +từ một thành phần khác bằng cách chuyển {@link android.content.Intent} sang {@link +android.content.Context#bindService bindService()}</code>. Để biết thêm thông tin, hãy xem hướng dẫn <a href="{@docRoot}guide/components/services.html">Dịch vụ</a>.</p></li> + +<li><b>Để chuyển một quảng bá:</b> +<p>Quảng bá là một tin nhắn mà bất kỳ ứng dụng nào cũng có thể nhận được. Hệ thống sẽ chuyển các quảng bá +khác nhau tới các sự kiện hệ thống, chẳng hạn như khi hệ thống khởi động hoặc thiết bị bắt đầu sạc. +Bạn có thể chuyển một quảng bá tới các ứng dụng khác bằng cách chuyển một {@link android.content.Intent} +tới {@link android.content.Context#sendBroadcast(Intent) sendBroadcast()}, +{@link android.content.Context#sendOrderedBroadcast(Intent, String) +sendOrderedBroadcast()}, hoặc {@link +android.content.Context#sendStickyBroadcast sendStickyBroadcast()}.</p> +</li> +</ul> + + + + +<h2 id="Types">Các Loại Ý định</h2> + +<p>Có hai loại ý định:</p> + +<ul> +<li><b>Ý định biểu thị</b> quy định thành phần cần bắt đầu theo tên (tên lớp hoàn toàn đạt tiêu chuẩn +). Thường bạn sẽ sử dụng một ý định biểu thị để bắt đầu một thành phần trong +ứng dụng của chính mình, vì bạn biết tên lớp của hoạt động hay dịch vụ mà mình muốn bắt đầu. Ví dụ +, bắt đầu một hoạt động mới để hồi đáp một hành động của người dùng hay bắt đầu một dịch vụ để tải xuống +tệp dưới nền.</li> + +<li><b>Ý định không biểu thị</b> không chỉ định một thành phần cụ thể mà thay vào đó, sẽ khai báo một hành động thông thường +cần thực hiện, cho phép một thành phần từ một ứng dụng khác xử lý nó. Ví dụ, nếu bạn muốn +hiển thị cho người dùng một vị trí trên bản đồ, bạn có thể sử dụng một ý định không biểu thị để yêu cầu một ứng dụng +có khả năng khác hiển thị một vị trí được chỉ định trên bản đồ.</li> +</ul> + +<p>Khi bạn tạo một ý định biểu thị để bắt đầu một hoạt động hoặc dịch vụ, hệ thống ngay lập tức +sẽ bắt đầu thành phần ứng dụng được quy định trong đối tượng {@link android.content.Intent}.</p> + +<div class="figure" style="width:446px"> +<img src="{@docRoot}images/components/intent-filters@2x.png" width="446" alt="" /> +<p class="img-caption"><strong>Hình 1.</strong> Minh họa về cách một ý định không biểu thị được +chuyển thông qua hệ thống để bắt đầu một hoạt động khác: <b>[1]</b> <em>Hoạt động A</em> tạo một +{@link android.content.Intent} bằng một mô tả hành động và chuyển nó cho {@link +android.content.Context#startActivity startActivity()}. <b>[2]</b> Hệ thống Android tìm kiếm tất cả +ứng dụng xem có một bộ lọc ý định khớp với ý định đó không. Khi tìm thấy kết quả khớp, <b>[3]</b> hệ thống +sẽ bắt đầu hoạt động so khớp đó (<em>Hoạt động B</em>) bằng cách gọi ra phương pháp {@link +android.app.Activity#onCreate onCreate()} và chuyển nó cho {@link android.content.Intent}. +</p> +</div> + +<p>Khi bạn tạo một ý định không biểu thị, hệ thống Android sẽ tìm kiếm thành phần phù hợp để bắt đầu +bằng cách so sánh nội dung của ý định với các <em>bộ lọc ý định</em> được khai báo trong <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">tệp bản kê khai</a> của các ứng dụng khác trên +thiết bị. Nếu ý định khớp với một bộ lọc ý định, hệ thống sẽ bắt đầu thành phần đó và chuyển cho nó +đối tượng {@link android.content.Intent}. Nếu có nhiều bộ lọc ý định tương thích, hệ thống +sẽ hiển thị một hộp thoại để người dùng có thể chọn ứng dụng sẽ sử dụng.</p> + +<p>Bộ lọc ý định là một biểu thức trong tệp bản kê khai của một ứng dụng, có chức năng +chỉ định loại ý định mà thành phần +muốn nhận. Ví dụ, bằng cách khai báo một bộ lọc ý định cho một hoạt động, +bạn giúp các ứng dụng khác có thể trực tiếp bắt đầu hoạt động của mình với một loại ý định nhất định. +Tương tự, nếu bạn <em>không</em> khai báo bất kỳ bộ lọc ý định nào cho một hoạt động, khi đó nó chỉ có thể +được bắt đầu bằng một ý định biểu thị.</p> + +<p class="caution"><strong>Chú ý:</strong> Để đảm bảo ứng dụng của bạn được bảo mật, luôn sử dụng một ý định +biểu thị khi bắt đầu một {@link android.app.Service} và không được +khai báo bộ lọc ý định cho các dịch vụ của bạn. Việc sử dụng một ý định không biểu thị để bắt đầu một dịch vụ sẽ là một nguy cơ +về bảo mật vì bạn không thể chắc chắn dịch vụ nào sẽ hồi đáp ý định đó, +và người dùng không thể thấy dịch vụ nào bắt đầu. Bắt đầu với Android 5.0 (API mức 21), hệ thống +sẽ đưa ra lỗi ngoại lệ nếu bạn gọi {@link android.content.Context#bindService bindService()} +bằng một ý định không biểu thị.</p> + + + + + +<h2 id="Building">Xây dựng một Ý định</h2> + +<p>Đối tượng {@link android.content.Intent} mang thông tin mà hệ thống Android sử dụng +để xác định thành phần nào sẽ bắt đầu (chẳng hạn như tên thành phần chính xác hoặc thể loại +thành phần mà sẽ nhận ý định), cộng với thông tin mà thành phần nhận sử dụng để +thực hiện hành động cho phù hợp (chẳng hạn như hành động sẽ thực hiện và dữ liệu để dựa vào đó mà thực hiện).</p> + + +<p>Thông tin chính chứa trong một {@link android.content.Intent} như sau:</p> + +<dl> + +<dt><b>Tên thành phần</b></dt> +<dd>Tên của thành phần sẽ bắt đầu. + +<p>Nội dung này không bắt buộc, nhưng đó là một thông tin trọng yếu để khiến một ý định trở nên +<b>biểu thị</b>, có nghĩa là ý định nên chỉ được chuyển tới thành phần ứng dụng +được xác định bởi tên thành phần đó. Nếu thiếu một tên thành phần, ý định trở thành <b>không biểu thị</b> và hệ thống +sẽ quyết định thành phần nào nhận ý định đó dựa trên các thông tin còn lại của ý định +(chẳng hạn như hành động, dữ liệu và thể loại—được mô tả bên dưới). Vì vậy, nếu bạn bắt đầu một thành phần +cụ thể trong ứng dụng của mình, bạn nên chỉ định tên thành phần.</p> + +<p class="note"><strong>Lưu ý:</strong> Khi bắt đầu một {@link android.app.Service}, bạn nên +<strong>luôn chỉ định tên thành phần</strong>. Nếu không, bạn không thể chắc chắn dịch vụ nào +sẽ hồi đáp ý định và người dùng không thể thấy dịch vụ nào bắt đầu.</p> + +<p>Trường này của {@link android.content.Intent} là một đối tượng +{@link android.content.ComponentName} mà bạn có thể chỉ định bằng cách sử dụng một tên lớp +hoàn toàn đủ tiêu chuẩn của thành phần đích, bao gồm tên gói của ứng dụng. Ví dụ, +{@code com.example.ExampleActivity}. Bạn có thể đặt tên thành phần bằng {@link +android.content.Intent#setComponent setComponent()}, {@link android.content.Intent#setClass +setClass()}, {@link android.content.Intent#setClassName(String, String) setClassName()}, hoặc bằng + hàm dựng {@link android.content.Intent}.</p> + +</dd> + +<p><dt><b>Hành động</b></dt> +<dd>Một xâu quy định hành động thông thường sẽ thực hiện (chẳng hạn như <em>xem</em> hoặc <em>chọn</em>). + +<p>Trong trường hợp một ý định quảng bá, đây là hành động đã diễn ra và đang được báo cáo. +Hành động này quyết định phần lớn cách thức xác định cấu trúc phần còn lại của ý định—đặc biệt là +những gì chứa trong dữ liệu và phụ thêm. + +<p>Bạn có thể quy định các hành động của chính mình để các ý định bên trong ứng dụng của bạn sử dụng (hoặc để +các ứng dụng khác sử dụng nhằm gọi ra các thành phần trong ứng dụng của mình), nhưng bạn nên thường xuyên sử dụng hằng số hành động +được định nghĩa bởi lớp {@link android.content.Intent} hoặc các lớp khuôn khổ khác. Sau đây là một số +hành động thường dùng để bắt đầu một hoạt động:</p> + +<dl> +<dt>{@link android.content.Intent#ACTION_VIEW}</dt> + <dd>Sử dụng hành động này trong một ý định với {@link + android.content.Context#startActivity startActivity()} khi bạn có một số thông tin mà + một hoạt động có thể hiển thị cho người dùng, chẳng hạn như ảnh để xem trong một ứng dụng bộ sưu tập ảnh, hay địa chỉ để + xem trong ứng dụng bản đồ.</dd> + +<dt>{@link android.content.Intent#ACTION_SEND}</dt> + <dd>Còn được biết đến như là ý định "chia sẻ", bạn nên sử dụng kiểu này trong một ý định với {@link + android.content.Context#startActivity startActivity()} khi bạn có một số dữ liệu mà người dùng có thể + chia sẻ thông qua một ứng dụng khác, chẳng hạn như một ứng dụng e-mail hay ứng dụng chia sẻ mạng xã hội.</dd> +</dl> + +<p>Xem tham chiếu lớp {@link android.content.Intent} để biết thêm +hằng số có chức năng định nghĩa các hành động thông thường. Những hành động khác được định nghĩa +ở phần khác trong khuôn khổ Android, chẳng hạn như trong {@link android.provider.Settings} đối với những hành động +có chức năng mở màn hình cụ thể trong ứng dụng Cài đặt của hệ thống.</p> + +<p>Bạn có thể quy định hành động cho một ý định với {@link android.content.Intent#setAction +setAction()} hoặc với một hàm dựng {@link android.content.Intent}.</p> + +<p>Nếu bạn định nghĩa các hành động của chính mình, nhớ nêu tên gói ứng dụng của bạn +làm tiền tố. Ví dụ:</p> +<pre>static final String ACTION_TIMETRAVEL = "com.example.action.TIMETRAVEL";</pre> +</dd> + +<dt><b>Dữ liệu</b></dt> +<dd>URI (một đối tượng {@link android.net.Uri}) tham chiếu dữ liệu sẽ được hành động dựa trên nó và/hoặc kiểu MIME +của dữ liệu đó. Kiểu dữ liệu được cung cấp thường sẽ bị chi phối bởi hành động của ý định. Ví +dụ, nếu hành động là {@link android.content.Intent#ACTION_EDIT}, dữ liệu cần chứa +URI của tài liệu cần chỉnh sửa. + +<p>Khi tạo một ý định, +một điều thường quan trọng đó là quy định kiểu dữ liệu (kiểu MIME của nó) ngoài URI của nó. +Ví dụ, một hoạt động có thể hiển thị hình ảnh có thể sẽ không +phát được tệp âm thanh, ngay cả khi định dạng URI có thể tương tự. +Vì thế, việc quy định kiểu MIME cho dữ liệu của bạn sẽ giúp hệ thống +Android tìm được thành phần tốt nhất để nhận ý định của bạn. +Tuy nhiên, kiểu MIME đôi khi có thể được suy ra từ URI—cụ thể, khi dữ liệu là một URI +{@code content:}, có chức năng cho biết dữ liệu nằm trên thiết bị và được kiểm soát bởi một +{@link android.content.ContentProvider}, điều này khiến kiểu MIME của dữ liệu hiển thị đối với hệ thống.</p> + +<p>Để chỉ đặt URI dữ liệu, hãy gọi {@link android.content.Intent#setData setData()}. +Để chỉ đặt kiểu MIME, hãy gọi {@link android.content.Intent#setType setType()}. Nếu cần, bạn +bạn có thể công khai đặt cả hai với {@link +android.content.Intent#setDataAndType setDataAndType()}.</p> + +<p class="caution"><strong>Chú ý:</strong> Nếu bạn muốn đặt cả URI và kiểu MIME, +<strong>không</strong> gọi {@link android.content.Intent#setData setData()} và +{@link android.content.Intent#setType setType()} vì chúng sẽ vô hiệu hóa giá trị của nhau. +Luôn sử dụng {@link android.content.Intent#setDataAndType setDataAndType()} để đặt cả +URI và kiểu MIME.</p> +</dd> + +<p><dt><b>Thể loại</b></dt> +<dd>Một xâu chứa thông tin bổ sung về kiểu thành phần +sẽ xử lý ý định. Trong một ý định có thể chứa +nhiều mô tả thể loại, nhưng hầu hết các ý định lại không yêu cầu thể loại. +Sau đây là một số thể loại thường gặp: + +<dl> +<dt>{@link android.content.Intent#CATEGORY_BROWSABLE}</dt> + <dd>Hoạt động mục tiêu cho phép chính nó được bắt đầu bởi một trình duyệt web để hiển thị dữ liệu + được một liên kết tham chiếu—chẳng hạn như một hình ảnh hay thư e-mail. + </dd> +<dt>{@link android.content.Intent#CATEGORY_LAUNCHER}</dt> + <dd>Hoạt động là hoạt động ban đầu của một tác vụ và được liệt kê trong + trình khởi chạy ứng dụng của hệ thống. + </dd> +</dl> + +<p>Xem mô tả lớp {@link android.content.Intent} để biết danh sách đầy đủ về các +thể loại.</p> + +<p>Bạn có thể quy định một thể loại bằng {@link android.content.Intent#addCategory addCategory()}.</p> +</dd> +</dl> + + +<p>Những tính chất này được liệt kê ở trên (tên thành phần, hành động, dữ liệu và thể loại) biểu hiện các +đặc điểm xác định của một ý định. Bằng cách đọc những tính chất này, hệ thống Android +có thể quyết định nó sẽ bắt đầu thành phần ứng dụng nào.</p> + +<p>Tuy nhiên, một ý định có thể mang thông tin bổ sung không ảnh hưởng tới +cách nó được giải quyết đối với một thành phần ứng dụng. Một ý định cũng có thể cung cấp:</p> + +<dl> +<dt><b>Phụ thêm</b></dt> +<dd>Các cặp khóa-giá trị mang thông tin bổ sung cần thiết để hoàn thành hành động được yêu cầu. +Giống như việc một số hành động sử dụng các kiểu URI dữ liệu riêng, một số hành động cũng sử dụng các phần phụ thêm riêng. + +<p>Bạn có thể thêm dữ liệu phụ thêm bằng các phương pháp {@link android.content.Intent#putExtra putExtra()} khác nhau, +mỗi phương pháp chấp nhận hai tham số: tên khóa và giá trị. +Bạn cũng có thể tạo một đối tượng {@link android.os.Bundle} bằng tất cả dữ liệu phụ thêm, sau đó chèn + {@link android.os.Bundle} vào {@link android.content.Intent} bằng {@link +android.content.Intent#putExtras putExtras()}.</p> + +<p>Ví dụ, khi tạo một ý định để gửi một e-mail bằng +{@link android.content.Intent#ACTION_SEND}, bạn có thể chỉ định người nhận "tới" bằng khóa +{@link android.content.Intent#EXTRA_EMAIL}, và chỉ định "chủ đề" bằng khóa +{@link android.content.Intent#EXTRA_SUBJECT}.</p> + +<p>Lớp {@link android.content.Intent} quy định nhiều hằng số {@code EXTRA_*} cho +các kiểu dữ liệu chuẩn hóa. Nếu bạn cần khai báo các khóa phụ thêm của riêng mình (cho những ý định +mà ứng dụng của bạn nhận), hãy chắc chắn nêu tên gói ứng dụng của bạn +làm tiền tố. Ví dụ:</p> +<pre>static final String EXTRA_GIGAWATTS = "com.example.EXTRA_GIGAWATTS";</pre> +</dd> + +<dt><b>Cờ</b></dt> +<dd>Cờ được định nghĩa trong lớp {@link android.content.Intent} có chức năng như siêu dữ liệu cho +ý định. Cờ có thể chỉ lệnh hệ thống Android về cách khởi chạy một hoạt động (ví dụ, hoạt động sẽ thuộc về +<a href="{@docRoot}guide/components/tasks-and-back-stack.html">tác vụ</a> nào +) và cách xử lý sau khi nó được khởi chạy (ví dụ, nó có thuộc về danh sách hoạt động +gần đây hay không). + +<p>Để biết thêm thông tin, hãy xem phương pháp {@link android.content.Intent#setFlags setFlags()}.</p> +</dd> + +</dl> + + + + +<h3 id="ExampleExplicit">Ví dụ về ý định biểu thị</h3> + +<p>Ý định biểu thị là ý định mà bạn sử dụng để khởi chạy một thành phần ứng dụng cụ thể +chẳng hạn như một hoạt động hay dịch vụ cụ thể trong ứng dụng của bạn. Để tạo một ý định biểu thị, hãy định nghĩa +tên thành phần cho đối tượng {@link android.content.Intent} —tất cả các +tính chất ý định khác đều không bắt buộc.</p> + +<p>Ví dụ, nếu bạn đã xây dựng một dịch vụ trong ứng dụng của mình, đặt tên là {@code DownloadService}, +được thiết kế để tải xuống một tệp từ web, bạn có thể bắt đầu nó bằng mã sau:</p> + +<pre> +// Executed in an Activity, so 'this' is the {@link android.content.Context} +// The fileUrl is a string URL, such as "http://www.example.com/image.png" +Intent downloadIntent = new Intent(this, DownloadService.class); +downloadIntent.setData({@link android.net.Uri#parse Uri.parse}(fileUrl)); +startService(downloadIntent); +</pre> + +<p>Hàm dựng {@link android.content.Intent#Intent(Context,Class)} +cung cấp cho ứng dụng {@link android.content.Context} và thành phần +một đối tượng {@link java.lang.Class}. Như vậy, +ý định này rõ ràng sẽ bắt đầu lớp {@code DownloadService} trong ứng dụng.</p> + +<p>Để biết thêm thông tin về việc xây dựng và bắt đầu một dịch vụ, hãy xem hướng dẫn +<a href="{@docRoot}guide/components/services.html">Dịch vụ</a>.</p> + + + + +<h3 id="ExampleSend">Ví dụ về ý định không biểu thị</h3> + +<p>Ý định không biểu thị quy định một hành động mà có thể gọi ra bất kỳ ứng dụng nào trên thiết bị mà có +khả năng thực hiện hành động đó. Việc sử dụng ý định không biểu thị có ích khi ứng dụng của bạn không thể thực hiện +hành động, nhưng các ứng dụng khác có thể và bạn muốn người dùng chọn ứng dụng sẽ sử dụng.</p> + +<p>Ví dụ, nếu bạn có nội dung mà mình muốn người dùng chia sẻ với người khác, hãy tạo một ý định +với hành động {@link android.content.Intent#ACTION_SEND} và +bổ sung phần phụ thêm quy định nội dung sẽ chia sẻ. Khi bạn gọi +{@link android.content.Context#startActivity startActivity()} bằng ý định đó, người dùng có thể +chọn một ứng dụng để chia sẻ nội dung thông qua đó.</p> + +<p class="caution"><strong>Chú ý:</strong> Có thể là người dùng sẽ không có <em>bất kỳ</em> +ứng dụng nào xử lý được ý định không biểu thị mà bạn gửi tới {@link android.content.Context#startActivity +startActivity()}. Nếu chuyện đó xảy ra, phương pháp gọi sẽ thất bại và ứng dụng của bạn sẽ gặp lỗi. Để xác minh rằng +một hoạt động sẽ nhận được ý định, hãy gọi {@link android.content.Intent#resolveActivity +resolveActivity()} trên đối tượng {@link android.content.Intent} của bạn. Nếu kết quả không rỗng +thì có ít nhất một ứng dụng có thể xử lý ý định và sẽ an toàn nếu gọi +{@link android.content.Context#startActivity startActivity()}. Nếu kết quả rỗng, +bạn không nên sử dụng ý định và, nếu có thể, bạn nên vô hiệu hóa tính năng phát hành +ý định.</p> + + +<pre> +// Create the text message with a string +Intent sendIntent = new Intent(); +sendIntent.setAction(Intent.ACTION_SEND); +sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage); +sendIntent.setType("text/plain"); + +// Verify that the intent will resolve to an activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(sendIntent); +} +</pre> + +<p class="note"><strong>Lưu ý:</strong> Trong trường hợp này, URI không được sử dụng, nhưng kiểu dữ liệu của ý định +sẽ được khai báo để quy định nội dung được thực hiện bởi phần phụ thêm.</p> + + +<p>Khi {@link android.content.Context#startActivity startActivity()} được gọi, hệ thống +sẽ kiểm tra tất cả ứng dụng đã cài đặt để xác định những ứng dụng có thể xử lý kiểu ý định này (một +ý định với hành động {@link android.content.Intent#ACTION_SEND} và có mang dữ liệu +"văn bản/thuần"). Nếu chỉ có một ứng dụng có thể xử lý nó, ứng dụng đó sẽ mở ngay lập tức và được cấp cho +ý định. Nếu có nhiều hoạt động chấp nhận ý định, hệ thống +sẽ hiển thị một hộp thoại để người dùng có thể chọn ứng dụng sẽ sử dụng.</p> + + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/training/basics/intent-chooser.png" alt=""> + <p class="img-caption"><strong>Hình 2.</strong> Hộp thoại bộ chọn.</p> +</div> + +<h3 id="ForceChooser">Bắt buộc một bộ chọn ứng dụng</h3> + +<p>Khi có nhiều hơn một ứng dụng hồi đáp ý định không biểu thị của bạn, +người dùng có thể chọn ứng dụng nào sẽ sử dụng và đặt ứng dụng đó làm lựa chọn mặc định cho +hành động. Điều này tốt khi thực hiện một hành động mà người dùng +có thể muốn sử dụng ứng dụng tương tự từ lúc này trở đi, chẳng hạn như khi mở một trang web (người dùng +thường thích ưu tiên sử dụng chỉ một trình duyệt web).</p> + +<p>Tuy nhiên, nếu nhiều ứng dụng có thể hồi đáp ý định và người dùng có thể muốn sử dụng mỗi +lần một ứng dụng khác, bạn nên công khai hiển thị một hộp thoại bộ chọn. Hộp thoại bộ chọn yêu cầu +người dùng phải chọn ứng dụng sẽ sử dụng mỗi lần cho hành động (người dùng không thể chọn một ứng dụng mặc định cho +hành động). Ví dụ, khi ứng dụng của bạn thực hiện "chia sẻ" với hành động {@link +android.content.Intent#ACTION_SEND}, người dùng có thể muốn chia sẻ bằng cách sử dụng một ứng dụng khác tùy vào +tình hình thực tế của họ, vì thế bạn nên luôn sử dụng hộp thoại bộ chọn như minh họa trong hình 2.</p> + + + + +<p>Để hiển thị bộ chọn, hãy tạo một {@link android.content.Intent} bằng cách sử dụng {@link +android.content.Intent#createChooser createChooser()} và chuyển nó sang {@link +android.app.Activity#startActivity startActivity()}. Ví dụ:</p> + +<pre> +Intent sendIntent = new Intent(Intent.ACTION_SEND); +... + +// Always use string resources for UI text. +// This says something like "Share this photo with" +String title = getResources().getString(R.string.chooser_title); +// Create intent to show the chooser dialog +Intent chooser = Intent.createChooser(sendIntent, title); + +// Verify the original intent will resolve to at least one activity +if (sendIntent.resolveActivity(getPackageManager()) != null) { + startActivity(chooser); +} +</pre> + +<p>Một hộp thoại hiển thị với một danh sách ứng dụng hồi đáp lại ý định được chuyển sang phương pháp {@link +android.content.Intent#createChooser createChooser()} và sử dụng văn bản được cung cấp làm +tiêu đề của hộp thoại.</p> + + + + + + + + + +<h2 id="Receiving">Nhận một Ý định Không biểu thị</h2> + +<p>Để quảng cáo những ý định không biểu thị mà ứng dụng của bạn có thể nhận, hãy khai báo một hoặc nhiều bộ lọc ý định cho +từng thành phần ứng dụng của bạn với một phần tử <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +trong <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">tệp bản kê khai của mình</a>. +Mỗi bộ lọc ý định sẽ quy định loại ý định mà nó chấp nhận dựa trên hành động, +dữ liệu và thể loại của ý định. Hệ thống sẽ chỉ chuyển một ý định không biểu thị tới thành phần ứng dụng của bạn nếu +ý định đó có thể chuyển qua một trong các bộ lọc ý định của bạn.</p> + +<p class="note"><strong>Lưu ý:</strong> Ý định biểu thị luôn được chuyển tới mục tiêu của mình, +không phụ thuộc vào bất kỳ bộ lọc ý định nào mà thành phần khai báo.</p> + +<p>Một thành phần ứng dụng nên khai báo các bộ lọc riêng cho từng công việc duy nhất mà nó có thể thực hiện. +Ví dụ, một hoạt động trong một ứng dụng bộ sưu tập ảnh có thể có hai bộ lọc: một bộ lọc +để xem một hình ảnh và một bộ lọc để chỉnh sửa một hình ảnh. Khi hoạt động bắt đầu, +nó sẽ kiểm tra {@link android.content.Intent} và quyết định cách xử lý dựa trên thông tin +trong {@link android.content.Intent} (chẳng hạn như có hiển thị các điều khiển của trình chỉnh sửa hoặc không).</p> + +<p>Mỗi bộ lọc ý định sẽ được định nghĩa bởi một phần tử <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a> +trong tệp bản kê khai của ứng dụng, được lồng trong thành phần ứng dụng tương ứng (chẳng hạn như +một phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +). Bên trong <a href="{@docRoot}guide/topics/manifest/intent-filter-element.html">{@code <intent-filter>}</a>, +bạn có thể quy định loại ý định sẽ chấp nhận bằng cách sử dụng một hoặc nhiều +phần tử trong ba phần tử sau:</p> + +<dl> +<dt><a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a></dt> + <dd>Khai báo hành động ý định được chấp nhận, trong thuộc tính {@code name}. Giá trị + phải là giá trị xâu ký tự của một hành động chứ không phải hằng số lớp.</dd> +<dt><a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a></dt> + <dd>Khai báo kiểu dữ liệu được chấp nhận, bằng cách sử dụng một hoặc nhiều thuộc tính quy định + các khía cạnh của URI dữ liệu (<code>scheme</code>, <code>host</code>, <code>port</code>, + <code>path</code>, v.v.) và kiểu MIME.</dd> +<dt><a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a></dt> + <dd>Khai báo thể loại ý định được chấp nhận, trong thuộc tính {@code name}. Giá trị + phải là giá trị xâu ký tự của một hành động chứ không phải hằng số lớp. + + <p class="note"><strong>Lưu ý:</strong> Để nhận các ý định không biểu thị, bạn + <strong>phải nêu</strong> thể loại + {@link android.content.Intent#CATEGORY_DEFAULT} trong bộ lọc ý định. Các phương pháp + {@link android.app.Activity#startActivity startActivity()} và + {@link android.app.Activity#startActivityForResult startActivityForResult()} xử lý tất cả ý định + như thể chúng khai báo thể loại {@link android.content.Intent#CATEGORY_DEFAULT}. + Nếu bạn không khai báo thể loại này trong bộ lọc ý định của mình, không có ý định không biểu thị nào sẽ phân giải thành + hoạt động của bạn.</p> + </dd> +</dl> + +<p>Ví dụ, sau đây là một khai báo hoạt động với một bộ lọc ý định để nhận một ý định +{@link android.content.Intent#ACTION_SEND} khi kiểu dữ liệu là văn bản:</p> + +<pre> +<activity android:name="ShareActivity"> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> +</activity> +</pre> + +<p>Không sao nếu tạo một bộ lọc chứa nhiều hơn một thực thể của +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code <action>}</a>, +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, hoặc +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code <category>}</a>. +Nếu làm vậy, bạn chỉ cần chắc chắn rằng thành phần có thể xử lý bất kỳ và tất cả các cách kết hợp +những phần tử bộ lọc đó.</p> + +<p>Khi bạn muốn xử lý nhiều kiểu ý định, nhưng chỉ theo các cách kết hợp cụ thể giữa +hành động, dữ liệu và kiểu thể loại, khi đó bạn cần tạo nhiều bộ lọc ý định.</p> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h2>Hạn chế truy cập vào các thành phần</h2> +<p>Sử dụng một bộ lọc ý định không phải là một cách bảo mật để ngăn các ứng dụng khác bắt đầu +các thành phần của bạn. Mặc dù bộ lọc ý định hạn chế một thành phần chỉ hồi đáp +một số kiểu ý định không biểu thị nhất định, một ứng dụng khác có thể có khả năng bắt đầu thành phần ứng dụng của bạn +bằng cách sử dụng ý định biểu thị nếu nhà phát triển xác định tên thành phần của bạn. +Nếu điều quan trọng là <em>chỉ ứng dụng của chính bạn</em> mới có thể bắt đầu một trong các thành phần của bạn, +hãy đặt thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#exported">{@code +exported}</a> thành {@code "false"} cho thành phần đó. +</p> +</div> +</div> + +<p>Ý định không biểu thị sẽ được kiểm tra dựa trên một bộ lọc bằng cách so sánh ý định với từng phần tử trong số +ba phần tử. Để được chuyển tới thành phần, ý định phải vượt qua tất cả ba lần kiểm tra. +Nếu không khớp với thậm chí chỉ một lần thì hệ thống Android sẽ không chuyển ý định tới +thành phần. Tuy nhiên, vì một thành phần có thể có nhiều bộ lọc ý định, ý định mà không chuyển qua +một trong các bộ lọc của thành phần có thể chuyển qua trên một bộ lọc khác. +Bạn có thể tìm hiểu thêm thông tin về cách hệ thống giải quyết ý định trong phần bên dưới +về <a href="#Resolution">Giải quyết Ý định</a>.</p> + +<p class="caution"><strong>Chú ý:</strong> Để tránh vô ý chạy +{@link android.app.Service} của một ứng dụng khác, hãy luôn sử dụng một ý định biểu thị để bắt đầu dịch vụ của chính bạn và không được +khai báo các bộ lọc ý định cho dịch vụ của bạn.</p> + +<p class="note"><strong>Lưu ý:</strong> +Đối với tất cả hoạt động, bạn phải khai báo các bộ lọc ý định của mình trong một tệp bản kê khai. +Tuy nhiên, các bộ lọc cho hàm nhận quảng bá có thể được đăng ký linh hoạt bằng cách gọi +{@link android.content.Context#registerReceiver(BroadcastReceiver, IntentFilter, String, +Handler) registerReceiver()}. Sau đó, bạn có thể bỏ đăng ký hàm nhận đó bằng {@link +android.content.Context#unregisterReceiver unregisterReceiver()}. Làm vậy sẽ cho phép ứng dụng của bạn +lắng nghe các quảng bá cụ thể chỉ trong một khoảng thời gian xác định trong khi ứng dụng của bạn +đang chạy.</p> + + + + + + + +<h3 id="ExampleFilters">Ví dụ về bộ lọc</h3> + +<p>Để hiểu hơn về một số hành vi của bộ lọc ý định, hãy xem đoạn mã HTML sau +từ tệp bản kê khai của một ứng dụng chia sẻ mạng xã hội.</p> + +<pre> +<activity android:name="MainActivity"> + <!-- This activity is the main entry, should appear in app launcher --> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> +</activity> + +<activity android:name="ShareActivity"> + <!-- This activity handles "SEND" actions with text data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="text/plain"/> + </intent-filter> + <!-- This activity also handles "SEND" and "SEND_MULTIPLE" with media data --> + <intent-filter> + <action android:name="android.intent.action.SEND"/> + <action android:name="android.intent.action.SEND_MULTIPLE"/> + <category android:name="android.intent.category.DEFAULT"/> + <data android:mimeType="application/vnd.google.panorama360+jpg"/> + <data android:mimeType="image/*"/> + <data android:mimeType="video/*"/> + </intent-filter> +</activity> +</pre> + +<p>Hoạt động thứ nhất, {@code MainActivity}, là điểm mục nhập chính của ứng dụng—hoạt động này +sẽ mở khi người dùng khởi tạo ban đầu ứng dụng bằng biểu tượng trình khởi chạy:</p> +<ul> + <li>Hành động {@link android.content.Intent#ACTION_MAIN} thể hiện + đây là điểm mục nhập chính và không yêu cầu bất kỳ dữ liệu ý định nào.</li> + <li>Thể loại {@link android.content.Intent#CATEGORY_LAUNCHER} cho biết rằng biểu tượng + của hoạt động này nên được đặt trong trình khởi chạy ứng dụng của hệ thống. Nếu phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> + không quy định một biểu tượng bằng {@code icon}, khi đó hệ thống sẽ sử dụng biểu tượng từ phần tử <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +.</li> +</ul> +<p>Hai nội dung này phải được ghép đôi cùng nhau để hoạt động xuất hiện trong trình khởi chạy ứng dụng.</p> + +<p>Hoạt động thứ hai, {@code ShareActivity}, có mục đích để tạo điều kiện chia sẻ nội dung văn bản và +phương tiện. Mặc dù người dùng có thể nhập hoạt động này bằng cách điều hướng tới nó từ {@code MainActivity}, +họ cũng có thể nhập {@code ShareActivity} trực tiếp từ một ứng dụng khác mà phát hành +ý định không biểu thị khớp với một trong hai bộ lọc ý định.</p> + +<p class="note"><strong>Lưu ý:</strong> Kiểu MIME, +<a href="https://developers.google.com/panorama/android/">{@code +application/vnd.google.panorama360+jpg}</a>, là một kiểu dữ liệu đặc biệt quy định +ảnh chụp toàn cảnh mà bạn có thể xử lý bằng các API <a href="{@docRoot}reference/com/google/android/gms/panorama/package-summary.html">Google +panorama</a>.</p> + + + + + + + + + + + + + +<h2 id="PendingIntent">Sử dụng một Ý định Chờ</h2> + +<p>Đối tượng {@link android.app.PendingIntent} là một trình bao bọc xung quanh một đối tượng {@link +android.content.Intent}. Mục đích chính của một {@link android.app.PendingIntent} + là cấp quyền cho một ứng dụng ngoài +để sử dụng {@link android.content.Intent} chứa trong nó như thể nó được thực thi từ tiến trình +của chính ứng dụng của bạn.</p> + +<p>Các trường hợp sử dụng chính đối với một ý định chờ bao gồm:</p> +<ul> + <li>Khai báo một ý định cần được thực thi khi người dùng thực hiện một hành động bằng <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Thông báo</a> của bạn + ({@link android.app.NotificationManager} + của hệ thống Android thực thi {@link android.content.Intent}). + <li>Khai báo một ý định cần được thực thi khi người dùng thực hiện một hành động bằng + <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widget</a> của bạn + (ứng dụng màn hình Trang chủ thực thi {@link android.content.Intent}). + <li>Khai báo một ý định cần được thực thi tại một thời điểm xác định trong tương lai ( +{@link android.app.AlarmManager} của hệ thống Android thực thi {@link android.content.Intent}). +</ul> + +<p>Vì mỗi đối tượng {@link android.content.Intent} được thiết kế để được xử lý bởi một +loại thành phần ứng dụng cụ thể (hoặc là {@link android.app.Activity}, {@link android.app.Service}, hay + {@link android.content.BroadcastReceiver}), vì thế {@link android.app.PendingIntent} cũng +phải được tạo lập với cân nhắc tương tự. Khi sử dụng một ý định chờ, ứng dụng của bạn sẽ không +thực thi ý định bằng một lệnh gọi chẳng hạn như {@link android.content.Context#startActivity +startActivity()}. Thay vào đó, bạn phải khai báo loại thành phần theo ý định khi bạn tạo lập +{@link android.app.PendingIntent} bằng cách gọi phương pháp trình tạo lập tương ứng:</p> + +<ul> + <li>{@link android.app.PendingIntent#getActivity PendingIntent.getActivity()} đối với một + {@link android.content.Intent} mà bắt đầu {@link android.app.Activity}.</li> + <li>{@link android.app.PendingIntent#getService PendingIntent.getService()} đối với một + {@link android.content.Intent} mà bắt đầu {@link android.app.Service}.</li> + <li>{@link android.app.PendingIntent#getBroadcast PendingIntent.getBroadcast()} đối với một + {@link android.content.Intent} mà bắt đầu {@link android.content.BroadcastReceiver}.</li> +</ul> + +<p>Trừ khi ứng dụng của bạn đang <em>nhận</em> ý định chờ từ các ứng dụng khác, +các phương pháp để tạo lập {@link android.app.PendingIntent} trên là những phương pháp +{@link android.app.PendingIntent} duy nhất mà bạn sẽ cần.</p> + +<p>Mỗi phương pháp sẽ lấy ứng dụng {@link android.content.Context} hiện tại, +{@link android.content.Intent} mà bạn muốn bao bọc, và một hoặc nhiều cờ quy định +cách thức sử dụng ý định (chẳng hạn như ý định có thể được sử dụng nhiều hơn một lần hay không).</p> + +<p>Bạn có thể tham khảo thêm thông tin về việc sử dụng ý định chờ trong tài liệu cho từng +trường hợp sử dụng tương ứng chẳng hạn như trong hướng dẫn về API <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Thông báo</a> +và <a href="{@docRoot}guide/topics/appwidgets/index.html">App Widgets</a>.</p> + + + + + + + +<h2 id="Resolution">Giải quyết Ý định</h2> + + +<p>Khi hệ thống nhận được một ý định không biểu thị nhằm bắt đầu một hoạt động, nó sẽ tìm +hoạt động tốt nhất cho ý định đó bằng cách so sánh ý định với các bộ lọc ý định dựa trên ba phương diện:</p> + +<ul> + <li>Hành động của ý định + <li>Dữ liệu của ý định (cả URI và kiểu dữ liệu) + <li>Thể loại của ý định +</ul> + +<p>Các phần sau mô tả cách một ý định được so khớp với (các) thành phần phù hợp +về phương diện bộ lọc ý định được khai báo như thế nào trong tệp bản kê khai của một ứng dụng.</p> + + +<h3 id="ActionTest">Kiểm tra hành động</h3> + +<p>Để quy định các hành động của ý định được chấp nhận, một bộ lọc ý định có thể khai báo 0 phần tử +<a href="{@docRoot}guide/topics/manifest/action-element.html">{@code +<action>}</a> hoặc nhiều hơn. Ví dụ:</p> + +<pre> +<intent-filter> + <action android:name="android.intent.action.EDIT" /> + <action android:name="android.intent.action.VIEW" /> + ... +</intent-filter> +</pre> + +<p>Để vượt qua bộ lọc này, hành động được quy định trong {@link android.content.Intent} + phải khớp với một trong các hành động được liệt kê trong bộ lọc.</p> + +<p>Nếu bộ lọc không liệt kê bất kỳ hành động nào thì sẽ không có gì để +ý định so khớp, vì thế tất cả ý định sẽ không vượt qua kiểm tra. Tuy nhiên, nếu một {@link android.content.Intent} +không quy định một hành động, nó sẽ vượt qua kiểm tra (miễn là bộ lọc +chứa ít nhất một hành động).</p> + + + +<h3 id="CategoryTest">Kiểm tra thể loại</h3> + +<p>Để quy định các thể loại của ý định được chấp nhận, một bộ lọc ý định có thể khai báo 0 phần tử +<a href="{@docRoot}guide/topics/manifest/category-element.html">{@code +<category>}</a> hoặc nhiều hơn. Ví dụ:</p> + +<pre> +<intent-filter> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + ... +</intent-filter> +</pre> + +<p>Để một ý định vượt qua kiểm tra thể loại, mỗi thể loại trong {@link android.content.Intent} +phải khớp với một thể loại trong bộ lọc. Trường hợp ngược lại là không cần thiết—bộ lọc ý định có thể +khai báo nhiều thể loại hơn được quy định trong {@link android.content.Intent} và +{@link android.content.Intent} sẽ vẫn vượt qua. Vì thế, ý định không có thể loại +luôn vượt qua kiểm tra này, không phụ thuộc vào những thể loại nào được khai báo trong bộ lọc.</p> + +<p class="note"><strong>Lưu ý:</strong> +Android sẽ tự động áp dụng thể loại {@link android.content.Intent#CATEGORY_DEFAULT} +cho tất cả ý định không biểu thị được chuyển tới {@link +android.content.Context#startActivity startActivity()} và {@link +android.app.Activity#startActivityForResult startActivityForResult()}. +Vì thế, nếu bạn muốn hoạt động của mình nhận ý định không biểu thị, nó phải +nêu một thể loại cho {@code "android.intent.category.DEFAULT"} trong các bộ lọc ý định của mình (như +được minh họa trong ví dụ {@code <intent-filter>} trước đó.</p> + + + +<h3 id="DataTest">Kiểm tra dữ liệu</h3> + +<p>Để quy định dữ liệu của ý định được chấp nhận, một bộ lọc ý định có thể khai báo 0 phần tử +<a href="{@docRoot}guide/topics/manifest/data-element.html">{@code +<data>}</a> hoặc nhiều hơn. Ví dụ:</p> + +<pre> +<intent-filter> + <data android:mimeType="video/mpeg" android:scheme="http" ... /> + <data android:mimeType="audio/mpeg" android:scheme="http" ... /> + ... +</intent-filter> +</pre> + +<p>Mỗi phần tử <code><a href="{@docRoot}guide/topics/manifest/data-element.html"><data></a></code> +có thể quy định một cấu trúc URI và kiểu dữ liệu (kiểu phương tiện MIME). Có các thuộc tính +riêng — {@code scheme}, {@code host}, {@code port}, +và {@code path} — cho từng phần của URI: +</p> + +<p style="margin-left: 2em">{@code <scheme>://<host>:<port>/<path>}</p> + +<p> +Ví dụ: +</p> + +<p style="margin-left: 2em">{@code content://com.example.project:200/folder/subfolder/etc}</p> + +<p>Trong URI này, lược đồ là {@code content}, máy chủ là {@code com.example.project}, +cổng là {@code 200}, và đường dẫn là {@code folder/subfolder/etc}. +</p> + +<p>Mỗi thuộc tính sau đều không bắt buộc trong một phần tử <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a>, +nhưng có sự phụ thuộc mang tính chất tuyến tính:</p> +<ul> + <li>Nếu không quy định một lược đồ thì máy chủ bị bỏ qua.</li> + <li>Nếu không quy định một máy chủ thì cổng bị bỏ qua.</li> + <li>Nếu không quy định cả lược đồ và máy chủ thì đường dẫn bị bỏ qua.</li> +</ul> + +<p>Khi URI trong một ý định được so sánh với đặc tả URI trong một bộ lọc, +nó chỉ được so sánh với các bộ phận của URI được nêu trong bộ lọc. Ví dụ:</p> +<ul> + <li>Nếu một bộ lọc chỉ quy định một lược đồ, tất cả URI có lược đồ đó sẽ khớp +với bộ lọc.</li> + <li>Nếu một bộ lọc quy định một lược đồ và thẩm quyền nhưng không có đường dẫn, tất cả URI +với cùng lược đồ và thẩm quyền sẽ thông qua bộ lọc, không phụ thuộc vào đường dẫn của nó.</li> + <li>Nếu bộ lọc quy định một lược đồ, thẩm quyền và đường dẫn, chỉ những URI có cùng lược đồ, +thẩm quyền và đường dẫn mới thông qua bộ lọc.</li> +</ul> + +<p class="note"><strong>Lưu ý:</strong> Đặc tả đường dẫn có thể +chứa một ký tự đại diện dấu sao (*) để yêu cầu chỉ khớp một phần với tên đường dẫn.</p> + +<p>Kiểm tra dữ liệu so sánh cả URI và kiểu MIME trong ý định với một URI +và kiểu MIME được quy định trong bộ lọc. Các quy tắc như sau: +</p> + +<ol type="a"> +<li>Một ý định mà không chứa URI cũng như kiểu MIME sẽ chỉ vượt qua +kiểm tra nếu bộ lọc không quy định bất kỳ URI hay kiểu MIME nào.</li> + +<li>Một ý định chứa URI nhưng không có kiểu MIME (không biểu thị cũng như suy luận được từ +URI) sẽ chỉ vượt qua kiểm tra nếu URI của nó khớp với định dạng URI của bộ lọc +và bộ lọc tương tự không quy định một kiểu MIME.</li> + +<li>Một ý định chứa kiểu MIME nhưng không chứa URI sẽ chỉ vượt qua kiểm tra +nếu bộ lọc liệt kê cùng kiểu MIME và không quy định một định dạng URI.</li> + +<li>Ý định mà chứa cả URI và kiểu MIME (hoặc biểu thị hoặc suy ra được từ +URI) sẽ chỉ vượt qua phần kiểu MIME của kiểm tra nếu kiểu đó +khớp với kiểu được liệt kê trong bộ lọc. Nó vượt qua phần URI của kiểm tra +nếu URI của nó khớp với một URI trong bộ lọc hoặc nếu nó có một {@code content:} +hoặc {@code file:} URI và bộ lọc không quy định một URI. Nói cách khác, +một thành phần được giả định là hỗ trợ dữ liệu {@code content:} và {@code file:} nếu +bộ lọc của nó liệt kê <em>chỉ</em> một kiểu MIME.</p></li> +</ol> + +<p> +Quy tắc cuối cùng này, quy tắc (d), phản ánh kỳ vọng +rằng các thành phần có thể nhận được dữ liệu cục bộ từ một tệp hoặc trình cung cấp nội dung. +Vì thế, các bộ lọc của chúng có thể chỉ liệt kê một kiểu dữ liệu và không cần công khai +nêu tên {@code content:} và các lược đồ {@code file:}. +Đây là một trường hợp điển hình. Phần tử <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> như + sau, ví dụ, sẽ thông báo cho Android biết rằng thành phần có thể nhận được dữ liệu ảnh từ một trình cung cấp +nội dung và sẽ hiển thị nó: +</p> + +<pre> +<intent-filter> + <data android:mimeType="image/*" /> + ... +</intent-filter></pre> + +<p> +Vì hầu hết dữ liệu có sẵn đều được cấp phát bởi các trình cung cấp nội dung, những bộ lọc mà +quy định một kiểu dữ liệu chứ không phải URI có lẽ là phổ biến nhất. +</p> + +<p> +Một cấu hình phổ biến khác đó là các bộ lọc có một lược đồ và một kiểu dữ liệu. Ví +dụ, một phần tử <a href="{@docRoot}guide/topics/manifest/data-element.html">{@code <data>}</a> +như sau thông báo cho Android rằng +thành phần có thể truy xuất dữ liệu video từ mạng để thực hiện hành động: +</p> + +<pre> +<intent-filter> + <data android:scheme="http" android:type="video/*" /> + ... +</intent-filter></pre> + + + +<h3 id="imatch">So khớp ý định</h3> + +<p>Các ý định được so khớp với các bộ lọc ý định không chỉ để khám phá một thành phần +mục tiêu cần kích hoạt, mà còn để khám phá điều gì đó về tập hợp +các thành phần trên thiết bị. Ví dụ, ứng dụng Trang chủ đưa trình khởi chạy ứng dụng +vào bằng cách tìm tất cả hoạt động có bộ lọc ý định mà quy định hành động +{@link android.content.Intent#ACTION_MAIN} và thể loại +{@link android.content.Intent#CATEGORY_LAUNCHER}.</p> + +<p>Ứng dụng của bạn có thể sử dụng so khớp ý định theo cách tương tự. +{@link android.content.pm.PackageManager} có một tập hợp các phương pháp{@code query...()} +trả về tất cả thành phần có thể chấp nhận một ý định cụ thể, và +một chuỗi các phương pháp {@code resolve...()} tương tự để xác định thành phần +tốt nhất nhằm hồi đáp lại một ý định. Ví dụ, +{@link android.content.pm.PackageManager#queryIntentActivities +queryIntentActivities()} sẽ trả về một danh sách tất cả hoạt động có thể thực hiện +ý định được chuyển qua như một tham đối, và {@link +android.content.pm.PackageManager#queryIntentServices +queryIntentServices()} trả về một danh sách dịch vụ tương tự. +Cả hai phương pháp đều không kích hoạt các thành phần; chúng chỉ liệt kê những thành phần +có thể hồi đáp. Có một phương pháp tương tự, +{@link android.content.pm.PackageManager#queryBroadcastReceivers +queryBroadcastReceivers()}, dành cho hàm nhận quảng bá. +</p> + + + + diff --git a/docs/html-intl/intl/vi/guide/components/loaders.jd b/docs/html-intl/intl/vi/guide/components/loaders.jd new file mode 100644 index 000000000000..b6d277f3d527 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/loaders.jd @@ -0,0 +1,494 @@ +page.title=Trình tải +parent.title=Hoạt động +parent.link=activities.html +@jd:body +<div id="qv-wrapper"> +<div id="qv"> + <h2>Trong tài liệu này</h2> + <ol> + <li><a href="#summary">Tổng quan về API Trình tải</a></li> + <li><a href="#app">Sử dụng các Trình tải trong một Ứng dụng</a> + <ol> + <li><a href="#requirements"></a></li> + <li><a href="#starting">Khởi động một Trình tải</a></li> + <li><a href="#restarting">Khởi động lại một Trình tải</a></li> + <li><a href="#callback">Sử dụng các Phương pháp Gọi lại LoaderManager</a></li> + </ol> + </li> + <li><a href="#example">Ví dụ</a> + <ol> + <li><a href="#more_examples">Thêm Ví dụ</a></li> + </ol> + </li> + </ol> + + <h2>Lớp khóa</h2> + <ol> + <li>{@link android.app.LoaderManager}</li> + <li>{@link android.content.Loader}</li> + + </ol> + + <h2>Các mẫu liên quan</h2> + <ol> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html"> +LoaderCursor</a></li> + <li> <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> +LoaderThrottle</a></li> + </ol> + </div> +</div> + +<p>Được giới thiệu trong Android 3.0, trình tải giúp việc tải dữ liệu không đồng bộ +trong một hoạt động hoặc phân đoạn trở nên dễ dàng. Trình tải có những đặc điểm sau:</p> + <ul> + <li>Chúng sẵn có cho mọi {@link android.app.Activity} và {@link +android.app.Fragment}.</li> + <li>Chúng cung cấp khả năng tải dữ liệu không đồng bộ.</li> + <li>Chúng theo dõi nguồn dữ liệu của mình và chuyển giao kết quả mới khi nội dung +thay đổi.</li> + <li>Chúng tự động kết nối lại với con chạy của trình tải cuối cùng khi được +tạo lại sau khi cấu hình thay đổi. Vì thế, chúng không cần truy vấn lại dữ liệu +của mình.</li> + </ul> + +<h2 id="summary">Tổng quan về API Trình tải</h2> + +<p>Có nhiều lớp và giao diện có thể có liên quan trong khi sử dụng +các trình tải trong một ứng dụng. Chúng được tóm tắt trong bảng này.</p> + +<table> + <tr> + <th>Lớp/Giao diện</th> + <th>Mô tả</th> + </tr> + <tr> + <td>{@link android.app.LoaderManager}</td> + <td>Một lớp tóm tắt được liên kết với {@link android.app.Activity} hoặc +{@link android.app.Fragment} để quản lý một hoặc nhiều thực thể {@link +android.content.Loader}. Nó giúp ứng dụng quản lý +các thao tác chạy lâu hơn cùng với vòng đời {@link android.app.Activity} +hoặc {@link android.app.Fragment}; công dụng phổ biến nhất của lớp này là khi dùng với +{@link android.content.CursorLoader}, tuy nhiên, các ứng dụng được tự do ghi +trình tải của chính mình để tải các kiểu dữ liệu khác. + <br /> + <br /> + Chỉ có một {@link android.app.LoaderManager} trên mỗi hoạt động hoặc phân đoạn. Nhưng một {@link android.app.LoaderManager} có thể có +nhiều trình tải.</td> + </tr> + <tr> + <td>{@link android.app.LoaderManager.LoaderCallbacks}</td> + <td>Một giao diện gọi lại để một máy khách tương tác với {@link +android.app.LoaderManager}. Ví dụ, bạn sử dụng phương pháp gọi lại {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +để tạo một trình tải mới.</td> + </tr> + <tr> + <td>{@link android.content.Loader}</td> + <td>Một lớp tóm tắt có vai trò thực hiện việc tải dữ liệu không đồng bộ. Đây là +lớp cơ bản cho một trình tải. Thông thường, bạn sẽ sử dụng {@link +android.content.CursorLoader}, nhưng bạn có thể triển khai lớp con của chính mình. Trong khi +các trình tải đang hoạt động, chúng sẽ theo dõi nguồn dữ liệu của mình và chuyển giao +kết quả mới khi nội dung thay đổi. </td> + </tr> + <tr> + <td>{@link android.content.AsyncTaskLoader}</td> + <td>Trình tải tóm tắt có chức năng cung cấp {@link android.os.AsyncTask} để thực hiện công việc.</td> + </tr> + <tr> + <td>{@link android.content.CursorLoader}</td> + <td>Một lớp con của {@link android.content.AsyncTaskLoader} có chức năng truy vấn +{@link android.content.ContentResolver} và trả về một {@link +android.database.Cursor}. Lớp này triển khai giao thức {@link +android.content.Loader} theo một cách chuẩn hóa để truy vấn các con chạy, +xây dựng trên {@link android.content.AsyncTaskLoader} để thực hiện truy vấn con chạy +trên một luồng nền sao cho nó không chặn UI của ứng dụng. Sử dụng +trình tải này là cách tốt nhất để tải dữ liệu không đồng bộ từ một {@link +android.content.ContentProvider}, thay vì phải thực hiện một truy vấn được quản lý thông qua +phân đoạn hoặc các API của hoạt động.</td> + </tr> +</table> + +<p>Các lớp và giao diện trong bảng trên là những thành phần thiết yếu +mà bạn sẽ sử dụng để triển khai một trình tải trong ứng dụng của mình. Bạn sẽ không cần tất cả chúng +cho từng trình tải mà bạn tạo lập, nhưng bạn sẽ luôn cần một tham chiếu tới {@link +android.app.LoaderManager} để khởi tạo một trình tải và triển khai +một lớp {@link android.content.Loader} chẳng hạn như {@link +android.content.CursorLoader}. Các phần sau đây trình bày với bạn cách sử dụng những +lớp và giao diện này trong một ứng dụng.</p> + +<h2 id ="app">Sử dụng các Trình tải trong một Ứng dụng</h2> +<p>Phần này mô tả cách sử dụng các trình tải trong một ứng dụng Android. Một +ứng dụng sử dụng trình tải thường bao gồm:</p> +<ul> + <li>Một {@link android.app.Activity} hoặc {@link android.app.Fragment}.</li> + <li>Một thực thể của {@link android.app.LoaderManager}.</li> + <li>Một {@link android.content.CursorLoader} để tải dữ liệu được dự phòng bởi một {@link +android.content.ContentProvider}. Hoặc cách khác, bạn có thể triển khai lớp con +của {@link android.content.Loader} hoặc {@link android.content.AsyncTaskLoader} của chính mình để tải +dữ liệu từ một số nguồn khác.</li> + <li>Một triển khai cho {@link android.app.LoaderManager.LoaderCallbacks}. +Đây là nơi bạn tạo trình tải mới và quản lý các tham chiếu của mình tới các +trình tải hiện có.</li> +<li>Một cách để hiển thị dữ liệu của trình tải, chẳng hạn như {@link +android.widget.SimpleCursorAdapter}.</li> + <li>Một nguồn dữ liệu, chẳng hạn như một {@link android.content.ContentProvider}, khi sử dụng một +{@link android.content.CursorLoader}.</li> +</ul> +<h3 id="starting">Khởi động một Trình tải</h3> + +<p>{@link android.app.LoaderManager} quản lý một hoặc nhiều thực thể {@link +android.content.Loader} trong một {@link android.app.Activity} hoặc +{@link android.app.Fragment}. Chỉ có một {@link +android.app.LoaderManager} trên mỗi hoạt động hoặc phân đoạn.</p> + +<p>Thông thường, bạn +sẽ khởi tạo một {@link android.content.Loader} bên trong phương pháp {@link +android.app.Activity#onCreate onCreate()} của hoạt động, hoặc trong phương pháp +{@link android.app.Fragment#onActivityCreated onActivityCreated()} của phân đoạn. Bạn +làm điều này như sau:</p> + +<pre>// Prepare the loader. Either re-connect with an existing one, +// or start a new one. +getLoaderManager().initLoader(0, null, this);</pre> + +<p>Phương pháp {@link android.app.LoaderManager#initLoader initLoader()} sẽ lấy những +tham số sau:</p> +<ul> + <li>Một ID duy nhất xác định trình tải. Trong ví dụ này, ID là 0.</li> +<li>Các tham đối tùy chọn để cung cấp cho trình tải khi +xây dựng (<code>null</code> trong ví dụ này).</li> + +<li>Triển khai {@link android.app.LoaderManager.LoaderCallbacks}, phương pháp mà +{@link android.app.LoaderManager} gọi để báo cáo các sự kiện trình tải. Trong ví dụ này +, lớp cục bộ triển khai giao diện {@link +android.app.LoaderManager.LoaderCallbacks}, vì thế nó chuyển một tham chiếu +tới chính nó, {@code this}.</li> +</ul> +<p>Lệnh gọi {@link android.app.LoaderManager#initLoader initLoader()} đảm bảo rằng một trình tải +được khởi tạo và hiện hoạt. Nó có hai kết quả có thể xảy ra:</p> +<ul> + <li>Nếu trình tải được quy định bởi ID đã tồn tại, trình tải được tạo lập cuối cùng +sẽ được sử dụng lại.</li> + <li>Nếu trình tải được quy định bởi ID <em>không</em> tồn tại, +{@link android.app.LoaderManager#initLoader initLoader()} sẽ kích khởi phương pháp +{@link android.app.LoaderManager.LoaderCallbacks}{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. +Đây là nơi bạn triển khai mã để khởi tạo và trả về một trình tải mới. +Để bàn thêm, hãy xem phần <a href="#onCreateLoader">onCreateLoader</a>.</li> +</ul> +<p>Dù trong trường hợp nào, triển khai {@link android.app.LoaderManager.LoaderCallbacks} +đã cho được liên kết với trình tải, và sẽ được gọi khi +trạng thái của trình tải thay đổi. Nếu tại điểm thực hiện lệnh gọi này, hàm gọi đang trong trạng thái +được khởi động của nó, và trình tải được yêu cầu đã tồn tại và đã khởi tạo +dữ liệu của nó, khi đó hệ thống sẽ gọi {@link +android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +ngay lập tức (trong khi {@link android.app.LoaderManager#initLoader initLoader()}), +vì thế bạn phải sẵn sàng khi điều này xảy ra. Xem <a href="#onLoadFinished"> +onLoadFinished</a> để thảo luận thêm về lệnh gọi lại này</p> + +<p>Lưu ý rằng phương pháp {@link android.app.LoaderManager#initLoader initLoader()} +sẽ trả về {@link android.content.Loader} đã được tạo lập, nhưng bạn không +cần bắt lại một tham chiếu tới nó. {@link android.app.LoaderManager} tự động quản lý +vòng đời của trình tải. {@link android.app.LoaderManager} +khởi động và dừng tải khi cần và duy trì trạng thái của trình tải +và nội dung đi kèm của nó. Như hàm ý, bạn hiếm khi tương tác trực tiếp với các trình tải +(thông qua một ví dụ về việc sử dụng các phương pháp trình tải để tinh chỉnh hành vi +của một trình tải, hãy xem ví dụ <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a>). +Bạn thường sử dụng nhất là các phương pháp {@link +android.app.LoaderManager.LoaderCallbacks} để can thiệp vào tiến trình tải +khi diễn ra một sự kiện đặc biệt. Để thảo luận thêm về chủ đề này, hãy xem phần <a href="#callback">Sử dụng Phương pháp Gọi lại LoaderManager</a>.</p> + +<h3 id="restarting">Khởi động lại một Trình tải</h3> + +<p>Khi bạn sử dụng {@link android.app.LoaderManager#initLoader initLoader()}, như +trình bày bên trên, nó sử dụng một trình tải hiện hữu với ID được quy định nếu có. +Nếu không có, nó sẽ tạo một trình tải. Nhưng đôi khi bạn muốn bỏ dữ liệu cũ của mình +và bắt đầu lại.</p> + +<p>Để bỏ dữ liệu cũ của mình, hãy sử dụng {@link +android.app.LoaderManager#restartLoader restartLoader()}. Ví dụ, việc +triển khai {@link android.widget.SearchView.OnQueryTextListener} này sẽ khởi động lại +trình tải khi truy vấn của người dùng thay đổi. Trình tải cần được khởi động lại sao cho +nó có thể sử dụng bộ lọc tìm kiếm được điều chỉnh để thực hiện một truy vấn mới:</p> + +<pre> +public boolean onQueryTextChanged(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; +}</pre> + +<h3 id="callback">Sử dụng các Phương pháp Gọi lại LoaderManager</h3> + +<p>{@link android.app.LoaderManager.LoaderCallbacks} là một giao diện gọi lại +cho phép một máy khách tương tác với {@link android.app.LoaderManager}. </p> +<p>Các trình tải, đặc biệt là {@link android.content.CursorLoader}, được kỳ vọng sẽ +giữ lại dữ liệu của chúng sau khi bị dừng. Điều này cho phép ứng dụng giữ lại +dữ liệu của chúng qua hoạt động hoặc các phương pháp {@link android.app.Activity#onStop +onStop()} và {@link android.app.Activity#onStart onStart()} của phân đoạn, sao cho khi +người dùng quay lại một ứng dụng, họ không phải chờ dữ liệu +tải lại. Bạn sử dụng các phương pháp {@link android.app.LoaderManager.LoaderCallbacks} +khi cần biết khi nào thì nên tạo một trình tải mới, và để thông báo với ứng dụng khi nào + thì đến lúc để dừng sử dụng dữ liệu của một trình tải.</p> + +<p>{@link android.app.LoaderManager.LoaderCallbacks} bao gồm những phương pháp +sau:</p> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} — +Khởi tạo và trả về một {@link android.content.Loader} mới cho ID đã cho. +</li></ul> +<ul> + <li> {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()} +— Được gọi khi một trình tải được tạo trước đó đã hoàn tất việc tải. +</li></ul> +<ul> + <li>{@link android.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()} + — Được gọi khi một trình tải được tạo trước đó đang được đặt lại, vì thế mà khiến dữ liệu +của nó không sẵn có. +</li> +</ul> +<p>Những phương pháp này được mô tả chi tiết hơn trong các phần sau.</p> + +<h4 id ="onCreateLoader">onCreateLoader</h4> + +<p>Khi bạn định truy cập một trình tải (ví dụ, thông qua {@link +android.app.LoaderManager#initLoader initLoader()}), nó kiểm tra xem +trình tải được quy định bởi ID có tồn tại không. Nếu không, nó sẽ kích khởi phương pháp {@link +android.app.LoaderManager.LoaderCallbacks} {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()}. Đây +là lúc bạn tạo một trình tải mới. Thông thường sẽ có một {@link +android.content.CursorLoader}, nhưng bạn có thể triển khai lớp con {@link +android.content.Loader} của chính mình. </p> + +<p>Trong ví dụ này, phương pháp gọi lại {@link +android.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()} +sẽ tạo một {@link android.content.CursorLoader}. Bạn phải xây dựng +{@link android.content.CursorLoader} bằng cách sử dụng phương pháp hàm dựng của nó mà yêu cầu +trọn bộ thông tin cần thiết để thực hiện một truy vấn tới {@link +android.content.ContentProvider}. Cụ thể, nó cần:</p> +<ul> + <li><em>uri</em> — URI của nội dung cần truy xuất. </li> + <li><em>dự thảo</em> — Một danh sách các cột sẽ trả về. Việc chuyển +<code>null</code> sẽ trả về tất cả cột, điều này không hiệu quả. </li> + <li><em>lựa chọn</em> — Một bộ lọc khai báo các hàng nào sẽ trả về, +có định dạng như một mệnh đề SQL WHERE (không gồm chính mệnh đề WHERE). Việc chuyển +<code>null</code> sẽ trả về tất cả hàng cho URI đã cho. </li> + <li><em>selectionArgs</em> — Bạn có thể thêm ?s vào lựa chọn, +chúng sẽ được thay thế bằng các giá trị từ <em>selectionArgs</em>, theo thứ tự xuất hiện trong +lựa chọn. Giá trị sẽ được gắn kết thành các Xâu. </li> + <li><em>sortOrder</em> — Cách sắp xếp thứ tự các hàng, được định dạng như một mệnh đề SQL +ORDER BY (không bao gồm chính mệnh đề ORDER BY). Việc chuyển <code>null</code> sẽ +sử dụng thứ tự sắp xếp mặc định, điều này có thể dẫn đến kết quả không theo thứ tự.</li> +</ul> +<p>Ví dụ:</p> +<pre> + // If non-null, this is the current filter the user has provided. +String mCurFilter; +... +public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); +}</pre> +<h4 id="onLoadFinished">onLoadFinished</h4> + +<p>Phương pháp này được gọi khi một trình tải được tạo trước đó đã hoàn thành việc tải của mình. +Phương pháp này được bảo đảm sẽ được gọi trước khi giải phóng dữ liệu cuối cùng +được cung cấp cho trình tải này. Tại điểm này, bạn nên loại bỏ mọi trường hợp sử dụng +dữ liệu cũ (do nó sẽ được giải phóng sớm), nhưng không nên +tự mình giải phóng dữ liệu do trình tải sở hữu dữ liệu và sẽ đảm nhận việc này.</p> + + +<p>Trình tải sẽ giải phóng dữ liệu sau khi nó biết ứng dụng đang không còn +sử dụng nó nữa. Ví dụ, nếu dữ liệu là một con chạy từ một {@link +android.content.CursorLoader}, bạn không nên tự mình gọi {@link +android.database.Cursor#close close()} trên dữ liệu đó. Nếu con chạy đang được đặt +trong một {@link android.widget.CursorAdapter}, bạn nên sử dụng phương pháp {@link +android.widget.SimpleCursorAdapter#swapCursor swapCursor()} sao cho + {@link android.database.Cursor} cũ không bị đóng. Ví dụ:</p> + +<pre> +// This is the Adapter being used to display the list's data.<br +/>SimpleCursorAdapter mAdapter; +... + +public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); +}</pre> + +<h4 id="onLoaderReset">onLoaderReset</h4> + +<p>Phương pháp này được gọi khi một trình tải được tạo trước đó đang được đặt lại, vì thế mà khiến +dữ liệu của nó không sẵn có. Lệnh gọi lại này cho phép bạn tìm hiểu xem khi nào thì dữ liệu +sẽ được giải phóng để bạn có thể loại bỏ tham chiếu của mình tới nó. </p> +<p>Sự triển khai này gọi ra +{@link android.widget.SimpleCursorAdapter#swapCursor swapCursor()} +với một giá trị <code>null</code>:</p> + +<pre> +// This is the Adapter being used to display the list's data. +SimpleCursorAdapter mAdapter; +... + +public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); +}</pre> + + +<h2 id="example">Ví dụ</h2> + +<p>Lấy một ví dụ, sau đây là triển khai đầy đủ của {@link +android.app.Fragment} có chức năng hiển thị một {@link android.widget.ListView} chứa +kết quả của một truy vấn đối với trình cung cấp nội dung danh bạ. Nó sử dụng một {@link +android.content.CursorLoader} để quản lý truy vấn trên trình cung cấp.</p> + +<p>Để một ứng dụng truy cập danh bạ của một người dùng, như minh họa trong ví dụ này, bản kê khai +của nó phải bao gồm quyền +{@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS}.</p> + +<pre> +public static class CursorLoaderListFragment extends ListFragment + implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + // If non-null, this is the current filter the user has provided. + String mCurFilter; + + @Override public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText("No phone numbers"); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_2, null, + new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, + new int[] { android.R.id.text1, android.R.id.text2 }, 0); + setListAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // Place an action bar item for searching. + MenuItem item = menu.add("Search"); + item.setIcon(android.R.drawable.ic_menu_search); + item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + SearchView sv = new SearchView(getActivity()); + sv.setOnQueryTextListener(this); + item.setActionView(sv); + } + + public boolean onQueryTextChange(String newText) { + // Called when the action bar search text has changed. Update + // the search filter, and restart the loader to do a new query + // with this filter. + mCurFilter = !TextUtils.isEmpty(newText) ? newText : null; + getLoaderManager().restartLoader(0, null, this); + return true; + } + + @Override public boolean onQueryTextSubmit(String query) { + // Don't care about this. + return true; + } + + @Override public void onListItemClick(ListView l, View v, int position, long id) { + // Insert desired behavior here. + Log.i("FragmentComplexList", "Item clicked: " + id); + } + + // These are the Contacts rows that we will retrieve. + static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { + Contacts._ID, + Contacts.DISPLAY_NAME, + Contacts.CONTACT_STATUS, + Contacts.CONTACT_PRESENCE, + Contacts.PHOTO_ID, + Contacts.LOOKUP_KEY, + }; + public Loader<Cursor> onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri; + if (mCurFilter != null) { + baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, + Uri.encode(mCurFilter)); + } else { + baseUri = Contacts.CONTENT_URI; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + + Contacts.DISPLAY_NAME + " != '' ))"; + return new CursorLoader(getActivity(), baseUri, + CONTACTS_SUMMARY_PROJECTION, select, null, + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + } + + public void onLoaderReset(Loader<Cursor> loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +}</pre> +<h3 id="more_examples">Thêm Ví dụ</h3> + +<p>Có một vài mẫu khác trong <strong>ApiDemos</strong> để +minh họa cách sử dụng các trình tải:</p> +<ul> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html"> +LoaderCursor</a> — Một phiên bản hoàn chỉnh của +đoạn mã HTML trình bày ở trên.</li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html"> LoaderThrottle</a> — Một ví dụ về cách sử dụng điều chỉnh để giảm +số truy vấn mà một trình cung cấp nội dung thực hiện khi dữ liệu của nó thay đổi.</li> +</ul> + +<p>Để biết thông tin về việc tải xuống và cài đặt các mẫu SDK, hãy xem phần <a href="http://developer.android.com/resources/samples/get.html"> Tải +Mẫu</a>. </p> + diff --git a/docs/html-intl/intl/vi/guide/components/processes-and-threads.jd b/docs/html-intl/intl/vi/guide/components/processes-and-threads.jd new file mode 100644 index 000000000000..390ca156a1f0 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/processes-and-threads.jd @@ -0,0 +1,411 @@ +page.title=Tiến trình và Luồng +page.tags=vòng đời, nền + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Trong tài liệu này</h2> +<ol> +<li><a href="#Processes">Tiến trình</a> + <ol> + <li><a href="#Lifecycle">Vòng đời tiến trình</a></li> + </ol> +</li> +<li><a href="#Threads">Luồng</a> + <ol> + <li><a href="#WorkerThreads">Luồng trình thực hiện</a></li> + <li><a href="#ThreadSafe">Phương pháp an toàn với luồng</a></li> + </ol> +</li> +<li><a href="#IPC">Truyền thông Liên Tiến trình</a></li> +</ol> + +</div> +</div> + +<p>Khi một thành phần ứng dụng bắt đầu và ứng dụng không có bất kỳ thành phần nào khác +đang chạy, hệ thống Android sẽ khởi động một tiến trình Linux mới cho ứng dụng bằng một luồng +thực thi đơn lẻ. Theo mặc định, tất cả thành phần của cùng ứng dụng sẽ chạy trong cùng tiến trình và luồng +(được gọi là luồng "chính"). Nếu một thành phần ứng dụng bắt đầu và đã tồn tại một tiến trình +cho ứng dụng đó (bởi một thành phần khác từ ứng dụng đã tồn tại), khi đó thành phần được +bắt đầu bên trong tiến trình đó và sử dụng cùng luồng thực thi. Tuy nhiên, bạn có thể sắp xếp cho +các thành phần khác nhau trong ứng dụng của mình để chạy trong các tiến trình riêng biệt, và bạn có thể tạo thêm +luồng cho bất kỳ tiến trình nào.</p> + +<p>Tài liệu này trình bày về cách các tiến trình và luồng vận hành trong một ứng dụng Android.</p> + + +<h2 id="Processes">Tiến trình</h2> + +<p>Theo mặc định, tất cả thành phần của cùng ứng dụng sẽ chạy trong cùng tiến trình và hầu hết các ứng dụng +sẽ không thay đổi điều này. Tuy nhiên, nếu bạn thấy rằng mình cần kiểm soát một thành phần +cụ thể thuộc về một tiến trình nào đó, bạn có thể làm vậy trong tệp bản kê khai.</p> + +<p>Mục nhập bản kê khai đối với mỗi loại phần tử thành phần—<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a>, <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code +<service>}</a>, <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code +<receiver>}</a>, và <a href="{@docRoot}guide/topics/manifest/provider-element.html">{@code +<provider>}</a>—sẽ hỗ trợ một thuộc tính {@code android:process} mà có thể quy định một +tiến trình mà thành phần đó sẽ chạy trong đó. Bạn có thể đặt thuộc tính này sao cho từng thành phần chạy +trong chính tiến trình của nó hoặc sao cho một số thành phần chia sẻ một tiến trình trong khi các thành phần khác thì không. Bạn cũng có thể đặt +{@code android:process} sao cho các thành phần của những ứng dụng khác nhau chạy trong cùng +tiến trình—với điều kiện rằng ứng dụng chia sẻ cùng ID người dùng Linux và được ký bằng cùng các +chứng chỉ.</p> + +<p>Phần tử <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code +<application>}</a> cũng hỗ trợ một thuộc tính {@code android:process}, để đặt một +giá trị mặc định áp dụng cho tất cả thành phần.</p> + +<p>Android có thể quyết định tắt một tiến trình tại một thời điểm nào đó, khi bộ nhớ thấp và theo yêu cầu của các +tiến trình khác đang phục vụ người dùng tức thì hơn. Các thành phần +ứng dụng đang chạy trong tiến trình bị tắt bỏ thì sau đó sẽ bị hủy. Tiến trình được +khởi động lại cho những thành phần đó khi lại có việc cho chúng thực hiện.</p> + +<p>Khi quyết định bỏ những tiến trình nào, hệ thống Android sẽ cân nhắc tầm quan trọng tương đối so với +người dùng. Ví dụ, hệ thống sẵn sàng hơn khi tắt một tiến trình lưu trữ các hoạt động không còn +hiển thị trên màn hình, so với một tiến trình lưu trữ các hoạt động đang hiển thị. Vì thế, quyết định về việc có +chấm dứt một tiến trình hay không phụ thuộc vào trạng thái của các thành phần đang chạy trong tiến trình đó. Các quy tắc +được sử dụng để quyết định sẽ chấm dứt tiến trình nào được trình bày ở bên dưới. </p> + + +<h3 id="Lifecycle">Vòng đời tiến trình</h3> + +<p>Hệ thống Android cố gắng duy trì một tiến trình ứng dụng lâu nhất có thể, nhưng +cuối cùng thì nó cũng cần loại bỏ các tiến trình cũ để lấy lại bộ nhớ cho các tiến trình mới hoặc quan trọng hơn. Để +xác định giữ lại những tiến trình nào +và loại bỏ những tiến trình nào, hệ thống sẽ đặt từng tiến trình vào một "phân cấp tầm quan trọng" dựa trên +những thành phần đang chạy trong tiến trình và trạng thái của những thành phần đó. Những tiến trình có tầm quan trọng +thấp nhất bị loại bỏ trước, rồi đến những tiến trình có tầm quan trọng thấp thứ hai, và cứ thế tiếp tục, miễn là còn cần thiết +để khôi phục tài nguyên của hệ thống.</p> + +<p>Có năm cấp trong phân cấp tầm quan trọng. Danh sách sau trình bày các loại +tiến trình khác nhau theo thứ tự tầm quan trọng (tiến trình thứ nhất là <em>quan trọng nhất</em> và được +<em>tắt bỏ sau cùng</em>):</p> + +<ol> + <li><b>Tiến trình tiền cảnh</b> + <p>Một tiến trình được yêu cầu cho việc mà người dùng đang thực hiện. Một + tiến trình được coi là đang trong tiền cảnh nếu bất kỳ điều nào sau đây là đúng:</p> + + <ul> + <li>Nó lưu trữ một {@link android.app.Activity} mà người dùng đang tương tác với (phương pháp của {@link +android.app.Activity}, {@link android.app.Activity#onResume onResume()}, đã được +gọi).</li> + + <li>Nó lưu trữ một {@link android.app.Service} gắn kết với hoạt động mà người dùng đang +tương tác với.</li> + + <li>Nó lưu trữ một {@link android.app.Service} đang chạy "trong tiền cảnh"—mà +dịch vụ đã gọi {@link android.app.Service#startForeground startForeground()}. + + <li>Nó lưu trữ một {@link android.app.Service} mà đang thực thi một trong các lệnh +gọi lại của vòng đời của nó ({@link android.app.Service#onCreate onCreate()}, {@link android.app.Service#onStart +onStart()}, hoặc {@link android.app.Service#onDestroy onDestroy()}).</li> + + <li>Nó lưu trữ một {@link android.content.BroadcastReceiver} mà đang thực thi phương pháp {@link + android.content.BroadcastReceiver#onReceive onReceive()} của nó.</li> + </ul> + + <p>Nhìn chung, tại bất kỳ thời điểm xác định nào cũng chỉ tồn tại một vài tiến trình tiền cảnh. Chúng chỉ bị tắt bỏ +như một giải pháp cuối cùng—nếu bộ nhớ quá thấp tới mức chúng đều không thể tiếp tục chạy được. Nhìn chung, tại thời điểm +đó, thiết bị đã đạt tới trạng thái phân trang bộ nhớ, vì thế việc tắt bỏ một số tiến trình tiền cảnh là +bắt buộc để đảm bảo giao diện người dùng có phản hồi.</p></li> + + <li><b>Tiến trình hiển thị</b> + <p>Một tiến trình mà không có bất kỳ thành phần tiền cảnh nào, nhưng vẫn có thể + ảnh hưởng tới nội dung mà người dùng nhìn thấy trên màn hình. Một tiến trình được coi là hiển thị nếu một trong hai + điều kiện sau là đúng:</p> + + <ul> + <li>Nó lưu trữ một {@link android.app.Activity} mà không nằm trong tiền cảnh, nhưng vẫn +hiển thị với người dùng (phương pháp {@link android.app.Activity#onPause onPause()} của nó đã được gọi). +Điều này có thể xảy ra, ví dụ, nếu hoạt động tiền cảnh đã bắt đầu một hộp thoại, nó cho phép +hoạt động trước được nhìn thấy phía sau nó.</li> + + <li>Nó lưu trữ một {@link android.app.Service} được gắn kết với một hoạt động +hiển thị (hoặc tiền cảnh).</li> + </ul> + + <p>Một tiến trình tiền cảnh được coi là cực kỳ quan trọng và sẽ không bị tắt bỏ trừ khi làm vậy +là bắt buộc để giữ cho tất cả tiến trình tiền cảnh chạy. </p> + </li> + + <li><b>Tiến trình dịch vụ</b> + <p>Một tiến trình mà đang chạy một dịch vụ đã được bắt đầu bằng phương pháp {@link +android.content.Context#startService startService()} và không rơi vào một trong hai +thể loại cao hơn. Mặc dù tiến trình dịch vụ không trực tiếp gắn với bất kỳ thứ gì mà người dùng thấy, chúng +thường đang làm những việc mà người dùng quan tâm đến (chẳng hạn như phát nhạc chạy ngầm hoặc +tải xuống dữ liệu trên mạng), vì thế hệ thống vẫn giữ chúng chạy trừ khi không có đủ bộ nhớ để +duy trì chúng cùng với tất cả tiến trình tiền cảnh và hiển thị. </p> + </li> + + <li><b>Tiến trình nền</b> + <p>Một tiến trình lưu trữ một hoạt động mà hiện tại không hiển thị với người dùng (phương pháp +{@link android.app.Activity#onStop onStop()} của hoạt động đã được gọi). Những tiến trình này không có tác động +trực tiếp tới trải nghiệm người dùng, và hệ thống có thể bỏ chúng đi vào bất cứ lúc nào để lấy lại bộ nhớ cho một +tiến trình tiền cảnh, +hiển thị hoặc dịch vụ. Thường thì có nhiều tiến trình ngầm đang chạy, vì thế chúng được giữ +trong một danh sách LRU (ít sử dụng gần đây nhất) để đảm bảo rằng tiến trình với hoạt động +mà người dùng nhìn thấy gần đây nhất là tiến trình cuối cùng sẽ bị tắt bỏ. Nếu một hoạt động triển khai các phương pháp vòng đời của nó +đúng cách, và lưu trạng thái hiện tại của nó, việc tắt bỏ tiến trình của hoạt động đó sẽ không có ảnh hưởng có thể thấy được tới +trải nghiệm người dùng, vì khi người dùng điều hướng lại hoạt động đó, hoạt động sẽ khôi phục +tất cả trạng thái hiển thị của nó. Xem tài liệu <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Hoạt động</a> +để biết thông tin về việc lưu và khôi phục trạng thái.</p> + </li> + + <li><b>Tiến trình trống</b> + <p>Một tiến trình mà không giữ bất kỳ thành phần ứng dụng hiện hoạt nào. Lý do duy nhất để giữ cho +kiểu tiến trình này hoạt động đó là nhằm mục đích lưu bộ nhớ ẩn, để cải thiện thời gian khởi động vào lần tới khi thành phần +cần chạy trong nó. Hệ thống thường tắt bỏ những tiến trình này để cân bằng tài nguyên tổng thể +của hệ thống giữa các bộ đệm ẩn tiến trình và bộ đệm ẩn nhân liên quan.</p> + </li> +</ol> + + + <p>Android xếp hạng một tiến trình ở mức cao nhất mà nó có thể, dựa vào tầm quan trọng của +các thành phần đang hoạt động trong tiến trình đó. Ví dụ, nếu một tiến trình lưu giữ một dịch vụ và hoạt động +hiển thị, tiến trình đó sẽ được xếp hạng là tiến trình hiển thị chứ không phải tiến trình dịch vụ.</p> + + <p>Ngoài ra, xếp hạng của một tiến trình có thể tăng bởi các tiến trình khác phụ thuộc vào +nó—một tiến trình mà đang phục vụ một tiến trình khác không thể bị xếp thấp hơn tiến trình mà nó +đang phục vụ. Ví dụ, nếu một trình cung cấp nội dung trong tiến trình A đang phục vụ một máy khách trong tiến trình B, hoặc nếu một +dịch vụ trong tiến trình A được gắn kết với một thành phần trong tiến trình B, ít nhất tiến trình A sẽ luôn được coi +là quan trọng như tiến trình B.</p> + + <p>Do một tiến trình đang chạy một dịch vụ được xếp hạng cao hơn một tiến trình có các hoạt động nền, +một hoạt động mà khởi động một thao tác nhấp giữ có thể làm tốt việc khởi động một <a href="{@docRoot}guide/components/services.html">dịch vụ</a> cho thao tác đó, thay vì +chỉ tạo một luồng trình thực hiện—nhất là khi thao tác đó sẽ có thể diễn ra lâu hơn hoạt động. +Ví dụ, một hoạt động mà đang tải một ảnh lên một trang web nên bắt đầu một dịch vụ để thực hiện +việc tải lên sao cho việc tải lên có thể tiếp tục chạy ngầm ngay cả khi người dùng rời khỏi hoạt động. +Việc sử dụng một dịch vụ sẽ bảo đảm rằng thao tác ít nhất sẽ có mức ưu tiên như "tiến trình dịch vụ", +không phụ thuộc vào điều xảy ra với hoạt động. Đây cũng chính là lý do hàm nhận quảng bá nên +sử dụng dịch vụ thay vì chỉ đưa các thao tác tốn thời gian vào một luồng.</p> + + + + +<h2 id="Threads">Luồng</h2> + +<p>Khi một ứng dụng được khởi chạy, hệ thống sẽ tạo một luồng thực thi cho ứng dụng, +gọi là luồng "chính." Luồng này rất quan trọng bởi nó phụ trách phân phối các sự kiện tới +những widget giao diện người dùng phù hợp, bao gồm các sự kiện vẽ. Nó cũng là luồng mà +trong đó ứng dụng của bạn tương tác với các thành phần từ bộ công cụ UI của Android (các thành phần từ các gói {@link +android.widget} và {@link android.view}). Như vậy, luồng chính đôi khi cũng được gọi là +luồng UI.</p> + +<p>Hệ thống <em>không</em> tạo một luồng riêng cho từng thực thể của thành phần. Tất cả +thành phần chạy trong cùng tiến trình đều được khởi tạo trong luồng UI, và các lệnh gọi của hệ thống tới +từng thành phần được phân phối từ luồng đó. Hệ quả là các phương pháp hồi đáp lại lệnh +gọi lại của hệ thống (chẳng hạn như {@link android.view.View#onKeyDown onKeyDown()} để báo cáo hành động của người dùng +hoặc một phương pháp gọi lại vòng đời) sẽ luôn chạy trong luồng UI của tiến trình.</p> + +<p>Ví dụ, khi người dùng chạm vào một nút trên màn hình, luồng UI của ứng dụng của bạn sẽ phân phối +sự kiện chạm tới widget, đến lượt mình, widget sẽ đặt trạng thái được nhấn và đăng một yêu cầu vô hiệu hóa tới +hàng đợi sự kiện. Luồng UI loại yêu cầu khỏi hàng đợi và thông báo với widget rằng nó nên tự vẽ lại +.</p> + +<p>Khi ứng dụng của bạn thực hiện công việc nặng để hồi đáp tương tác của người dùng, mô hình luồng đơn nhất +này có thể dẫn đến hiệu năng kém trừ khi bạn triển khai ứng dụng của mình một cách phù hợp. Cụ thể, nếu +mọi thứ đang xảy ra trong luồng UI, việc thực hiện những thao tác kéo dài như truy cập mạng hay +truy vấn cơ sở dữ liệu sẽ chặn toàn bộ UI. Khi luồng bị chặn, không sự kiện nào có thể được phân phối, +bao gồm cả sự kiện vẽ. Từ phương diện của người dùng, ứng dụng +có vẻ như đang bị treo. Thậm chí tệ hơn, nếu luồng UI bị chặn trong lâu hơn vài giây +(hiện tại là khoảng 5 giây), người dùng sẽ được hiển thị hộp thoại không phổ biến "<a href="http://developer.android.com/guide/practices/responsiveness.html">ứng dụng +không phản hồi</a>" (ANR). Khi đó, người dùng có thể quyết định thoát ứng dụng của mình và gỡ cài đặt nó +nếu họ không thoải mái.</p> + +<p>Ngoài ra, bộ công cụ UI của Android <em>không</em> an toàn với luồng. Vì vậy, bạn không được thao tác +UI của mình từ một luồng trình thực hiện—bạn phải thực hiện tất cả thao tác đối với giao diện người dùng của mình từ luồng +UI. Vì vậy, có hai quy tắc đơn giản đối với mô hình luồng đơn lẻ của Android:</p> + +<ol> +<li>Không được chặn luồng UI +<li>Không được truy cập bộ công cụ UI của Android từ bên ngoài luồng UI +</ol> + +<h3 id="WorkerThreads">Luồng trình thực hiện</h3> + +<p>Vì mô hình luồng đơn lẻ nêu trên, điều thiết yếu đối với tính phản hồi của UI +ứng dụng của bạn đó là bạn không được chặn luồng UI. Nếu bạn có thao tác cần thực hiện +không mang tính chất tức thời, bạn nên đảm bảo thực hiện chúng trong các luồng riêng (luồng “chạy ngầm" hoặc +"trình thực hiện").</p> + +<p>Ví dụ, bên dưới là một số mã cho một đối tượng theo dõi nhấp có chức năng tải xuống một hình ảnh từ một luồng +riêng và hiển thị nó trong một {@link android.widget.ImageView}:</p> + +<pre> +public void onClick(View v) { + new Thread(new Runnable() { + public void run() { + Bitmap b = loadImageFromNetwork("http://example.com/image.png"); + mImageView.setImageBitmap(b); + } + }).start(); +} +</pre> + +<p>Thoạt đầu, điều này có vẻ như diễn ra ổn thỏa, vì nó tạo một luồng mới để xử lý thao tác +mạng. Tuy nhiên, nó vi phạm quy tắc thứ hai của mô hình luồng đơn nhất: <em>không được truy cập +bộ công cụ UI của Android từ bên ngoài luồng UI</em>—mẫu này sửa đổi {@link +android.widget.ImageView} từ luồng trình thực hiện thay vì từ luồng UI. Điều này có thể dẫn đến +hành vi bất ngờ, không được định nghĩa mà có thể gây khó khăn và tốn thời gian theo dõi.</p> + +<p>Để sửa vấn đề này, Android giới thiệu một vài cách để truy cập luồng UI từ các luồng +khác. Sau đây là một danh sách các phương pháp có thể trợ giúp:</p> + +<ul> +<li>{@link android.app.Activity#runOnUiThread(java.lang.Runnable) +Activity.runOnUiThread(Runnable)}</li> +<li>{@link android.view.View#post(java.lang.Runnable) View.post(Runnable)}</li> +<li>{@link android.view.View#postDelayed(java.lang.Runnable, long) View.postDelayed(Runnable, +long)}</li> +</ul> + +<p>Ví dụ, bạn có thể sửa mã trên bằng cách sử dụng phương pháp {@link +android.view.View#post(java.lang.Runnable) View.post(Runnable)}:</p> + +<pre> +public void onClick(View v) { + new Thread(new Runnable() { + public void run() { + final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); + mImageView.post(new Runnable() { + public void run() { + mImageView.setImageBitmap(bitmap); + } + }); + } + }).start(); +} +</pre> + +<p>Giờ thì triển khai này đã an toàn với luồng: thao tác mạng được thực hiện từ một luồng riêng +trong khi {@link android.widget.ImageView} được thao tác từ luồng UI.</p> + +<p>Tuy nhiên, khi mà sự phức tạp của thao tác tăng lên, kiểu mã này có thể bị phức tạp hóa và +khó duy trì. Để xử lý những tương tác phức tạp hơn bằng một luồng trình thực hiện, bạn có thể cân nhắc +sử dụng một {@link android.os.Handler} trong luồng trình thực hiện của mình, để xử lý các thư được chuyển từ luồng +UI. Mặc dù vậy, giải pháp tốt nhất là mở rộng lớp {@link android.os.AsyncTask}, +điều này sẽ đơn giản hóa việc thực thi các tác vụ của luồng trình thực hiện cần tương tác với UI.</p> + + +<h4 id="AsyncTask">Sử dụng AsyncTask</h4> + +<p>{@link android.os.AsyncTask} cho phép bạn thực hiện công việc không đồng bộ trên giao diện +người dùng của mình. Nó thực hiện các thao tác chặn trong một luồng trình thực hiện rồi phát hành kết quả trên +luồng UI mà không yêu cầu bạn tự xử lý các luồng và/hoặc trình xử lý.</p> + +<p>Để sử dụng nó, bạn phải tạo lớp con {@link android.os.AsyncTask} và triển khai phương pháp gọi lại {@link +android.os.AsyncTask#doInBackground doInBackground()}, phương pháp này chạy trong một tập hợp +các luồng chạy ngầm. Để cập nhật UI của mình, bạn nên triển khai {@link +android.os.AsyncTask#onPostExecute onPostExecute()}, nó sẽ mang lại kết quả từ {@link +android.os.AsyncTask#doInBackground doInBackground()} và chạy trong luồng UI, vì thế bạn có thể nâng cấp +UI của mình một cách an toàn. Sau đó, bạn có thể chạy tác vụ bằng cách gọi {@link android.os.AsyncTask#execute execute()} +từ luồng UI.</p> + +<p>Ví dụ, bạn có thể triển khai ví dụ trước bằng cách sử dụng {@link android.os.AsyncTask} theo +cách này:</p> + +<pre> +public void onClick(View v) { + new DownloadImageTask().execute("http://example.com/image.png"); +} + +private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { + /** The system calls this to perform work in a worker thread and + * delivers it the parameters given to AsyncTask.execute() */ + protected Bitmap doInBackground(String... urls) { + return loadImageFromNetwork(urls[0]); + } + + /** The system calls this to perform work in the UI thread and delivers + * the result from doInBackground() */ + protected void onPostExecute(Bitmap result) { + mImageView.setImageBitmap(result); + } +} +</pre> + +<p>Lúc này, UI an toàn và mã đơn giản hơn, vì nó tách riêng công việc thành +phần sẽ được thực hiện trên một luồng trình thực hiện và phần sẽ được thực hiện trên luồng UI.</p> + +<p>Bạn nên đọc tài liệu tham khảo {@link android.os.AsyncTask}để hiểu đầy đủ về +cách sử dụng lớp này, nhưng sau đây là phần trình bày tổng quan nhanh về hoạt động của nó:</p> + +<ul> +<li>Bạn có thể quy định loại tham số, các giá trị tiến độ, và giá trị +cuối cùng của tác vụ, bằng cách sử dụng các kiểu chung</li> +<li>Phương pháp {@link android.os.AsyncTask#doInBackground doInBackground()} sẽ tự động thực thi +trên một luồng trình thực hiện</li> +<li>{@link android.os.AsyncTask#onPreExecute onPreExecute()}, {@link +android.os.AsyncTask#onPostExecute onPostExecute()}, và {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} đều được gọi ra trên luồng UI</li> +<li>Giá trị được trả về bởi {@link android.os.AsyncTask#doInBackground doInBackground()} được gửi tới +{@link android.os.AsyncTask#onPostExecute onPostExecute()}</li> +<li>Bạn có thể gọi {@link android.os.AsyncTask#publishProgress publishProgress()} vào bất cứ lúc nào trong {@link +android.os.AsyncTask#doInBackground doInBackground()} để thực thi {@link +android.os.AsyncTask#onProgressUpdate onProgressUpdate()} trên luồng UI</li> +<li>Bạn có thể hủy bỏ tác vụ vào bất cứ lúc nào từ bất kỳ luồng nào</li> +</ul> + +<p class="caution"><strong>Chú ý:</strong> Một vấn đề khác mà bạn có thể gặp phải khi sử dụng một luồng +trình thực hiện đó là những lần khởi động lại bất ngờ trong hoạt động của bạn do một <a href="{@docRoot}guide/topics/resources/runtime-changes.html">thay đổi trong cấu hình thời gian chạy</a> +(chẳng hạn như khi người dùng thay đổi hướng màn hình), điều này có thể làm hỏng luồng trình thực hiện của bạn. Để +xem cách bạn có thể duy trì tác vụ của mình khi diễn ra một trong những lần khởi động lại này và cách hủy bỏ tác vụ cho phù hợp +khi hoạt động bị hủy, hãy xem mã nguồn cho ứng dụng mẫu <a href="http://code.google.com/p/shelves/">Shelves</a>.</p> + + +<h3 id="ThreadSafe">Phương pháp an toàn với luồng</h3> + +<p> Trong một số tình huống, các phương pháp bạn triển khai có thể được gọi ra từ nhiều hơn một luồng, và vì thế +phải được ghi sao cho an toàn với luồng. </p> + +<p>Điều này chủ yếu đúng với các phương pháp mà có thể được gọi từ xa—chẳng hạn như các phương pháp trong một <a href="{@docRoot}guide/components/bound-services.html">dịch vụ gắn kết</a>. Khi một lệnh gọi trên một +phương pháp được triển khai trong một {@link android.os.IBinder} khởi đầu trong cùng tiến trình mà +{@link android.os.IBinder IBinder} đang chạy, phương pháp đó sẽ được thực thi trong luồng của hàm gọi. +Tuy nhiên, khi lệnh gọi khởi đầu trong một tiến trình khác, phương pháp sẽ được thực thi trong một luồng được chọn từ +một tập hợp các luồng mà hệ thống duy trì trong cùng tiến trình như {@link android.os.IBinder +IBinder} (nó không được thực thi trong luồng UI của tiến trình). Ví dụ, trong khi phương pháp +{@link android.app.Service#onBind onBind()} của dịch vụ sẽ được gọi từ luồng UI của tiến trình +của dịch vụ, các phương pháp được triển khai trong đối tượng mà {@link android.app.Service#onBind +onBind()} trả về (ví dụ, một lớp con triển khai các phương pháp RPC) sẽ được gọi từ các luồng +trong tập hợp. Vì một dịch vụ có thể có nhiều hơn một máy khách, nhiều hơn một luồng tập hợp có thể sử dụng +cùng một phương pháp {@link android.os.IBinder IBinder} tại cùng một thời điểm. Vì thế, các phương pháp {@link android.os.IBinder +IBinder} phải được triển khai sao cho an toàn với luồng.</p> + +<p> Tương tự, một trình cung cấp nội dung có thể nhận các yêu cầu dữ liệu khởi nguồn trong các tiến trình khác. +Mặc dù các lớp {@link android.content.ContentResolver} và {@link android.content.ContentProvider} +ẩn đi chi tiết về cách truyền thông liên tiến trình được quản lý, các phương pháp {@link +android.content.ContentProvider} hồi đáp những yêu cầu đó—các phương pháp {@link +android.content.ContentProvider#query query()}, {@link android.content.ContentProvider#insert +insert()}, {@link android.content.ContentProvider#delete delete()}, {@link +android.content.ContentProvider#update update()}, và {@link android.content.ContentProvider#getType +getType()}—được gọi từ một tập hợp luồng trong tiến trình của trình cung cấp nội dung, chứ không phải luồng +UI cho tiến trình đó. Vì những phương pháp này có thể được gọi từ bất kỳ số lượng luồng nào tại +cùng thời điểm, chúng cũng phải được triển khai sao cho an toàn với luồng. </p> + + +<h2 id="IPC">Truyền thông Liên Tiến trình</h2> + +<p>Android cung cấp một cơ chế cho truyền thông liên tiến trình (IPC) bằng cách sử dụng các lệnh gọi thủ tục từ xa +(RPC), trong đó một phương pháp được gọi bởi một hoạt động hoặc thành phần ứng dụng khác, nhưng được thực thi +từ xa (trong một tiến trình khác), với bất kỳ kết quả nào được trả về +hàm gọi. Điều này đòi hỏi việc phân tích một lệnh gọi phương pháp và dữ liệu của nó về cấp độ mà hệ điều hành +có thể hiểu được, truyền phát nó từ tiến trình và khoảng trống địa chỉ cục bộ đến tiến trình và +khoảng trống địa chỉ từ xa, sau đó tổ hợp lại và phát hành lại lệnh gọi ở đó. Sau đó, các giá trị trả về được +phát theo hướng ngược lại. Android cung cấp tất cả mã để thực hiện những giao tác +IPC này, vì thế bạn có thể tập trung vào việc định nghĩa và triển khai giao diện lập trình RPC. </p> + +<p>Để thực hiện IPC, ứng dụng của bạn phải liên kết với một dịch vụ, bằng cách sử dụng {@link +android.content.Context#bindService bindService()}. Để biết thêm thông tin, hãy xem hướng dẫn cho nhà phát triển <a href="{@docRoot}guide/components/services.html">Dịch vụ</a>.</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>For information about how to perform work in the background for an indefinite period of time +(without a user interface), continue with the <b><a +href="{@docRoot}guide/components/services.html">Services</a></b> document.</p> +--> diff --git a/docs/html-intl/intl/vi/guide/components/recents.jd b/docs/html-intl/intl/vi/guide/components/recents.jd new file mode 100644 index 000000000000..0a176145f9ca --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/recents.jd @@ -0,0 +1,256 @@ +page.title=Màn hình Tổng quan +page.tags="recents","overview" + +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + + <h2>Trong tài liệu này</h2> + <ol> + <li><a href="#adding">Thêm Tác vụ vào Màn hình Tổng quan</a> + <ol> + <li><a href="#flag-new-doc">Sử dụng cờ Ý định để thêm một tác vụ</a></li> + <li><a href="#attr-doclaunch">Sử dụng thuộc tính Hoạt động để thêm một tác vụ</a></li> + </ol> + </li> + <li><a href="#removing">Loại bỏ Tác vụ</a> + <ol> + <li><a href="#apptask-remove">Sử dụng lớp AppTask để loại bỏ tác vụ</a></li> + <li><a href="#retain-finished">Giữ lại tác vụ đã hoàn thành</a></li> + </ol> + </li> + </ol> + + <h2>Lớp khóa</h2> + <ol> + <li>{@link android.app.ActivityManager.AppTask}</li> + <li>{@link android.content.Intent}</li> + </ol> + + <h2>Mã mẫu</h2> + <ol> + <li><a href="{@docRoot}samples/DocumentCentricApps/index.html">Ứng dụng tập trung vào tài liệu</a></li> + </ol> + +</div> +</div> + +<p>Màn hình tổng quan (còn được gọi là màn hình gần đây, danh sách tác vụ gần đây, hay ứng dụng gần đây) +là một UI cấp hệ thống liệt kê các <a href="{@docRoot}guide/components/activities.html"> +hoạt động</a> và <a href="{@docRoot}guide/components/tasks-and-back-stack.html">tác vụ</a> mới được truy cập gần đây. Người dùng +có thể điều hướng qua danh sách này và chọn một tác vụ để tiếp tục, hoặc người dùng có thể loại bỏ một tác vụ khỏi +danh sách bằng cách trượt nhanh nó đi. Với việc phát hành Android 5.0 (API mức 21), nhiều thực thể của +hoạt động tương tự chứa các tài liệu khác nhau có thể xuất hiện dưới dạng các tác vụ trong màn hình tổng quan. Ví dụ, +Google Drive có thể có một tác vụ cho từng tài liệu trong một vài tài liệu Google. Mỗi tài liệu xuất hiện thành một +tác vụ trong màn hình tổng quan.</p> + +<img src="{@docRoot}images/components/recents.png" alt="" width="284" /> +<p class="img-caption"><strong>Hình 1.</strong> Màn hình tổng quan hiển thị ba tài liệu Google Drive +, mỗi tài liệu được biểu diễn như một tác vụ riêng.</p> + +<p>Thường thì bạn sẽ cho phép hệ thống định nghĩa cách tác vụ và +hoạt động của mình được biểu diễn như thế nào trong màn hình tổng quan, và bạn không cần sửa đổi hành vi này. +Tuy nhiên, ứng dụng của bạn có thể xác định cách thức và thời gian các hoạt động xuất hiện trong màn hình tổng quan. Lớp +{@link android.app.ActivityManager.AppTask} cho phép bạn quản lý tác vụ, và cờ hoạt động của +lớp {@link android.content.Intent} cho phép bạn quy định khi nào thì một hoạt động được thêm hoặc loại bỏ khỏi +màn hình tổng quan. Đồng thời, thuộc tính <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code> cho phép bạn đặt hành vi trong bản kê khai.</p> + +<h2 id="adding">Thêm Tác vụ vào Màn hình Tổng quan</h2> + +<p>Sử dụng cờ của lớp {@link android.content.Intent} để thêm một tác vụ cho phép kiểm soát nhiều hơn +đối với thời điểm và cách thức một tài liệu được mở hoặc mở lại trong màn hình tổng quan. Khi sử dụng các thuộc tính +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +, bạn có thể chọn giữa luôn mở tài liệu trong một tác vụ mới hoặc sử dụng lại một +tác vụ hiện có cho tài liệu.</p> + +<h3 id="flag-new-doc">Sử dụng cờ Ý định để thêm một tác vụ</h3> + +<p>Khi tạo một tài liệu mới cho hoạt động của bạn, bạn gọi phương pháp +{@link android.app.ActivityManager.AppTask#startActivity(android.content.Context, android.content.Intent, android.os.Bundle) startActivity()} +của lớp {@link android.app.ActivityManager.AppTask}, chuyển cho nó ý định có +chức năng khởi chạy hoạt động. Để chèn một ngắt lô-gic sao cho hệ thống coi hoạt động của bạn như một tác vụ +mới trong màn hình tổng quan, hãy chuyển cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +trong phương pháp {@link android.content.Intent#addFlags(int) addFlags()} của {@link android.content.Intent} +có chức năng khởi chạy hoạt động.</p> + +<p class="note"><strong>Lưu ý:</strong> Cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} +thay thế cờ {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET}, +được rút bớt kể từ phiên bản Android 5.0 (API mức 21).</p> + +<p>Nếu bạn đặt cờ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} khi tạo +tài liệu mới, hệ thống sẽ luôn tạo một tác vụ mới lấy hoạt động mục tiêu đó làm gốc. +Thiết đặt này cho phép mở cùng tài liệu trong nhiều hơn một tác vụ. Đoạn mã sau thể hiện +cách mà hoạt động chính thực hiện điều này:</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +public void createNewDocument(View view) { + final Intent newDocumentIntent = newDocumentIntent(); + if (useMultipleTasks) { + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + } + startActivity(newDocumentIntent); + } + + private Intent newDocumentIntent() { + boolean useMultipleTasks = mCheckbox.isChecked(); + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; + } + + private static int incrementAndGet() { + Log.d(TAG, "incrementAndGet(): " + mDocumentCounter); + return mDocumentCounter++; + } +} +</pre> + +<p class="note"><strong>Lưu ý:</strong> Các hoạt động được khởi chạy bằng cờ {@code FLAG_ACTIVITY_NEW_DOCUMENT} +phải có giá trị thuộc tính {@code android:launchMode="standard"} (mặc định) được đặt trong +bản kê khai.</p> + +<p>Khi hoạt động chính khởi chạy một hoạt động mới, hệ thống sẽ tìm kiếm thông qua các tác vụ hiện tại để +xem tác vụ nào có ý định khớp với tên thành phần ý định và dữ liệu Ý định cho hoạt động đó. Nếu tác vụ +không được tìm thấy, hoặc ý định chứa cờ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK} +thì một tác vụ mới sẽ được tạo lấy hoạt động làm gốc. Nếu tìm thấy, nó sẽ mang tác vụ đó +tới phía trước và chuyển ý định mới tới {@link android.app.Activity#onNewIntent onNewIntent()}. +Hoạt động mới sẽ nhận ý định và tạo một tài liệu mới trong màn hình tổng quan, như trong ví dụ +sau:</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +NewDocumentActivity.java</a></p> +<pre> +@Override +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_new_document); + mDocumentCount = getIntent() + .getIntExtra(DocumentCentricActivity.KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0); + mDocumentCounterTextView = (TextView) findViewById( + R.id.hello_new_document_text_view); + setDocumentCounterText(R.string.hello_new_document_counter); +} + +@Override +protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + /* If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this activity + is reused to create a new document. + */ + setDocumentCounterText(R.string.reusing_document_counter); +} +</pre> + + +<h3 id="#attr-doclaunch">Sử dụng thuộc tính hoạt động để thêm một tác vụ</h3> + +<p>Một hoạt động cũng có thể quy định trong bản kê khai của nó rằng nó luôn khởi chạy vào một tác vụ mới bằng cách sử dụng +thuộc tính <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +, <a href="{@docRoot}guide/topics/manifest/activity-element.html#dlmode"> +{@code android:documentLaunchMode}</a>. Thuộc tính này có bốn giá trị tạo ra hiệu ứng +sau khi người dùng mở một tài liệu bằng ứng dụng:</p> + +<dl> + <dt>"{@code intoExisting}"</dt> + <dd>Hoạt động sử dụng lại một tác vụ hiện có cho tài liệu. Điều này giống như khi thiết đặt cờ + {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} mà <em>không</em> thiết đặt cờ + {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, như được mô tả trong phần + <a href="#flag-new-doc">Sử dụng cờ Ý định để thêm một tác vụ</a> bên trên.</dd> + + <dt>"{@code always}"</dt> + <dd>Hoạt động tạo một tác vụ mới cho tài liệu, ngay cả khi tài liệu đã được mở. Sử dụng + giá trị này giống như thiết đặt cả cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + và {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}.</dd> + + <dt>"{@code none”}"</dt> + <dd>Hoạt động không tạo một tác vụ mới cho tài liệu. Màn hình tổng quan xử lý hoạt động + như theo mặc định: nó hiển thị một tác vụ đơn lẻ cho ứng dụng, tác vụ này + tiếp tục từ bất kỳ hoạt động nào mà người dùng đã gọi ra cuối cùng.</dd> + + <dt>"{@code never}"</dt> + <dd>Hoạt động không tạo một tác vụ mới cho tài liệu. Việc thiết đặt giá trị này sẽ khống chế + hành vi của {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT} + và cờ {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK}, nếu một trong hai được đặt + trong ý định, và màn hình tổng quan sẽ hiển thị một tác vụ đơn lẻ cho ứng dụng, tác vụ này tiếp tục từ + bất kỳ hoạt động nào mà người dùng đã gọi ra cuối cùng.</dd> +</dl> + +<p class="note"><strong>Lưu ý:</strong> Đối với những giá trị ngoài {@code none} và {@code never} +hoạt động phải được định nghĩa bằng {@code launchMode="standard"}. Nếu thuộc tính này không được quy định thì +{@code documentLaunchMode="none"} sẽ được sử dụng.</p> + +<h2 id="removing">Loại bỏ Tác vụ</h2> + +<p>Theo mặc định, một tác vụ tài liệu sẽ tự động được loại bỏ khỏi màn hình tổng quan khi hoạt động của nó +hoàn thành. Bạn có thể khống chế hành vi này bằng lớp {@link android.app.ActivityManager.AppTask}, +bằng một cờ {@link android.content.Intent}, hoặc bằng một thuộc tính<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"> +<activity></a></code>.</p> + +<p>Bạn có thể luôn loại trừ hoàn toàn một tác vụ khỏi màn hình tổng quan bằng cách thiết đặt thuộc tính +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +, <a href="{@docRoot}guide/topics/manifest/activity-element.html#exclude"> +{@code android:excludeFromRecents}</a> thành {@code true}.</p> + +<p>Bạn có thể thiết đặt số lượng tác vụ tối đa mà ứng dụng của bạn có thể bao gồm trong màn hình tổng quan bằng cách đặt thuộc tính + <code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> + <a href="{@docRoot}guide/topics/manifest/activity-element.html#maxrecents">{@code android:maxRecents} +</a> thành một giá trị số nguyên. Mặc định là 16. Khi đạt được số tác vụ tối đa, +tác vụ ít sử dụng gần đây nhất sẽ bị loại bỏ khỏi màn hình tổng quan. Giá trị tối đa {@code android:maxRecents} +bằng 50 (25 trên các thiết bị có bộ nhớ thấp); giá trị thấp hơn 1 không hợp lệ.</p> + +<h3 id="#apptask-remove">Sử dụng lớp AppTask để loại bỏ tác vụ</h3> + +<p>Trong hoạt động mà tạo một tác vụ mới trong màn hình tổng quan, bạn có thể +quy định khi nào thì loại bỏ tác vụ và hoàn thành tất cả các hoạt động gắn liền với nó bằng cách gọi +phương pháp {@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()}.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +NewDocumentActivity.java</a></p> +<pre> +public void onRemoveFromRecents(View view) { + // The document is no longer needed; remove its task. + finishAndRemoveTask(); +} +</pre> + +<p class="note"><strong>Lưu ý:</strong> Sử dụng phương pháp +{@link android.app.ActivityManager.AppTask#finishAndRemoveTask() finishAndRemoveTask()} +sẽ khống chế việc sử dụng tag {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, +như được trình bày ở bên dưới.</p> + +<h3 id="#retain-finished">Giữ lại tác vụ đã hoàn thành</h3> + +<p>Nếu bạn muốn giữ lại một tác vụ trong màn hình tổng quan, ngay cả khi hoạt động của nó đã hoàn thành, hãy chuyển +cờ {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS} trong phương pháp +{@link android.content.Intent#addFlags(int) addFlags()} của Ý định mà khởi chạy hoạt động.</p> + +<p class="code-caption"><a href="{@docRoot}samples/DocumentCentricApps/index.html"> +DocumentCentricActivity.java</a></p> +<pre> +private Intent newDocumentIntent() { + final Intent newDocumentIntent = new Intent(this, NewDocumentActivity.class); + newDocumentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | + android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS); + newDocumentIntent.putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, incrementAndGet()); + return newDocumentIntent; +} +</pre> + +<p>Để đạt được cùng kết quả như vậy, hãy đặt thuộc tính +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +<a href="{@docRoot}guide/topics/manifest/activity-element.html#autoremrecents"> +{@code android:autoRemoveFromRecents}</a> thành {@code false}. Giá trị mặc định bằng {@code true} +đối với các hoạt động tài liệu, và {@code false} đối với các hoạt động thông thường. Việc sử dụng thuộc tính này sẽ khống chế +cờ {@link android.content.Intent#FLAG_ACTIVITY_RETAIN_IN_RECENTS}, như đã trình bày trước đó.</p> + + + + + + + diff --git a/docs/html-intl/intl/vi/guide/components/services.jd b/docs/html-intl/intl/vi/guide/components/services.jd new file mode 100644 index 000000000000..9e3e6c75eccf --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/services.jd @@ -0,0 +1,813 @@ +page.title=Dịch vụ +@jd:body + +<div id="qv-wrapper"> +<ol id="qv"> +<h2>Trong tài liệu này</h2> +<ol> +<li><a href="#Basics">Nội dung Cơ bản</a></li> +<ol> + <li><a href="#Declaring">Khai báo một dịch vụ trong bản kê khai</a></li> +</ol> +<li><a href="#CreatingAService">Tạo một Dịch vụ được Bắt đầu</a> + <ol> + <li><a href="#ExtendingIntentService">Mở rộng lớp IntentService</a></li> + <li><a href="#ExtendingService">Mở rộng lớp Dịch vụ</a></li> + <li><a href="#StartingAService">Bắt đầu một dịch vụ</a></li> + <li><a href="#Stopping">Dừng một dịch vụ</a></li> + </ol> +</li> +<li><a href="#CreatingBoundService">Tạo một Dịch vụ Gắn kết</a></li> +<li><a href="#Notifications">Gửi Thông báo tới Người dùng</a></li> +<li><a href="#Foreground">Chạy một Dịch vụ trong Tiền cảnh</a></li> +<li><a href="#Lifecycle">Quản lý Vòng đời của một Dịch vụ</a> +<ol> + <li><a href="#LifecycleCallbacks">Triển khai gọi lại vòng đời</a></li> +</ol> +</li> +</ol> + +<h2>Lớp khóa</h2> +<ol> + <li>{@link android.app.Service}</li> + <li>{@link android.app.IntentService}</li> +</ol> + +<h2>Mẫu</h2> +<ol> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ServiceStartArguments.html">{@code + ServiceStartArguments}</a></li> + <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/LocalService.html">{@code + LocalService}</a></li> +</ol> + +<h2>Xem thêm</h2> +<ol> +<li><a href="{@docRoot}guide/components/bound-services.html">Dịch vụ Gắn kết</a></li> +</ol> + +</div> + + +<p>{@link android.app.Service} là một thành phần ứng dụng có khả năng thực hiện +các thao tác chạy kéo dài trong nền và không cung cấp giao diện người dùng. Một +thành phần ứng dụng khác có thể bắt đầu một dịch vụ và nó sẽ tiếp tục chạy ngầm ngay cả khi người dùng +chuyển sang một ứng dụng khác. Ngoài ra, một thành phần có thể gắn kết với một dịch vụ để +tương tác với nó và thậm chí thực hiện truyền thông liên tiến trình (IPC). Ví dụ, một dịch vụ có thể +xử lý các giao dịch mạng, phát nhạc, thực hiện I/O tệp, hoặc tương tác với một trình cung cấp nội dung, tất cả +đều xuất phát từ nền.</p> + +<p>Về cơ bản, một dịch vụ có thể có hai dạng:</p> + +<dl> + <dt>Được bắt đầu</dt> + <dd>Dịch vụ có dạng "được bắt đầu" khi một thành phần ứng dụng (chẳng hạn như một hoạt động) bắt đầu nó bằng cách +gọi {@link android.content.Context#startService startService()}. Sau khi được bắt đầu, dịch vụ +có thể chạy ngầm vô thời hạn, ngay cả khi thành phần bắt đầu nó bị hủy. Thông thường, +dịch vụ được bắt đầu sẽ thực hiện một thao tác đơn lẻ và không trả về kết quả cho hàm gọi. +Ví dụ, nó có thể tải xuống hoặc tải lên một tệp thông qua mạng. Khi thao tác được hoàn thành, dịch vụ +tự nó sẽ dừng lại.</dd> + <dt>Gắn kết</dt> + <dd>Dịch vụ có dạng "gắn kết" khi một thành phần ứng dụng gắn kết với nó bằng cách gọi {@link +android.content.Context#bindService bindService()}. Dịch vụ gắn kết sẽ đưa ra +một giao diện máy khách-máy chủ cho phép các thành phần tương tác với dịch vụ, gửi yêu cầu, nhận kết quả, và thậm chí +làm vậy thông qua truyền thông liên tiến trình (IPC). Dịch vụ gắn kết chỉ chạy trong khi +một thành phần ứng dụng khác được gắn kết với nó. Nhiều thành phần có thể gắn kết cùng lúc với dịch vụ, +nhưng khi tất cả bị bỏ gắn kết thì dịch vụ sẽ bị hủy.</dd> +</dl> + +<p>Mặc dù tài liệu này thường đề cập tới hai loại dịch vụ riêng rẽ, dịch vụ +của bạn có thể hoạt động theo cả hai cách—nó có thể được bắt đầu (để chạy vô thời hạn) và cũng cho phép gắn kết. +Đó đơn giản là vấn đề bạn có triển khai một cặp phương pháp gọi lại hay không: {@link +android.app.Service#onStartCommand onStartCommand()} để cho phép thành phần bắt đầu nó và {@link +android.app.Service#onBind onBind()} để cho phép nó gắn kết.</p> + +<p>Không phụ thuộc vào việc ứng dụng của bạn được bắt đầu, gắn kết, hay cả hai, bất kỳ thành phần ứng dụng nào +cũng có thể sử dụng dịch vụ (thậm chí từ một ứng dụng riêng biệt), giống như cách mà bất kỳ thành phần nào cũng có thể sử dụng +một hoạt động—bằng cách bắt đầu nó bằng một {@link android.content.Intent}. Tuy nhiên, bạn có thể khai báo +dịch vụ là riêng tư trong tệp bản kê khai, và chặn truy cập từ các ứng dụng khác. Điều này +được trình bày kỹ hơn trong phần về <a href="#Declaring">Khai báo dịch vụ trong +bản kê khai</a>.</p> + +<p class="caution"><strong>Chú ý:</strong> Một dịch vụ chạy trong +luồng chính của tiến trình lưu trữ của nó—dịch vụ <strong>không</strong> tạo luồng của chính nó +và <strong>không</strong> chạy trong một tiến trình riêng biệt (trừ khi bạn quy định khác). Điều này có nghĩa +là, nếu dịch vụ của bạn định thực hiện bất kỳ công việc nặng nào đối với CPU hay chặn các thao tác (chẳng hạn như phát lại MP3 +hay kết nối mạng), bạn nên tạo một luồng mới bên trong dịch vụ để thực hiện công việc đó. Bằng cách sử dụng +một luồng riêng biệt, bạn sẽ giảm rủi ro gặp lỗi Ứng dụng Không Hồi đáp (ANR) và luồng chính của ứng dụng có thể +vẫn dành riêng cho tương tác giữa người dùng với các hoạt động của bạn.</p> + + +<h2 id="Basics">Nội dung Cơ bản</h2> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Bạn nên sử dụng dịch vụ hay luồng?</h3> + <p>Dịch vụ đơn thuần là một thành phần có thể chạy ngầm ngay cả khi người dùng không +đang tương tác với ứng dụng của bạn. Vì thế, bạn chỉ nên tạo một dịch vụ nếu đó là điều bạn +cần.</p> + <p>Nếu bạn cần thực hiện công việc bên ngoài luồng chính của mình, nhưng chỉ trong khi người dùng đang tương tác với +ứng dụng của bạn, thì thay vào đó, bạn nên tạo một luồng mới chứ không phải một dịch vụ. Ví +dụ, nếu bạn muốn phát một bản nhạc, nhưng chỉ trong khi hoạt động của bạn đang chạy, bạn có thể tạo +một luồng trong {@link android.app.Activity#onCreate onCreate()}, bắt đầu chạy nó trong {@link +android.app.Activity#onStart onStart()}, rồi dừng nó trong {@link android.app.Activity#onStop +onStop()}. Cũng xem xét việc sử dụng {@link android.os.AsyncTask} hoặc {@link android.os.HandlerThread}, +thay vì sử dụng lớp {@link java.lang.Thread} truyền thống. Xem tài liệu <a href="{@docRoot}guide/components/processes-and-threads.html#Threads">Tiến trình và +Luồng</a> để biết thêm thông tin về luồng.</p> + <p>Hãy nhớ rằng nếu bạn sử dụng một dịch vụ, nó vẫn chạy trong luồng chính của ứng dụng của bạn theo +mặc định, vì thế bạn vẫn nên tạo một luồng mới trong dịch vụ nếu nó thực hiện các thao tác tăng cường hoặc +chặn.</p> +</div> +</div> + +<p>Để tạo một dịch vụ, bạn phải tạo một lớp con của {@link android.app.Service} (hoặc một +trong các lớp con hiện tại của nó). Trong triển khai của mình, bạn cần khống chế một số phương pháp gọi lại có chức năng +xử lý những khía cạnh chính trong vòng đời của dịch vụ và cung cấp một cơ chế để các thành phần gắn kết với +dịch vụ đó, nếu phù hợp. Những phương pháp gọi lại quan trọng nhất mà bạn nên khống chế là:</p> + +<dl> + <dt>{@link android.app.Service#onStartCommand onStartCommand()}</dt> + <dd>Hệ thống sẽ gọi phương pháp này khi một thành phần khác, chẳng hạn như một hoạt động, +yêu cầu dịch vụ phải được bắt đầu, bằng cách gọi {@link android.content.Context#startService +startService()}. Sau khi phương pháp này thực thi, dịch vụ sẽ được bắt đầu và có thể chạy vô thời hạn trong +nền. Nếu bạn triển khai điều này, bạn có trách nhiệm dừng dịch vụ khi +công việc của nó được hoàn thành, bằng cách gọi {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}. (Nếu chỉ muốn cung cấp khả năng gắn kết, bạn không +cần triển khai phương pháp này.)</dd> + <dt>{@link android.app.Service#onBind onBind()}</dt> + <dd>Hệ thống sẽ gọi phương pháp này khi một thành phần khác muốn gắn kết với +dịch vụ (chẳng hạn như để thực hiện RPC), bằng cách gọi {@link android.content.Context#bindService +bindService()}. Trong triển khai phương pháp này của mình, bạn phải cung cấp một giao diện mà các máy khách +sử dụng để giao tiếp với dịch vụ, bằng cách trả về {@link android.os.IBinder}. Bạn phải luôn +triển khai phương pháp này, nhưng nếu bạn không muốn cho phép gắn kết thì bạn nên trả về rỗng.</dd> + <dt>{@link android.app.Service#onCreate()}</dt> + <dd>Hệ thống sẽ gọi phương pháp này khi dịch vụ được tạo lập lần đầu, để thực hiện quy trình thiết lập một lần +(trước khi nó có thể gọi hoặc {@link android.app.Service#onStartCommand onStartCommand()} hoặc +{@link android.app.Service#onBind onBind()}). Nếu dịch vụ đã đang chạy, phương pháp này sẽ không được +gọi.</dd> + <dt>{@link android.app.Service#onDestroy()}</dt> + <dd>Hệ thống sẽ gọi phương pháp này khi dịch vụ không còn được sử dụng và đang bị hủy. +Dịch vụ của bạn sẽ triển khai phương pháp này để dọn dẹp mọi tài nguyên như luồng, đối tượng theo dõi +được đăng ký, hàm nhận, v.v... Đây là lệnh gọi cuối cùng mà dịch vụ nhận được.</dd> +</dl> + +<p>Nếu một thành phần bắt đầu dịch vụ bằng cách gọi {@link +android.content.Context#startService startService()} (kết quả là một lệnh gọi tới {@link +android.app.Service#onStartCommand onStartCommand()}), khi đó dịch vụ +sẽ vẫn chạy tới khi tự nó dừng bằng {@link android.app.Service#stopSelf()} hoặc một +thành phần khác dừng nó bằng cách gọi {@link android.content.Context#stopService stopService()}.</p> + +<p>Nếu một thành phần gọi +{@link android.content.Context#bindService bindService()} để tạo dịch vụ (và {@link +android.app.Service#onStartCommand onStartCommand()} <em>không</em> được gọi), khi đó dịch vụ sẽ chỉ chạy +khi nào mà thành phần đó còn gắn kết với nó. Sau khi dịch vụ được bỏ gắn kết khỏi tất cả máy khách, hệ thống +sẽ hủy nó.</p> + +<p>Hệ thống Android sẽ buộc dừng một dịch vụ chỉ khi bộ nhớ thấp và nó phải khôi phục tài nguyên +của hệ thống cho hoạt động có tiêu điểm của người dùng. Nếu dịch vụ gắn kết với một hoạt động mà có tiêu điểm +của người dùng, khi đó sẽ có ít khả năng nó sẽ bị tắt bỏ hơn, và nếu dịch vụ được khai báo là <a href="#Foreground">chạy trong tiền cảnh</a> (đề cập sau), khi đó nó sẽ hầu như không bao giờ bị tắt bỏ. +Mặt khác, nếu dịch vụ được bắt đầu và chạy trong thời gian dài, hệ thống sẽ hạ thấp vị trí của nó +trong danh sách tác vụ chạy ngầm qua thời gian và dịch vụ sẽ rất có thể bị +tắt bỏ—nếu dịch vụ của bạn được bắt đầu, khi đó bạn phải thiết kế nó để +xử lý việc khởi động lại do hệ thống một cách uyển chuyển. Nếu hệ thống tắt bỏ dịch vụ của bạn, nó sẽ khởi động lại dịch vụ ngay khi tài nguyên +có sẵn trở lại (mặc dù điều này cũng phụ thuộc vào giá trị mà bạn trả về từ {@link +android.app.Service#onStartCommand onStartCommand()}, vấn đề này sẽ được bàn sau). Để biết thêm thông tin +về thời điểm mà hệ thống có thể hủy một dịch vụ, hãy xem tài liệu <a href="{@docRoot}guide/components/processes-and-threads.html">Tiến trình và Luồng</a> +.</p> + +<p>Trong những phần sau, bạn sẽ thấy cách bạn có thể tạo từng loại dịch vụ và cách sử dụng +nó từ các thành phần ứng dụng khác.</p> + + + +<h3 id="Declaring">Khai báo một dịch vụ trong bản kê khai</h3> + +<p>Giống như hoạt động (và các thành phần khác), bạn phải khai báo tất cả dịch vụ trong tệp bản kê khai +của ứng dụng của mình.</p> + +<p>Để khai báo dịch vụ của bạn, hãy thêm một phần tử <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> làm +con của phần tử <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +. Ví dụ:</p> + +<pre> +<manifest ... > + ... + <application ... > + <service android:name=".ExampleService" /> + ... + </application> +</manifest> +</pre> + +<p>Xem tham chiếu phần tử <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> +để biết thêm thông tin về việc khai báo dịch vụ của bạn trong bản kê khai.</p> + +<p>Có các thuộc tính khác mà bạn có thể bao gồm trong phần tử <a href="{@docRoot}guide/topics/manifest/service-element.html">{@code <service>}</a> để +định nghĩa các tính chất chẳng hạn như những quyền cần để bắt đầu dịch vụ và tiến trình mà +dịch vụ sẽ chạy trong đó. Thuộc tính <a href="{@docRoot}guide/topics/manifest/service-element.html#nm">{@code android:name}</a> +là thuộc tính bắt buộc duy nhất—nó quy định tên lớp của dịch vụ. Một khi +bạn phát hành ứng dụng của mình, bạn không nên thay đổi tên này, vì nếu bạn làm vậy, bạn sẽ gặp rủi ro làm gãy +mã do sự phụ thuộc vào các ý định biểu thị để bắt đầu hoặc gắn kết dịch vụ (đọc bài đăng blog, <a href="http://android-developers.blogspot.com/2011/06/things-that-cannot-change.html">Những Điều +Không Thay Đổi Được</a>). + +<p>Để đảm bảo ứng dụng của bạn được bảo mật, <strong>luôn sử dụng một ý định biểu thị khi bắt đầu hoặc gắn kết +{@link android.app.Service}</strong> của bạn và không được khai báo bộ lọc ý định cho dịch vụ. Nếu +điều trọng yếu là bạn phải cho phép một chút không rõ ràng về dịch vụ nào sẽ bắt đầu, bạn có thể +cung cấp bộ lọc ý định cho dịch vụ của mình và loại bỏ tên thành phần khỏi {@link +android.content.Intent}, nhưng sau đó bạn có thể đặt gói cho ý định bằng {@link +android.content.Intent#setPackage setPackage()}, điều này cung cấp sự không rõ ràng vừa đủ cho +dịch vụ mục tiêu đó.</p> + +<p>Ngoài ra, bạn có thể đảm bảo rằng dịch vụ của mình chỉ sẵn có cho ứng dụng của bạn bằng cách +đưa vào thuộc tính <a href="{@docRoot}guide/topics/manifest/service-element.html#exported">{@code android:exported}</a> +và đặt nó thành {@code "false"}. Điều này sẽ dừng việc các ứng dụng khác bắt đầu +dịch vụ của bạn, ngay cả khi sử dụng một ý định biểu thị.</p> + + + + +<h2 id="CreatingStartedService">Tạo một Dịch vụ được Bắt đầu</h2> + +<p>Dịch vụ được bắt đầu là dịch vụ mà một thành phần khác bắt đầu bằng cách gọi {@link +android.content.Context#startService startService()}, kết quả là một lệnh gọi tới phương pháp +{@link android.app.Service#onStartCommand onStartCommand()} của dịch vụ.</p> + +<p>Khi một dịch vụ được bắt đầu, nó có một vòng đời độc lập với +thành phần đã bắt đầu nó và dịch vụ có thể chạy ngầm vô thời hạn, ngay cả khi +thành phần bắt đầu nó bị hủy. Như vậy, dịch vụ sẽ tự dừng khi làm xong công việc của nó +bằng cách gọi {@link android.app.Service#stopSelf stopSelf()}, hoặc một thành phần khác có thể dừng nó +bằng cách gọi {@link android.content.Context#stopService stopService()}.</p> + +<p>Một thành phần ứng dụng chẳng hạn như một hoạt động có thể bắt đầu dịch vụ bằng cách gọi {@link +android.content.Context#startService startService()} và chuyển một {@link android.content.Intent} +trong đó quy định dịch vụ và bao gồm bất kỳ dữ liệu nào để cho dịch vụ sử dụng. Dịch vụ sẽ nhận +{@link android.content.Intent} này trong phương pháp {@link android.app.Service#onStartCommand +onStartCommand()}.</p> + +<p>Ví dụ, giả sử một hoạt động cần lưu một số dữ liệu vào cơ sở dữ liệu trực tuyến. Hoạt động có thể +bắt đầu một dịch vụ đồng hành và truyền cho nó dữ liệu để lưu bằng cách chuyển một ý định tới {@link +android.content.Context#startService startService()}. Dịch vụ sẽ nhận ý định trong {@link +android.app.Service#onStartCommand onStartCommand()}, kết nối với Internet và thực hiện +giao tác cơ sở dữ liệu. Khi giao tác được thực hiện, dịch vụ sẽ tự dừng lại và nó bị +hủy.</p> + +<p class="caution"><strong>Chú ý:</strong> Một dịch vụ sẽ chạy trong cùng tiến trình như ứng dụng +mà nó được khai báo trong đó và trong luồng chính của ứng dụng đó theo mặc định. Vì vậy, nếu dịch vụ của bạn +thực hiện các thao tác tăng cường hoặc chặn trong khi người dùng tương tác với một hoạt động từ cùng +ứng dụng, dịch vụ sẽ làm chậm hiệu năng của hoạt động. Để tránh tác động tới hiệu năng của +ứng dụng, bạn nên bắt đầu một luồng mới bên trong dịch vụ.</p> + +<p>Thông thường, có hai lớp mà bạn có thể mở rộng để tạo một dịch vụ được bắt đầu:</p> +<dl> + <dt>{@link android.app.Service}</dt> + <dd>Đây là lớp cơ bản cho tất cả dịch vụ. Khi bạn mở rộng lớp này, điều quan trọng là +bạn tạo một luồng mới để thực hiện tất cả công việc của dịch vụ trong đó, do dịch vụ sử dụng luồng chính +của ứng dụng của bạn, theo mặc định, điều này có thể làm chậm hiệu năng của bất kỳ hoạt động nào mà ứng dụng +của bạn đang chạy.</dd> + <dt>{@link android.app.IntentService}</dt> + <dd>Đây là một lớp con của {@link android.app.Service} có chức năng sử dụng một luồng trình thực hiện để xử lý tất cả +yêu cầu bắt đầu một cách lần lượt. Đây là lựa chọn tốt nhất nếu bạn không yêu cầu dịch vụ của mình +xử lý đồng thời nhiều yêu cầu. Tất cả những gì bạn cần làm đó là triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()}, nó sẽ nhận ý định cho mỗi +yêu cầu bắt đầu để bạn có thể thực hiện công việc chạy ngầm.</dd> +</dl> + +<p>Các phần sau mô tả cách bạn có thể triển khai dịch vụ của mình bằng cách sử dụng một trong các cách cho những lớp +này.</p> + + +<h3 id="ExtendingIntentService">Mở rộng lớp IntentService</h3> + +<p>Vì phần lớn các dịch vụ được bắt đầu không cần xử lý nhiều yêu cầu một cách đồng thời +(điều này thực sự có thể là một kịch bản tạo đa luồng nguy hiểm), có lẽ tốt nhất là nếu bạn +triển khai dịch vụ của mình bằng cách sử dụng lớp {@link android.app.IntentService}.</p> + +<p>{@link android.app.IntentService} làm điều sau đây:</p> + +<ul> + <li>Tạo một luồng trình thực hiện mặc định để thực thi tất cả ý định được chuyển tới {@link +android.app.Service#onStartCommand onStartCommand()} tách riêng với luồng +chính của ứng dụng của bạn.</li> + <li>Tạo một hàng đợi công việc để chuyển lần lượt từng ý định tới triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()} của bạn, vì thế bạn không bao giờ phải +lo lắng về vấn đề tạo đa luồng.</li> + <li>Dừng dịch vụ sau khi tất cả yêu cầu bắt đầu đều đã được xử lý, vì thế bạn không bao giờ phải gọi +{@link android.app.Service#stopSelf}.</li> + <li>Cung cấp triển khai mặc định của {@link android.app.IntentService#onBind onBind()} mà +trả về rỗng.</li> + <li>Cung cấp triển khai mặc định của {@link android.app.IntentService#onStartCommand +onStartCommand()} mà gửi ý định tới hàng đợi công việc rồi tới triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()} của bạn.</li> +</ul> + +<p>Tất cả đều nói lên một thực tế rằng tất cả những việc bạn cần làm đó là triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()} để thực hiện công việc mà +máy khách cung cấp. (Mặc dù bạn cũng cần cung cấp một hàm dựng nhỏ cho dịch vụ.)</p> + +<p>Sau đây là ví dụ về triển khai {@link android.app.IntentService}:</p> + +<pre> +public class HelloIntentService extends IntentService { + + /** + * A constructor is required, and must call the super {@link android.app.IntentService#IntentService} + * constructor with a name for the worker thread. + */ + public HelloIntentService() { + super("HelloIntentService"); + } + + /** + * The IntentService calls this method from the default worker thread with + * the intent that started the service. When this method returns, IntentService + * stops the service, as appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + } +} +</pre> + +<p>Đó là tất cả những gì bạn cần: một hàm dựng và triển khai {@link +android.app.IntentService#onHandleIntent onHandleIntent()}.</p> + +<p>Nếu bạn quyết định cũng khống chế các phương pháp gọi lại khác, chẳng hạn như {@link +android.app.IntentService#onCreate onCreate()}, {@link +android.app.IntentService#onStartCommand onStartCommand()}, hoặc {@link +android.app.IntentService#onDestroy onDestroy()}, hãy nhớ gọi ra siêu triển khai, sao +cho {@link android.app.IntentService} có thể xử lý hợp lý vòng đời của luồng trình thực hiện.</p> + +<p>Ví dụ, {@link android.app.IntentService#onStartCommand onStartCommand()} phải trả về +triển khai mặc định (đó là cách mà ý định được chuyển tới {@link +android.app.IntentService#onHandleIntent onHandleIntent()}):</p> + +<pre> +@Override +public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + return super.onStartCommand(intent,flags,startId); +} +</pre> + +<p>Bên cạnh {@link android.app.IntentService#onHandleIntent onHandleIntent()}, phương pháp duy nhất mà +từ đó bạn không cần gọi siêu lớp là {@link android.app.IntentService#onBind +onBind()} (nhưng bạn chỉ cần triển khai điều đó nếu dịch vụ của bạn cho phép gắn kết).</p> + +<p>Trong phần tiếp theo, bạn sẽ thấy cách mà cùng loại dịch vụ được triển khai khi mở rộng +lớp {@link android.app.Service} cơ sở, nó có nhiều mã hơn nhưng có thể +phù hợp nếu bạn cần xử lý các yêu cầu bắt đầu đồng thời.</p> + + +<h3 id="ExtendingService">Mở rộng lớp Dịch vụ</h3> + +<p>Như bạn thấy trong phần trước, sử dụng {@link android.app.IntentService} giúp việc +triển khai một dịch vụ được bắt đầu của bạn trở nên rất đơn giản. Tuy nhiên, nếu bạn cần dịch vụ của mình +thực hiện tạo đa luồng (thay vì xử lý các yêu cầu bắt đầu thông qua một hàng đợi công việc), khi đó bạn +có thể mở rộng lớp {@link android.app.Service} để xử lý từng ý định.</p> + +<p>Để so sánh, đoạn mã mẫu sau là triển khai lớp {@link +android.app.Service} mà thực hiện chính xác cùng công việc như ví dụ bên trên bằng cách sử dụng {@link +android.app.IntentService}. Cụ thể, đối với mỗi yêu cầu bắt đầu, nó sẽ sử dụng một luồng trình thực hiện để thực hiện +công việc và chỉ xử lý lần lượt từng yêu cầu một.</p> + +<pre> +public class HelloService extends Service { + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + @Override + public void handleMessage(Message msg) { + // Normally we would do some work here, like download a file. + // For our sample, we just sleep for 5 seconds. + long endTime = System.currentTimeMillis() + 5*1000; + while (System.currentTimeMillis() < endTime) { + synchronized (this) { + try { + wait(endTime - System.currentTimeMillis()); + } catch (Exception e) { + } + } + } + // Stop the service using the startId, so that we don't stop + // the service in the middle of handling another job + stopSelf(msg.arg1); + } + } + + @Override + public void onCreate() { + // Start up the thread running the service. Note that we create a + // separate thread because the service normally runs in the process's + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); + + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the job + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + mServiceHandler.sendMessage(msg); + + // If we get killed, after returning from here, restart + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // We don't provide binding, so return null + return null; + } + + @Override + public void onDestroy() { + Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); + } +} +</pre> + +<p>Như bạn có thể thấy, có nhiều việc hơn nhiều so với việc sử dụng {@link android.app.IntentService}.</p> + +<p>Tuy nhiên, do bạn tự mình xử lý từng lệnh gọi đến {@link android.app.Service#onStartCommand +onStartCommand()}, bạn có thể thực hiện nhiều yêu cầu một cách đồng thời. Đó không phải là việc +mà ví dụ này làm, nhưng nếu đó là việc bạn muốn, vậy bạn có thể tạo một luồng mới cho từng +yêu cầu và ngay lập tức trả chúng về (thay vì đợi tới khi yêu cầu trước hoàn thành).</p> + +<p>Để ý rằng phương pháp {@link android.app.Service#onStartCommand onStartCommand()} phải trả về một +số nguyên. Số nguyên là một giá trị mô tả cách hệ thống nên tiếp tục dịch vụ trong +trường hợp hệ thống tắt bỏ nó (như được đề cập ở trên, triển khai mặc định cho {@link +android.app.IntentService} sẽ xử lý điều này cho bạn dù bạn có thể sửa đổi nó). Giá trị trả về +từ {@link android.app.Service#onStartCommand onStartCommand()} phải là một trong các +hằng số sau:</p> + +<dl> + <dt>{@link android.app.Service#START_NOT_STICKY}</dt> + <dd>Nếu hệ thống tắt bỏ dịch vụ sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về, <em>không</em> được tạo lại dịch vụ đó, trừ khi có các ý định +đang chờ để được chuyển. Đây là lựa chọn an toàn nhất để tránh chạy dịch vụ của bạn khi không cần thiết +và khi ứng dụng của bạn có thể đơn thuần khởi động lại bất kỳ công việc chưa hoàn thành nào.</dd> + <dt>{@link android.app.Service#START_STICKY}</dt> + <dd>Nếu hệ thống tắt bỏ dịch vụ sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về, hãy tạo lại dịch vụ và gọi {@link +android.app.Service#onStartCommand onStartCommand()}, nhưng <em>không</em> chuyển lại ý định cuối cùng. +Thay vào đó, hệ thống sẽ gọi {@link android.app.Service#onStartCommand onStartCommand()} bằng một +ý định rỗng, trừ khi có các ý định đang chờ để bắt đầu dịch vụ, trong trường hợp đó, +những ý định này sẽ được chuyển. Điều này phù hợp với các trình phát phương tiện (hoặc dịch vụ tương tự) mà không +đang thực thi lệnh, nhưng đang chạy vô thời hạn và chờ một tác vụ.</dd> + <dt>{@link android.app.Service#START_REDELIVER_INTENT}</dt> + <dd>Nếu hệ thống tắt bỏ dịch vụ sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về, hãy tạo lại dịch vụ và gọi {@link +android.app.Service#onStartCommand onStartCommand()} bằng ý định cuối cùng được chuyển tới +dịch vụ. Mọi ý định chờ đều được chuyển lần lượt. Điều này phù hợp với các dịch vụ đang +chủ động thực hiện một công việc mà nên được tiếp tục ngay lập tức, chẳng hạn như tải xuống một tệp.</dd> +</dl> +<p>Để biết thêm chi tiết về những giá trị trả về này, hãy xem tài liệu tham khảo được liên kết cho từng +hằng số.</p> + + + +<h3 id="StartingAService">Bắt đầu một Dịch vụ</h3> + +<p>Bạn có thể bắt đầu một dịch vụ từ một hoạt động hoặc thành phần ứng dụng khác bằng cách chuyển một +{@link android.content.Intent} (quy định dịch vụ sẽ bắt đầu) đến {@link +android.content.Context#startService startService()}. Hệ thống Android sẽ gọi phương pháp {@link +android.app.Service#onStartCommand onStartCommand()} của dịch vụ và chuyển cho nó {@link +android.content.Intent}. (Bạn tuyệt đối không nên trực tiếp gọi {@link android.app.Service#onStartCommand +onStartCommand()}.)</p> + +<p>Ví dụ, một hoạt động có thể bắt đầu dịch vụ ví dụ trong phần trước ({@code +HelloSevice}) bằng cách sử dụng một ý định biểu thị với {@link android.content.Context#startService +startService()}:</p> + +<pre> +Intent intent = new Intent(this, HelloService.class); +startService(intent); +</pre> + +<p>Phương pháp {@link android.content.Context#startService startService()} ngay lập tức trả về và +hệ thống Android sẽ gọi phương pháp {@link android.app.Service#onStartCommand +onStartCommand()} của dịch vụ. Nếu dịch vụ không đang chạy, trước tiên hệ thống sẽ gọi {@link +android.app.Service#onCreate onCreate()}, rồi gọi {@link android.app.Service#onStartCommand +onStartCommand()}.</p> + +<p>Nếu dịch vụ cũng không cung cấp khả năng gắn kết, ý định được chuyển bằng {@link +android.content.Context#startService startService()} sẽ là phương thức giao tiếp duy nhất giữa +thành phần ứng dụng và dịch vụ. Tuy nhiên, nếu bạn muốn dịch vụ gửi một kết quả trở lại, khi đó +máy khách mà bắt đầu dịch vụ có thể tạo một {@link android.app.PendingIntent} cho một quảng bá +(bằng {@link android.app.PendingIntent#getBroadcast getBroadcast()}) và chuyển nó tới dịch vụ +trong {@link android.content.Intent} mà bắt đầu dịch vụ. Khi đó, dịch vụ có thể sử dụng +quảng bá để chuyển kết quả.</p> + +<p>Nhiều yêu cầu bắt đầu dịch vụ sẽ dẫn đến nhiều lệnh gọi tương ứng tới +{@link android.app.Service#onStartCommand onStartCommand()} của dịch vụ. Tuy nhiên, chỉ có một yêu cầu dừng +dịch vụ (bằng {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}) là bắt buộc để dừng nó.</p> + + +<h3 id="Stopping">Dừng một dịch vụ</h3> + +<p>Dịch vụ được bắt đầu phải quản lý vòng đời của chính nó. Cụ thể, hệ thống không dừng +hay hủy dịch vụ trừ khi nó phải khôi phục bộ nhớ của hệ thống và dịch vụ +sẽ tiếp tục chạy sau khi {@link android.app.Service#onStartCommand onStartCommand()} trả về. Vì vậy, +dịch vụ phải tự dừng bằng cách gọi {@link android.app.Service#stopSelf stopSelf()} hoặc một thành phần +khác có thể dừng nó bằng cách gọi {@link android.content.Context#stopService stopService()}.</p> + +<p>Sau khi được yêu cầu dừng bằng {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}, hệ thống sẽ hủy dịch vụ ngay khi +có thể.</p> + +<p>Tuy nhiên, nếu dịch vụ của bạn xử lý nhiều yêu cầu {@link +android.app.Service#onStartCommand onStartCommand()} đồng thời, khi đó bạn không nên dừng +dịch vụ khi bạn đã hoàn thành xử lý yêu cầu bắt đầu, vì bạn có thể đã nhận được một +yêu cầu bắt đầu mới kể từ thời điểm đó (dừng khi kết thúc yêu cầu thứ nhất sẽ chấm dứt yêu cầu thứ hai). Để tránh +vấn đề này, bạn có thể sử dụng {@link android.app.Service#stopSelf(int)} để đảm bảo rằng yêu cầu +dừng dịch vụ của bạn luôn được dựa trên yêu cầu bắt đầu gần đây nhất. Cụ thể, khi bạn gọi {@link +android.app.Service#stopSelf(int)}, bạn sẽ chuyển ID của yêu cầu bắt đầu (<code>startId</code> +được chuyển tới {@link android.app.Service#onStartCommand onStartCommand()}) mà yêu cầu dừng của bạn +tương ứng với. Khi đó, nếu dịch vụ đã nhận được một yêu cầu bắt đầu mới trước khi bạn có thể gọi {@link +android.app.Service#stopSelf(int)}, vậy ID sẽ không khớp và dịch vụ sẽ không dừng.</p> + +<p class="caution"><strong>Chú ý:</strong> Điều quan trọng là ứng dụng của bạn dừng dịch vụ của nó +khi nó hoàn thành xong công việc để tránh lãng phí tài nguyên của hệ thống và tốn pin. Nếu cần, +các thành phần khác có thể dừng dịch vụ bằng cách gọi {@link +android.content.Context#stopService stopService()}. Ngay cả khi bạn kích hoạt gắn kết cho dịch vụ, +bạn phải luôn tự mình dừng dịch vụ nếu dịch vụ đã nhận được lệnh gọi tới {@link +android.app.Service#onStartCommand onStartCommand()}.</p> + +<p>Để biết thêm thông tin về vòng đời của một dịch vụ, hãy xem phần bên dưới về <a href="#Lifecycle">Quản lý Vòng đời của một Dịch vụ</a>.</p> + + + +<h2 id="CreatingBoundService">Tạo một Dịch vụ Gắn kết</h2> + +<p>Dịch vụ gắn kết là một dịch vụ cho phép các thành phần ứng dụng gắn kết với nó bằng cách gọi {@link +android.content.Context#bindService bindService()} để tạo một kết nối lâu dài +(và thường không cho phép các thành phần <em>bắt đầu</em> nó bằng cách gọi {@link +android.content.Context#startService startService()}).</p> + +<p>Bạn nên tạo một dịch vụ gắn kết khi muốn tương tác với dịch vụ từ hoạt động +và các thành phần khác trong ứng dụng của mình hoặc để hiển thị một số tính năng trong ứng dụng của bạn cho +các ứng dụng khác thông qua truyền thông liên tiến trình (IPC).</p> + +<p>Để tạo một dịch vụ gắn kết, bạn phải triển khai phương pháp gọi lại {@link +android.app.Service#onBind onBind()} để trả về một {@link android.os.IBinder} mà +định nghĩa giao diện cho giao tiếp với dịch vụ đó. Khi đó, các thành phần ứng dụng khác có thể gọi +{@link android.content.Context#bindService bindService()} để truy xuất giao diện và +bắt đầu các phương pháp gọi trên dịch vụ. Dịch vụ tồn tại chỉ nhằm phục vụ thành phần ứng dụng mà +được gắn kết với nó, vì thế khi không có thành phần được gắn kết với dịch vụ, hệ thống sẽ hủy nó +(bạn <em>không</em> cần dừng một dịch vụ gắn kết theo cách phải làm khi dịch vụ được bắt đầu +thông qua {@link android.app.Service#onStartCommand onStartCommand()}).</p> + +<p>Để tạo một dịch vụ gắn kết, điều đầu tiên bạn phải làm là định nghĩa giao diện quy định +cách thức mà một máy khách có thể giao tiếp với dịch vụ. Giao diện giữa dịch vụ và +máy khách này phải là một triển khai {@link android.os.IBinder} và được dịch vụ của bạn phải +trả về từ phương pháp gọi lại {@link android.app.Service#onBind +onBind()}. Sau khi máy khách nhận được {@link android.os.IBinder}, nó có thể bắt đầu +tương tác với dịch vụ thông qua giao diện đó.</p> + +<p>Nhiều máy khách có thể gắn kết với dịch vụ đồng thời. Khi một máy khách hoàn thành tương tác với +dịch vụ, nó sẽ gọi {@link android.content.Context#unbindService unbindService()} để bỏ gắn kết. Sau khi +không còn máy khách nào được gắn kết với dịch vụ, hệ thống sẽ hủy dịch vụ.</p> + +<p>Có nhiều cách để triển khai một dịch vụ gắn kết và triển khai sẽ phức tạp +hơn so với dịch vụ được bắt đầu, vì thế nội dung bàn về dịch vụ gắn kết được trình bày trong một +tài liệu riêng về <a href="{@docRoot}guide/components/bound-services.html">Dịch vụ Gắn kết</a>.</p> + + + +<h2 id="Notifications">Gửi Thông báo tới Người dùng</h2> + +<p>Sau khi chạy, một dịch vụ có thể thông báo cho người dùng về sự kiện bằng cách sử dụng <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Thông báo Cửa sổ</a> hoặc <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Thông báo Thanh Trạng thái</a>.</p> + +<p>Thông báo cửa sổ là một thông báo xuất hiện một lúc trên bề mặt của cửa sổ hiện tại +rồi biến mất, trong khi thông báo thanh trạng thái cung cấp một biểu tượng trong thanh trạng thái cùng một +thông báo, người dùng có thể chọn nó để thực hiện một hành động (chẳng hạn như bắt đầu một hoạt động).</p> + +<p>Thông thường thông báo thanh trạng thái là kỹ thuật tốt nhất khi một công việc nền nào đó đã hoàn thành +(chẳng hạn như một tệp đã hoàn thành +việc tải xuống) và lúc này người dùng có thể hành động dựa trên nó. Khi người dùng chọn thông báo từ dạng xem mở rộng +, thông báo có thể bắt đầu một hoạt động (chẳng hạn như xem tệp được tải xuống).</p> + +<p>Xem hướng dẫn dành cho nhà phát triển <a href="{@docRoot}guide/topics/ui/notifiers/toasts.html">Thông báo Cửa sổ</a> hoặc <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Thông báo Thanh Trạng thái</a> +để biết thêm thông tin.</p> + + + +<h2 id="Foreground">Chạy một Dịch vụ trong Tiền cảnh</h2> + +<p>Dịch vụ tiền cảnh là một dịch vụ được coi là điều mà +người dùng đang chủ động quan tâm, vì thế nó không được đề nghị để hệ thống tắt bỏ khi bộ nhớ thấp. Dịch vụ +tiền cảnh phải cung cấp một thông báo cho thanh trạng thái, nó được đặt dưới tiêu đề +"Đang diễn ra", điều này có nghĩa là thông báo không thể loại bỏ được trừ khi dịch vụ +bị dừng hoặc loại bỏ khỏi tiền cảnh.</p> + +<p>Ví dụ, một trình chơi nhạc đang phát nhạc từ một dịch vụ nên được đặt để chạy trong +tiền cảnh, vì người dùng rõ ràng ý thức được +hoạt động của nó. Thông báo trong thanh trạng thái có thể cho biết bài hát đang chơi và cho phép +người dùng khởi chạy một hoạt động để tương tác với trình chơi nhạc.</p> + +<p>Để yêu cầu dịch vụ của bạn chạy trong tiền cảnh, hãy gọi {@link +android.app.Service#startForeground startForeground()}. Phương pháp này dùng hai tham số: một số nguyên +để xác định duy nhất thông báo và {@link +android.app.Notification} cho thanh trạng thái. Ví dụ:</p> + +<pre> +Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text), + System.currentTimeMillis()); +Intent notificationIntent = new Intent(this, ExampleActivity.class); +PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); +notification.setLatestEventInfo(this, getText(R.string.notification_title), + getText(R.string.notification_message), pendingIntent); +startForeground(ONGOING_NOTIFICATION_ID, notification); +</pre> + +<p class="caution"><strong>Chú ý:</strong> ID số nguyên mà bạn cấp cho {@link +android.app.Service#startForeground startForeground()} không được bằng 0.</p> + + +<p>Để xóa bỏ dịch vụ khỏi tiền cảnh, hãy gọi {@link +android.app.Service#stopForeground stopForeground()}. Phương pháp này dùng một boolean, cho biết +có loại bỏ cả thông báo thanh trạng thái hay không. Phương pháp này <em>không</em> dừng +dịch vụ. Tuy nhiên, nếu bạn dừng dịch vụ trong khi nó vẫn đang chạy trong tiền cảnh, khi đó thông báo +cũng bị loại bỏ.</p> + +<p>Để biết thêm thông tin về thông báo, hãy xem phần <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">Tạo Thông báo +Thanh Trạng thái</a>.</p> + + + +<h2 id="Lifecycle">Quản lý Vòng đời của một Dịch vụ</h2> + +<p>Vòng đời của một dịch vụ đơn giản hơn nhiều so với vòng đời của một hoạt động. Tuy nhiên, một điều thậm chí còn quan trọng hơn +đó là bạn phải thật chú ý tới cách dịch vụ của bạn được tạo và hủy, bởi một dịch vụ +có thể chạy ngầm mà người dùng không biết.</p> + +<p>Vòng đời của dịch vụ—từ khi nó được tạo tới khi nó bị hủy—có thể đi theo hai +con đường khác nhau:</p> + +<ul> +<li>Dịch vụ được bắt đầu + <p>Dịch vụ được tạo khi một thành phần khác gọi {@link +android.content.Context#startService startService()}. Sau đó, dịch vụ sẽ chạy vô thời hạn và phải +tự dừng bằng cách gọi {@link +android.app.Service#stopSelf() stopSelf()}. Một thành phần khác cũng có thể dừng +dịch vụ bằng cách gọi {@link android.content.Context#stopService +stopService()}. Khi dịch vụ bị dừng, hệ thống sẽ hủy nó.</p></li> + +<li>Dịch vụ gắn kết + <p>Dịch vụ được tạo khi một thành phần khác (máy khách) gọi {@link +android.content.Context#bindService bindService()}. Khi đó, máy khách giao tiếp với dịch vụ +thông qua một giao diện {@link android.os.IBinder}. Máy khách có thể đóng kết nối bằng cách gọi +{@link android.content.Context#unbindService unbindService()}. Nhiều máy khách có thể gắn kết với +cùng dịch vụ và khi tất cả chúng bỏ gắn kết, hệ thống sẽ hủy dịch vụ. (Dịch vụ +<em>không</em> cần tự mình dừng.)</p></li> +</ul> + +<p>Hai con đường này hoàn toàn riêng biệt. Cụ thể, bạn có thể gắn kết với một dịch vụ đã +được bắt đầu bằng {@link android.content.Context#startService startService()}. Ví dụ, một dịch vụ +nhạc nền có thể được bắt đầu bằng cách gọi {@link android.content.Context#startService +startService()} bằng một {@link android.content.Intent} mà sẽ nhận biết nhạc để phát. Sau đó, +có thể là khi người dùng muốn thực thi một quyền điều khiển đối với trình phát đó hoặc lấy thông tin về +bài hát đang phát, hoạt động có thể gắn kết với dịch vụ bằng cách gọi {@link +android.content.Context#bindService bindService()}. Trong những trường hợp như vậy, {@link +android.content.Context#stopService stopService()} hoặc {@link android.app.Service#stopSelf +stopSelf()} không thực sự dừng dịch vụ tới khi tất cả máy khách bỏ gắn kết. </p> + + +<h3 id="LifecycleCallbacks">Triển khai gọi lại vòng đời</h3> + +<p>Giống như một hoạt động, dịch vụ có các phương pháp gọi lại vòng đời mà bạn có thể triển khai để theo dõi +những thay đổi về trạng thái của dịch vụ và thực hiện công việc tại những thời điểm phù hợp. Dịch vụ khung sau +minh họa từng phương pháp vòng đời:</p> + +<pre> +public class ExampleService extends Service { + int mStartMode; // indicates how to behave if the service is killed + IBinder mBinder; // interface for clients that bind + boolean mAllowRebind; // indicates whether onRebind should be used + + @Override + public void {@link android.app.Service#onCreate onCreate}() { + // The service is being created + } + @Override + public int {@link android.app.Service#onStartCommand onStartCommand}(Intent intent, int flags, int startId) { + // The service is starting, due to a call to {@link android.content.Context#startService startService()} + return <em>mStartMode</em>; + } + @Override + public IBinder {@link android.app.Service#onBind onBind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()} + return <em>mBinder</em>; + } + @Override + public boolean {@link android.app.Service#onUnbind onUnbind}(Intent intent) { + // All clients have unbound with {@link android.content.Context#unbindService unbindService()} + return <em>mAllowRebind</em>; + } + @Override + public void {@link android.app.Service#onRebind onRebind}(Intent intent) { + // A client is binding to the service with {@link android.content.Context#bindService bindService()}, + // after onUnbind() has already been called + } + @Override + public void {@link android.app.Service#onDestroy onDestroy}() { + // The service is no longer used and is being destroyed + } +} +</pre> + +<p class="note"><strong>Lưu ý:</strong> Không như các phương pháp gọi lại vòng đời của hoạt động, bạn +<em>không</em> phải gọi triển khai siêu lớp với những phương pháp gọi lại này.</p> + +<img src="{@docRoot}images/service_lifecycle.png" alt="" /> +<p class="img-caption"><strong>Hình 2.</strong> Vòng đời dịch vụ. Sơ đồ phía bên trái +minh họa vòng đời khi dịch vụ được tạo bằng {@link android.content.Context#startService +startService()} và sơ đồ phía bên phải minh họa vòng đời khi dịch vụ được tạo +bằng {@link android.content.Context#bindService bindService()}.</p> + +<p>Bằng việc triển khai những phương pháp này, bạn có thể theo dõi hai vòng lặp lồng nhau trong vòng đời của dịch vụ: </p> + +<ul> +<li><strong>Toàn bộ vòng đời</strong> của một dịch vụ xảy ra giữa thời điểm gọi {@link +android.app.Service#onCreate onCreate()} và thời điểm {@link +android.app.Service#onDestroy} trả về. Giống như hoạt động, dịch vụ thực hiện thiết lập ban đầu của nó trong +{@link android.app.Service#onCreate onCreate()} và giải phóng tất cả tài nguyên còn lại trong {@link +android.app.Service#onDestroy onDestroy()}. Ví dụ, một +dịch vụ phát lại nhạc có thể tạo luồng mà tại đó nhạc sẽ được phát trong {@link +android.app.Service#onCreate onCreate()}, sau đó dừng luồng trong {@link +android.app.Service#onDestroy onDestroy()}. + +<p>Các phương pháp {@link android.app.Service#onCreate onCreate()} và {@link android.app.Service#onDestroy +onDestroy()} được gọi cho tất cả dịch vụ, dù +chúng được tạo bởi {@link android.content.Context#startService startService()} hay {@link +android.content.Context#bindService bindService()}.</p></li> + +<li><strong>Vòng đời hiện hoạt</strong> của một dịch vụ sẽ bắt đầu bằng một lệnh gọi đến hoặc {@link +android.app.Service#onStartCommand onStartCommand()} hoặc {@link android.app.Service#onBind onBind()}. +Mỗi phương pháp sẽ được giao {@link +android.content.Intent} mà được chuyển tương ứng cho hoặc {@link android.content.Context#startService +startService()} hoặc {@link android.content.Context#bindService bindService()}. +<p>Nếu dịch vụ được bắt đầu, vòng đời hiện hoạt sẽ chấm dứt tại cùng thời điểm khi toàn bộ vòng đời +chấm dứt (dịch vụ sẽ vẫn hiện hoạt ngay cả sau khi {@link android.app.Service#onStartCommand +onStartCommand()} trả về). Nếu dịch vụ bị gắn kết, vòng đời hiện hoạt sẽ chấm dứt khi {@link +android.app.Service#onUnbind onUnbind()} trả về.</p> +</li> +</ul> + +<p class="note"><strong>Lưu ý:</strong> Mặc dù dịch vụ được bắt đầu bị dừng bởi một lệnh gọi đến +hoặc {@link android.app.Service#stopSelf stopSelf()} hoặc {@link +android.content.Context#stopService stopService()}, sẽ không có một lệnh gọi lại tương ứng cho +dịch vụ (không có lệnh gọi lại {@code onStop()}). Vì thế, trừ khi dịch vụ được gắn kết với một máy khách, +hệ thống sẽ hủy nó khi dịch vụ bị dừng—{@link +android.app.Service#onDestroy onDestroy()} là lệnh gọi lại duy nhất nhận được.</p> + +<p>Hình 2 minh họa các phương pháp gọi lại điển hình cho một dịch vụ. Mặc dù hình tách riêng +các dịch vụ được tạo bởi {@link android.content.Context#startService startService()} với các dịch vụ +được tạo bởi {@link android.content.Context#bindService bindService()}, hãy +ghi nhớ rằng bất kỳ dịch vụ nào, dù được bắt đầu như thế nào, đều có thể cho phép máy khách gắn kết với nó. +Vì thế, một dịch vụ được bắt đầu từ đầu bằng {@link android.app.Service#onStartCommand +onStartCommand()} (bởi một máy khách gọi {@link android.content.Context#startService startService()}) +vẫn có thể nhận một lệnh gọi đến {@link android.app.Service#onBind onBind()} (khi máy khách gọi +{@link android.content.Context#bindService bindService()}).</p> + +<p>Để biết thêm thông tin về việc tao một dịch vụ có tính năng gắn kết, hãy xem tài liệu <a href="{@docRoot}guide/components/bound-services.html">Dịch vụ Gắn kết</a>, +trong đó có thêm thông tin về phương pháp gọi lại {@link android.app.Service#onRebind onRebind()} +trong phần về <a href="{@docRoot}guide/components/bound-services.html#Lifecycle">Quản lý Vòng đời của +một Dịch vụ Gắn kết</a>.</p> + + +<!-- +<h2>Beginner's Path</h2> + +<p>To learn how to query data from the system or other applications (such as contacts or media +stored on the device), continue with the <b><a +href="{@docRoot}guide/topics/providers/content-providers.html">Content Providers</a></b> +document.</p> +--> diff --git a/docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd b/docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd new file mode 100644 index 000000000000..85afffff5ba5 --- /dev/null +++ b/docs/html-intl/intl/vi/guide/components/tasks-and-back-stack.jd @@ -0,0 +1,578 @@ +page.title=Tác vụ và Ngăn xếp +parent.title=Hoạt động +parent.link=activities.html +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> + +<h2>Trong tài liệu này</h2> +<ol> +<li><a href="#ActivityState">Lưu Trạng thái của Hoạt động</a></li></li> +<li><a href="#ManagingTasks">Quản lý Tác vụ</a> + <ol> + <li><a href="#TaskLaunchModes">Định nghĩa các chế độ khởi chạy</a></li> + <li><a href="#Affinities">Xử lý quan hệ</a></li> + <li><a href="#Clearing">Xóa ngăn xếp</a></li> + <li><a href="#Starting">Bắt đầu một tác vụ</a></li> + </ol> +</li> +</ol> + +<h2>Bài viết</h2> +<ol> + <li><a href="http://android-developers.blogspot.com/2010/04/multitasking-android-way.html"> + Đa nhiệm theo cách của Android</a></li> +</ol> + +<h2>Xem thêm</h2> +<ol> + <li><a href="{@docRoot}design/patterns/navigation.html">Thiết kế Android: +Điều hướng</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html">Phần tử bản kê khai {@code <activity>} +</a></li> + <li><a href="{@docRoot}guide/components/recents.html">Màn hình Tổng quan</a></li> +</ol> +</div> +</div> + + +<p>Một ứng dụng thường chứa nhiều <a href="{@docRoot}guide/components/activities.html">hoạt động</a>. Mỗi hoạt động +nên được thiết kế xung quanh một kiểu hành động cụ thể mà người dùng có thể thực hiện và bắt đầu các hoạt động +khác. Ví dụ, một ứng dụng e-mail có thể có một hoạt động để hiển thị một danh sách các thư mới. +Khi người dùng chọn một thư, một hoạt động mới sẽ mở ra để xem thư đó.</p> + +<p>Hoạt động thậm chí có thể bắt đầu các hoạt động tồn tại trong các ứng dụng khác trên thiết bị. Ví +dụ, nếu ứng dụng của bạn muốn gửi một thư e-mail, bạn có thể định nghĩa một ý định để thực hiện một hành động +"gửi" và bao gồm một số dữ liệu, chẳng hạn như địa chỉ e-mail và nội dung. Một hoạt động từ một ứng dụng +khác tự khai báo là có khả năng xử lý kiểu ý định này sẽ mở ra. Trong trường hợp này, +ý định sẽ gửi một e-mail, sao cho hoạt động "soạn" của một ứng dụng e-mail sẽ bắt đầu (nếu nhiều hoạt động +hỗ trợ cùng ý định, khi đó hệ thống cho phép người dùng chọn hoạt động sẽ sử dụng). Khi e-mail được +gửi, hoạt động của bạn tiếp tục và dường như hoạt động e-mail là một phần ứng dụng của bạn. Mặc dù +các hoạt động có thể đến từ những ứng dụng khác nhau, Android duy trì trải nghiệm người dùng +mượt mà này bằng cách giữ cả hai hoạt động trong cùng <em>tác vụ</em>.</p> + +<p>Tác vụ là một tập hợp gồm nhiều hoạt động mà người dùng tương tác với +khi thực hiện một công việc nhất định. Các hoạt động được sắp xếp trong một chồng (<em>ngăn xếp</em>), theo +thứ tự mở mỗi hoạt động.</p> + +<!-- SAVE FOR WHEN THE FRAGMENT DOC IS ADDED +<div class="sidebox-wrapper"> +<div class="sidebox"> +<h3>Adding fragments to a task's back stack</h3> + +<p>Your activity can also include {@link android.app.Fragment}s to the back stack. For example, +suppose you have a two-pane layout using fragments, one of which is a list view (fragment A) and the +other being a layout to display an item from the list (fragment B). When the user selects an item +from the list, fragment B is replaced by a new fragment (fragment C). In this case, it might be +desireable for the user to navigate back to reveal fragment B, using the <em>Back</em> button.</p> +<p>In order to add fragment B to the back stack so that this is possible, you must call {@link +android.app.FragmentTransaction#addToBackStack addToBackStack()} before you {@link +android.app.FragmentTransaction#commit()} the transaction that replaces fragment B with fragment +C.</p> +<p>For more information about using fragments and adding them to the back stack, see the {@link +android.app.Fragment} class documentation.</p> + +</div> +</div> +--> + +<p>Màn hình Trang chủ của thiết bị là nơi bắt đầu đối với hầu hết tác vụ. Khi người dùng chạm vào một biểu tượng trong trình khởi chạy +ứng dụng + (hoặc lối tắt trên màn hình Trang chủ), tác vụ của ứng dụng đó sẽ tiến ra tiền cảnh. Nếu không +có tác vụ nào cho ứng dụng (ứng dụng chưa được sử dụng gần đây), khi đó một tác vụ mới +sẽ được tạo và hoạt động "chính" cho ứng dụng đó sẽ mở ra thành hoạt động gốc trong chồng.</p> + +<p>Khi hoạt động hiện tại bắt đầu một hoạt động khác, hoạt động mới sẽ bị đẩy lên trên cùng của chồng và +có tiêu điểm. Hoạt động trước vẫn nằm trong chồng, nhưng bị dừng lại. Khi một hoạt động +dừng, hệ thống giữ lại trạng thái hiện tại của giao diện người dùng của hoạt động đó. Khi người dùng nhấn nút +<em>Quay lại</em> +, hoạt động hiện tại được bật khỏi trên cùng của chồng (hoạt động bị hủy) và +hoạt động trước tiếp tục (trạng thái trước đó của UI của nó được khôi phục). Các hoạt động trong chồng +không bao giờ được sắp xếp lại, mà chỉ bị đẩy và bật khỏi chồng—bị đẩy lên trên chồng khi được bắt đầu bởi +hoạt động hiện tại và bị bật khỏi chồng khi người dùng rời nó bằng cách sử dụng nút <em>Quay lại</em>. Như vậy, +ngăn xếp +vận hành như một cấu trúc đối tượng "vào cuối, ra đầu". Hình 1 minh họa +hành vi này cùng một dòng thời gian thể hiện tiến độ giữa các hoạt động dọc theo ngăn xếp +hiện tại ở từng thời điểm.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack.png" alt="" /> +<p class="img-caption"><strong>Hình 1.</strong> Biểu diễn cách mỗi hoạt động mới trong một +tác vụ thêm một mục vào ngăn xếp. Khi người dùng nhấn nút <em>Quay lại</em>, hoạt động +hiện tại +bị hủy và hoạt động trước đó tiếp tục.</p> + + +<p>Nếu người dùng tiếp tục nhấn <em>Quay lại</em>, khi đó mỗi hoạt động trong chồng bị bật khỏi để +hiện ra +hoạt động trước, tới khi người dùng quay lại màn hình Trang chủ (hoặc trở về hoạt động đang chạy khi +tác vụ bắt đầu). Khi tất cả hoạt động bị loại bỏ khỏi chồng, tác vụ sẽ không còn tồn tại.</p> + +<div class="figure" style="width:287px"> +<img src="{@docRoot}images/fundamentals/diagram_multitasking.png" alt="" /> <p +class="img-caption"><strong>Hình 2.</strong> Hai tác vụ: Tác vụ B nhận tương tác người dùng +trong tiền cảnh, trong khi Tác vụ A nằm dưới nền, chờ được tiếp tục.</p> +</div> +<div class="figure" style="width:215px"> + <img src="{@docRoot}images/fundamentals/diagram_multiple_instances.png" alt="" /> <p +class="img-caption"><strong>Hình 3.</strong> Một hoạt động đơn lẻ được khởi tạo nhiều lần.</p> +</div> + +<p>Tác vụ là một đơn vị dính kết, có thể di chuyển tới "nền" khi người dùng bắt đầu một tác vụ mới hoặc đi đến +màn hình Trang chủ, thông qua nút <em>Home</em>. Khi ở trong nền, tất cả hoạt động trong +tác vụ bị +dừng, nhưng ngăn xếp cho tác vụ vẫn không bị ảnh hưởng—tác vụ chỉ đơn thuần mất tiêu điểm trong khi +một tác vụ khác thay thế, như minh họa trong hình 2. Khi đó, một tác vụ có thể quay lại "tiền cảnh" để người dùng +có thể chọn ở nơi họ đã rời đi. Ví dụ, giả sử rằng tác vụ hiện tại (Tác vụ A) có ba +hoạt động trong chồng của mình—hai trong số đó nằm dưới hoạt động hiện tại. Người dùng nhấn nút <em>Trang chủ</em> +, sau đó +bắt đầu một ứng dụng mới từ trình khởi chạy ứng dụng. Khi màn hình Trang chủ xuất hiện, Tác vụ A đi vào +nền. Khi ứng dụng mới bắt đầu, hệ thống sẽ bắt đầu một tác vụ cho ứng dụng đó +(Tác vụ B) bằng chồng các hoạt động của chính mình. Sau khi tương tác với +ứng dụng đó, người dùng quay lại Trang chủ lần nữa và chọn ứng dụng +đã bắt đầu Tác vụ A lúc đầu. Lúc này, Tác vụ A đi đến +tiền cảnh—cả ba hoạt động trong chồng của nó đều giữ nguyên và hoạt động trên cùng +của chồng được tiếp tục. Tại +điểm này, người dùng cũng có thể chuyển trở lại Tác vụ B bằng cách đến Trang chủ và chọn biểu tượng ứng dụng +đã bắt đầu tác vụ đó (hoặc bằng cách chọn tác vụ của ứng dụng từ +<a href="{@docRoot}guide/components/recents.html">màn hình tổng quan</a>). +Đây là một ví dụ về đa nhiệm trên Android.</p> + +<p class="note"><strong>Lưu ý:</strong> Nhiều tác vụ có thể được lưu giữ cùng lúc trong nền. +Tuy nhiên, nếu người dùng đang chạy nhiều tác vụ nền tại cùng thời điểm, hệ thống có thể bắt đầu +hủy các hoạt động nền để khôi phục bộ nhớ, khiến trạng thái của hoạt động bị mất. +Xem phần sau đây về <a href="#ActivityState">Trạng thái của hoạt động</a>.</p> + +<p>Vì các hoạt động trong ngăn xếp không bao giờ được sắp xếp lại, nếu ứng dụng của bạn cho phép +người dùng bắt đầu một hoạt động cụ thể từ nhiều hơn một hoạt động, một thực thể mới của +hoạt động đó sẽ được tạo và đẩy lên chồng (thay vì mang bất kỳ thực thể nào trước đó của +hoạt động lên trên cùng). Như vậy, một hoạt động trong ứng dụng của bạn có thể được tạo phiên bản nhiều +lần (thậm chí từ các tác vụ khác nhau), như minh họa trong hình 3. Như vậy, nếu người dùng điều hướng ngược lại +bằng cách sử dụng nút <em>Quay lại</em>, mỗi thực thể của hoạt động được hiển thị theo thứ tự được +mở (mỗi hoạt động +có trạng thái UI của chính chúng). Tuy nhiên, bạn có thể sửa đổi hành vi này nếu không muốn một hoạt động được +khởi tạo nhiều hơn một lần. Cách làm như vậy được bàn trong phần sau về <a href="#ManagingTasks">Quản lý Tác vụ</a>.</p> + + +<p>Để tóm tắt hành vi mặc định đối với các hoạt động và tác vụ:</p> + +<ul> + <li>Khi Hoạt động A bắt đầu Hoạt động B, Hoạt động A bị dừng, nhưng hệ thống giữ lại trạng thái của nó +(chẳng hạn như vị trí cuộn và văn bản được nhập vào các mẫu). +Nếu người dùng nhấn nút <em>Quay lại</em> khi đang trong Hoạt động B, Hoạt động A sẽ tiếp tục với trạng thái +được khôi phục.</li> + <li>Khi người dùng rời khỏi một tác vụ bằng cách nhấn nút <em>Trang chủ</em>, hoạt động hiện tại bị +dừng và +tác vụ của nó sẽ đưa xuống dưới nền. Hệ thống sẽ giữ lại trạng thái của mọi hoạt động trong tác vụ. Nếu +sau đó người dùng tiếp tục tác vụ bằng cách chọn biểu tượng trình khởi chạy đã bắt đầu tác vụ, tác vụ sẽ vào +tiền cảnh và tiếp tục hoạt động ở trên cùng của chồng.</li> + <li>Nếu người dùng nhấn nút <em>Quay lại</em>, hoạt động hiện tại bị bật khỏi chồng +và +bị hủy. Hoạt động trước đó ở trong chồng sẽ được tiếp tục. Khi một hoạt động bị hủy, hệ thống +<em>không</em> giữ lại trạng thái của hoạt động đó.</li> + <li>Hoạt động có thể được khởi tạo nhiều lần, thậm chí từ các tác vụ khác.</li> +</ul> + + +<div class="note design"> +<p><strong>Thiết kế Điều hướng</strong></p> + <p>Để biết thêm về cách điều hướng ứng dụng hoạt động trên Android, hãy đọc hướng dẫn <a href="{@docRoot}design/patterns/navigation.html">Điều hướng</a> của Thiết kế Android.</p> +</div> + + +<h2 id="ActivityState">Lưu Trạng thái của Hoạt động</h2> + +<p>Như được đề cập ở trên, hành vi mặc định của hệ thống giữ nguyên trạng thái của một hoạt động khi nó bị +dừng. Bằng cách này, khi người dùng điều hướng trở lại một hoạt động trước đó, giao diện người dùng của nó sẽ xuất hiện + như lúc bị rời đi. Tuy nhiên, bạn có thể—và <strong>nên</strong>—chủ động giữ lại +trạng thái của các hoạt động của mình bằng cách sử dụng các phương pháp gọi lại, trong trường hợp hoạt động bị hủy và phải +được tạo lại.</p> + +<p>Khi hệ thống dừng một trong các hoạt động của bạn (chẳng hạn như khi một hoạt động mới bắt đầu hoặc tác vụ +di chuyển về nền), hệ thống có thể hoàn toàn hủy hoạt động đó nếu nó cần khôi phục +bộ nhớ hệ thống. Khi điều này xảy ra, thông tin về trạng thái của hoạt động sẽ bị mất. Nếu điều này xảy ra, +hệ thống vẫn +biết rằng hoạt động có một vị trí trong ngăn xếp, nhưng khi hoạt động được đưa tới vị trí trên cùng +của chồng, hệ thống phải tạo lại nó (thay vì tiếp tục). Để tránh +làm mất công việc của người dùng, bạn nên chủ động giữ lại nó bằng cách triển khai phương pháp gọi lại +{@link android.app.Activity#onSaveInstanceState onSaveInstanceState()} trong +hoạt động của mình.</p> + +<p>Để biết thêm thông tin về cách lưu trạng thái hoạt động của mình, hãy xem tài liệu <a href="{@docRoot}guide/components/activities.html#SavingActivityState">Hoạt động</a> +.</p> + + + +<h2 id="ManagingTasks">Quản lý Tác vụ</h2> + +<p>Cách Android quản lý tác vụ và ngăn xếp, như được mô tả bên trên—bằng cách đặt tất cả +hoạt động được bắt đầu nối tiếp nhau vào cùng tác vụ và trong một chồng "vào cuối, ra đầu"—rất +hiệu quả đối với hầu hết ứng dụng và bạn không phải lo lắng về cách thức các hoạt động của mình được liên kết với +các tác vụ hay cách chúng tồn tại trong ngăn xếp. Tuy nhiên, bạn có thể quyết định rằng mình muốn gián đoạn +hành vi thông thường. Có thể bạn muốn một hoạt động trong ứng dụng của mình bắt đầu một tác vụ mới khi nó được +bắt đầu (thay vì được đặt trong tác vụ hiện tại); hoặc, khi bạn bắt đầu một hoạt động, bạn muốn +mang lên trước một thực thể hiện tại của nó (thay vì tạo một +thực thể mới trên cùng của ngăn xếp); hoặc, bạn muốn ngăn xếp của mình được xóa sạch tất cả các +hoạt động, ngoại trừ hoạt động gốc khi người dùng rời khỏi tác vụ.</p> + +<p>Bạn có thể làm những điều này và nhiều điều khác với các thuộc tính trong phần tử bản kê khai +<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +và cờ trong ý định mà bạn chuyển cho +{@link android.app.Activity#startActivity startActivity()}.</p> + +<p>Về mặt này, các thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html"> +{@code <activity>}</a> chính mà bạn có thể sử dụng là:</p> + +<ul class="nolist"> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#aff"> + {@code taskAffinity}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode"> + {@code launchMode}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> + {@code allowTaskReparenting}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#clear"> + {@code clearTaskOnLaunch}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> + {@code alwaysRetainTaskState}</a></li> + <li><a href="{@docRoot}guide/topics/manifest/activity-element.html#finish"> + {@code finishOnTaskLaunch}</a></li> +</ul> + +<p>Và các cờ ý định chính mà bạn có thể sử dụng là:</p> + +<ul class="nolist"> + <li>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</li> + <li>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</li> +</ul> + +<p>Trong những phần sau, bạn sẽ thấy cách bạn có thể sử dụng những thuộc tính của bản kê khai +và cờ ý định này để định nghĩa cách các hoạt động được liên kết với tác vụ và hành vi của chúng như thế nào trong ngăn xếp.</p> + +<p>Bên cạnh đó, có phần bàn riêng về việc cân nhắc cách các tác vụ và hoạt động có thể +được biểu diễn và quản lý trong màn hình tổng quan. Xem phần <a href="{@docRoot}guide/components/recents.html">Màn hình Tổng quan</a> +để biết thêm thông tin. Thông thường, bạn nên cho phép hệ thống định nghĩa tác vụ và các hoạt động +của bạn được biểu diễn như thế nào trong màn hình tổng quan, và bạn không cần sửa đổi hành vi này.</p> + +<p class="caution"><strong>Chú ý:</strong> Hầu hết các ứng dụng không nên gián đoạn hành vi +mặc định cho các hoạt động và tác vụ. Nếu bạn xác định rằng hoạt động của bạn cần phải sửa đổi +hành vi mặc định, hãy thận trọng và đảm bảo kiểm tra khả năng sử dụng được của hoạt động đó trong khi +khởi chạy và khi điều hướng trở lại nó từ các hoạt động và tác vụ khác bằng nút <em>Quay lại</em>. +Đảm bảo kiểm tra hành vi điều hướng mà có thể xung đột với hành vi được kỳ vọng của người dùng.</p> + + +<h3 id="TaskLaunchModes">Định nghĩa các chế độ khởi chạy</h3> + +<p>Các chế độ khởi chạy cho phép bạn định nghĩa một thực thể mới của một hoạt động được liên kết với tác vụ hiện tại +như thế nào. Bạn có thể định nghĩa các chế độ khởi chạy khác nhau theo hai cách:</p> +<ul class="nolist"> + <li><a href="#ManifestForTasks">Sử dụng tệp bản kê khai</a> + <p>Khi bạn khai báo một hoạt động trong tệp bản kê khai của mình, bạn có thể quy định hoạt động +sẽ liên kết với các tác vụ như thế nào khi nó bắt đầu.</li> + <li><a href="#IntentFlagsForTasks">Sử dụng cờ Ý định</a> + <p>Khi bạn gọi {@link android.app.Activity#startActivity startActivity()}, +bạn có thể thêm một cờ vào {@link android.content.Intent} mà khai báo cách (hoặc +liệu có hay không) hoạt động mới sẽ liên kết với tác vụ hiện tại.</p></li> +</ul> + +<p>Như vậy, nếu Hoạt động A bắt đầu Hoạt động B, Hoạt động B có thể định nghĩa trong bản kê khai của mình cách nó +sẽ liên kết với tác vụ hiện tại (nếu có) và Hoạt động A cũng có thể yêu cầu cách mà Hoạt động +B sẽ liên kết với tác vụ hiện tại. Nếu cả hai hoạt động đều định nghĩa cách Hoạt động B +nên liên kết với một tác vụ thì yêu cầu của Hoạt động A (như định nghĩa trong ý định) được ưu tiên +so với yêu cầu của Hoạt động B (như được định nghĩa trong bản kê khai của nó).</p> + +<p class="note"><strong>Lưu ý:</strong> Một số chế độ khởi chạy sẵn có cho tệp bản kê khai +không sẵn có dưới dạng cờ cho một ý định và, tương tự, một số chế độ khởi chạy sẵn có dưới dạng cờ +cho một ý định không thể được định nghĩa trong bản kê khai.</p> + + +<h4 id="ManifestForTasks">Sử dụng tệp bản kê khai</h4> + +<p>Khi khai báo một hoạt động trong tệp bản kê khai của bạn, bạn có thể quy định cách mà hoạt động +sẽ liên kết với một tác vụ bằng cách sử dụng thuộc tính của phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +, <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a>.</p> + +<p>Thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code +launchMode}</a> quy định một chỉ lệnh về cách hoạt động sẽ được khởi chạy vào một +tác vụ. Có bốn chế độ khởi chạy khác nhau mà bạn có thể gán cho thuộc tính +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">launchMode</a></code> +:</p> + +<dl> +<dt>{@code "standard"} (chế độ mặc định)</dt> + <dd>Mặc định. Hệ thống tạo một thực thể mới của hoạt động trong tác vụ là nơi +mà nó được bắt đầu và định tuyến ý định tới đó. Hoạt động có thể được khởi tạo nhiều lần, +mỗi thực thể có thể thuộc về các tác vụ khác nhau, và một tác vụ có thể có nhiều thực thể.</dd> +<dt>{@code "singleTop"}</dt> + <dd>Nếu một thực thể của hoạt động đã tồn tại ở trên cùng của tác vụ hiện tại, hệ thống +sẽ định tuyến ý định tới thực thể đó thông qua một lệnh gọi tới phương pháp {@link +android.app.Activity#onNewIntent onNewIntent()} của nó, thay vì tạo một thực thể mới của +hoạt động. Hoạt động có thể được tạo phiên bản nhiều lần, mỗi thực thể có thể +thuộc về các tác vụ khác nhau, và một tác vụ có thể có nhiều thực thể (nhưng chỉ nếu +hoạt động nằm trên cùng của ngăn xếp <em>không</em> phải là một thực thể của hoạt động hiện có). + <p>Ví dụ, giả sử ngăn xếp của một tác vụ bao gồm hoạt động gốc A với các hoạt động B, C, +và D ở trên cùng (chồng là A-B-C-D; D ở trên cùng). Một ý định đến cho loại hoạt động D. +Nếu D có chế độ khởi chạy {@code "standard"} mặc định, một thực thể mới của lớp sẽ được khởi chạy và +chồng trở thành A-B-C-D-D. Tuy nhiên, nếu chế độ khởi chạy của D là {@code "singleTop"}, thực thể hiện tại +của D sẽ nhận ý định thông qua {@link +android.app.Activity#onNewIntent onNewIntent()}, bởi nó nằm ở vị trí trên cùng của chồng—chồng +vẫn là A-B-C-D. Tuy nhiên, nếu một ý định đến cho hoạt động loại B, khi đó một thực thể +mới của B sẽ được thêm vào chồng ngay cả khi chế độ khởi chạy của nó là {@code "singleTop"}.</p> + <p class="note"><strong>Lưu ý:</strong> Khi một thực thể mới của hoạt động được tạo, +người dùng có thể nhấn nút <em>Quay lại</em> để quay về hoạt động trước đó. Nhưng khi một thực thể +hiện tại của +hoạt động xử lý một ý định mới, người dùng không thể nhấn nút <em>Quay lại</em> để quay về trạng thái +của +hoạt động trước khi ý định mới đến trong {@link android.app.Activity#onNewIntent +onNewIntent()}.</p> +</dd> + +<dt>{@code "singleTask"}</dt> + <dd>Hệ thống sẽ tạo ra một tác vụ mới và khởi tạo hoạt động ở gốc của tác vụ mới. +Tuy nhiên, nếu một thực thể của hoạt động đã tồn tại trong một tác vụ riêng, hệ thống sẽ định tuyến +ý định tới thực thể hiện tại thông qua một lệnh gọi tới phương pháp {@link +android.app.Activity#onNewIntent onNewIntent()} của nó thay vì tạo một thực thể mới. Chỉ +một thực thể của hoạt động có thể tồn tại ở một thời điểm. + <p class="note"><strong>Lưu ý:</strong> Mặc dù hoạt động bắt đầu một tác vụ mới, nút +<em>Quay lại</em> sẽ vẫn đưa người dùng quay về hoạt động trước đó.</p></dd> +<dt>{@code "singleInstance"}.</dt> + <dd>Giống như {@code "singleTask"}, trừ khi hệ thống không khởi chạy bất kỳ hoạt động nào khác vào +tác vụ đang nắm giữ thực thể. Hoạt động luôn là thành viên đơn lẻ và duy nhất của tác vụ; +bất kỳ hoạt động nào được bắt đầu bởi hoạt động này sẽ mở ra trong một tác vụ riêng.</dd> +</dl> + + +<p>Lấy một ví dụ khác, ứng dụng Trình duyệt của Android khai báo rằng hoạt động trình duyệt web sẽ +luôn mở tác vụ của chính mình—bằng cách quy định chế độ khởi chạy {@code singleTask} trong phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a>. +Điều này có nghĩa rằng nếu ứng dụng của bạn phát hành một +ý định để mở Trình duyệt Android, hoạt động của nó <em>không</em> được đặt trong cùng +tác vụ như ứng dụng của bạn. Thay vào đó, hoặc là một tác vụ mới bắt đầu cho Trình duyệt hoặc, nếu Trình duyệt +đã có một tác vụ đang chạy trong nền thì tác vụ đó sẽ được đưa lên trước để xử lý +ý định mới.</p> + +<p>Không phụ thuộc vào việc một hoạt động bắt đầu trong một tác vụ mới hay trong cùng tác vụ mà hoạt động +đã bắt đầu, nút <em>Quay lại</em> sẽ luôn đưa người dùng về hoạt động trước đó. Tuy nhiên, nếu bạn +bắt đầu một hoạt động mà quy định chế độ khởi chạy {@code singleTask}, khi đó nếu một thực thể của +hoạt động đó tồn tại trong một tác vụ nền, toàn bộ tác vụ đó sẽ được đưa ra tiền cảnh. Tại thời điểm này +, lúc này ngăn xếp bao gồm tất cả hoạt động từ tác vụ được mang ra, ở vị trí trên cùng của +chồng. Hình 4 minh họa loại kịch bản này.</p> + +<img src="{@docRoot}images/fundamentals/diagram_backstack_singletask_multiactivity.png" alt="" /> +<p class="img-caption"><strong>Hình 4.</strong> Biểu diễn cách thức một hoạt động với +chế độ khởi chạy "singleTask" được thêm vào ngăn xếp. Nếu hoạt động đã là một phần của một +tác vụ nền với ngăn xếp của chính nó, khi đó toàn bộ ngăn xếp cũng tiến +về phía trước, ở trên cùng tác vụ hiện tại.</p> + +<p>Để biết thêm thông tin về việc sử dụng các chế độ khởi chạy trong tệp bản kê khai, hãy xem tài liệu phần tử +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +, trong đó có trình bày thêm về thuộc tính {@code launchMode} và các giá trị +được chấp nhận.</p> + +<p class="note"><strong>Lưu ý:</strong> Hành vi mà bạn quy định cho hoạt động của mình bằng thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> có thể +bị khống chế bởi cờ có ý định bắt đầu hoạt động của bạn, như được trình bày trong +phần tiếp theo.</p> + + + +<h4 id="#IntentFlagsForTasks">Sử dụng cờ Ý định</h4> + +<p>Khi bắt đầu một hoạt động, bạn có thể sửa đổi liên kết mặc định giữa một hoạt động với tác vụ của nó +bằng cách thêm cờ vào trong ý định mà bạn chuyển tới {@link +android.app.Activity#startActivity startActivity()}. Những cờ mà bạn có thể sử dụng để sửa đổi +hành vi mặc định là:</p> + +<p> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK}</dt> + <dd>Bắt đầu một hoạt động trong một tác vụ mới. Nếu đã có một tác vụ đang chạy cho hoạt động mà bạn đang +bắt đầu, tác vụ đó sẽ được đưa ra tiền cảnh với trạng thái cuối cùng được khôi phục và hoạt động +nhận ý định mới trong {@link android.app.Activity#onNewIntent onNewIntent()}. + <p>Điều này sẽ tạo ra cùng hành vi như giá trị {@code "singleTask"} <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a>, +đã được trình bày ở phần trước.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_SINGLE_TOP}</dt> + <dd>Nếu hoạt động đang được bắt đầu là hoạt động hiện tại (ở trên cùng của ngăn xếp), khi đó +thực thể hiện có sẽ nhận một lệnh gọi đến {@link android.app.Activity#onNewIntent onNewIntent()}, +thay vì tạo một thực thể của hoạt động mới. + <p>Điều này sẽ tạo ra cùng hành vi như giá trị {@code "singleTop"} <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a>, +đã được trình bày ở phần trước.</p></dd> + <dt>{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP}</dt> + <dd>Nếu hoạt động đang được bắt đầu đang chạy trong tác vụ hiện tại, khi đó +thay vì khởi chạy một thực thể mới của hoạt động đó, tất cả các hoạt động khác bên trên nó đều +bị hủy và ý định này được chuyển tới thực thể được tiếp tục của hoạt động (lúc này đang ở trên cùng), +thông qua {@link android.app.Activity#onNewIntent onNewIntent()}). + <p>Không có giá trị cho thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#lmode">{@code launchMode}</a> +mà sinh ra hành vi này.</p> + <p>{@code FLAG_ACTIVITY_CLEAR_TOP} được sử dụng thường xuyên nhất cùng với + {@code FLAG_ACTIVITY_NEW_TASK}. +Khi được sử dụng cùng nhau, những cờ này là một cách để định vị hoạt động hiện tại +trong một tác vụ khác và đặt nó vào vị trí nơi nó có thể hồi đáp ý định. </p> + <p class="note"><strong>Lưu ý:</strong> Nếu chế độ khởi chạy của hoạt động được chỉ định là + {@code "standard"}, +nó cũng bị loại bỏ khỏi chồng và một thực thể mới sẽ được khởi chạy thay chỗ nó để xử lý +ý định đến. Đó là bởi một thực thể mới luôn được tạo cho ý định mới khi chế độ khởi chạy +là {@code "standard"}. </p> +</dd> +</dl> + + + + + +<h3 id="Affinities">Xử lý quan hệ</h3> + +<p><em>Quan hệ</em> sẽ cho biết một hoạt động ưu tiên thuộc về tác vụ nào hơn. Theo mặc định, tất cả +các hoạt động từ cùng ứng dụng có quan hệ với nhau. Vì thế, theo mặc định, tất cả +hoạt động trong cùng ứng dụng ưu tiên ở trong cùng tác vụ hơn. Tuy nhiên, bạn có thể sửa đổi +quan hệ mặc định cho một hoạt động. Các hoạt động được định nghĩa trong +các ứng dụng khác nhau có thể chia sẻ một quan hệ, hoặc các hoạt động được định nghĩa trong cùng ứng dụng có thể +được gán các quan hệ tác vụ khác nhau.</p> + +<p>Bạn có thể sửa đổi quan hệ cho bất kỳ hoạt động đã cho nào bằng thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> của +phần tử <a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> +.</p> + +<p>Thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +lấy một giá trị xâu, đó phải là giá trị duy nhất từ tên gói mặc định +được khai báo trong phần tử <a href="{@docRoot}guide/topics/manifest/manifest-element.html"> +{@code <manifest>} +</a>, do hệ thống sử dụng tên đó để nhận biết quan hệ +tác vụ mặc định cho ứng dụng.</p> + +<p>Vấn đề quan hệ được xét trong hai trường hợp:</p> +<ul> + <li>Khi ý định khởi chạy hoạt động chứa cờ + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +. + +<p>Theo mặc định, một hoạt động mới được khởi chạy vào tác vụ của hoạt động +đã gọi {@link android.app.Activity#startActivity startActivity()}. Nó được đẩy lên cùng +ngăn xếp như trình gọi. Tuy nhiên, nếu ý định được chuyển tới +{@link android.app.Activity#startActivity startActivity()} +chứa cờ {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} +, hệ thống sẽ tìm một tác vụ khác để chứa hoạt động mới. Thường thì đó là một tác vụ mới. +Tuy nhiên, không nhất thiết phải như vậy. Nếu đã có một tác vụ hiện tại với cùng quan hệ như +hoạt động mới, hoạt động sẽ được khởi chạy vào tác vụ đó. Nếu không, nó sẽ bắt đầu một tác vụ mới.</p> + +<p>Nếu cờ này khiến một hoạt động bắt đầu một tác vụ mới và người dùng nhấn nút <em>Home</em> +để rời +nó thì phải có một cách nào đó để người dùng điều hướng quay lại tác vụ. Một số đối tượng (chẳng hạn như +trình quản lý thông báo) luôn bắt đầu các hoạt động trong một tác vụ bên ngoài, không bao giờ bắt đầu trong chính tác vụ của mình, vì thế +chúng luôn đặt {@code FLAG_ACTIVITY_NEW_TASK} vào ý định mà chúng chuyển tới +{@link android.app.Activity#startActivity startActivity()}. +Nếu bạn có một hoạt động có thể được gọi ra bởi +một đối tượng bên ngoài mà có thể sử dụng cờ này, hãy lưu ý là người dùng có một cách độc lập để quay lại +tác vụ đã được bắt đầu, chẳng hạn như bằng một biểu tượng trình khởi chạy (hoạt động gốc của tác vụ +có một bộ lọc ý định {@link android.content.Intent#CATEGORY_LAUNCHER}; xem phần <a href="#Starting">Bắt đầu một tác vụ</a> ở bên dưới).</p> +</li> + + <li>Khi một hoạt động có thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#reparent"> +{@code allowTaskReparenting}</a> của nó được đặt thành {@code "true"}. + <p>Trong trường hợp này, hoạt động có thể di chuyển từ tác vụ mà nó bắt đầu tới tác vụ mà nó có quan hệ + khi tác vụ đó đi ra tiền cảnh.</p> + <p>Ví dụ, giả sử rằng một hoạt động với chức năng báo cáo tình hình thời tiết ở các thành phố được chọn +được định nghĩa là một phần trong một ứng dụng du lịch. Nó có cùng quan hệ như các hoạt động khác trong cùng +ứng dụng (quan hệ ứng dụng mặc định) và nó cho phép tạo lại tập mẹ với thuộc tính này. +Khi một trong các hoạt động của bạn bắt đầu hoạt động trình báo cáo thời tiết, ban đầu nó thuộc về cùng +tác vụ như hoạt động của bạn. Tuy nhiên, khi tác vụ của ứng dụng du lịch đi ra tiền cảnh, hoạt động +của trình báo cáo thời tiết được gán lại cho tác vụ đó và được hiển thị bên trong nó.</p> +</li> +</ul> + +<p class="note"><strong>Mẹo:</strong> Nếu một tệp {@code .apk} chứa nhiều hơn một "ứng dụng" +từ quan điểm của người dùng, bạn có thể muốn sử dụng thuộc tính <a href="{@docRoot}guide/topics/manifest/activity-element.html#aff">{@code taskAffinity}</a> +để gán các quan hệ khác nhau cho hoạt động được liên kết với từng "ứng dụng".</p> + + + +<h3 id="Clearing">Xóa ngăn xếp</h3> + +<p>Nếu người dùng rời một tác vụ trong khoảng thời gian dài, hệ thống sẽ xóa tác vụ của tất cả hoạt động ngoại trừ +hoạt động gốc. Khi người dùng quay trở lại tác vụ, chỉ hoạt động gốc được khôi phục. +Hệ thống sẽ hoạt động theo cách này, vì, sau một khoảng thời gian dài, người dùng có thể đã từ bỏ +việc mà họ đang làm trước đó và quay lại tác vụ để bắt đầu một việc mới. </p> + +<p>Có một số thuộc tính hoạt động mà bạn có thể sử dụng để sửa đổi hành vi này: </p> + +<dl> +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#always">alwaysRetainTaskState</a></code> +</dt> +<dd>Nếu thuộc tính này được đặt thành {@code "true"} trong hoạt động gốc của một tác vụ, +hành vi mặc định được mô tả sẽ không xảy ra. +Tác vụ giữ lại tất cả hoạt động trong chồng của mình kể cả sau một khoảng thời gian dài.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#clear">clearTaskOnLaunch</a></code></dt> +<dd>Nếu thuộc tính này được đặt thành {@code "true"} trong hoạt động gốc của một tác vụ, +chồng sẽ bị xóa tới hoạt động gốc bất cứ khi nào người dùng rời khỏi tác vụ +và quay lại. Nói cách khác, nó ngược với +<a href="{@docRoot}guide/topics/manifest/activity-element.html#always"> +{@code alwaysRetainTaskState}</a>. Người dùng luôn quay lại tác vụ ở +trạng thái ban đầu của nó, ngay cả sau khi rời khỏi tác vụ trong chỉ một lúc.</dd> + +<dt><code><a +href="{@docRoot}guide/topics/manifest/activity-element.html#finish">finishOnTaskLaunch</a></code> +</dt> +<dd>Thuộc tính này giống như <a href="{@docRoot}guide/topics/manifest/activity-element.html#clear">{@code clearTaskOnLaunch}</a>, +nhưng nó hoạt động trên một tác vụ +đơn lẻ chứ không phải một tác vụ toàn bộ. Nó cũng có thể khiến bất kỳ hoạt động nào +thoát mất, bao gồm cả hoạt động gốc. Khi nó được đặt thành {@code "true"}, hoạt động +vẫn là một bộ phận của tác vụ chỉ cho phiên làm việc hiện tại. Nếu người dùng rời đi +rồi quay lại tác vụ, nó không còn xuất hiện nữa.</dd> +</dl> + + + + +<h3 id="Starting">Bắt đầu một tác vụ</h3> + +<p>Bạn có thể thiết lập một hoạt động làm điểm bắt đầu cho một tác vụ bằng cách đưa cho nó một bộ lọc ý định với +{@code "android.intent.action.MAIN"} là hành động được quy định và +{@code "android.intent.category.LAUNCHER"} +là thể loại được quy định. Ví dụ:</p> + +<pre> +<activity ... > + <intent-filter ... > + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + ... +</activity> +</pre> + +<p>Một bộ lọc ý định loại này khiến một biểu tượng và nhãn cho +hoạt động được hiển thị trong trình khởi chạy ứng dụng, cho phép người dùng khởi chạy hoạt động và +quay lại tác vụ mà nó tạo ra vào bất cứ lúc nào sau khi nó được khởi chạy. +</p> + +<p>Khả năng thứ hai này là quan trọng: Người dùng chắc chắn có thể rời một tác vụ rồi quay lại +sau bằng cách sử dụng trình khởi chạy hoạt động này. Vì lý do này, hai <a href="#LaunchModes">chế độ +khởi chạy</a> mà đánh dấu các hoạt động là luôn khởi tạo một tác vụ, {@code "singleTask"} và +{@code "singleInstance"}, sẽ chỉ được sử dụng khi hoạt động có một +{@link android.content.Intent#ACTION_MAIN} +và một bộ lọc {@link android.content.Intent#CATEGORY_LAUNCHER}. Ví dụ, hãy tưởng tượng chuyện gì +sẽ xảy ra nếu thiếu bộ lọc: Một ý định khởi chạy một hoạt động {@code "singleTask"}, khởi đầu một +tác vụ mới và người dùng dành một khoảng thời gian làm việc trong tác vụ đó. Khi đó, người dùng nhấn nút <em>Home</em> +. Lúc này, tác vụ được gửi tới nền và không hiển thị. Bây giờ, người dùng không có cách nào để quay lại +tác vụ bởi nó không được biểu diễn trong trình khởi chạy ứng dụng.</p> + +<p>Đối với những trường hợp mà bạn không muốn người dùng có thể quay lại một hoạt động, hãy đặt giá trị của phần tử +<code><a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a></code> +, +<a href="{@docRoot}guide/topics/manifest/activity-element.html#finish">{@code finishOnTaskLaunch}</a> +thành {@code "true"} (xem <a href="#Clearing">Xóa chồng</a>).</p> + +<p>Bạn có thể tham khảo thêm thông tin về cách các tác vụ và hoạt động được trình bày và quản lý trong +màn hình tổng quan sẵn có tại phần <a href="{@docRoot}guide/components/recents.html"> +Màn hình Tổng quan</a>.</p> + +<!-- +<h2>Beginner's Path</h2> + +<p>For more information about how to use intents to +activate other application components and publish the intents to which your components +respond, continue with the <b><a +href="{@docRoot}guide/components/intents-filters.html">Intents and Intent +Filters</a></b> document.</p> +--> |