我正在制作一个android库。在我的库中,我想允许用户在后台或主线程中执行特定的任务。
我想做这样的事情。
<1场景/strong>
MyLibabry.with(context)
.performSomeTask(<params>)
.execute();
当用户编写上面的代码片段时。任务应该在后台执行。因此,我将返回使用任何侦听器的任务结果。
现在考虑下面的代码片段: <第二场景/strong>
Result result = MyLibabry.with(context)
.performSomeTask(<params>)
.get();
现在,当用户在语句末尾附加get()时。任务应该在主线程上执行,并阻塞其他线程。这就是为什么result被立即初始化的原因。
所以我的问题是我如何实现的功能,如果用户附加。get(),该线程在performSomeTask()
应该运行在主线程上。否则在后台。
注意:不要关注返回结果。我将在Java泛型中实现它。我想知道的是如何使代码可重用,以便当用户只附加.get()
时,它将在主线程上运行。否则,相同的代码应该在后台运行。我不想写重复的代码
您可以参考现有的库。
- AsyncTask - Android官方执行后台任务。
- ION -用于下载/上传文件
所以两个库有相同的功能,还有很多库有相同的功能,我也想这样做。
如果有人为我提供一个小例子,那将是伟大的。(我希望,您在上面的例子中明确了返回值)所以这是您的任务的几种类型的单一解决方案。仔细观察在新线程中运行任务。你需要在后台ThreadPool....
启动你的动作注意!(我也希望你澄清使用后台线程。例如,使用上面的代码与函数。execute() -不启动新线程,而只是将新任务从BlockingQueue中执行到静态ThreadPool)
那么下一步是什么?使用function .get()在主线程中执行任务,您需要在当前动作循环中发布此任务。你要帮助汉德勒和环形使者。回答你的问题,你应该知道,你只有一种方法来完成这个任务。使用。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)
}
}
您必须使用Future Task类在后台执行任务。
<1场景/strong>如果任务在后台,那么您将使用结果接口方法返回。
<第二场景/strong>如果任务在前台,那么您将直接向调用类返回输出。
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();
}
这里不是要打破任何泡沫,但我们不只是重新创建java.util.concurrent.Executors
,这已经是android的最佳实践,用于灵活的并发任务(即线程"处理程序管理)
源自:android.os.AsyncTask
AsyncTask被设计成一个围绕Thread和Handler的helper类并且不构成一个通用的线程框架。asynctask理想情况下,应该将其用于短期操作(在大多数)。如果你需要让线程长时间运行,强烈建议您使用java.util.concurrent包,如Executor, ThreadPoolExecutor和FutureTask .
From: 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的Executor Interface页面,以获得更多关于正确并发处理和包装Executor服务的信息。
编辑:此外,这里有一个非常好的FutureTask示例,使用ThreadPoolExecutor在Gray的深入回答中。