【问题标题】:How to implement .get feature with FutureTask or BackgroundTask using android?如何使用 android 使用 FutureTask 或 BackgroundTask 实现 .get 功能?
【发布时间】:2016-10-30 12:06:41
【问题描述】:

我正在制作一个 android 库。在我的库中我希望允许用户在后台或主线程中执行特定任务。

我想做这样的事情。

第一个场景

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

当用户编写上面的 sn-p 代码时。该任务应在后台执行。所以我将使用任何监听器返回任务的结果。

现在考虑下面的 sn-p 代码。

第二个场景

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

现在,当用户在语句末尾附加 get() 时。该任务应该在主线程上执行并阻塞其他线程。这就是 result 立即初始化的原因。

所以我的问题是我如何实现如果用户附加 .get() 的功能,performSomeTask() 中的线程应该在主线程上运行。否则在后台。

注意:不要专注于返回结果。我将在 Java 泛型中实现它。 我想知道的是如何使代码可重用,以便当用户只附加.get() 时,它将在主线程上运行。否则,相同的代码应该在后台运行。我不想写重复的代码。

您可以参考现有的库。

  • AsyncTask - Android 官方,用于执行后台任务。
  • ION - 用于下载/上传文件

所以两个库具有相同的功能,还有更多的库具有相同的功能,我也想做同样的。

如果有人为此提供一个小例子,那就太好了。

【问题讨论】:

  • 让我们把它分解成一个Runnable:你可以调用run,它就会在当前线程上运行。或者您可以设置一个线程new Thread(myRunnable).start() 让它在不同的线程中运行,或者您可以将它提交给某个 Executor ... 所以在您的情况下调用 get() 可能会映射到直接运行 run 方法和 execute将其发布到一些 HanlderThread 或 Executor 或类似...
  • 添加了一个答案,该答案为 Fildor 的 Runnable 示例提供了更多信息,并显示了基本的 Executor 代码,它是 Java 和 Android 最佳实践,适用于您尝试执行的那种并发包装。

标签: java android multithreading android-library


【解决方案1】:

(我希望您在上面的示例中通过返回值澄清)所以这是您任务的几种类型的单一解决方案。仔细查看在新线程中运行的任务。您需要在后台 ThreadPool 中启动您的操作....

注意!(另外我希望你澄清使用后台线程。例如,使用上面的代码和函数 .execute() - 不会启动新线程,而只是将新任务用于从静态线程池中的 BlockingQueue 执行)

那么接下来呢? 使用函数 .get() 在 MainThread 中执行任务,您需要将此任务发布到当前操作循环中。你将帮助 Handler 和 Looper。回答你的问题,你应该知道,你只有一种方法来完成这项任务。 使用方法 .execute() 在后台线程中启动操作并使用方法 .get() 向 Handler 启动新任务。就是这样!


如果你想了解一些实现的例子,有很多种解决方案。我只是使用 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)
    }
}

【讨论】:

  • 好答案,但我认为execute() 必须收到listener(或callback)来通知错误或成功。你怎么看?
  • 当然!但我错过了,只是为了展示主要问题的例子。会有返回值和工作状态的解决方案。
  • 如果你很了解 Handler。你知道,使用上面 HandlerThread 的例子 - 使用单个线程。但是你可以编写你的库 ThreadPool,所以它会使用简单的执行在多个线程中执行你的后台任务。
  • 请原谅任何看似对您的代码的抱怨。并不是你的例子不好,而是它是多年前完成的翻新工作。已经更加完善并包含在 Java 规范中的工作。
  • @J-Boss 肯定会使用 Executors 来完成这项任务——更好、更灵活的解决方案。我只是用处理程序写了一个例子。谢谢。
【解决方案2】:

您必须使用Future Task 类在后台执行任务。

第一个场景 如果任务在后台,您将使用结果接口方法返回。

第二个场景 如果任务在前台,那么您将直接将输出返回给调用类。

execute() 的示例代码

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

【讨论】:

  • 好的,我发现您正在使用 FutureTask 和 Executor,但您的代码看起来像 sn-p。你能提供更多的代码吗..?
【解决方案3】:

这里不是要爆破任何泡沫,但我们不只是重新创建java.util.concurrent.Executors,这已经是用于灵活并发任务(即线程和处理程序管理)的android最佳实践

发件人:android.os.AsyncTask

AsyncTask 被设计成一个围绕 Thread 和 Handler 的辅助类 并且不构成通用线程框架。异步任务 理想情况下应用于短操作(几秒钟在 大多数。)如果您需要保持线程长时间运行, 强烈建议您使用由 java.util.concurrent 包,例如 Executor、ThreadPoolExecutor 和 未来任务。

发件人:java.util.concurrent.Executors

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

这些都是教科书的例子,但很容易看出,因为它已经在那里,我们不需要重新创建轮子,特别是当它已经支持 MoinKahn 试图实现的那种包装并且已经是最好的练习。

我想我会添加一个指向Oracle's Executor Interface 页面的链接,以获取有关正确并发处理和包装执行器服务的更多信息。

编辑:此外,这里有一个 really good FutureTask example 使用 ThreadPoolExecutor 来深入回答来自 Gray 的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-10
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    相关资源
    最近更新 更多