2014-09-24 36 views
5

Gdy urządzenie z systemem iOS 8, na którym działa aplikacja internetowa (tj. Uruchamiane ze skrótu na ekranie głównym), wraca ze stanu wstrzymania, wszystkie asynchroniczne żądania sieciowe nie uruchamiają Oddzwanianie OnUpdateReady.Błąd systemu iOS 8 - OnUpdateReady nigdy nie było wywoływane ponownie po powrocie urządzenia ze stanu uśpienia

Problem jest dość łatwy do odtworzenia - wystarczy umieścić dwa poniższe pliki kodu na dowolnym serwerze WWW i spróbować.

Czy ktoś inny napotkał ten problem? Jeśli tak, to czy istnieją jakieś obejścia?

Zamieszczam to, aby spróbować zwrócić uwagę na ten błąd w systemie iOS 8, który zasadniczo zrujnował wszystkie moje aplikacje internetowe - musieliśmy zalecić NIE uaktualnianie systemu poza iOS 7. I tak, napisałem problem z Apple Bug Reporterem, ale myślę, że nikt na nie nie patrzy, ponieważ minęło dużo czasu.

app.manifest

CACHE MANIFEST 
# 2014-09-24 - Test 

CACHE: 
default.html 

default.html

<!DOCTYPE html> 
<html manifest="app.manifest"> 
<head> 
    <title>Test Harness</title> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"/> 
    <meta name="HandheldFriendly" content="true" /> 
    <meta name="apple-mobile-web-app-capable" content="yes" /> 
    <meta name="apple-mobile-web-app-status-bar-style" content="black" /> 
    <script language="javascript" type="text/javascript"> 
    var Test = new function() { 
     var _GetEnd = function (oResult) { 
     var sResult = ': ' + 
      ((oResult.Value === true) 
      ? 'Success' 
      : 'Failure<br>' + oResult.Reason) + 
      '<br>'; 

     var oLog = document.getElementById('idLog'); 
     oLog.innerHTML = (new Date()) + sResult + oLog.innerHTML 

     setTimeout(_GetBegin, 1000); 
     }; 

     var _GetBegin = function() { 
     var sURL = 'app.manifest'; 
     var hAsyncCallback = _GetEnd; 

     try { 
      var oRequest = new XMLHttpRequest(); 
      oRequest.onreadystatechange = 
      function() { 
       if (oRequest.readyState != 4) return; 
       if (oRequest.status != 200) { 
       hAsyncCallback({ Value: false, Reason: oRequest.responseText }); 
       } else { 
       hAsyncCallback({ Value: true, Reason: null }); 
       } 
      }; 
      oRequest.open('GET', sURL, true); 
      oRequest.send(null); 
     } catch (e) { 
      alert('Critical Error: ' + e.message); 
     } 
     }; 

     this.Start = function() { _GetBegin(); } 
    }; 
    </script> 
</head> 
<body onload="Test.Start();"> 
    <ol> 
    <li>Put both app.manifest and default.html on a web server.</li> 
    <li>Make sure this page is being launched from the Home screen as a web application.</li> 
    <li>Press the sleep button while it is running.</li> 
    <li>Press the wake button and unlock the phone to get back to this screen.</li> 
    <li>Under iOS7x the page continues, under iOS8 the onreadystatechange never gets called again.</li> 
    </ol> 
    <div id="idLog"></div> 
</body> 
</html> 
+2

Po prostu, aby umożliwić wszystkim, którzy są zainteresowani wiedzą - Apple Closed mój raport o błędzie z następującym komentarzem; "Inżynieria ustaliła, że ​​zgłoszenie błędu jest duplikatem innego problemu (# 18042389) i zostanie zamknięte." Jednak system zgłaszania błędów Apple nie pozwala mi na obejrzenie drugiego problemu, więc nie mam pojęcia, czy to naprawdę jest to samo, czy po prostu dezorientują ten problem. Ponadto, od wersji iOS 8.0.2 problem nadal występuje. – jrg

+0

Apple zrobił to samo z moim błędem, nie mam pojęcia, jaki jest status 18042389. odtworzy ten sam błąd, dopóki mnie nie zbanują, a następnie tweetują o byciu zbanowanym - nie można wymyślić innego sposobu obecnie –

+0

Kolejna aktualizacja iOS8 (8.1) i wciąż nie ma radości - problem nadal trwa. Znowu zgłosiłem błąd związany z iOS 8.1 i umieściłem link do tego artykułu. Mam nadzieję, że Apple wkrótce zauważy. – jrg

Odpowiedz

0

Ja również widząc ten sam problem, że mój przykład jest znacznie prostsze. Wystarczy mieć zastosowanie webclip z

<script> window.setInterval(function(){ console.log("Johnny Five Alive! : " + new Date()); },1000); </script>

na stronie. Po sprawdzeniu konsoli, po przebudzeniu się, nie ma już wyjścia konsoli. Działa to dobrze na iOS7 (moja rzeczywista aplikacja jest skomplikowaną rzeczą typu angularJS, po prostu rozwiązałem problem). Czy otrzymałeś odpowiedź na swój raport o błędzie?

+0

Brak odpowiedzi na Apple Bug Reporter - po raz pierwszy zgłosiłem błąd, więc podejrzewam, że po prostu znalazł się na samym dole listy - dlatego wysłałem go, aby przepełnić stos, mając nadzieję na podniesienie świadomości i zwrócenie uwagi Apple. – jrg

0

Nasz obejście (dla Ajax):

... 

this.onProgress = function(e) 
{ 
    var position = e.position || e.loaded; 
    var total = e.totalSize || e.total; 
    var percentage = 0.0; 
    if(total != 0) 
    { 
     percentage = position/total; 
    } 
    if(percentage == 1) { 
     if(this.isIOS8()) { 
      recovery_uuid.get(uuid, _.bind(this.ios8ScriptReturn, this)); 
     } 
    } 
} 

    ... 

    //this gets called when the script with this UUID is injected 
    this.ios8ScriptReturn = function(uuid, value) { 
//then we create a simpler non real one 
     this.xhr = {}; 
     this.xhr.readyState = 4; 
     this.xhr.status = 200; 
     this.xhr.responseText = value; 
     this.xhr.onreadystatechange = null; 
     this.xhr.isFake = true; 

     //fake stateChnage 
     this.onReadyStateChange(); 

    } 
  • dodać UUID do każdego żądania
if(this.isIOS8()) { 
    ajaxInfo.url += '&recoveryUUID='+ajaxInfo.uuid; 
} 
  • Potem jeszcze wykonać XHR Send (który faktycznie działa poprawnie, serwer pobiera i wysyła z powrotem w porządku).
  • Server Side zapisać „wynik” w bazie danych/pliku z UUID jako index/części pliku
//detect the need for saving the result, and save it till requested 
if(isset($_GET['recoveryUUID'])) { 
    $uuid = $_GET['recoveryUUID']; 
    RecoveryUUID::post($uuid, $result_json_string); 
} 
  • Na kliencie stworzenie małego pomocnika globalny obiekt, który słucha wstrzykuje kod i przekierowuje je do programu obsługi onProgress.
var RecoveryUUID = (function (_root) { 
    function RecoveryUUID() { 
     this.callbacks = {}; 
    } 
    var proto = RecoveryUUID.prototype; 
    proto.onLoaded = null; 
    proto.set = function(uuid, value) { 
     console.log('RECOVERY UUID: Received DATA: '+uuid+' value: '+value); 
     if(typeof this.callbacks[uuid] != 'undefined') { 
      this.callbacks[uuid](uuid, value); 
      delete this.callbacks[uuid]; //auto remove 
     } 
     if(this.onLoaded != null) { 
      this.onLoaded(uuid, value); 
     } 

     var script = document.getElementById("recoveryScript_"+uuid); 
     script.parentElement.removeChild(script); 

    } 
    proto.getURL = function(uuid) { 
     return "http://"+window.location.hostname+":8888/recoveryuuid/index.php?uuid="+uuid; 
    } 
    proto.get = function(uuid, callback) { 
     var script = document.createElement("script"); 
     script.setAttribute("id", "recoveryScript_"+uuid); 
     script.setAttribute("type", "text/javascript"); 
     script.setAttribute("src", this.getURL(uuid)); 
     if(typeof callback != 'undefined') { 
      this.callbacks[uuid] = callback; 
     } 
     document.getElementsByTagName("head")[0].appendChild(script); 
    } 


    return RecoveryUUID; 
})(); 

//global - just so the injected script knows what to call 
recovery_uuid = new RecoveryUUID(); 
  • skrypt, który jest ładowany natychmiast wykonuje (pcha, ponieważ setInterval jest martwy, jak również).
// this is: http://"+window.location.hostname+":8888/recoveryuuid/index.php?uuid=...." 
<?php 
header('Cache-Control: no-cache, no-store, must-revalidate, post-check=0, pre-check=0 '); // HTTP 1.1. //iOS force this file to keep fresh 
header('Pragma: no-cache'); // HTTP 1.0. 
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); 
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
header('Content-type: application/javascript; charset=UTF-8'); 
if(isset($_GET['uuid'])) { 
    $uuid = $_GET['uuid']; 
    $out = 'recovery_uuid.set('.json_encode($uuid).','.json_encode(RecoveryUUID::get($uuid)).');'; 
    echo $out; 
} 
?> 
  • Poniżej jest prosta implementacja filebased wyników.
<?php 
class RecoveryUUID { 
    static public function getFileName($uuid) { 
     return SOMESTATIC LOCATION.$uuid.'.json'; 
    } 
    static public function post($uuid, $stdClassOrString) { 
     $data = '{ "data": '.json_encode($stdClassOrString).', "uuid": '.json_encode($uuid).' }'; 
     file_put_contents(self::getFileName($uuid), $data); 
    } 
    //might not work first time as the script tag might request this file before it was written. 
    //so we wait just a bit. 
    static public function getFull($uuid) { 
     $tries = 10; 
     $filename = self::getFileName($uuid); 
     while ($tries > 0) { 
      if(file_exists($filename)) { 
       if (is_readable($filename)) { 
        $data = @file_get_contents($filename); 
        if($data !== FALSE) { 
         unlink($filename); 
         return $data; 
        } 
       } 
      } 
      $tries = $tries -1; 
      usleep(250000);//wait 0.25 secs ... 
     } 
     $data = new stdClass(); 
     $data->uuid = $uuid; 
     $data->data = 'ERROR RECOVERYUUID: timeout on reading file'; 
     return $data; 

    } 
    static public function get($uuid) { 
     $decoded = json_decode(self::getFull($uuid)); 
     if($decoded->uuid == $uuid) { 
      return $decoded->data; 
     } 
     return null; 
    } 
} 
?> 

Ponieważ nie używamy JQuery wszystko, co potrzebne do zrobienia było dodać dodatkowe logiki w naszej klasie Ajax i oczywiście zapisywanie do bazy danych dla wszystkich żądań ..

Wady:

  • Nasty
  • będzie kontynuować dodawanie zużycie pamięci dla każdego połączenia (dla nas nie problem, ponieważ pamięć jest czyszczona pomiędzy window.location.href połączeń (nie używamy SPA), więc ostatecznie przewracać się.
  • Dodatkowa logika serwerów.

Plusy:

  • Works aż pamięć skończy (tagi usuwania skryptów, co robimy nie usuwa pamięć powiązanych)

Komentarze:

  • Mogłabyś oczywiście po prostu wyślij wszystko za pomocą "połączenia", ale chcieliśmy mieć minimalny wpływ strony serwera (lub w ogóle nie działać dla nas) + zakładamy to zostanie naprawiony i oznacza, że ​​nasz kod "użytkownika" ma 0 wpływ.
0

Dziwne, Apple właśnie zamknął mój błąd i odniósł się do tego samego numeru błędu. Również aplikacja internetowa, ale znalazłem przejścia CSS3 nie działają po blokady ekranu patrz poniżej:

Inżynieria ustaliła, że ​​raport o błędzie (18556061) jest duplikatem innego problemu (18042389) i zostanie zamknięty

My raport:

Po dodaniu aplikacji HTML do ekranu głównego i jej otwarciu wszystkie przejścia CSS3 działają poprawnie. Bez zamykania aplikacji i naciskania blokady ekranu przejścia wydają się zatrzymywać i mogą powodować zamrożenie interfejsu użytkownika. Na przykład, jeśli zostanie wywołana bezwzględna nakładka (krycie: 0 do krycia: 1), pozostanie niewidoczna, dzięki czemu aplikacja nie będzie działała.

+0

Patrząc na twój kod, a następnie przeglądając mój, aby znaleźć wspólny problem, myślę, że może to mieć coś wspólnego z przerwami lub przekroczeniem limitu czasu całkowicie po ekranie lub spać. To również wpływa na wszelkie przejścia lub animacje CSS, aż dom zostanie przerysowany lub ponownie załadowany. –

+0

http://stackoverflow.com/questions/26008300/how-to-resume-javascript-timer-on-ios8-web-app-after-screen-unlock Podobne problemy –

0

Żądania Ajax, funkcje zegara i WebkitAnimation są przerywane po zablokowaniu ekranu w systemie iOS8.

Dla funkcji Ajax i Timer używamy tego rozwiązania w naszym systemie: How to resume JavaScript timer on iOS8 web app after screen unlock? (link do gitHub w komentarzu).

To nie jest część tego pytania, ale chciałbym podzielić się naszym obejściem z animacjami i wydarzeniami CSS3, ponieważ może to pomóc komuś w przyszłości.

Dla webkitAnimation odkryliśmy, że przerysowujemy element z włączoną animacją, lub bardziej drastycznie body restartuje animacje i zdarzenia zastosowane do nich (webkitAnimationEnd na przykład, który jest używany przez jquery-mobile).

więc nasz kod daje coś takiego:

document.body.style.display='none'; 
    setTimeout(function() { document.body.style.display = 'block'; }, 1); 

Może lub nie może wymagać funkcji setTimeout na drugim oświadczeniu. Najciekawsze jest to, że po ponownym przerobieniu, nigdy więcej nie zostanie zamrożone, bez względu na to, ile ekranów blokujących pojawi się po tym ...

0

Środowisko internetowe jest tak strasznie zepsute, gdy wznawia się po zablokowaniu ekranu. Zobacz, jak (a) Apple może zignorować to w nieskończoność i (b) jak każdy webapp może pewnie pracować wokół okaleczonego środowiska.

Moje rozwiązanie polega na wykryciu wznowienia po uśpieniu za pomocą setInterval (która przestaje działać po wznowieniu pracy), a następnie wysłaniu alertu() do użytkownika stwierdzającego, że aplikacja musi zostać ponownie uruchomiona z ekranu głównego, ponieważ iOS nie może wznowić to.

Tak się składa, że ​​alert() jest również przerywany po wznowieniu - wyświetla alert, a następnie aplikacja internetowa wychodzi na ekran główny, gdy użytkownik trafi OK! To zmusza użytkownika do ponownego uruchomienia.

Jedynym problemem przy ponownym uruchomieniu użytkownika jest obsługa stylu Apple-mobile-web-app-status-bar. Mam ten zestaw czarno-półprzezroczysty, który normalnie ustawia zawartość paska stanu na czarny (na jasnym tle) lub biały (na ciemnym tle). Przy pierwszym ponownym uruchomieniu po wznowieniu zawartość paska stanu jest zawsze czarna. Przy kolejnych ponownych uruchomieniach (nie przerywa sen/wznowienie) zachowanie wraca do normy.

Co za bałagan. Gdybym był odpowiedzialny za to w Apple, byłbym zawstydzony, a tutaj mamy 8.1 i nadal nie zostało to naprawione.

1

Instalacja iOS 8.1.1 naprawia to.

+0

Nie daje to odpowiedzi na pytanie.Aby skrytykować lub poprosić o wyjaśnienie od autora, zostaw komentarz pod swoim postem - zawsze możesz komentować swoje posty, a gdy już masz wystarczającą [reputację] (http://stackoverflow.com/help/whats-reputation), być w stanie [komentować dowolny wpis] (http://stackoverflow.com/help/privileges/comment). – rkhb

+0

Jak powiedział autor, opublikował swoje pytanie, aby zwrócić uwagę na ten błąd systemu iOS. A mój post w krótkich słowach dostarcza informacji, że Apple naprawił ten błąd w nowej wersji iOS. Miałem te same problemy i bardzo się cieszyłem, że ten błąd zniknął w wersji 8.1.1 –