2015-08-07 48 views
19

Điều này đã được yêu cầu trong một trong các cuộc phỏng vấn trên Android. Tôi đã được hỏi liệu có thể bắt đầu một tác vụ không đồng bộ khác (để cho nó là Task2) từ phương thức doInBackground() của tác vụ async 1 (để cho nó là Task1). Tôi đã xem qua các tài liệu có nội dung sau:Tác vụ không đồng bộ Android thực hiện

Ví dụ về tác vụ phải được tạo trên chuỗi giao diện người dùng.

thực thi (Params ...) phải được gọi trên chuỗi giao diện người dùng.

Theo những tuyên bố này, tôi cho rằng không nên bắt đầu tác vụ từ phương thức nền của tác vụ khác. Ngoài ra, tác vụ không đồng bộ có các phương thức giao diện người dùng (mà không thể được sử dụng trên một chuỗi nền), do đó củng cố lập luận của tôi và tôi đã trả lời nó là không thể.

Khi kiểm tra trên một ứng dụng demo đơn giản, tôi thấy rằng nó thực sự có thể làm như vậy. Một số code demo:

@Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     mContext = this; 
     init(); 
     Log.v ("gaurav", "Thread is : " + Thread.currentThread().getName()); 
     Task1 task = new Task1(); 
     task.execute(); 
    } 

class Task1 extends AsyncTask { 
    @Override 
    protected Object doInBackground(Object... params) { 
     // TODO Auto-generated method stub 
     Log.v ("gaurav", "Thread task 1 is : " + Thread.currentThread().getName()); 

     Task2 task = new Task2(); 

     task.execute(); 
     return null; 
    } 
} 

class Task2 extends AsyncTask { 
    @Override 
    protected Object doInBackground(Object... params) { 
     // TODO Auto-generated method stub 
     Log.v ("gaurav", "Thread task 2 is : " + Thread.currentThread().getName()); 

     Log.v ("gaurav", "Task 2 started"); 
     return null; 
    } 
} 

tôi nhận được các bản ghi sau chỉ thực hiện thành công:

> 08-07 09:46:25.564: V/gaurav(2100): Thread is : main 08-07 
> 09:46:25.564: V/gaurav(2100): Thread task 1 is : AsyncTask #3 08-07 
> 09:46:25.564: V/gaurav(2100): Thread task 2 is : AsyncTask #4 08-07 
> 09:46:25.564: V/gaurav(2100): Task 2 started 

Tôi đã kiểm tra này trên ICS, KK và L thiết bị và nó hoạt động tốt cho tất cả.

Một lý do tôi có thể nghĩ là tôi không ghi đè bất kỳ phương thức giao diện người dùng nào và thực hiện bất kỳ cập nhật giao diện người dùng nào trong nhiệm vụ thứ hai của mình, do đó nó không gây ra bất kỳ sự cố nào, nhưng tôi không chắc chắn. Ngay cả khi đó là trường hợp, nó vi phạm các quy tắc luồng được đề cập trong hướng dẫn dành cho nhà phát triển.

Để tham khảo, tôi cũng đã kiểm tra liên kết này: Start AsyncTask from another AsyncTask doInBackground() nhưng trạng thái câu trả lời để bắt đầu tác vụ thứ hai bằng phương thức runOnUiThread() bên trong doInBackground(). Tôi muốn được giúp đỡ về những gì đang xảy ra ở đây. Cảm ơn.

+0

Lý do khác tôi đã sử dụng câu hỏi tương tự trong cuộc phỏng vấn là xác định xem ứng cử viên k nows về phá vỡ các thay đổi hành vi thread trong AsyncTasks từ Honeycomb trở lên: http://stackoverflow.com/questions/21165505/mutliple-asynctasks-with-sequential-execution – adelphus

+0

@adelphus, tôi hoàn toàn không thể hiểu được ý nghĩa của "Bắt đầu với HONEYCOMB, các tác vụ được thực hiện trên một luồng đơn để tránh các lỗi ứng dụng phổ biến do thực thi song song. " Nó xác định về việc thực hiện nối tiếp, nhưng các nhiệm vụ lồng nhau sẽ vẫn chạy onPreExecute()/onPostExecute() của chúng trên luồng nền trong ngữ cảnh của câu hỏi của tôi? Hoặc như câu trả lời dưới đây nói về tải lớp khi khởi động ứng dụng, trong trường hợp này tất cả các phương thức onPre/onPost của nhiệm vụ lồng nhau chạy trên chuỗi giao diện người dùng bất kể từ nơi nhiệm vụ được bắt đầu? –

+0

Có hai vấn đề ở đây: nhận xét của tôi là nói về thực tế là nhiều AsyncTask được sử dụng để chạy song song với 1 chuỗi cho mỗi tác vụ. Kể từ khi tổ ong, nhiều AsyncTasks hiện đang xếp hàng để tất cả chúng chạy trên một sợi đơn - chỉ có 1 có thể chạy cùng một lúc. Sự thay đổi đã phá vỡ rất nhiều mã bởi vì các dev giả định các tác vụ chạy song song. Câu hỏi của bạn về việc thực thi onPreExecute/onPostExecute trên chuỗi giao diện người dùng có phải là vấn đề khác hay không, nhưng yaa110 mô tả nó tốt trong câu trả lời của anh ấy. – adelphus

Trả lời

12

Hãy thay đổi mã của bạn như sau:

class Task1 extends AsyncTask { 
    @Override 
    protected Object doInBackground(Object... params) { 
     // TODO Auto-generated method stub 
     Log.v ("gaurav", "Thread task 1 is : " + Thread.currentThread().getName()); 

     Task2 task = new Task2(); 
     task.execute(); 

     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     Log.v ("gaurav", "Log after sleeping"); 

     return null; 
    } 
} 

class Task2 extends AsyncTask { 
    @Override 
    protected Object doInBackground(Object... params) { 
     // TODO Auto-generated method stub 
     Log.v ("gaurav", "Thread task 2 is : " + Thread.currentThread().getName()); 

     Log.v ("gaurav", "Task 2 Started"); 
     return null; 
    } 
} 

Bây giờ trở về LogCat:

08-07 06:13:44.208 3073-3073/testapplication V/gaurav﹕ Thread is : main 
08-07 06:13:44.209 3073-3091/testapplication V/gaurav﹕ Thread task 1 is : AsyncTask #1 
08-07 06:13:49.211 3073-3091/testapplication V/gaurav﹕ Log after sleeping 
08-07 06:13:49.213 3073-3095/testapplication V/gaurav﹕ Thread task 2 is : AsyncTask #2 
08-07 06:13:49.213 3073-3095/testapplication V/gaurav﹕ Task 2 Started 

Như bạn có thể thấy Task 2 được thực hiện sau khi kết thúc các Task 1 thực hiện (kể cả sau khi ngủ trong 5 giây). Nó có nghĩa là nhiệm vụ thứ hai sẽ không được bắt đầu cho đến khi công việc đầu tiên được thực hiện.

Tại sao? Lý do nằm sau số source code of AsyncTask. Hãy xem xét các phương pháp execute():

public synchronized void execute(final Runnable r) { 
    mTasks.offer(new Runnable() { 
     public void run() { 
      try { 
       r.run(); 
      } finally { 
       scheduleNext(); 
      } 
     } 
    }); 
    if (mActive == null) { 
     scheduleNext(); 
    } 
} 

scheduleNext() phương pháp:

protected synchronized void scheduleNext() { 
    if ((mActive = mTasks.poll()) != null) { 
     THREAD_POOL_EXECUTOR.execute(mActive); 
    } 
} 

Các từ khóa quan trọng nhất trong các phương pháp này là synchronized đảm bảo các phương pháp này sẽ được chạy chỉ trong một thread cùng lúc. Khi bạn gọi phương thức execute, nó cung cấp Runnable mới đến mTask là phiên bản của lớp ArrayDeque<Runnable> hoạt động như bộ nối tiếp các yêu cầu khác nhau tại các chủ đề khác nhau [more info]. Nếu không được thực hiện Runnable (tức làif (mActive == null)), scheduleNext() sẽ được gọi, nếu không, scheduleNext() trong khối finally sẽ được gọi sau khi (vì bất kỳ lý do nào) kết thúc hiện tại được thực hiện Runnable. Tất cả Runnable s được thực thi trên một sợi riêng biệt theo THREAD_POOL_EXECUTOR.

Điều gì xảy ra với việc thực thi AsyncTask từ các chủ đề khác? Bắt đầu với Jelly Bean, AsyncTask được nạp vào lớp khi bắt đầu ứng dụng trên chuỗi giao diện người dùng, để các cuộc gọi lại được đảm bảo xảy ra trên chuỗi giao diện người dùng, tuy nhiên, trước bản phát hành Jelly Bean, nếu chuỗi khác tạo ra các cuộc gọi lại AsyncTaskcó thể không xảy ra trên chủ đề chính xác.

Vì vậy, AsyncTask triển khai chỉ nên được gọi từ chuỗi giao diện người dùng trên nền tảng trước Jelly Bean (++).


Làm rõ: xin vui lòng xem xét ví dụ sau đây mà chỉ đơn giản là làm rõ sự khác biệt giữa các phiên bản nền tảng khác nhau của Android:

protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main2); 

    new Thread() { 
     @Override 
     public void run() { 
      Task1 task = new Task1(); 
      task.execute(); 
     } 
    }.start(); 
} 

class Task1 extends AsyncTask { 
    @Override 
    protected Object doInBackground(Object... params) { 
     return null; 
    } 
} 

Nó hoạt động tốt trên Android 5.1, nhưng tai nạn với ngoại lệ sau trên Android 2.3:

08-07 12:05:20.736  584-591/github.yaa110.testapplication E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-8 
    java.lang.ExceptionInInitializerError 
      at github.yaa110.testapplication.Main2Activity$1.run(Main2Activity.java:21) 
    Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() 
      at android.os.Handler.<init>(Handler.java:121) 
      at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421) 
      at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421) 
      at android.os.AsyncTask.<clinit>(AsyncTask.java:152) 
            at github.yaa110.testapplication.Main2Activity$1.run(Main2Activity.java:21) 
+1

Theo nhận xét của bạn, tôi suy ra rằng nó thực sự là có thể. Điểm duy nhất là các tác vụ sẽ được xếp hàng đợi bởi trình thực thi nối tiếp mặc định được sử dụng để chạy các tác vụ. Nếu đó là trường hợp không nên doc xác định rằng điều này là có thể. 'Phải' cho phép tôi giả định rằng nó sẽ gây ra sự cố. –

+0

@gauravjain câu trả lời đã được chỉnh sửa. –

+0

Phần trên là khá rõ ràng với tôi, mà tóm tắt rằng Android sẽ xếp hàng các yêu cầu bằng cách sử dụng mTask mà là một serializer. Nhưng phần cuối cùng mà bạn đã đề cập rằng điều này chỉ nên được thực hiện trên API> = JellyBean hơi phức tạp vì tôi đã thử nghiệm cùng một mã trên ICS. Bạn có thể kiểm tra lý do tại sao điều này hoạt động trên ICS không? Hoặc là nó là một kịch bản 'có thể làm việc' mà có thể không chạy thành công mỗi lần? –

Các vấn đề liên quan