2014-09-24 4 views
5

Quando un dispositivo iOS 8 che esegue un'applicazione Web (ad esempio lanciato da un collegamento sulla schermata iniziale) restituisce dallo stato di sospensione tutte le richieste web asincrone fatte non attivano il Callback OnUpdateReady.iOS 8 Bug - OnUpdateReady non ha mai chiamato di nuovo quando il dispositivo ritorna dallo stato di riposo

Il problema è abbastanza semplice da riprodurre: è sufficiente inserire i due file di codice in basso su qualsiasi server Web e provarlo.

Qualcun altro si è imbattuto in questo problema? Se è così c'è qualche soluzione?

Sto postando questo per cercare di attirare l'attenzione su questo bug in iOS 8 che ha praticamente rovinato tutte le mie applicazioni web - abbiamo dovuto raccomandare di NON aggiornare oltre iOS 7. E sì, ho postato il problema su Apple Bug Reporter ma penso che nessuno li stia guardando da molto tempo.

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

Solo per far sapere a chiunque sia interessato - Apple ha chiuso il mio bug report con il seguente commento; "L'ingegneria ha stabilito che la segnalazione del bug è un duplicato di un altro problema (n. 18042389) e verrà chiusa." Tuttavia, il sistema di segnalazione dei bug di Apple non mi consente di visualizzare l'altro problema, quindi non ho idea se sia davvero lo stesso o se stiano semplicemente scaricando questo problema. Inoltre, a partire da iOS 8.0.2 il problema persiste ancora. – jrg

+0

Apple ha fatto lo stesso con il mio bug, non ha idea di quale sia lo stato di 18042389. ripubblicherà lo stesso bug fino a quando non mi bannano, poi tweet su di essere bannato - al momento non riesco a pensare ad altro modo –

+0

Un altro aggiornamento iOS8 (8.1) e ancora nessuna gioia - il problema continua. Ho segnalato di nuovo il bug questa volta specifico per iOS 8.1 e incluso un collegamento a questo articolo. Speriamo che Apple prenda nota presto. – jrg

risposta

0

Sto anche vedendo lo stesso problema, anche se il mio esempio è molto più semplice. È sufficiente disporre di un'applicazione webclip con

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

sulla pagina. Ispezionando la console, dopo che il sonno si è svegliato, nessun'altra uscita della console. Funziona perfettamente su iOS7 (la mia applicazione reale è una cosa angularJS complicata, ho semplicemente ridotto il problema a questo). Hai avuto qualche risposta sul tuo bug report?

+0

Nessuna risposta ancora su Apple's Bug Reporter - è stata la mia prima volta che ho segnalato un bug, quindi ho il sospetto che sia andato in fondo alla lista - è per questo che ho postato per impilare l'overflow sperando di sensibilizzare e attirare l'attenzione di Apple. – jrg

0

La nostra soluzione (per 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(); 

    } 
  • aggiungere un UUID per ogni richiesta
if(this.isIOS8()) { 
    ajaxInfo.url += '&recoveryUUID='+ajaxInfo.uuid; 
} 
  • Poi ancora eseguire l'XHR Send (che in realtà funziona benissimo, server riceve e rispedisce fine).
  • Server Side salvare il 'risultato' nel database/file con l'UUID come indice/parte del nome del file
//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); 
} 
  • Sul client creare un oggetto globale piccolo aiutante che ascolta le inietta codice e li reindirizza al gestore 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(); 
  • Lo script che viene caricato immediatamente esegue (spinge, in quanto setInterval è morto pure).
// 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; 
} 
?> 
  • Ecco un semplice implementazione risultati Filebased.
<?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; 
    } 
} 
?> 

Come non usiamo JQuery tutti abbiamo bisogno di fare era aggiungere la logica in più nella nostra classe Ajax, e, naturalmente, il risparmio di database per tutte le richieste ..

Svantaggi:

  • Nasty
  • continuerà a aggiungere occupazione di memoria per ogni chiamata (per noi non è un problema in quanto la memoria viene cancellata tra window.location.href chiamate (non usiamo SPA) così alla fine si cadere.
  • Logica server extra.

Upsides:

  • lavori fino a quando la memoria si esaurisce (tag rimozione di script, che facciamo non rimuove la memoria associata)

Commenti:

  • Si potrebbe ovviamente basta inviare tutto con la 'chiamata', ma volevamo avere un impatto sul lato server minimo (o non molto lavoro per noi comunque) + presumiamo questo sarà corretto e significa che il nostro codice "utente" ha 0 impatto.
0

Strano, Apple ha appena chiuso il mio bug e si è riferito allo stesso numero di bug. Anche una web app, ma ho trovato le transizioni CSS3 per smettere di funzionare dopo il blocco dello schermo vedere di seguito:

Engineering ha determinato che il bug report (18.556.061) è un duplicato di un altro problema (18.042.389) e sarà chiuso

mio report:

Se si aggiunge un'applicazione HTML alla schermata iniziale e aperta, tutte le transizioni CSS3 funzionano correttamente. Senza chiudere l'app e premendo il blocco schermo, le transizioni sembrano fermarsi e può causare il blocco dell'interfaccia utente. Ad esempio, se viene attivata una sovrapposizione assoluta (opacità: da 0 a opacità: 1) rimane invisibile rendendo l'app non funzionante

+0

Guardando il tuo codice, quindi rivedendo il mio per cercare un problema comune, penso che questo potrebbe avere qualcosa a che fare con intervalli o timeout che si arrestano completamente dopo aver guardato lo schermo o aver dormito. Ciò influisce anche su eventuali transizioni o animazioni di css fino a quando la dom non viene ridisegnata o ricaricata. –

+0

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

0

Le richieste Ajax, le funzioni timer e WebkitAnimation vengono interrotte dopo una schermata di blocco su iOS8.

Per le funzioni Ajax e Timer, stiamo utilizzando questa soluzione nel nostro sistema: How to resume JavaScript timer on iOS8 web app after screen unlock? (collegamento a gitHub nel commento).

Non è esattamente la parte della domanda, ma mi piacerebbe condividere la nostra soluzione con le animazioni e gli eventi CSS3, poiché potrebbe aiutare qualcuno in futuro.

Per la webkitAnimation, abbiamo scoperto che ridisegnare l'elemento con l'animazione sul, o più drasticamente il body sarebbe riavviare le animazioni e gli eventi applicati a loro (webkitAnimationEnd per esempio, che viene utilizzato pesantemente da jquery-mobile).

così il nostro codice dà qualcosa di simile:

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

si può o non può avere bisogno della funzione setTimeout sulla seconda istruzione. La cosa più interessante è che, una volta ridisegnato, non verrà mai congelato di nuovo, non importa quante schermate di blocco si presentano dopo ...

0

L'ambiente webapp è così orribilmente rotto quando si riprende dopo il blocco dello schermo vedere come (a) Apple potrebbe ignorare questo indefinitamente e (b) come ogni webapp può lavorare con sicurezza attorno all'ambiente menomato.

La mia soluzione è di rilevare il ripristino dopo il sonno utilizzando setInterval (che smette di funzionare dopo il ripristino) e quindi di inviare un avviso() all'utente che dichiara che l'app deve essere riavviata dalla schermata iniziale perché iOS non può riprendere esso.

Accade che anche l'avviso() venga interrotto dopo il ripristino: visualizza l'avviso e quindi la webapp termina sulla schermata iniziale quando l'utente fa clic su OK! Quindi questo costringe l'utente a rilanciare.

L'unico problema quando l'utente si riavvia è la gestione dello stile di apple-mobile-web-app-status-bar. Ho impostato su nero-traslucido che normalmente imposta il contenuto della barra di stato su nero (su sfondi chiari) o bianco (su sfondi scuri). Al primo riavvio dopo la ripresa, il contenuto della barra di stato è sempre nero. Nei successivi rilanci (non interrotti da sospensione/ripresa) il comportamento ritorna normale.

Che pasticcio. Se fossi responsabile di questo a Apple sarei imbarazzato, e qui siamo con 8.1 e non è stato ancora risolto.

1

L'installazione di iOS 8.1.1 risolve questo problema.

+0

Ciò non fornisce una risposta alla domanda.Per criticare o richiedere chiarimenti da un autore, lascia un commento sotto il loro post - puoi sempre commentare i tuoi post, e una volta che hai [reputazione] sufficiente (http://stackoverflow.com/help/whats-reputation) essere in grado di [commentare qualsiasi post] (http://stackoverflow.com/help/privileges/comment). – rkhb

+0

Come ci ha detto l'autore, ha pubblicato la sua domanda per attirare l'attenzione su questo bug di iOS. E il mio post in breve fornisce informazioni che Apple ha corretto questo bug nella nuova versione di iOS. Ho avuto gli stessi problemi e sono stato molto contento di sapere che questo errore è avvenuto nella 8.1.1 –

Problemi correlati