Android SDK AsyncTask doInBackground không chạy (lớp con)


90

Tính đến ngày 15/2/2012, tôi vẫn chưa tìm ra lời giải thích hay lý do tại sao điều này không hoạt động. Giải pháp gần nhất là sử dụng cách tiếp cận Chủ đề truyền thống , nhưng sau đó tại sao lại bao gồm một lớp (dường như) không hoạt động trong Android SDK?

Evenin 'VẬY!

Tôi có một lớp con AsyncTask:

// ParseListener had a callback which was called when an item was parsed in a
// RSS-xml, but as stated further down it is not used at all right now.
private class xmlAsync extends AsyncTask<String, RSSItem, Void> implements ParseListener

Điều đó được thực hiện như thế này:

xmlAsync xmlThread = new xmlAsync();

xmlThread.execute("http://www.nothing.com");

Bây giờ lớp con này đã gặp một lỗi nhỏ. Trước đây, nó đã thực hiện một số phân tích cú pháp xml, nhưng khi tôi nhận thấy rằng nó không được gọi là doInBackground (), tôi đã loại bỏ nó, từng dòng một, cuối cùng chỉ kết thúc bằng điều này:

@Override
protected Void doInBackground(String... params) 
{
    Log.v(TAG, "doInBackground");
        return null;
}

Mà, vì một số lý do, không ghi nhật ký gì. Tuy nhiên, tôi đã thêm điều này:

@Override
protected void onPreExecute() 
{
        Log.v(TAG, "onPreExecute");
        super.onPreExecute();
}

Và dòng đó thực sự được ghi khi thực thi luồng. Vì vậy, bằng cách nào đó onPreExecute () được gọi nhưng không phải doInBackground () . Tôi có một AsyncTask khác đang chạy trong nền đồng thời hoạt động tốt.

Tôi hiện đang chạy ứng dụng trên trình giả lập, Phiên bản SDK 15, Eclipse, Mac OS X 10.7.2, gần với Bắc Cực.

BIÊN TẬP:

@Override
    protected void onProgressUpdate(RSSItem... values) {

        if(values[0] == null)
        {
                            // activity function which merely creates a dialog
            showInputError();
        }
        else
        {

            Log.v(TAG, "adding "+values[0].toString());
            _tableManager.addRSSItem(values[0]);
        }


        super.onProgressUpdate(values);
    }

_tableManager.addRSSItem () ít nhiều thêm một hàng vào SQLiteDatabase, được khởi tạo với ngữ cảnh của hoạt động. printProgress () được gọi bởi lệnh gọi lại của Interface ParseListener. Tuy nhiên, vì tôi thậm chí không làm bất cứ điều gì ngoại trừ log.v trong doInBackground (), lần đầu tiên tôi thấy điều này thậm chí không cần thiết.

CHỈNH SỬA 2:

Được rồi, nói một cách hoàn toàn rõ ràng, đây là AsyncTask khác, đang thực thi trong cùng một hoạt động và hoạt động hoàn toàn tốt.

private class dbAsync extends AsyncTask<Void, RSSItem, Void>
{
    Integer prevCount;
    boolean run;

    @Override
    protected void onPreExecute() {
        run = true;
        super.onPreExecute();
    }

    @Override
    protected Void doInBackground(Void... params) {
        // TODO Auto-generated method stub
        run = true;
        prevCount = 0;

        while(run)
        {
            ArrayList<RSSItem> items = _tableManager.getAllItems();

            if(items != null)
            {
                if(items.size() > prevCount)
                {
                    Log.v("db Thread", "Found new item(s)!");
                    prevCount = items.size();

                    RSSItem[] itemsArray = new RSSItem[items.size()];

                    publishProgress(items.toArray(itemsArray));
                }
            }               

            SystemClock.sleep(5000);
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(RSSItem... values) {

        ArrayList<RSSItem> list = new ArrayList<RSSItem>();

        for(int i = 0; i < values.length; i++)
        {
            list.add(i, values[i]);
        }

        setItemsAndUpdateList(list);

        super.onProgressUpdate(values);
    }

    @Override
    protected void onCancelled() {
        run = false;

        super.onCancelled();
    }
}

CHỈNH SỬA 3:

Haizz, xin lỗi, tôi không biết đặt câu hỏi. Nhưng đây là phần khởi tạo của Nhiệm vụ.

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.execute("http://www.nothing.com", null);
}

Có thể là hoạt động kết thúc trước khi nhiệm vụ hoàn thành?
Paul Nikonowicz

Bất thường. Trong trường hợp đó, luồng khác của tôi sẽ không chạy đúng không? Và tôi chỉ có một hoạt động ngay bây giờ.
SeruK

2
Ứng dụng của tôi cũng gặp vấn đề tương tự - doInBackground không được gọi hoặc được gọi với độ trễ rất lâu. Đây là quan sát hạn chế của tôi: cùng một mã hoạt động hoàn hảo trên điện thoại thông minh Android 2.3.3 và trình giả lập Android 2.3.3, nhưng có vấn đề này trên máy tính bảng Android 4.0.3 và một loạt trình giả lập Android 4.xx. Rất hấp dẫn để kết luận rằng vấn đề này đã được đưa ra trong các phiên bản Android mới hơn.
Hồng

Xin lỗi, nhưng tôi quên đề cập vấn đề này chỉ xảy ra với AsyncTask thứ hai của một Activity AsyncTask đầu tiên luôn hoạt động tốt.
Hồng

Hong, bạn đã thử câu trả lời của Matthieu chưa? Tôi hầu như không sử dụng máy ATM trò chơi Android và đã không làm việc với nó trong một thời gian, vì vậy tôi không thể biết liệu câu trả lời của anh ấy có thực sự hoạt động hay không. Nếu nó không cho bạn, hơn có thể nó là xấu của tôi để chấp nhận câu trả lời của mình ...
SeruK

Câu trả lời:


107

Giải pháp của Matthieu sẽ hoạt động tốt đối với hầu hết mọi người, nhưng một số có thể gặp vấn đề; trừ khi đào nhiều liên kết được cung cấp ở đây hoặc từ web, như lời giải thích của Anders Göransson . Tôi đang cố gắng tóm tắt một số bài đọc khác ngay tại đây và nhanh chóng giải thích giải pháp nếu executeOnExecutor vẫn đang hoạt động trong luồng đơn ...

Hành vi của AsyncTask().execute();đã thay đổi qua các phiên bản Android. Trước khi các tác vụ Donut (Android: 1.6 API: 4) được thực thi tuần tự, các tác vụ từ Donut đến Gingerbread (Android: 2.3 API: 9) được thực thi song song; kể từ khi thực thi Honeycomb (Android: 3.0 API: 11) được chuyển trở lại tuần tự; AsyncTask().executeOnExecutor(Executor)Tuy nhiên, một phương thức mới đã được thêm vào để thực thi song song.

Trong xử lý tuần tự, tất cả các tác vụ Async chạy trong một luồng duy nhất và do đó phải đợi trước khi tác vụ trước đó kết thúc. Nếu bạn cần thực thi mã ngay lập tức, bạn cần các tác vụ được xử lý song song trong các luồng riêng biệt.

Với AsyncTask thực thi nối tiếp không khả dụng giữa các phiên bản Donut và Honeycomb, trong khi thực thi song song không khả dụng trước Donut.

Để xử lý song song sau Donut: Kiểm tra phiên bản Build và dựa trên đó sử dụng phương thức .execute () hoặc .executeOnExecutor (). Mã sau có thể giúp ...

AsyncTask<Void,Void,Void> myTask = new AsyncTask<Void,Void,Void>() { ... }; // ... your AsyncTask code goes here
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
    myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
    myTask.execute();

NOTE:Chức năng .executeOnExecutor()có kiểm tra nếu targetSdkVersiondự án nhỏ hơn hoặc bằng HONEYCOMB_MR1(Android: 2.1 API: 7) thì nó buộc người thực thi phải THREAD_POOL_EXECUTOR(chạy các Nhiệm vụ tuần tự trong bài Honeycomb).
Nếu bạn chưa xác định một targetSdkVersionthì minSdkVersiontự động được coi là targetSdkVersion.
Do đó, để chạy AsyncTask của bạn song song trên bài Honeycomb, bạn không thể để targetSdkVersiontrống.


1
Câu trả lời rất hay. Trong khi Matthieu nói không sai, tôi chấp nhận điều này, vì bạn thêm một loạt thông tin quan trọng.
SeruK,

@Nashe Cảm ơn rất nhiều. Nó thực sự rất hữu ích. Tôi đã chiến đấu với vấn đề tương tự trong 3 ngày. Cảm ơn một lần nữa :)

Đã lưu ngày của tôi! Chúc câu trả lời này dễ tìm hơn.
zjk

4
Này @Nashe, Vấn đề của tôi hơi khó xử. Trước ngày hôm nay, tôi đã sử dụng phương thức .execute () trên AsyncTask và mã này hoạt động hoàn hảo. Nhưng hôm nay tôi gặp sự cố - điều khiển không đi vào phương thức doInBackground (). Mặc dù giải pháp bạn cung cấp đang hoạt động, tôi không hiểu nó đã hoạt động như thế nào trước đây mà không có giải pháp. Tôi đang sử dụng cùng một bộ Thiết bị trước đây và bây giờ.
Ravi Sisodia,

Tại sao điều này phải là theo cách này? Tại sao nó không hoạt động theo mong đợi? :(
Nikolay R

160

Bạn nên kiểm tra câu trả lời này: https://stackoverflow.com/a/10406894/347565 và liên kết đến các nhóm google mà nó bao gồm.

Tôi đã gặp sự cố tương tự như bạn, vẫn không rõ tại sao nó không hoạt động, nhưng tôi đã thay đổi mã của mình như thế này và sự cố đã biến mất:

ASyncTask<Void,Void,Void> my_task = new ASyncTask<Void,Void,Void>() { ... };
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    my_task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
else
    my_task.execute((Void[])null);

Tôi đã rất tệ khi nhìn lại bài đăng này; Tôi đã rời xa dự án từ lâu. Tôi chỉ chấp nhận đây là câu trả lời vì mọi người dường như nói rằng nó hoạt động.
SeruK 20/12/12

điều này đã làm việc cho tôi ngay bây giờ nhưng tôi ước tôi hiểu tại sao. nó hoạt động tốt với thực thi trước khi nó dừng lại. một điều tôi đang làm là bắt đầu một asynctask mới bên trong onPostExecute của cùng một asynctask (tức là tôi đang gọi nó một cách đệ quy) có thể điều đó được kết nối với sự cố?
steveh

Tôi nghĩ rằng điều này sẽ cung cấp cho bạn tất cả những lời giải thích bạn cần: commonsware.com/blog/2012/04/20/...
Matthieu

1
@Matthieu: Thưa ông, tôi thực sự không thể cảm ơn đủ !!! Tôi đã đập đầu vào vấn đề này trong nhiều giờ và giải pháp của bạn đã khiến mọi thứ hoạt động như một cái duyên! Cảm ơn bạn rất nhiều vì câu trả lời tuyệt vời. Tôi ước mình có thể tặng nhiều hơn một phiếu ủng hộ !!
Swayam


9

Bạn có thể làm điều này bằng hai cách:

Cách 1 :

if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) // Above Api Level 13
  {
      asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
  }
else // Below Api Level 13
  {
      asyncTask.execute();
  }

Trong trường hợp cách 1 không hiệu quả, bạn hãy thử cách 2 .

Cách 2 :

int mCorePoolSize = 60;
int mMaximumPoolSize = 80;
int mKeepAliveTime = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(mMaximumPoolSize);
Executor mCustomThreadPoolExecutor = new ThreadPoolExecutor(mCorePoolSize, mMaximumPoolSize, mKeepAliveTime, TimeUnit.SECONDS, workQueue);
asyncTask.executeOnExecutor(mCustomThreadPoolExecutor);

Hy vọng điều này sẽ giúp bạn.


Thật tuyệt vời, nó đang hoạt động nhưng các tác vụ Async tiếp theo đang sử dụng AsyncTask.THREAD_POOL_EXECUTOR đang không thành công, vì vậy tôi cần thay đổi bằng CustomExecutor trong toàn bộ dự án, tôi đoán :(
kumar

@vinu, tôi khuyên bạn nên sử dụng tác vụ không đồng bộ phổ biến và phương pháp phổ biến để thực thi AsyncTask. Hy vọng điều này sẽ giúp bạn.
Hiren Patel

2
Này điều này đã giúp tôi rất nhiều! Cảm ơn!
Justin Ebby

6

Tôi đã gặp vấn đề tương tự: không thể thực thi AsyncTask thứ hai sau khi tôi gọi "thực thi" trên cái đầu tiên: doInBackground chỉ được gọi cho cái đầu tiên.

Để trả lời tại sao điều này xảy ra, hãy kiểm tra câu trả lời này (hành vi khác nhau tùy thuộc vào SDK)

Tuy nhiên, đối với trường hợp của bạn, có thể tránh được trở ngại này bằng cách sử dụng executeOnExecutor (có sẵn bắt đầu từ 3.0 đã hoạt động đối với tôi khi sử dụng 4.0.3) nhưng hãy cẩn thận với các giới hạn về kích thước nhóm luồng và xếp hàng.

Bạn có thể thử một cái gì đó như thế này:

xmlAsync _xmlParseThread;
dbAsync _dbLookup;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

_dbLookup = new dbAsync();
_dbLookup.execute();

_xmlParseThread = new xmlAsync();       
_xmlParseThread.executeOnExecutor(_dbLookup.THREAD_POOL_EXECUTOR
 ,"http://www.nothing.com", null);
}

Đối với câu hỏi cập nhật của bạn: nó được giải thích trong tài liệu Về cơ bản chỉ để tránh tất cả các vấn đề có thể đến từ đa luồng như tương thích ....


5

Một điều mà tôi muốn biết và nó thực sự có thể khắc phục sự cố của bạn, đó là bạn đang khởi tạo phiên bản của lớp mình ở đâu và gọi phương thức execute ()? Nếu bạn đọc tài liệu cho AsyncTask, cả hai hoạt động đó đều cần phải diễn ra trên chuỗi giao diện người dùng chính. Nếu bạn đang tạo đối tượng của mình và gọi thực thi từ một số luồng khác, thì onPreExecute có thể kích hoạt, tôi không chắc chắn 100% ở đây, nhưng luồng nền sẽ không được tạo và thực thi.

Nếu bạn đang tạo phiên bản AsyncTask của mình từ một chuỗi nền hoặc một số hoạt động khác không diễn ra trên chuỗi giao diện người dùng chính, bạn có thể xem xét sử dụng phương pháp: Activity.runOnUiThread (Runnable)

Bạn sẽ cần quyền truy cập vào một phiên bản Activity đang chạy của mình để gọi phương thức đó, nhưng nó sẽ cho phép bạn chạy mã trên chuỗi giao diện người dùng từ một số mã khác không chạy trên chuỗi giao diện người dùng.

Hy vọng điều đó có ý nghĩa. Hãy cho tôi biết nếu tôi có thể giúp thêm.

David


thêm vào câu trả lời của bạn, chuỗi này có câu trả lời thú vị stackoverflow.com/questions/4080808/… .
manjusg

Cảm ơn vì câu trả lời tuyệt vời! Tôi đã sửa điều này vì tôi nghĩ rằng đó có thể là một vấn đề phổ biến cho người mới bắt đầu sử dụng AsyncTask. Than ôi, nó không phải là câu trả lời hoàn toàn đúng cho vấn đề này. Cả hai lớp đều được khởi tạo trong onCreate () của một hoạt động đang chạy trên chuỗi giao diện người dùng chính. Tôi chỉ có một hoạt động trong dự án này.
SeruK

@manjusg Tôi đã xem xét tất cả rằng nó có gì đó liên quan đến việc AsyncTask không ổn định, có lẽ quá nhiều khi một số được chạy đồng thời. Nếu vậy, tại sao?
SeruK

Tôi thực sự không biết SO có những chính sách gì đối với việc đăng nhanh ba lần liên tiếp, nhưng tôi tìm thấy điều này trong chuỗi khác ... foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html "AsyncTask sử dụng hàng đợi công việc nội bộ tĩnh với giới hạn được mã hóa cứng là 10 phần tử. " Điều này có thể chứng minh điều gì đó, nhưng tôi chỉ có hai trường hợp của lớp con AsyncTask! Tôi thực sự muốn tránh sử dụng các phương pháp phân luồng thông thường vì cuối cùng sẽ có rất nhiều phân tích cú pháp được thực hiện trong dere.
SeruK

2

Android thật tàn bạo! Tôi không thể tin được điều này, sự triển khai mỏng manh thay đổi từ ngày này sang ngày khác. Một ngày của nó là một chủ đề duy nhất, ngày tiếp theo là 5 chuỗi còn lại là 128.

Dù sao thì ở đây gần như là một sự thay thế cho AsyncTask có sẵn. Bạn thậm chí có thể gọi nó là AsyncTask nếu bạn muốn, nhưng để tránh nhầm lẫn nó được gọi là ThreadedAsyncTask. Bạn cần gọi executeStart () thay vì thực thi bởi vì thực thi () là cuối cùng.

/**
 * @author Kevin Kowalewski
 *
 */
public abstract class ThreadedAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 
    public AsyncTask<Params, Progress, Result> executeStart(Params... params){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            return executePostHoneycomb(params);
        }else{
            return super.execute(params);
        }
    }


    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private AsyncTask<Params, Progress, Result> executePostHoneycomb(Params... params){
        return super.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params); 
    }
}

Chờ đã, bạn không nói rằng một số phiên bản Android hạn chế các hoạt động không đồng bộ trong một chuỗi đúng không? Điều đó sẽ vô cùng ngớ ngẩn. (Tôi đã thoát khỏi trò chơi Android được một thời gian. :))
SeruK

Có, trên Android 3.0+ nếu bạn không sử dụng AsyncTask.THREAD_POOL_EXECUTOR, bạn chỉ nhận được một nhóm chủ đề. Hãy thử nó cho chính bạn, hai AsyncTask và chỉ cần ngủ trong doInBackground của một người. Từ tài liệu Android AsyncTask: "Bắt đầu với HONEYCOMB, các tác vụ được thực thi trên một luồng duy nhất để tránh các lỗi ứng dụng phổ biến do thực thi song song."
Kevin Parker

1

Tôi biết điều này có thể thực sự muộn đối với chủ đề, nhưng có một lý do tại sao nó sẽ không hoạt động trên các trình giả lập Android sau này. Khi asynctask được giới thiệu, android chỉ cho phép bạn chạy từng cái một, sau đó đôi khi, tôi không chắc phiên bản nào, họ cho phép bạn chạy nhiều asynctasks cùng một lúc, điều này gây ra sự cố trong rất nhiều ứng dụng, và vì vậy trong Honeycomb +, chúng hoàn nguyên về chỉ cho phép một asynctask chạy cùng một lúc. Trừ khi bạn thay đổi nhóm chủ đề theo cách thủ công. Hy vọng rằng điều đó rõ ràng một hoặc hai điều cho mọi người.


0

tôi nghĩ đó là sdk. tôi đã gặp vấn đề tương tự và sau khi thay đổi sdk mục tiêu từ 15 thành 11, mọi thứ hoạt động hoàn hảo.

với sdk15, mặc dù AsyncTask.Status đang RUNNING, doInBackground không bao giờ được gọi. Tôi nghĩ rằng nó có một cái gì đó để làm với chủ đề ui.


Tôi không thể phủ nhận hay xác nhận vì tôi không thực sự có thời gian để kiểm tra nó. Tất cả những gì tôi có thể nói là tôi đang sử dụng SDK 15, vì vậy rất có thể xảy ra.
SeruK

0

Dựa trên câu trả lời của Matthieu, bên dưới một lớp trợ giúp để thực thi AsyncTaskchính xác của bạn tùy thuộc vào phiên bản SDK để tránh trùng lặp mã trong ứng dụng của bạn:

import android.annotation.SuppressLint;
import android.os.AsyncTask;
import android.os.Build;

public class AsyncTaskExecutor<Params, Progress, Result> {

  @SuppressLint("NewApi")
  public AsyncTask<Params, Progress, Result> execute(final AsyncTask<Params, Progress, Result> asyncTask, final Params... params){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
      return asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }  else{
      return asyncTask.execute(params);
    }
  }

}

Ví dụ sử dụng:

public class MyTask extends AsyncTask<Void, Void, List<String>> {

...

final MyTask myTask = new MyTask();
new AsyncTaskExecutor<Void, Void, List<String>>().execute(myTask);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.