Pergunta

Estou tentando executar dois AsyncTasks ao mesmo tempo.(Plataforma é o Android 1.5, HTC Hero.) No entanto, apenas o primeiro é executado.Aqui está um trecho simples para descrever meu problema:

public class AndroidJunk extends Activity {
 class PrinterTask extends AsyncTask<String, Void, Void> {
     protected Void doInBackground(String ... x) {
      while (true) {
       System.out.println(x[0]);
       try {
        Thread.sleep(1000);
       } catch (InterruptedException ie) {
        ie.printStackTrace();
       }
      }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        new PrinterTask().execute("bar bar bar");
        new PrinterTask().execute("foo foo foo");

        System.out.println("onCreate() is done.");
    }
}

A saída que espero é:

onCreate() is done.
bar bar bar
foo foo foo
bar bar bar
foo foo foo

E assim por diante.No entanto, o que recebo é:

onCreate() is done.
bar bar bar
bar bar bar
bar bar bar

O segundo AsyncTask nunca é executado.Se eu alterar a ordem das instruções execute(), apenas a tarefa foo produzirá saída.

Estou perdendo algo óbvio aqui e/ou fazendo algo estúpido?Não é possível executar duas AsyncTasks ao mesmo tempo?

Editar:Percebi que o telefone em questão roda Android 1.5, atualizei a descrição do problema.de acordo.Não tenho esse problema com um HTC Hero rodando Android 2.1.Hmmm ...

Foi útil?

Solução

AsyncTask usa um padrão de pool de threads para executar o material de doInBackground().O problema é que inicialmente (nas primeiras versões do sistema operacional Android) o tamanho do pool era de apenas 1, o que significa que não havia cálculos paralelos para vários AsyncTasks.Mas depois eles consertaram isso e agora o tamanho é 5, então no máximo 5 AsyncTasks podem ser executados simultaneamente.Infelizmente não me lembro em que versão exatamente eles mudaram isso.

ATUALIZAR:

Aqui está o que a API atual (27/01/2012) diz sobre isso:

Quando introduzido pela primeira vez, os AsyncTasks eram executados serialmente em um único thread em segundo plano.Começando com DONUT, isso foi alterado para um conjunto de threads permitindo que múltiplas tarefas operem em paralelo.Após o HONEYCOMB, está planejado voltar para um único thread para evitar erros comuns de aplicativos causados ​​​​pela execução paralela.Se você realmente deseja execução paralela, você pode usar a versão executeOnExecutor(Executor, Params...) deste método com THREAD_POOL_EXECUTOR;no entanto, consulte os comentários para obter avisos sobre seu uso.

DONUT é Android 1.6, HONEYCOMB é Android 3.0.

ATUALIZAR:2

Veja o comentário de kabuko de Mar 7 at 1:27.

Acontece que para APIs onde "um conjunto de threads permitindo que múltiplas tarefas operem em paralelo" é usado (começando em 1.6 e terminando em 3.0), o número de AsyncTasks em execução simultânea depende de quantas tarefas já foram passadas para execução, mas não terminaram seus doInBackground() ainda.

Isso foi testado/confirmado por mim em 2.2.Suponha que você tenha um AsyncTask personalizado que dorme apenas um segundo doInBackground().AsyncTasks usa uma fila de tamanho fixo internamente para armazenar tarefas atrasadas.O tamanho da fila é 10 por padrão.Se você iniciar 15 tarefas personalizadas seguidas, as primeiras 5 entrarão em seus doInBackground(), mas o restante aguardará em uma fila por um thread de trabalho livre.Assim que qualquer um dos 5 primeiros terminar e, portanto, liberar um thread de trabalho, uma tarefa da fila iniciará a execução.Portanto, neste caso, no máximo 5 tarefas serão executadas simultaneamente.No entanto, se você iniciar 16 tarefas personalizadas seguidas, as primeiras 5 entrarão em seus doInBackground(), os 10 restantes entrarão na fila, mas para o 16º um novo thread de trabalho será criado para iniciar a execução imediatamente.Portanto, neste caso, no máximo 6 tarefas serão executadas simultaneamente.

Há um limite de quantas tarefas podem ser executadas simultaneamente.Desde AsyncTask usa um executor de pool de threads com número máximo limitado de threads de trabalho (128) e a fila de tarefas atrasadas tem tamanho fixo 10, se você tentar executar mais de 138 tarefas personalizadas, o aplicativo irá travar java.util.concurrent.RejectedExecutionException.

A partir da versão 3.0, a API permite usar seu executor de pool de threads personalizado via AsyncTask.executeOnExecutor(Executor exec, Params... params) método.Isso permite, por exemplo, configurar o tamanho da fila de tarefas atrasadas caso o padrão 10 não seja o que você precisa.

Como @Knossos menciona, existe uma opção de usar AsyncTaskCompat.executeParallel(task, params); da biblioteca de suporte v.4 para executar tarefas em paralelo sem se preocupar com o nível da API.Este método tornou-se obsoleto no nível de API 26.0.0.

ATUALIZAR:3

Aqui está um aplicativo de teste simples para brincar com várias tarefas, serial vs.execução paralela: https://github.com/vitkhudenko/test_asynctask

ATUALIZAR:4 (obrigado @penkzhou por apontar isso)

A partir do Android 4.4 AsyncTask se comporta de maneira diferente do que foi descrito em ATUALIZAR:2 seção.Lá é uma correção prevenir AsyncTask de criar muitos tópicos.

Antes do Android 4.4 (API 19) AsyncTask tinha os seguintes campos:

private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(10);

No Android 4.4 (API 19), os campos acima foram alterados para isto:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

Essa alteração aumenta o tamanho da fila para 128 itens e reduz o número máximo de threads para o número de núcleos de CPU * 2 + 1.Os aplicativos ainda podem enviar o mesmo número de tarefas.

Outras dicas

Isso permite a execução paralela em todas as versões Android com API 4+ (Android 1.6+):

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
void startMyTask(AsyncTask asyncTask) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}

Este é um resumo da excelente resposta de Arhimed.

Certifique -se de usar o nível 11 ou superior da API como seu projeto de construção. No eclipse, isso é Project > Properties > Android > Project Build Target. Isso vai não Quebre a compatibilidade com antecedência para mais baixos níveis de API. Não se preocupe, você receberá erros de fiapos se você usar acidentalmente os recursos introduzidos mais tarde minSdkVersion. Se você realmente deseja usar os recursos introduzidos mais tarde minSdkVersion, você pode suprimir esses erros usando anotações, mas nesse caso, você precisa cuidar da compatibilidade você mesma. Foi exatamente o que aconteceu no snippet de código acima.

Tornando a sugestão @sulai mais genérica:

@TargetApi(Build.VERSION_CODES.HONEYCOMB) // API 11
public static <T> void executeAsyncTask(AsyncTask<T, ?, ?> asyncTask, T... params) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    else
        asyncTask.execute(params);
}   

Apenas para incluir a atualização mais recente (atualização 4) na resposta imaculada do @arhimed no resumo muito bom de @sulai:

void doTheTask(AsyncTask task) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // Android 4.4 (API 19) and above
        // Parallel AsyncTasks are possible, with the thread-pool size dependent on device
        // hardware
        task.execute(params);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Android 3.0 to
        // Android 4.3
        // Parallel AsyncTasks are not possible unless using executeOnExecutor
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    } else { // Below Android 3.0
        // Parallel AsyncTasks are possible, with fixed thread-pool size
        task.execute(params);
    }
}

Os desenvolvedores do Android Exemplo de Carregar Bitmaps usa com eficiência uma assíncada personalizada (copiada da Jellybean) para que você possa usar o executivo executivo em APIs inferiores a <11

http://developer.android.com/training/displaying-bitmaps/index.html

Faça o download do código e vá para o pacote Util.

É possível. A versão do meu dispositivo Android é 4.0.4 e Android.OS.Build.version.sdk_int é 15

Eu tenho 3 spinners

Spinner c_fruit=(Spinner) findViewById(R.id.fruits);
Spinner c_vegetable=(Spinner) findViewById(R.id.vegetables);
Spinner c_beverage=(Spinner) findViewById(R.id.beverages);

E também tenho uma aula de tachinha assíncrona.

Aqui está o meu código de carregamento de girador

RequestSend reqs_fruit = new RequestSend(this);
reqs_fruit.where="Get_fruit_List";
reqs_fruit.title="Loading fruit";
reqs_fruit.execute();

RequestSend reqs_vegetable = new RequestSend(this);
reqs_vegetable.where="Get_vegetable_List";
reqs_vegetable.title="Loading vegetable";
reqs_vegetable.execute();

RequestSend reqs_beverage = new RequestSend(this);
reqs_beverage.where="Get_beverage_List";
reqs_beverage.title="Loading beverage";
reqs_beverage.execute();

Isso está funcionando perfeitamente. Um por um meus spinners carregados. Não executei o usuário.

Aqui está minha aula de tarefa assíncrona

public class RequestSend  extends AsyncTask<String, String, String > {

    private ProgressDialog dialog = null;
    public Spinner spin;
    public String where;
    public String title;
    Context con;
    Activity activity;      
    String[] items;

    public RequestSend(Context activityContext) {
        con = activityContext;
        dialog = new ProgressDialog(activityContext);
        this.activity = activityContext;
    }

    @Override
    protected void onPostExecute(String result) {
        try {
            ArrayAdapter<String> adapter = new ArrayAdapter<String> (activity, android.R.layout.simple_spinner_item, items);       
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            spin.setAdapter(adapter);
        } catch (NullPointerException e) {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        } catch (Exception e)  {
            Toast.makeText(activity, "Can not load list. Check your connection", Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
        super.onPostExecute(result);

        if (dialog != null)
            dialog.dismiss();   
    }

    protected void onPreExecute() {
        super.onPreExecute();
        dialog.setTitle(title);
        dialog.setMessage("Wait...");
        dialog.setCancelable(false); 
        dialog.show();
    }

    @Override
    protected String doInBackground(String... Strings) {
        try {
            Send_Request();
            } catch (NullPointerException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        return null;
    }

    public void Send_Request() throws JSONException {

        try {
            String DataSendingTo = "http://www.example.com/AppRequest/" + where;
            //HttpClient
            HttpClient httpClient = new DefaultHttpClient();
            //Post header
            HttpPost httpPost = new HttpPost(DataSendingTo);
            //Adding data
            List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);

            nameValuePairs.add(new BasicNameValuePair("authorized","001"));

            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
            // execute HTTP post request
            HttpResponse response = httpClient.execute(httpPost);

            BufferedReader reader;
            try {
                reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
                StringBuilder builder = new StringBuilder();
                String line = null;
                while ((line = reader.readLine()) != null) {
                    builder.append(line) ;
                }

                JSONTokener tokener = new JSONTokener(builder.toString());
                JSONArray finalResult = new JSONArray(tokener);
                items = new String[finalResult.length()]; 
                // looping through All details and store in public String array
                for(int i = 0; i < finalResult.length(); i++) {
                    JSONObject c = finalResult.getJSONObject(i);
                    items[i]=c.getString("data_name");
                }

            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Se você deseja executar tarefas paralelas, você precisa ligar para o método executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name") Após a versão Android do 3.0; Mas esse método não existe antes do Android 3.0 e depois de 1.6 porque executa paralelo por si só, por isso sugiro que você personalize sua própria aula de assíncas em seu projeto, para evitar a exceção de lançar em diferentes versões do Android.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top