Xem thêm

Xử lý bất đồng bộ trong lập trình Android cơ bản

Huy Erick
Ở các bài học trước, chúng ta đã cùng nhau tìm hiểu về ANIMATION & CÁC HIỆU ỨNG ĐƠN GIẢN TRONG ANDROID. Tiếp theo ở bài học này chúng ta sẽ đề cập đến một...

Ở các bài học trước, chúng ta đã cùng nhau tìm hiểu về ANIMATION & CÁC HIỆU ỨNG ĐƠN GIẢN TRONG ANDROID. Tiếp theo ở bài học này chúng ta sẽ đề cập đến một khía cạnh quan trọng khác trong lập trình Android, đó là xử lý bất đồng bộ. Bài tiếp theo nữa cũng sẽ liên quan nên các bạn hãy chú ý kỹ nhé!

Đối tượng của bài viết

Trước khi tiếp tục, để đạt hiệu quả tốt nhất, các bạn nên có kiến thức về các phần sau:

  • CẤU TRÚC CƠ BẢN MỘT CHƯƠNG TRÌNH ANDROID
  • Kiến thức cơ bản về Thread và Handler (2 khái niệm cơ bản trong Java concurrency).
  • Tinh thần bình tĩnh, tự tin.

Nội dung

Trong bài học này, chúng ta sẽ cùng tìm hiểu các vấn đề sau:

  • ANR.
  • Làm quen với lỗi thao tác mạng phổ biến.
  • Làm ví dụ xử lý download thông tin trên mạng và hiển thị về máy.

ANR - Application Not Responding

ANR là viết tắt của Application Not Responding (Ứng dụng không phản hồi).

Nếu đã từng sử dụng máy Android chắc hẳn bạn cũng đã ít nhất một lần gặp phải trường hợp ứng dụng không phản hồi như sau:

Xử lý bất đồng bộ trong  <a href='https://nanado.edu.vn/lam-quen-voi-android-studio-nen-tang-phat-trien-ung-dung-android-chuyen-nghiep-a1591.html' title='lập trình android' class='hover-show-link replace-link-2128'> <a href='https://nanado.edu.vn/hoc-lap-trinh-app-android-dieu-gi-can-thiet-de-bat-kip-xu-huong-a1762.html' title='lập trình android' class='hover-show-link replace-link-2299'>lập trình android<span class='hover-show-content'></span></a> <span class='hover-show-content'></span></a>  cơ bản

Nguyên nhân có thể do rất nhiều: Lỗi null, các exception được bắn ra nhưng không xử lý,… Hoặc là:

  • Không có phản hồi cho các sự kiện xuất / nhập thông tin (ghi dữ liệu vào bộ nhớ, nhập văn bản,…). Nói đơn giản là TREO máy.
  • Broadcast Receiver không hoàn thành trong vòng 10 giây.
  • Với các máy Android 3.1 về trước: Thao tác liên quan đến mạng quá 5 giây. Sau phiên bản 3.1 thì Google đã cấm tiệt thao tác mạng trên Main Thread.

Về cơ bản là như vậy. Tuy nhiên, hẳn các bạn sẽ tự hỏi…

Main Thread và Worker Thread là gì?

Mình đã giới thiệu ở bài đầu tiên, Android là hệ điều hành đa tiến trình, đa luồng. Các ứng dụng Android cũng có thể sử dụng được nhiều thread (luồng) tài nguyên hệ thống.

Tuy nhiên các thành phần liên quan đến giao diện người dùng đều được nhét chung vào một thread chính, gọi là UI Thread (hay Main Thread).

Các thao tác xử lý nặng hầu hết đều bị cấm thực thi trên Main Thread do sẽ gây ra trải nghiệm xấu cho người dùng (bấm nút mãi không lên, chạy mãi cứ treo là hỏng rồi, người ta sẽ xóa app ngay). Do đó chúng ta cần đưa các tác vụ này vào một Thread khác, gọi là Worker Thread.

Ví dụ về Main Thread và Worker Thread

Và sau đó là cập nhật vào Main Thread. Cách làm này được gọi là thao tác xử lý bất đồng bộ (Asynchronous).

Trăm nghe không bằng một thấy, chúng ta sẽ làm thử ví dụ nhỏ. Chúng ta sẽ download thông tin dạng String từ một trang web bất kỳ, sau đó hiển thị nội dung đó lên màn hình Android, cả từ Main Thread và Worker Thread để xem chúng thực sự ra sao nhé.

ANR với thao tác mạng trên Main Thread

Bước 1: Như mọi khi, chúng ta tạo một project mới với tên là AsyncExample với API là 13 và loại Activity là Empty Activity.

Xử lý bất đồng bộ trong lập trình Android cơ bản

Bước 2: Để cho đơn giản, dữ liệu của chúng ta sẽ lấy từ trang icanhazip.com. Vào trang này, bạn sẽ thấy địa chỉ IP của mình hiện ra. Và chúng ta sẽ đặt một TextView để hiển thị địa chỉ này lên Activity.

activity_main.xml

         

Bước 3: Trong MainActivity.java, chúng ta sẽ viết một hàm đơn giản để download thông tin từ icanhazip về và đổ vào TextView, tên hàm là testNetwork().

MainActivity.java

package com.howkteam.asyncexample;  import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.widget.TextView;  import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL;  public class MainActivity extends AppCompatActivity {      TextView tvIP;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          tvIP = (TextView) findViewById(R.id.tv_ip);          testNetwork();     }      private void testNetwork() {         try {             StringBuilder sb;              // Website này sẽ cho biết địa chỉ IP của bạn.             URL url = new URL("http://icanhazip.com/");              HttpURLConnection connection = (HttpURLConnection) url.openConnection();              if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {                 BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));                 sb = new StringBuilder();                  String line;                 while ((line = br.readLine()) != null) {                     sb.append(line).append("\n");                 }                  tvIP.setText(sb.toString());                  Log.e("Địa chỉ IP ", sb.toString());                  br.close();             }          } catch (IOException e) {             e.printStackTrace();         }     } }

Nhớ đặt Permission Internet trong file AndroidManifest.xml

                                                                                                   

Và tất nhiên là chương trình sẽ bị crash rồi, do hàm testNetwork được gọi ngay trong UI Thread mà:

Xử lý bất đồng bộ trong lập trình Android cơ bản

Và khi nhìn vào log, bạn sẽ thấy NetworkOnMainThreadException.

Vậy phải làm sao đây?

AsyncTask

Nói về xử lý bất đồng bộ thì quả thực không đơn giản. Mình cũng thừa nhận là mình cực kỳ gà ở khoản này. Nhưng may mắn thay là Android có một cơ chế giúp đơn giản hóa quá trình xử lý bất đồng bộ hiệu quả, phù hợp với ứng dụng nhỏ.

Đó chính là AsyncTask.

Bước 1: Chúng ta tạo một class mới, extends từ lớp AsyncTask, tạm gọi là AsyncTaskNetwork và nó có dạng ban đầu như sau:

AsyncTaskNetwork.java

package com.howkteam.asyncexample;  import android.os.AsyncTask;  public class AsyncTaskNetwork extends AsyncTask {  }

Vào menu Code > Override Methods để tạo các method cần thiết. Thực ra chỉ cần doInBackground là đủ, nhưng theo mình, nên tạo method theo thứ tự sau: onPreExecute, doInBackgroundonPostExecute:

Và nó có dạng như này:

AsyncTaskNetwork.java

package com.howkteam.asyncexample;  import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; import android.util.Log; import android.widget.TextView;  import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL;  public class AsyncTaskNetwork extends AsyncTask {      ProgressDialog progressDialog;     Context context;     TextView textView;      public AsyncTaskNetwork(Context context, TextView textView) {         this.context = context;         this.textView = textView;     }      @Override     protected void onPreExecute() {         super.onPreExecute();          // Hiển thị Dialog khi bắt đầu xử lý.         progressDialog = new ProgressDialog(context);         progressDialog.setTitle("HowKteam");         progressDialog.setMessage("Đang xử lý...");         progressDialog.show();     }      @Override     protected String doInBackground(String... strings) {         try {             StringBuilder sb;              // Website này sẽ cho biết địa chỉ IP của bạn.             URL url = new URL(strings[0]);              HttpURLConnection connection = (HttpURLConnection) url.openConnection();              if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {                 BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));                 sb = new StringBuilder();                  String line;                 while ((line = br.readLine()) != null) {                     sb.append(line).append("\n");                 }                  Log.e("Địa chỉ IP ", sb.toString());                  br.close();                 return sb.toString();             }          } catch (IOException e) {             e.printStackTrace();             return null;         }          return null;     }      @Override     protected void onPostExecute(String aString) {         super.onPostExecute(aString);          // Hủy dialog đi.         progressDialog.dismiss();          // Hiển thị IP lên TextView.         textView.setText(aString);     } }

MainActivity

Quay lại MainActivity. Cách áp dụng AsyncTask rất đơn giản như sau, chú ý phần tham số của execute:

package com.howkteam.asyncexample;  import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView;  public class MainActivity extends AppCompatActivity {      TextView tvIP;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          tvIP = (TextView) findViewById(R.id.tv_ip);          new AsyncTaskNetwork(this, tvIP).execute("http://icanhazip.com/");     } }

Đó, 2 phần tham số mình đã đánh dấu tô đậm màu vàng. Cái địa chỉ kia chính là tham số thứ nhất trong mảng string… của AsyncTask (phần tử 0). Bạn có thể truyền vào bao nhiêu tham số cũng được, nhưng phải cùng kiểu và xử lý chu đáo.

Chạy app, và chúng ta có được:

Xử lý bất đồng bộ trong lập trình Android cơ bản

Lưu ý là IP trên là của mình, của các bạn có thể khác.

Kết luận

Qua bài này chúng ta đã nắm được AsyncTask là gì, cơ chế bắt buộc xử lý tác vụ mạng trên Worker Thread của Android.

Chúng ta sẽ tìm hiểu sâu hơn về thao tác với mạng bằng Moshi và thư viện OkHttp qua bài JSON & WEB API TRONG LẬP TRÌNH ANDROID.

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập - Thử thách - Không ngại khó”.

Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần BÌNH LUẬN bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng.

1