2013-05-18 9 views
8

Po wystąpieniu problemów z niestandardową klasą, która rozszerza AsyncTask. Moja aplikacja kieruje reklamy na Androida 4.0.3, a poniższy kod działa dobrze dla ponad 30 osób testujących to. Jednak są dwaj użytkownicy, którzy widzą awarię aplikacji po wywołaniu nowego AsyncRequest, jak poniżej.Tworzenie AsyncTask powoduje awarię

Mam działający program rejestrujący, który nagrywa do pliku tekstowego na magazynie użytkowników i nie rejestruje pozycji, która znajduje się w konstruktorze AsyncRequest. Muszę więc założyć, że katastrofa ma miejsce przed wywołaniem konstruktora.

Jedno z dwóch urządzeń, na którym występuje ta awaria, najwyraźniej ma Androida 4.0.4. Nie wiesz, co działa na drugim urządzeniu. Niestety nie mam dostępu do tych dwóch urządzeń, więc nie widzę wyjścia logcat.

Wszelkie uwagi dotyczące przyczyn powstawania obiektu powodującego awarię będą bardzo mile widziane.

String url = "www.google.com"; 

new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url); 

A oto pełnej klasa AsyncRequest

public class AsyncRequest extends AsyncTask<String, String, String>{ 

HttpURLConnection connection; 
InputStream inStream; 
IApiCallback callback; 
Context context_; 

public AsyncRequest(IApiCallback callback, Context context) { 
    // Log entry added for testing. Never gets called. 
    FileLogger.getFileLogger(context).ReportInfo("Enter AsyncRequest Constructor"); 
    this.callback = callback; 
    context_ = context; 
} 

@Override 
protected String doInBackground(String... uri) { 

    try { 
     URL url = new URL(uri[0] + "?format=json"); 
     FileLogger.getFileLogger(context_).ReportInfo("Async Request: Sending HTTP GET to " + url); 

     connection = (HttpURLConnection) url.openConnection(); 
     connection.setConnectTimeout(5000); 
     connection.setReadTimeout(5000); 
     connection.addRequestProperty("Accept-Encoding", "gzip"); 
     connection.addRequestProperty("Cache-Control", "no-cache"); 

     connection.connect(); 

     String encoding = connection.getContentEncoding(); 

     // Determine if the stream is compressed and uncompress it if needed. 
     if (encoding != null && encoding.equalsIgnoreCase("gzip")) { 
      inStream = new GZIPInputStream(connection.getInputStream()); 

     } else { 
      inStream = connection.getInputStream(); 
     } 

     if (inStream != null) { 
      // process response 
      BufferedReader br = new BufferedReader(new InputStreamReader(inStream)); 
      StringBuilder sb = new StringBuilder(); 
      String line; 
      while ((line = br.readLine()) != null) { 
       sb.append(line); 
      } 

      return sb.toString(); 

     } 

    } catch (SocketTimeoutException e) { 
     FileLogger.getFileLogger(context_).ReportException("Async Request: SocketTimeoutException", e); 
     Log.i("AsyncRequest", "Socket Timeout occured"); 
    } catch (MalformedURLException e) { 
     FileLogger.getFileLogger(context_).ReportException("Async Request: MalformedUrlException", e); 
    } catch (IOException e) { 
     FileLogger.getFileLogger(context_).ReportException("Async Request: IOException", e); 
     Log.i("doInBackground:","IOException"); 

     if (e != null && e.getMessage() != null) { 
      Log.i("doInBackground:",e.getMessage()); 
     } 
    } catch (Exception e) { 
     FileLogger.getFileLogger(context_).ReportException("Async Request: Exception", e); 

    } finally { 
     if (connection != null) 
      connection.disconnect(); 
    } 

    return null; 
} 

@Override 
protected void onPostExecute(String result) { 

    if (result != null) 
     FileLogger.getFileLogger(context_).ReportInfo("Async Request: Response is valid"); 
    else 
     FileLogger.getFileLogger(context_).ReportInfo("Async Request: Invalid response"); 

    callback.Execute(result); 
} 
} 

EDIT: Zgodnie komentarzach poniżej.

Oto pełna metoda, którą nazywam swoim niestandardowym AsyncTask od. Wszystkie komunikaty logowania, które mam do utworzenia AsyncTask są wyświetlane w dzienniku. Żaden z wyjątków nie jest.

Rejestrowanie wyświetla wartość adresu URL tuż przed utworzeniem AsyncRequest, a adres URL wcale nie jest zniekształcony. Tego się spodziewam.

public void GetServerInfoAsync(IApiCallback callback, Context context) throws IllegalArgumentException, Exception { 

    if (callback == null) 
     throw new IllegalArgumentException("callback"); 

    if (context == null) 
     throw new IllegalArgumentException("context"); 

    try { 
     FileLogger.getFileLogger(context).ReportInfo("Build URL"); 
     String url = GetApiUrl("System/Info"); 
     FileLogger.getFileLogger(context).ReportInfo("Finished building URL"); 

     if (url != null) { 
      FileLogger.getFileLogger(context).ReportInfo("GetServerInfoAsync: url is " + url); 
      new AsyncRequest(callback, context).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url); 
     } else { 
      FileLogger.getFileLogger(context).ReportError("GetServerInfoAsync: url is null"); 
     } 

    } catch (IllegalArgumentException iae) { 
     FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: IllegalArgumentException", iae); 
     throw iae; 
    } catch (Exception e) { 
     FileLogger.getFileLogger(context).ReportException("GetServerInfoAsync: Exception", e); 
     throw e; 
    } 
} 
+0

Jeśli już się dowiedziałeś, że błąd występuje przed tym żądaniem, pokaż kod przed wywołaniem go. – Gustek

+0

Czy zapisałeś kod w trakcie uruchamiania, aby utworzyć "AsyncTask"? Być może pozwoliłoby to zobaczyć, która linia faktycznie zawodzi? –

+1

Gustek. Błąd nie pojawia się, zanim zadzwonię. Dodałem metodę wywoływania do mojego oryginalnego wpisu. – Redshirt

Odpowiedz

0

Przede wszystkim, po prostu pamiętać, że nie jest dostępna executeOnExecutor() przed Api 11. Już powiedział, że problem jest z urządzeniem 4.0.4, ale po prostu o tym pamiętać.

Oto kroki, które podejmę w celu rozwiązania problemu. Wygląda na to, że wykonałeś już kilka z nich przy pomocy tych wszystkich instrukcji ReportInfo().

Po pierwsze, zakładam, że Twój numer GetServerInfoAsync jest w numerze try...catch, prawda? Sprawdzam, ponieważ używasz Throw. Ponadto dodano już rejestrowanie, aby sprawdzić błędy związane z adresem URL. Ponieważ błędy występują przed użyciem, błąd nie może dotyczyć adresu URL ani żadnych uprawnień do Internetu. Wywołujesz generację AsyncTask z odniesieniami do callback i context. Dodałeś rejestrację przez ReportInfo(), która odnosi się do kontekstu, a także do tych prac, tak? Dlatego kontekst nie jest twoim problemem. Jednak nigdy nie sprawdzasz, co to jest callback. Zgłaszasz błąd, jeśli jest on zerowy, ale nigdy nie robisz z nim nic, zanim zadzwonisz pod numer AsyncRequest. Wypróbuj ReportInfo(callback.toString()), aby zobaczyć, co to jest.

Jeśli wszystko inne zawiedzie, wydaje się, że jest to błąd związany z wątkami. Dlaczego nie spróbować użyć AsyncTask, zamiast executeOnExecutor(). Czy naprawdę potrzebujesz więcej niż 1 wątku tła?

+1

Dzięki za odpowiedź. Moje min./Docelowe wersje sdk to 15/17, testowałem bez 'ExecuteOnExecutor' z tą samą awarią. Metoda wywołująca jest rzeczywiście otoczona przez próbę/catch. Jak zasugerowałeś, rejestrowanie działa w metodzie GetServerInfoAsync, aż do utworzenia instancji AsyncTask (lub próby jej wykonania). Po pracy będę próbować sugestii Wolframs i mam nadzieję, że będę miał więcej konkretnych informacji dla wszystkich. – Redshirt

0

Przepraszam, że nie wrócę do tego wcześniej. Było tu wiele problemów.

Po pierwsze ... Dzięki sugestii Wolframa udało mi się złapać wyjątek i zdiagnozować, że problem polegał na tym, że mój FileLogger (i inna klasa) był statycznym odnośnikiem i te dwie tabletki nie mogły znaleźć odniesienia w czasie wykonywania . Usunąłem więc Logging z moich metod asynchronicznych.

Po drugie, po wprowadzeniu powyższych zmian pojawił się kolejny problem polegający na tym, że looper musiał zostać wywołany z głównego wątku. Okazało się, że te dwie tabletki nie wywoływały mojego asynchronicznego zadania z głównego wątku. Musiałem więc dołączyć wywołanie asynchroniczne do nowego Handler'a za pomocą MainLooper.