Можно ли синхронизировать Android AsyncTask doInBackground для сериализации выполнения задачи? - PullRequest
7 голосов
/ 27 марта 2012

Можно ли сделать AsyncTask.doInBackground синхронизированным - или достичь того же результата другим способом?

class SynchronizedTask extends AsyncTask {

    @Override
    protected synchronized Integer doInBackground(Object... params) {
        // do something that needs to be completed 
        // before another doInBackground can be called
    }
}

В моем случае любой AsyncTask.execute() может быть запущен до завершения предыдущего, ноМне нужно выполнить код в doInBackground только после завершения предыдущего задания.

EDIT : Как правильно указано, синхронизация работает только для одного экземпляра объекта.К сожалению, невозможно создать AsyncTask и вызывать execute() более одного раза для одного и того же экземпляра объекта, как указано в разделе «Правила потоков» документации AsyncTask .

Решение состоит в том, чтобы использовать пользовательский Executor для сериализации задач или, если вы используете API 11 или выше, AsyncTask.executeOnExecutor(), как предлагается в комментариях ниже.

Я отправил ответ , показывающий реализацию SerialExecutor, которую можно использовать для постановки в очередь задач, которые будут выполняться последовательно.

Ответы [ 4 ]

16 голосов
/ 28 марта 2012

В идеале я хотел бы иметь возможность использовать AsyncTask.executeOnExecutor() с SERIAL_EXECUTOR, но это доступно только для API уровня 11 или выше:

new AsyncTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);

Чтобы настроить API-интерфейсы Android ниже уровня 11, я реализовал собственный класс, который инкапсулирует ExecutorService с размером пула потоков 1. Полный код здесь с открытым исходным кодом здесь .

Executors.newFixedThreadPool(int nThreads) создает пул потоков, который повторно использует фиксированное количество потоков, работающих в общей неограниченной очереди. В любой момент не более nThreads потоков будут активными задачами обработки. В моем случае nThreads равно 1, что означает, что задачи могут быть поставлены в очередь, но в любой момент времени будет выполняться только одна задача.

Вот код:

public abstract class SerialExecutor {
    private final ExecutorService mExecutorService;

    public SerialExecutor() {
        mExecutorService = Executors.newFixedThreadPool(1);
    }

    public void queue(Context context, TaskParams params) {
        mExecutorService.submit(new SerialTask(context, params));
    }

    public void stop() {
        mExecutorService.shutdown();
    }

    public abstract void execute(TaskParams params);

    public static abstract class TaskParams { }

    private class SerialTask implements Runnable {
        private final Context mContext;
        private final TaskParams mParams;

        public SerialTask(Context context, TaskParams params) {
            mContext = context;
            mParams = params;
        }

        public void run() {
            execute(mParams);
            Activity a = (Activity) mContext;
            a.runOnUiThread(new OnPostExecute());
        }
    }

    /**
     * Used to notify the UI thread
     */
    private class OnPostExecute implements Runnable {

        public void run() {

        }
    }
}

Это можно расширить и использовать в качестве исполнителя последовательных задач в Activity:

public class MyActivity extends Activity {
    private MySerialExecutor mSerialExecutor;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // ...
        mSerialExecutor = new MySerialExecutor();
    }

    @Override
    protected void onDestroy() {
        if (mSerialExecutor != null) {
            mSerialExecutor.stop();
        }
        super.onDestroy();
    }

    public void onTrigger(int param) {
        mSerialExecutor.queue(this, new MySerialExecutor.MyParams(param));
    }

    private static class MySerialExecutor extends SerialExecutor {

        public MySerialExecutor() {
            super();
        }

        @Override
        public void execute(TaskParams params) {
            MyParams myParams = (MyParams) params;
            // do something...
        }

        public static class MyParams extends TaskParams {
            // ... params definition

            public MyParams(int param) {
                // ... params init
            }
        }
    }
}
3 голосов
/ 27 марта 2012

Возможно, вы захотите использовать IntentService .Кажется, что он может лучше подходить для вашего процесса, поскольку он имеет встроенные функции для организации очередей.

0 голосов
/ 17 октября 2016
public class RestAsyncTask1 extends AsyncTask<String, Void, String> {

    private AsyncTaskCompleteListener callback;
    private Context context;
    private String method;
    private static final AtomicInteger PROGRESS_NUM = new AtomicInteger(0);
    private static ProgressDialog PROGRESS_DIALOG;

    public RestAsyncTask1(Context context, AsyncTaskCompleteListener callback, String method) {
        this.callback = callback;
        this.context = context;
        this.method = method;
    }

    public static String format(String url, String... params) {
        String[] encoded = new String[params.length];

        for (int i = 0; i < params.length; i++) {
            encoded[i] = Uri.encode(params[i]);
        }

        return String.format(url, (String[]) encoded);
    }

    @Override
    protected void onPreExecute() {
        int x = PROGRESS_NUM.getAndIncrement();

        if (x == 0) {
            String title = "M_yug";
            PROGRESS_DIALOG = new ProgressDialog(context);
           // PROGRESS_DIALOG.setTitle(title);
            PROGRESS_DIALOG.setIndeterminate(true);
            PROGRESS_DIALOG.setCancelable(false);
            PROGRESS_DIALOG.setOnCancelListener(null);
            PROGRESS_DIALOG.setMessage("Loading. Please wait...");
            PROGRESS_DIALOG.show();
        }
    }

    @Override
    protected String doInBackground(String... params) {
        String url = params[0];
        String response = null;
        HttpURLConnection connection = null;

        if (params.length > 1) {
            if (method.equals(Method.GET)) {
                url = format(url, (String[]) Arrays.copyOfRange(params, 1, params.length));
            } else if (params.length > 2) {
                url = format(url, (String[]) Arrays.copyOfRange(params, 1, params.length - 1));
            }

            try {
                URL call = new URL(url);
                connection = (HttpURLConnection) call.openConnection();
                connection.setRequestProperty("Content-Type", "application/json");
                //connection.setRequestProperty("M-Yug", Utilities.VERSION);
                connection.setRequestMethod(method);
                connection.setDoOutput(true);

                if (method.equals("POST")) {
                    BufferedOutputStream outputStream = new BufferedOutputStream(connection.getOutputStream());
                    outputStream.write(params[params.length - 1].getBytes());
                    outputStream.flush();
                }

                int status = connection.getResponseCode();

                if (status == HttpURLConnection.HTTP_OK) {
                    InputStream is = connection.getInputStream();
                    response = readValue(is);
                } else if (status == 400) {
                    InputStream is = connection.getErrorStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
                    StringBuilder builder = new StringBuilder();
                    String line;

                    while ((line = reader.readLine()) != null) {
                        builder.append(line);
                    }

                    reader.close();
                    Toast.makeText(context, "" + builder.toString(), Toast.LENGTH_SHORT).show();
                }

                connection.disconnect();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }

        return response;
    }

    @Override
    protected void onPostExecute(String s) {
        int x = PROGRESS_NUM.decrementAndGet();

        if (x == 0 && PROGRESS_DIALOG != null && PROGRESS_DIALOG.isShowing()) {
            PROGRESS_DIALOG.dismiss();
        }

        if (s!=null) {
            String resopnse=s.toString();
            callback.onSuccess(resopnse);
        } else {
           Toast.makeText(context,"Server Not Responding",Toast.LENGTH_SHORT).show();
        }
    }

    private String readValue(InputStream is) {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        String line;

        try {
            br = new BufferedReader(new InputStreamReader(is));

            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
        } catch (Exception e) {
        }

        return sb.toString();
    }

    enum Method {
        GET, POST
    }
}
0 голосов
/ 27 марта 2012

AsyncTask используется для запуска фонового потока, чтобы текущий процесс не прерывался.

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
 protected Long doInBackground(URL... urls) {
     int count = urls.length;
     long totalSize = 0;
     for (int i = 0; i < count; i++) {
         totalSize += Downloader.downloadFile(urls[i]);
         publishProgress((int) ((i / (float) count) * 100));
     }
     return totalSize;
 }

 protected void onProgressUpdate(Integer... progress) {
     setProgressPercent(progress[0]);
 }

 protected void onPostExecute(Long result) {
     showDialog("Downloaded " + result + " bytes");
 }

}

, где в первую очередь вызывается ваша функция doInBackground, и возвращаемый объект будет перемещен в после выполнения. какую строку кода вы хотите запустить после какого-то процесса, вы можете поместить это в функцию PostExecute. это наверняка поможет вам

...