首页 文章

同时运行多个AsyncTasks - 不可能?

提问于
浏览
235

我正在尝试同时运行两个AsyncTasks . (平台是Android 1.5,HTC Hero . )但是,只有第一个被执行 . 这是一个描述我的问题的简单片段:

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.");
    }
}

我期望的输出是:

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

等等 . 但是,我得到的是:

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

第二个AsyncTask永远不会被执行 . 如果我改变execute()语句的顺序,只有foo任务会产生输出 .

我错过了一些明显的东西和/或做一些愚蠢的事情吗?是不是可以同时运行两个AsyncTasks?

编辑:我意识到有问题的手机运行Android 1.5,我更新了问题descr . 因此 . 运行Android 2.1的HTC Hero没有这个问题 . 嗯......

8 回答

  • 3

    AsyncTask使用线程池模式来运行doInBackground()中的东西 . 问题最初(在早期的Android OS版本中)池大小只有1,这意味着没有对一堆AsyncTasks进行并行计算 . 但后来他们修复了这个问题,现在大小为5,所以最多可以同时运行5个AsyncTasks . 不幸的是我不记得他们改变了什么版本 .

    UPDATE:

    以下是当前(2012-01-27)API对此的说法:

    首次引入时,AsyncTasks在单个后台线程上串行执行 . 从DONUT开始,这被改为一个线程池,允许多个任务并行运行 . 在HONEYCOMB之后,计划将其更改回单个线程以避免由并行执行引起的常见应用程序错误 . 如果你真的想要并行执行,你可以使用THREAD_POOL_EXECUTOR这个方法的executeOnExecutor(Executor,Params ...)版本;但是,请在那里查看有关其使用的警告 .

    DONUT是Android 1.6,HONEYCOMB是Android 3.0 .

    UPDATE: 2

    请参阅 Mar 7 at 1:27kabuko 评论 .

    事实证明,对于使用"a pool of threads allowing multiple tasks to operate in parallel"的API(从1.6开始到3.0结束),同时运行的AsyncTasks的数量取决于已经执行了多少任务,但尚未完成 doInBackground() .

    这是我在2.2上测试/确认的 . 假设您有一个自定义的AsyncTask,只需在 doInBackground() 中休眠一秒钟 . AsyncTasks在内部使用固定大小的队列来存储延迟的任务 . 队列大小默认为10 . 如果您连续启动15个自定义任务,那么前5个将输入 doInBackground() ,但其余的将在队列中等待一个免费的工作线程 . 只要前5个中的任何一个完成,从而释放工作线程,队列中的任务就会开始执行 . 因此,在这种情况下,最多5个任务将同时运行 . 但是,如果您连续启动16个自定义任务,那么前5个将输入 doInBackground() ,其余10个将进入队列,但是对于第16个,将创建一个新的工作线程,因此它将立即开始执行 . 因此,在这种情况下,最多6个任务将同时运行 .

    可以同时运行多少任务的限制 . 由于 AsyncTask 使用具有有限最大工作线程数(128)的线程池执行程序,并且延迟任务队列具有固定大小10,因此如果您尝试执行超过138个自定义任务,则应用程序将崩溃 java.util.concurrent.RejectedExecutionException .

    从3.0开始,API允许通过 AsyncTask.executeOnExecutor(Executor exec, Params... params) 方法使用您的自定义线程池执行程序 . 例如,如果默认值10不是您需要的,则允许配置延迟任务队列的大小 .

    正如@Knossos所提到的,可以选择使用支持v.4库中的 AsyncTaskCompat.executeParallel(task, params); 来并行运行任务,而不必担心API级别 . 此方法在API级别26.0.0中已弃用 .

    UPDATE: 3

    这是一个简单的测试应用程序,可以执行大量任务,串行与并行执行:https://github.com/vitkhudenko/test_asynctask

    UPDATE: 4 (感谢@penkzhou指出这一点)

    从Android 4.4 AsyncTask 开始,其行为与 UPDATE: 2 部分中描述的不同 . 有is a fix阻止 AsyncTask 创建太多线程 .

    在Android 4.4(API 19) AsyncTask 之前有以下字段:

    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);
    

    在Android 4.4(API 19)中,上述字段更改为:

    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);
    

    此更改将队列的大小增加到128个项目,并将最大线程数减少到CPU核心数* 2 1.应用程序仍然可以提交相同数量的任务 .

  • 203

    这允许在所有具有API 4(Android 1.6)的Android版本上并行执行:

    @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);
    }
    

    这是Arhimed优秀的总结回答 .

    请确保使用API级别11或更高级别作为项目构建目标 . 在Eclipse中,这是 Project > Properties > Android > Project Build Target . 这将 not 打破向后兼容性降低API级别 . 不用担心,如果您意外使用 minSdkVersion 之后引入的功能,则会出现Lint错误 . 如果您确实想使用 minSdkVersion 之后引入的功能,可以使用注释来抑制这些错误,但在这种情况下,您需要自己处理兼容性问题 . 这正是上面代码片段中发生的事情 .

  • 415

    使@sulai建议更通用:

    @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);
    }
    
  • 20

    只是在@sulai的非常好的总结中包含最新更新(更新4)@Arhimed完美无瑕的答案:

    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);
        }
    }
    
  • 7

    Android开发人员加载位图的示例有效地使用自定义asynctask(从jellybean复制),因此您可以在低于<11的apis中使用executeOnExecutor

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

    下载代码并转到util包 .

  • 1

    它是可行的 . 我的Android设备版本是4.0.4,android.os.Build.VERSION.SDK_INT是15

    我有3个微调器

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

    而且我还有一个Async-Tack课程 .

    这是我的微调器加载代码

    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();
    

    这很完美 . 我的旋转器一个接一个地加载 . 我没有使用executeOnExecutor .

    这是我的Async-task类

    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();
            }
        }
    }
    
  • 0

    如果你想并行执行任务,你需要在Android版3.0之后调用方法 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "your task name") ;但是这个方法在Android 3.0之前和1.6之后都不存在,因为它本身就是并行执行,所以我建议你在你的项目中自定义你自己的AsyncTask类,以避免在不同的Android版本中抛出异常 .

  • 4
    Replace this `asyncTask.execute();
    asyncTask2.execute();
    asyncTask3.execute();
    asyncTask4.execute();
    asyncTask5.execute();`
    
    with this 
    
    
    
    asyncTask1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
      asyncTask2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
      asyncTask3.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
      asyncTask4.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 
      asyncTask5.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    
    it works after android honeycomb.
    

    Read More https://www.shaikhutech.com/2018/12/biggest-problem-when-it-comes-to-run.html

相关问题