2013-06-26 15 views
13

To może być duplikat pytania, ale nie znalazłem tego, czego szukałem. Wywołuję AsyncTask w aktywności interfejsu użytkownika new LoadData().execute(); i w DoInBackground wywołuję metodę, która wymaga czasu. Chcę przerwać ten wątek, jeśli dane nie powrócą po pewnym czasie. Poniżej znajduje się kod, jak próbowałem to zrobić.Anuluj AsyncTask po pewnym czasie

class LoadData extends AsyncTask<String, String, String> 
{ 
    @Override 
    protected void onPreExecute() { 
    super.onPreExecute(); 
    startTime = System.currentTimeMillis(); 
    } 
    protected String doInBackground(String... args) 
    { 

     DataCollector dc = new DataCollector(); 
     data = dc.collectData(query); 
     //Here I check if the time is greater than 30 seconds then cancel 
     if(((System.currentTimeMillis()-startTime)/1000)>30) 
     { 
      cancel(true); 
     } 
    return null; 
    } 
} 

Ale to nie zatrzymuje zadania po 30 sekundach, w rzeczywistości zajmuje więcej czasu. Próbowałem również get(long timeout, TimeUnit unit);, ale to też nie działa.

Czy ktoś może mi pokazać, jak mogę to zrobić i jak używać metody isCancelled() w doInBackground.

Dzięki.

+0

Może ta odpowiedź pomaga: http://stackoverflow.com/a/11191070/3307066. – jbarrameda

Odpowiedz

10

Potrzebujesz wątku, który anuluje zadanie po upływie określonego czasu. Że wątek mógłby wyglądać następująco:

public class TaskCanceler implements Runnable{ 
    private AsyncTask task; 

    public TaskCanceler(AsyncTask task) { 
     this.task = task; 
    } 

    @Override 
    public void run() { 
     if (task.getStatus() == AsyncTask.Status.RUNNING) 
      task.cancel(true); 
    } 
} 

A kiedy zadzwonić do AsyncTask, trzeba uruchomić zadanie cancle po pewnym czasie (= limit czasu, w tym przypadku 20 sek)

private Handler handler = new Handler(); 
private TaskCanceler taskCanceler; 
... 
LoadData task = new LoadData(); 
taskCanceler = new TaskCanceler(task); 
handler.postDelayed(taskCanceler, 20*1000); 
task.execute(...) 

to dobry pomysł, jeśli to posprzątać na anulować lub zakończyć

if(taskCanceler != null && handler != null) { 
    handler.removeCallbacks(taskCanceler); 
} 

można oczywiście zawinąć to w realizacji niestandardowej AsyncTask. Używałem tego wzoru wiele razy i działa jak urok.Warto zauważyć, że w rzadkich przypadkach program obsługi nie uruchomi się, podejrzewam, że jeśli utworzysz go w niewłaściwym kontekście, nie przetrwa on w niektórych przypadkach, więc zmusiłem program obsługi do utworzenia wątku interfejsu użytkownika z handler= new Handler(Looper.getMainLooper());

+0

Dzięki .... Spróbuję tego i dam znać ... –

3

Musisz wykonać kontrolę czasu na innym wątku.

To, co obecnie robisz, to: wykonanie dc.collectData(query) (w tle), a gdy wszystko będzie gotowe, sprawdzisz, czy anulować. Więc jeśli zapytanie zajmuje 1 minutę, wykonasz test anulowania po 1 minucie, co jest już za późno.

Co można zrobić, to zaplanować TimerTask który powinien uruchomić 30 sekund po loaddata(). Execute() i jeśli zadanie czasomierza jest uruchamiany, można anulować AsyncTask (jeśli jest on nadal działa)

+1

dzięki za odpowiedź .... to brzmi logicznie ... Spróbuję .... dzięki –

+0

fajne! Daj mi znać, jeśli to działa dla Ciebie. – peshkira

0

spróbuj tego:

public class MyTask extends AsyncTask<Void, Void, Void> { 

    private volatile boolean running = true; 
    private final ProgressDialog progressDialog; 

    public MyTask(Context ctx) { 
     progressDialog = gimmeOne(ctx); 

     progressDialog.setCancelable(true); 
     progressDialog.setOnCancelListener(new OnCancelListener() { 
      @Override 
      public void onCancel(DialogInterface dialog) { 
       // actually could set running = false; right here, but I'll 
       // stick to contract. 
       cancel(true); 
      } 
     }); 

    } 

    @Override 
    protected void onPreExecute() { 
     progressDialog.show(); 
    } 

    @Override 
    protected void onCancelled() { 
     running = false; 
    } 

    @Override 
    protected Void doInBackground(Void... params) { 

     while (running) { 
      // does the hard work 
     } 
     return null; 
    } 

    // ... 

} 

Courtesy. Aby uzyskać więcej informacji, zobacz tę odpowiedź.

+1

jest to poprawne, ale pozwoli użytkownikowi zawsze anulować zadanie i nie anuluje go automatycznie po 30 sekundach – peshkira

+1

możesz użyć timera wewnątrz zrobić w tle, ustawić czas powiedzmy 30 sekund i po tym poście anuluj(). .it zatrzyma twój asynctask w dowolnym punkcie – Shruti

+0

@ Shruti: Dzięki za referencję, ale nie chcę pozwolić użytkownikom na anulowanie operacji. A 'cancel (true)' nie wydaje się działać dla mnie. Jak mogę użyć 'get (long timeout, jednostka TimeUnit)' becuase brzmi bardziej wykonalne. –

0

Krótka odpowiedź brzmi: nie można anulować zadania AsyncTask po jego uruchomieniu. Jedyne, co możesz zrobić, to wstawić pętlę wewnątrz doInBackGround(), która sprawdzi, czy nie ma wartości isCancelled() i jeśli zostanie ustawiona na true kiedyś w przyszłości - zwróć wartość z funkcji (która z kolei wywoła onPostExecute(), jeśli ją zdefiniowałeś);

Należy pamiętać, że to, że nie można zatrzymać zadania AsyncTask, nie oznacza, że ​​system operacyjny nie anuluje go, jeśli brakuje pamięci. Powinieneś o tym pamiętać, jeśli robisz podstawowe zadania w AsyncTask (te, które chcesz wykonać w 100%). Jeśli tak, lepiej jest użyć komponentu Service - komponentu, który jest automatycznie zabijany i restartowany przez system operacyjny w razie potrzeby.

+1

Aktualnie zbieram dane z zewnętrznych czujników przez połączenia gniazd, czasami z powodu problemu z połączeniem lub jeśli czujnik jest w trybie offline, proces ten może potrwać długo, dlatego chcę przerwać zadanie. Mówisz, że nie można go anulować, to dlaczego mamy 'cancel (stan boolean); get (long timeout, jednostka TimeUnit); "metody dostępne? –

1

przełożyłoby to na problem asynchroniczny/oczekujący, czyniąc wszystkie kosztowne metody asynchronicznymi.

Najpierw zmodyfikuj kolekcjonowanie danych DataCollector (zapytanie) w celu zebrania danych synchronizacji danych (zapytania). (Jeśli nie możesz zmodyfikować DataCollector, istnieją works do zawinięcia go w funkcję lambda lub coś podobnego).

drugie, zmienić doInBackground jako zadanie asynchronicznym, coś takiego:

protected async Task<String> doInBackgroundAsync(String... args) 
{ 
    DataCollector dc = new DataCollector(); 
    int timeout = 1000; 
    var task = dc.collectDataAsync(query); 
    if (await Task.WhenAny(task, Task.Delay(timeout)) == task) { 
     // task completed within timeout 
     data = task.Result; 
    } else { 
     // timeout logic 
    } 
} 

Zasadniczo masz dwa zadania wewnątrz doInBackgroundAsync: collectDataAsync i zadanie opóźnienie. Twój kod czeka na szybszy. Wtedy wiesz, który z nich był i możesz odpowiednio zareagować.

Jeśli chcesz również anulować zadanie collectDataAsync, użyj funkcji cancelellationToken. Używam tego do rozwiązania twojego problemu https://stackoverflow.com/a/11191070/3307066.

Zauważ, że teraz doInBackgroundAsync jest asynchroniczne, więc zmienia nieco sposób korzystania z niego.

Mam nadzieję, że to pomaga.

+0

Jeśli uważasz, że to pytanie jest duplikatem, powinieneś zgłosić się jako duplikat zamiast umieszczać link do innego pytania. –

+0

Ja zmodyfikowałem moją odpowiedź. Nie sądzę, że jest duplikatem, mam nadzieję, że teraz jest jasne. – jbarrameda

Powiązane problemy