2014-01-21 27 views
7

我使用的是Guava的ListenableFuture,關於它們的一個好處是,一個將Executor傳遞給Futures.addCallback方法,即要求在給定的線程/執行程序上執行回調。捕獲當前線程的執行程序

在我的Android應用程序中,我希望能夠在UI線程中啓動基於ListenableFuture的異步執行,並安排也在UI線程上執行的回調。因此,我想以某種方式將UI線程執行程序提交給上述的Futures.addCallback方法。如何實現這一目標?

或換句話說,我想要一個UI線程的執行器。它是否已經在Android中可用,或者,如果我必須創建自己的,我該怎麼做?

編輯:作爲這個問題的擴展,是否有可能做同樣的事情,但不只是與UI線程,但任何特定的線程,在哪裏調用異步方法?

我很樂意知道如何在不使用Android專用的東西(如HandlerLooper)的情況下獲得相同的效果,只需使用純Java。

+0

無法理解您的問題!什麼是執行者?請詳細說明一下!謝謝 –

回答

17

我想我已經看到一些實現。其基本思想是大致

class UiThreadExecutor implements Executor { 
    private final Handler mHandler = new Handler(Looper.getMainLooper()); 

    @Override 
    public void execute(Runnable command) { 
     mHandler.post(command); 
    } 
} 

您可以通過委託它傳遞到處理程序的主線程運行在主線程什麼。

編輯:https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit/android/MainThreadExecutor.java例如

EDIT2:您可以配置例如像處理程序SensorManager#registerListener(..., Handler handler)允許你這樣做。

class HandlerThreadExecutor implements Executor { 
    private final Handler mHandler; 
    public HandlerThreadExecutor(Handler optionalHandler) { 
     mHandler = optionalHandler != null ? optionalHandler : new Handler(Looper.getMainLooper()); 
    } 

    @Override 
    public void execute(Runnable command) { 
     mHandler.post(command); 
    } 
} 

在使用當前線程的彎針的優點是,它使你明確使用的Looper。在你的解決方案中,你可以使用任何線程調用的Looper new ExecuteOnCaller() - 而這通常不是你以後運行代碼的線程。

我很高興知道如何在不使用Handler和Looper等Android特有的東西的情況下實現相同的效果,只需使用純Java。

Looper,Handler並且所有邏輯後面的消息隊列都是由大多數純Java構成的。通用解決方案的問題在於,您無法「注入」代碼以運行到線程中。該線程必須定期檢查某種類型的任務隊列以查看是否需要運行。

如果你寫這樣

new Thread(new Runnable() { 
     @Override 
     public void run() { 
      while (!Thread.interrupted()) { 
       System.out.println("Hello"); 
      } 
     } 
    }).start(); 

代碼那麼有沒有辦法讓這個線程做任何事情,但始終打印「Hello」。如果你能做到這一點,就像動態插入跳轉到其他代碼到程序代碼。這會讓IMO成爲一個可怕的想法。

final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      try { 
       while (true) { 
        Runnable codeToRunInThisThread = queue.take(); 
        codeToRunInThisThread.run(); 
       } 
      } catch (InterruptedException ignored) {} 
     } 
    }).start(); 

。另一方面是一個簡單的線程循環永遠都在排隊。該線程可以在其中執行其他任務,但您必須在代碼中添加手動檢查。

並且你可以通過

queue.put(new Runnable() { 
     @Override 
     public void run() { 
      System.out.println("Hello!"); 
     } 
    }); 

這裏有沒有定義特殊的處理,但是這是一個什麼樣處理器&尺蠖在做Android的核心發送任務。 Android中的Handler允許您定義Message的回調,而不僅僅是Runnable

Executors.newCachedThreadPool()和類似的大致相同的事情。在單個隊列中只有多個線程正在等待代碼。


進行了擴展,這個問題,是有可能做同樣的事情,但不只是UI線程,但與任何特定線程,在調用異步方法制成?

通用答案是否定的。只有當有一種方法注入代碼才能在該線程中運行。

+0

那麼http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)呢? – Fildor

+0

@Fildor這是相同的,但需要引用一個「執行者」中不需要的「活動」。 – zapl

+0

謝謝,這絕對回答了這個問題。不過,我想知道更多:是否有可能在當前線程中捕獲並稍後執行,無論它是否是UI線程?我來自C#,並且我知道在那裏做的方式,能夠很好地學習Java的相同內容。 – Haspemulator

5

基於從@zapl asnwer,這裏是我的實現,這也回答了編輯(擴展)問題:https://gist.github.com/RomanIakovlev/8540439

想通了,我也會把它放在這裏,在情況下,如果鏈接將腐爛的某一天:

package com.example.concurrent; 

import android.os.Handler; 
import android.os.Looper; 

import java.util.concurrent.Executor; 

/** 
* When the calling thread has a Looper installed (like the UI thread), an instance of ExecuteOnCaller will submit 
* Runnables into the caller thread. Otherwise it will submit the Runnables to the UI thread. 
*/ 
public class ExecuteOnCaller implements Executor { 

    private static ThreadLocal<Handler> threadLocalHandler = new ThreadLocal<Handler>() { 
     @Override 
     protected Handler initialValue() { 
      Looper looper = Looper.myLooper(); 
      if (looper == null) 
      looper = Looper.getMainLooper(); 
      return new Handler(looper); 
     } 
    }; 

    private final Handler handler = threadLocalHandler.get(); 

    @Override 
    public void execute(Runnable command) { 
     handler.post(command); 
    } 
} 

我的模式來使用它會是這樣:

/** 
* in SomeActivity.java or SomeFragment.java 
*/ 
Futures.addCallback(myModel.asyncOperation(param), new FutureCallback<Void>() { 
     @Override 
     public void onSuccess(Void aVoid) { 
      // handle success 
     } 

     @Override 
     public void onFailure(Throwable throwable) { 
      // handle exception 
     } 
    }, new ExecuteOnCaller()); 
3

使用com.google.android.gms.tasks.TaskExecutors.MAIN_THREAD

使用主應用程序線程的執行程序。

來源:Android docs

任務的API是谷歌的Play服務since version 9.0.0一部分。

+0

+1,但這需要使用Google Play服務。另一方面,看起來這個API(任務)正在解決很多'ListenableFuture'的問題,特別是關於Activity生命週期問題(但好奇的不是Fragment生命週期問題)。 – Haspemulator