2013-01-16 20 views
7

Tôi đang gặp sự cố với AsyncTask và onPostExecute. Tôi thấy rằng onPostExecute đang thực thi trên một luồng khác với luồng ui chính, mà gây ra một lời gọi CalledFromWrongThreadException xảy ra khi tôi sửa đổi bất kỳ khung nhìn nào.Android AsyncTask onPostExecute tắt chủ đề ui chính

Tôi đặt trong một số đăng nhập để xem những chủ đề nào trênPreExecute, doInBackground và onPostExecute đang chạy. Tôi sẽ thấy một kết quả như thế này ...

onPreExecute ThreadId: 1 
doInBackground ThreadId: 25 
onPostExecute ThreadId: 18 

Tôi tin rằng thread ui chính id là 1 và tôi mong chờ cả onPre và onPost để cả hai thực hiện trên thread 1. Tôi đang làm cho chắc chắn để tạo ra và cũng gọi phương thức thực hiện từ chuỗi ui (ví dụ trong onCreate of a Activity).

Một điều cần lưu ý là tôi đã nhận thấy rằng các tác vụ không đồng bộ sau này sẽ chạy phương thức onPostExecute của họ trên cùng một luồng như các tác vụ không đồng bộ trước phương thức onPostExecute (trong trường hợp này là chuỗi 18).

Ngay bây giờ để giải quyết vấn đề này, tôi gói mã trong các phương thức onPostExecute của tôi trong một cuộc gọi đến runOnUiThread, nhưng tôi nghĩ rằng điều này là hacky và muốn gặp vấn đề thực sự.

Tôi hết ý tưởng! Bất kỳ ai có bất kỳ cái nhìn sâu sắc? Tôi rất sẵn lòng trả lời bất kỳ câu hỏi nào có thể trợ giúp cho việc điều tra thêm!

EDIT:

Có hai cách mà tác vụ không đồng bộ đang được chạy trong mã. Tôi tự hỏi nếu sau này trong những ví dụ này gây ra một cái gì đó kỳ lạ xảy ra?

public class SomeActivity extends Activity { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main_layout); 

     new SomeAsyncTask().execute(); 
    } 

    private class SomeAsyncTask extends AsyncTask<String, Void, Integer> { 
     @Override 
     public void onPreExecute() { 
      Thread.currentThread().getId() // 1 
      //Show a dialog 
     } 

     @Override 
     public Integer doInBackground(String... params) { 
      Thread.currentThread().getId() // 25 
      return 0; 
     } 

     @Override 
     public void onPostExecute(Integer result) { 
      Thread.currentThread().getId() // 18 
      //hide dialog 
      //update text view -> CalledFromWrongThreadException!!! 
     } 
    } 

}

dường như ở trên giống như một sử dụng vani của AsyncTask, nhưng tôi vẫn thấy vấn đề này xảy ra ngay cả trong trường hợp đơn giản như thế này. Ví dụ tiếp theo sử dụng tác vụ không đồng bộ để chạy các tác vụ không đồng bộ khác. Có lẽ có một cái gì đó tôi không biết về những gì sẽ xảy ra khi một nhiệm vụ async được xây dựng đang gây ra một số hành vi kỳ lạ?

public class SomeActivity extends Activity implements TaskRunner.OnFinishListener { 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.main_layout); 

     TaskRunner taskRunner = new TaskRunner(); 
     taskRunner.setOnFinishListener(this); 
     taskRunner.addTask(new SingleTask()); 
     taskRunner.addTask(new SingleTask()); 
     taskRunner.execute(); 
    } 

    @Override 
    public void onTaskFinish(List<Integer> results) { 
     //Thread id is 18 when it should be 1 
     //do something to a view - CalledFromWrongThreadException!! 
    } 

} 

//In a different file 
public class SingleTask extends AsyncTask<String, Void, Integer> { 
    //This is a an async task so we can run it separately as an asynctask 
    //Or run it on whatever thread runnerExecute is called on 
    @Override 
    public Integer doInBackground(String... params) { 
     return runnerExecute(params); 
    } 

    //Can be called outside of doInBackground 
    public Integer runnerExecute(String... params) { 
     //some long running task 
     return 0; 
    } 
} 

//In a different file 
public class TaskRunner { 

    private List<SingleTask> tasks; 
    private OnFinishListener onFinishListener; 

    public interface OnFinishListener { 
     public void onTaskFinish(List<Integer> results); 
    } 

    public TaskRunner() { 
     this.tasks = new ArrayList<SingleTask>(); 
    } 

    public void setOnFinishListener(OnFinishListener listener) { 
     this.onFinishListener = listener; 
    } 

    public void addTask(SingleTask task) { 
     tasks.add(task); 
    } 

    public void executeTasks() { 
     new RunnerTask().execute((SingleTask[]) tasks.toArray()); 
    } 

    //Calls the runnerExecute method on each SingleTask 
    private class RunnerTask extends AsyncTask<SingleTask, Integer, List<Integer>> { 
     @Override 
     public void onPreExecute() { 
      //Runs on thread 1 
     } 

     @Override 
     public List<Integer> doInBackground(SingleTask... params) { 
      //Runs on arbitrary thread 
      List<Integer> results = new ArrayList<Integer>(); 
      for(SingleTask task : params) { 
       int result =task.runnerExecute(task.getParams()); 
       results.add(result); 
      } 
      return results; 
     } 

     @Override 
     public void onPostExecute(List<Integer> results) { 
      //Runs on thread 18 
      onFinishListener.onTaskFinish(results); 
     } 
    } 
} 

Có thể những gì đang xảy ra ở đây chỉ là siêu lạ và không phải là cách sử dụng các tác vụ không đồng bộ, cách tốt nhất để tìm hiểu vấn đề.

Hãy cho tôi biết nếu bạn cần thêm bất kỳ ngữ cảnh nào.

+0

Bạn có thể hiển thị mã có liên quan không? – Eric

+0

Bạn sẽ không thực hiện 'Hoạt động' khác trước khi 'AsyncTask' kết thúc, đúng không? – codeMagic

+0

Không, tôi đợi cho đến khi asynctask hoàn tất và sau đó bắt đầu một hoạt động trong onPostExecuteMethod. Tôi sẽ thêm một số mã có liên quan vào câu hỏi của mình. – fxfilmxf

Trả lời

4

Tôi đã gặp phải vấn đề tương tự và hóa ra vấn đề là sử dụng Flurry 3.2.1. Tuy nhiên, vấn đề không giới hạn trong thư viện Flurry.

Vấn đề đằng sau hậu trường là có lần đầu tiên (khi ứng dụng được tải lần đầu tiên) AsyncTask gọi từ một chuỗi looper không phải là chuỗi giao diện người dùng chính. Cuộc gọi này khởi tạo biến tĩnh sHandler trong AsyncTask thành id luồng sai, và id này sau đó được sử dụng trong tất cả các cuộc gọi AsyncTask $ onPostExecute() tiếp theo.

Để giải quyết sự cố, tôi gọi một AsyncTask trống (không có gì) khi tải ứng dụng đầu tiên, chỉ để khởi tạo AsyncTask chính xác.

+0

Tuyệt vời. Tôi đã suy nghĩ một cái gì đó như thế này đã gây ra vấn đề, nhưng tôi không biết về biến tĩnh trong AsyncTask. Cảm ơn nhiều!! – fxfilmxf

1

hãy thử sử dụng:

getBaseContext().runOnUiThread(new Runnable() 
{ 
@override 
public void run() 
{ 

} 
}); 

và viết mã của bạn bên trong run chức năng

+0

Cảm ơn bạn đã phản hồi. Bạn có đề xuất tôi đặt điều này trong phương thức onPostExecute không? Nếu vậy, tôi đã gói mã onPostExecute của tôi trong một cuộc gọi đến runOnUiThread hoạt động tốt. Tôi đang cố gắng tìm hiểu lý do tại sao onPostExecute đang chạy ra khỏi chủ đề chính, mà theo hiểu biết của tôi, không được phép xảy ra. – fxfilmxf

+0

vì AsyncTask là một Chủ đề khác với UIThread chính. Nó sẽ được thực hiện trong Background trong khi Thread chính đang thực thi mã hiện tại. Nó được sử dụng để tránh chặn ứng dụng trong khi thực thi mã cần một thời gian dài như Tải xuống từ Internet. Nếu AsyncTask được thực thi từ luồng chính, nó sẽ chặn ứng dụng trong khi nó hoàn thành việc thực thi mã. Tôi ước điều đó rõ ràng cho bạn^_^ – KhalidTaha

+0

Không hoàn toàn không may! Bạn có nghĩa vụ phải thực hiện một asynctask từ chủ đề chính, nhưng tất cả các công việc mà nhiệm vụ async sẽ làm sẽ xảy ra một chủ đề riêng biệt. Sau khi công việc được hoàn thành onPostExecute được gọi và có nghĩa là để được thực hiện trên các chủ đề chính, do đó bạn có thể sửa đổi các yếu tố ui vv Tôi tự hỏi tại sao onPostExecute không thực hiện trên thread chính – fxfilmxf

0

tôi chỉ cố gắng mã của bạn và onPreExecute và onPostExecute không chạy trên cùng một sợi, làm thế nào để bạn xuất thread id? thử:

Log.d("THREADTEST","PRE"+Long.toString(Thread.currentThread().getId())); 

Log.d("THREADTEST","BACKGROUND"+Long.toString(Thread.currentThread().getId())); 

Log.d("THREADTEST","POST"+Long.toString(Thread.currentThread().getId())); 

P.S. nó nên là:

new SomeAsyncTask().execute(); 

private class SomeAsyncTask extends AsyncTask<String, Void, Integer> { ... } 
+0

Cảm ơn bạn đã chỉnh sửa, nó chỉ là một số lỗi chính tả khi tôi viết bài này, tôi sẽ sửa chúng. Tôi outputting id thread trong cùng một cách mà bạn đang mô tả, với Thread.currentThread(). GetId(). Trên hầu hết các điện thoại tôi thử nghiệm trên, các id là những gì bạn mong đợi, nhưng tôi có thể tin cậy repro vấn đề trên một thiên hà s2 tôi có. Tôi cũng thấy CalledFromWrongThreadException xảy ra trên điện thoại của người dùng khác trong các báo cáo sự cố. – fxfilmxf

+0

có thể hoạt động bị hủy và tái tạo trong khi asyntask của bạn đang chạy? ví dụ. do sự thay đổi định hướng. đây là hai chủ đề/bài viết hay về nó: –

+0

@fxfilmxf http://stackoverflow.com/questions/3821423/background-task-progress-dialog-orientation-change-is-there-any-100-working http: //blog.doityourselfandroid.com/2010/11/14/handling-progress-dialogs-and-screen-orientation-changes/ –

1

Các AsyncTask được thiết kế để được sử dụng từ các chủ đề chính.Vấn đề của bạn là trường hợp thứ hai và bạn gọi thực thi trên SingleTask từ một chuỗi nền. Bạn gọi nó trong phương thức doInBackground của RunnerTask. Sau đó onPostExecute được chạy từ backgroundthread của RunnerTask

Hai tùy chọn cho bạn.

1: Thùng rác RunnerTask, và thực hiện SingleTasks từ bạn thread chính, họ sẽ tất cả chạy trong parallell và bạn sẽ không biết được kết thúc đầu tiên, nhưng onPreExecute và onPostExecute được gọi vào các chủ đề chính

2 : Thùng rác SingleTask và định nghĩa chúng như Runnables thay vào đó, sau đó bạn có thể chạy chúng theo thứ tự trong doTBackBackground của RunnerTask. Tất cả chúng sẽ chạy trong chuỗi nền của RunnerTask, theo thứ tự bạn gọi là Chạy. Khi nó kết thúc, onPostExecute của RunnerTask được chạy trên luồng chính.

0

bạn thực sự đang thực thi SingleTask từ phương thức doinbackground của RunnerTask không chính xác vì asynctask chỉ được thực thi từ một chuỗi chính. Bạn cần phải chuyển sang logic chạy bộ SingleTask từ RunnerTask.

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