java android - Valore di ritorno da Thread




on background (8)

Java 8 fornisce CompletableFuture. sarà la soluzione unica per questo. http://www.baeldung.com/java-completablefuture

Ho un metodo con un HandlerThread . Un valore viene modificato all'interno del Thread e vorrei restituirlo al metodo test() . C'è un modo per fare questo?

public void test()
{   
    Thread uiThread = new HandlerThread("UIHandler"){
        public synchronized void run(){
            int value; 
            value = 2; //To be returned to test()
        }
    };
    uiThread.start();
}

È possibile utilizzare un array di variabili finali locali. La variabile deve essere di tipo non primitivo, quindi è possibile utilizzare un array. È inoltre necessario sincronizzare i due thread, ad esempio utilizzando CountDownLatch :

public void test()
{   
    final CountDownLatch latch = new CountDownLatch(1);
    final int[] value = new int[1];
    Thread uiThread = new HandlerThread("UIHandler"){
        @Override
        public void run(){
            value[0] = 2;
            latch.countDown(); // Release await() in the test thread.
        }
    };
    uiThread.start();
    latch.await(); // Wait for countDown() in the UI thread. Or could uiThread.join();
    // value[0] holds 2 at this point.
}

Puoi anche usare un Executor e un Callable come questo:

public void test() throws InterruptedException, ExecutionException
{   
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Callable<Integer> callable = new Callable<Integer>() {
        @Override
        public Integer call() {
            return 2;
        }
    };
    Future<Integer> future = executor.submit(callable);
    // future.get() returns 2 or raises an exception if the thread dies, so safer
    executor.shutdown();
}

Di solito lo faresti in questo modo

 public class Foo implements Runnable {
     private volatile int value;

     @Override
     public void run() {
        value = 2;
     }

     public int getValue() {
         return value;
     }
 }

Quindi puoi creare il thread e recuperare il valore (dato che il valore è stato impostato)

Foo foo = new Foo();
new Thread(foo).start();
// ... join through some method
int value = foo.getValue();

tl;dr un thread non può restituire un valore (almeno non senza un meccanismo di callback). Dovresti fare riferimento a una discussione come una classe ordinaria e chiedere il valore.


Se si desidera il valore del metodo di chiamata, è necessario attendere il termine del thread, il che rende l'uso dei thread un po 'inutile.

Per rispondere direttamente alla domanda, il valore può essere memorizzato in qualsiasi oggetto mutabile sia al metodo chiamante sia al thread hanno un riferimento. Si potrebbe usare this esterno, ma questo non sarà particolarmente utile se non per semplici esempi.

Una piccola nota sul codice nella domanda: Estendere la Thread solito è di scarsa qualità. In effetti, estendere inutilmente le classi è una cattiva idea. Ho notato che il metodo di run è sincronizzato per qualche motivo. Ora, dato che l'oggetto in questo caso è il Thread puoi interferire con qualunque Thread usi il suo lock per (nell'implementazione di riferimento, qualcosa da fare con join , IIRC).


Con piccole modifiche al tuo codice, puoi ottenerlo in un modo più generico.

 final Handler responseHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                //txtView.setText((String) msg.obj);
                Toast.makeText(MainActivity.this,
                        "Result from UIHandlerThread:"+(int)msg.obj,
                        Toast.LENGTH_LONG)
                        .show();
            }
        };

        HandlerThread handlerThread = new HandlerThread("UIHandlerThread"){
            public void run(){
                Integer a = 2;
                Message msg = new Message();
                msg.obj = a;
                responseHandler.sendMessage(msg);
                System.out.println(a);
            }
        };
        handlerThread.start();

Soluzione:

  1. Creare un Handler nel thread dell'interfaccia utente, che viene chiamato come responseHandler
  2. Inizializza questo Handler da Looper of UI Thread.
  3. In HandlerThread , HandlerThread un messaggio su questa responseHandler
  4. handleMessgae mostra un Toast con valore ricevuto dal messaggio. Questo oggetto Message è generico e puoi inviare diversi tipi di attributi.

Con questo approccio, puoi inviare più valori al thread dell'interfaccia utente in diversi momenti. È possibile eseguire (postare) molti oggetti Runnable su questo HandlerThread e ogni Runnable può impostare il valore nell'oggetto Message , che può essere ricevuto da UI Thread.


Quello che stai cercando è probabilmente l'interfaccia Callable<V> al posto di Runnable e il recupero del valore con un oggetto Future<V> , che ti consente anche di attendere fino a quando il valore non è stato calcolato. È possibile ottenere ciò con un ExecutorService , che è possibile ottenere da Executors.newSingleThreadExecutor() .

public void test() {
    int x;
    ExecutorService es = Executors.newSingleThreadExecutor();
    Future<Integer> result = es.submit(new Callable<Integer>() {
        public Integer call() throws Exception {
            // the other thread
            return 2;
        }
    });
    try {
        x = result.get();
    } catch (Exception e) {
        // failed
    }
    es.shutdown();
}

Usare Future descritto nelle risposte precedenti fa il lavoro, ma un po 'meno significativamente come f.get (), blocca il thread fino a quando non ottiene il risultato, il che viola la concorrenza.

La soluzione migliore è utilizzare ListenableFuture di Guava. Un esempio :

    ListenableFuture<Void> future = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1, new NamedThreadFactory).submit(new Callable<Void>()
    {
        @Override
        public Void call() throws Exception
        {
            someBackgroundTask();
        }
    });
    Futures.addCallback(future, new FutureCallback<Long>()
    {
        @Override
        public void onSuccess(Long result)
        {
            doSomething();
        }

        @Override
        public void onFailure(Throwable t)
        {

        }
    };

Java non supporta l'ereditarietà multipla, quindi se estendi la classe Thread non verrà estesa alcuna altra classe.

Ad esempio: se crei un'applet, deve estendere la classe Applet, quindi qui l'unico modo per creare thread è implementare l'interfaccia Runnable





java android multithreading