Как реализовать функцию .get с помощью FutureTask или BackgroundTask с помощью Android?

Я делаю одну андроидную библиотеку. И в моей библиотеке я хочу разрешить пользователю выполнять конкретную задачу либо в фоновом режиме, либо в основном потоке.

Я хочу сделать что-то вроде этого.

1-й сценарий

MyLibabry.with(context)
     .performSomeTask(<params>)
     .execute();

Когда пользователь записывает вышеуказанный код фрагмента. задача должна выполняться в фоновом режиме. Поэтому я верну результат задачи с помощью любых слушателей.

Теперь рассмотрим приведенный ниже фрагмент кода.

2-й сценарий

Result result = MyLibabry.with(context)
     .performSomeTask(<params>)
     .get();

Теперь, когда пользователь присоединяет get() в конце инструкции. Задача должна выполняться в основном потоке и блокировать другие потоки. Поэтому результат инициализируется немедленно.

Итак, мой вопрос заключается в том, как я могу реализовать функцию, которая, если пользователь прикрепляет файл .get(), этот поток в performSomeTask() должен запускаться в основном потоке. иначе в фоновом режиме.

Примечание. Не фокусируйтесь на возвращении результата. Я буду реализовывать его в Java Generics. Я хочу знать, как сделать код повторно используемым, чтобы при подключении пользователя .get() он запускался в основном потоке. В противном случае этот же код должен работать на фоне. Я не хочу писать повторяющийся код.

Вы можете ссылаться на существующую библиотеку.

  • AsyncTask - официальный представитель Android для выполнения фоновой задачи.
  • ION - для загрузки/выгрузки файла

Итак, у двух библиотек есть одна и та же функция, и есть много другой библиотеки, которая делает то же самое, и я хочу сделать то же самое.

Будет здорово, если кто-нибудь предоставит мне небольшой пример для этого.

Ответы

Ответ 1

(Надеюсь, вы уточните с возвращаемыми значениями в приведенных выше примерах). Это несколько типов решения для вашей задачи. Посмотрите внимательно на выполнение задачи в новом потоке. Вам нужно начать действие в фоновом режиме ThreadPool....

Примечание! (Также надеюсь, что вы уточните с использованием фоновых потоков. Например, используя ваш код выше с функцией .execute() - не запускает новый поток, а просто ставит новую задачу для выполнение из BlockingQueue в статическом ThreadPool)

Так что же дальше? Используя функцию .get() для выполнения задачи в MainThread, вам нужно опубликовать эту задачу в текущем цикле действий. И вы поможете Хендлеру и Луперу. И, отвечая на вопрос, вы должны знать, что у вас есть только один способ решения этой задачи. Запуск действия в фоновом потоке с использованием метода .execute() и запуск новой задачи обработчику с помощью метода .get(). Это все!


Если вы хотите узнать о некоторых примерах реализации, существует много видов решений. Я просто размещаю сингл с помощью Handler и HandlerThread для занижения работы этого примера.

public class MyLibrary extend HandlerThread {

    private static Handler backgroundHandler;
    private static Handler foregroundHandler;
    private static MyLibrary myLibrary

    private MyLibrary () {
        super(MyLibrary.class.getSimpleName());
        start();
    }

    public static MyLibrary getInstance() {
        if (myLibrary == null) {
            synchronized (MyLibrary.class) {
                if (myLibrary == null) {
                    myLibrary = new MyLibrary();
                }
            }
        }
        return myLibrary;
    }

    public static WorkingTask with (Context context) {
         //Just update, if there are null
         if (foregroundHandler == null) {
              foregroundHandler = new Handler(context.getMainLooper);
         }

         if (backgroundHandler == null) {
             backgroundHandler = new Handler(getLooper);
         }
         return new WorkingTask();
    }

    public void getBackgroundHandler () {
        return backgroundHandler;
    }
    public void getForegroundHandler () {
        return foregroundHandler;
    }
 }


  // ..........


public class WorkingTask  {
   private Runnable workingRunnable;

   public performTask (Runnable run) {
       this.workingRunnable = run; 
       return this;
   }

    public void execute () {
       MyLibrary.getInstance().getBackgoundHandler()
          .postRunnable(workingRunnable)
    }

    public void get () {
       MyLibrary.getInstance().getForegroundHanlder()
          .postRunnable(workingRunnable)
    }
}

Ответ 2

Вы должны использовать Будущая задача для выполнения задачи в фоновом режиме.

1-й сценарий Если задача находится в фоновом режиме, вы вернетесь с использованием метода интерфейса результата.

2-й сценарий Если задача находится на переднем плане, вы вернете вывод непосредственно в вызывающий класс.

Пример кода для выполнения()

public void execute() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Future future = mExecutorService.submit(mCursorBinderTask);
                Log.d("asd", "onCreate: started ");
                while (true) {
                    if (future.isDone()) {
                        if (mResultListener != null) {
                            try {
                                mResultListener.onResult(future.get());
                            } catch (InterruptedException e) {
                                mResultListener.onResult(null);
                            } catch (ExecutionException e) {
                                mResultListener.onResult(null);
                            }
                        }
                        break;
                    }
                }
            }
        }).start();
    }

Пример кода для get()

public ArrayList<T> get() throws ExecutionException, InterruptedException {
        return mCursorBinderTask.call();
    }

Ответ 3

Не означает, что здесь всплывают пузырьки, но мы просто не воссоздаем java.util.concurrent.Executors. Это уже самая эффективная практика Android для использования для гибких параллельных задач (т.е. управления потоками и обработчиками)

От: android.os.AsyncTask

AsyncTask предназначен для вспомогательного класса вокруг Thread and Handler и не представляет собой общую структуру потоков. AsyncTasks в идеале следует использовать для коротких операций (несколько секунд на большинство.) Если вам нужно поддерживать потоки в течение длительного времени, настоятельно рекомендуется использовать различные API-интерфейсы, предоставляемые пакет java.util.concurrent, такой как Исполнитель, ThreadPoolExecutor и FutureTask.

От: java.util.concurrent.Executors

Factory и утилиты для Executor, ExecutorService, Определены классы ScheduledExecutorService, ThreadFactory и Callable в этом пакете. Этот класс поддерживает следующие методы:

  • Способы создания и возврата службы ExecutorService с использованием обычно полезных настроек конфигурации.
  • Способы создания и возврата ScheduledExecutorService с использованием обычно полезных настроек конфигурации.
  • Способы создания и возврата "завернутого" ExecutorService, который отключает реконфигурацию, применяя методы, специфичные для реализации недоступны.
  • Методы, которые создают и возвращают ThreadFactory, который устанавливает новые созданные потоки в известное состояние.
  • Методы, которые создают и возвращают Callable из других закрывающих форм, поэтому их можно использовать в методах выполнения, требующих Callable.

Для примера (и то, что я считаю, Fildor пытался получить):

 //An Executor and a set of RunnableTasks to be executed 
 Executor executor = anExecutor();
 executor.execute(new RunnableTask1());
 executor.execute(new RunnableTask2());

Самый простой способ прямого выполнения в потоке вызывающего:

 class DirectExecutor implements Executor {
   public void execute(Runnable r) {
     r.run();
   }
 }

Это не асинхронный вызов, но может быть легко выполнен как один, а не только фоновый поток, вот версия, которая порождает новый поток для каждой задачи:

 class ThreadPerTaskExecutor implements Executor {
   public void execute(Runnable r) {
     new Thread(r).start();
   }
 }

И вы можете навязывать любые ограничения, такие как серийное исполнение:

 class SerialExecutor implements Executor {
   final Queue<Runnable> tasks = new ArrayDeque<>();
   final Executor executor;
   Runnable active;

   SerialExecutor(Executor executor) {
     this.executor = executor;
   }

   public synchronized void execute(final Runnable r) {
     tasks.add(new Runnable() {
       public void run() {
         try {
           r.run();
         } finally {
           scheduleNext();
         }
       }
     });
     if (active == null) {
       scheduleNext();
     }
   }

   protected synchronized void scheduleNext() {
     if ((active = tasks.poll()) != null) {
       executor.execute(active);
     }
   }
 }

Это все примеры текстовых книг, но легко видеть, что, поскольку он уже там, нам не нужно воссоздавать колесо, особенно когда оно уже поддерживает тип упаковки, которую MoinKah пытается достичь и уже лучше практика.

Думаю, я добавлю ссылку на страницу Oracle Executor Interface, для получения дополнительной информации о правильной обработке concurrency и обертке служб Executor.

Edit: Кроме того, здесь представлен действительно хороший пример FutureTask с помощью ThreadPoolExecutor для углубленного ответа от Gray об этом.