admin管理员组

文章数量:1397031

Since more than 2 years, an app developed with Codename One works pretty fine on about 500 devices (70% Android, 30% iOS). Suddenly - probably since iOS 18.0 or iOS 18.1 - users reported troubles with the iOS version of this app.

In simple words:

The NetworkManager suddenly doesn't process requests reliable. Often, they are not processed. Neither readResponse(InputStream) nor the response code listener and the exception listener is called. Nothing happens. In the simulator and on Android, everything is fine with that code. The problem suddenly occurred when iOS 18 or iOS 18.1 was deployed by Apple.

In detail:

Because of iOS certicicates that are valid for 1 year only, we have to re-build the iOS app once a year in March. The newly built app depending on Codename One 7.0.175 is running well in the simulator and on Android devices, but not on Apple iOS 18. The newly built app has the same effect as users reported with the previous release.

The app I'm talking about, has a background thread, waiting for a given time. The app is in a Thread.sleep(10000) loop comparing the scheduled time against System.currentTimeMillis() once every 10 seconds. On the scheduled time, downloading of 10 files starts.

Therefore a class that extends ConnectionRequest is configured and NetworkManager.getInstance().addToQueue(request) gets called. Typically, the overridden readResponse(InputStream) will be called and the app stores the response payload as file in the storage. Afterwards, the overridden postResponse() gets called and starts the download of the next file. This sequence loops until the 10th file has been received.

On iOS, the NetworkManager is doing nothing. readResponse(InputStream) doesn't get invoked, and also, both callbacks that have been set by request.addResponseCodeListener() and request.addExceptionListener() don't write a log message. That means, they are not invoked as well. In addition, the app has a global network error callback set by NetworkManager.getInstance().addErrorListener() in the init(Object context) method (Lifecycle) of the app. But this callback is also not invoked. After a 60 second timeout, the download scheduler thread recognices that the download has not finished yet, calls request.kill() and schedules a retry in 60 seconds. Then the download starts again, but with the same bahaviour.

This is the stack trace:

[EDT] 9:20:54,562 - Initialized download of [mon.json]
[Network Thread] 9:20:54,927 - The network connection was lost.
[Network Thread] 9:20:54,929 - Exception: java.io.IOException - The network connection was lost.
java.io.IOException
    at com_codename1_impl_ios_IOSImplementation_NetworkConnection.ensureConnection:7254
    at com_codename1_impl_ios_IOSImplementation.getResponseCode:7591
    at com_codename1_io_ConnectionRequest.performOperationComplete:905
    at com_codename1_io_NetworkManager_NetworkThread.runCurrentRequest:314
    at com_codename1_io_NetworkManager_NetworkThread.run:390
    at com_codename1_impl_CodenameOneThread.run:176
    at java_lang_Thread.runImpl:153

Message "Initialized download of [mon.json]" is logged immediately after addToQueue(request) returns.

Sometimes (approx. every 10th time), the first file will be received and readResponse(InputStream) plus postResponse() get invoked. But afterwards the process ends on the 2nd file. The app never gets all 10 files.

On the server side, positive download requests can be seen in the log file. But when the download process doesn't work, no request comes to the server.

/**
 * Start to download one file from the onCALL Duty frontend.<br/>
 * @param fileName name of file to download, must not be {@code null}
 * @return {@code true} if the download has been initialized, {@code false} if an error occurred
 */
private static boolean downloadOneFile(final String fileName) {
    try {
        // set up the connection request
        final ConnectionRequest request = new DownloadService();
        request.setUrl(Settings.getServerDownloadURL() + fileName);
        request.setPost(false); // GET request
        request.setContentType("application/json; charset=utf-8");
        request.addRequestHeader("Accept", "application/json");
        request.addRequestHeader("User-Agent", request.getUserAgent());
        if (AccountService.isLoggedIn()) {
            request.addRequestHeader("Corporate-Account", Settings.getCorporateAccount());
        }
        request.setCookiesEnabled(true);
        request.setDestinationStorage(getTemporaryFileName(fileName));
        request.setTimeout(10000);
        request.setReadTimeout(30000);
        request.setFailSilently(true);
        request.setReadResponseForErrors(false);

        // response code listener for this request
        request.addResponseCodeListener(netEvent -> {
            netEvent.consume();
            if (netEvent.getResponseCode() <= 0 || netEvent.getResponseCode() >= 400) {
                Logger.p("Download of [" + fileName + "] failed with HTTP response code " + netEvent.getResponseCode(), Log.ERROR);
                logCookies();
                oneFileFailed(fileName);
            } else {
                Logger.p("Return code for download of [" + fileName + "] is " + netEvent.getResponseCode(), Log.INFO);
            }
        });

        // exception listener for this request
        request.addExceptionListener(netEvent -> {
            handleNetworkException(netEvent, fileName);
        });

        // file download will be processed by the network manager
        // when download is finished, the callback function readResponse(InputStream) will be invoked
        NetworkManager.getInstance().addToQueue(request);
        lastRequest = request;
        Logger.p("Initialized download of [" + fileName + "]", Log.DEBUG);
        return true;

    } catch (final Exception e) {
        Logger.p("Failed to initialze download of [" + fileName + "]", Log.ERROR);
        return false;
    }
}


@Override
protected void readResponse(final InputStream inputStream) {
    // connection request not pending any more
    lastRequest = null;

    // get file name from URL
    final String fileName = getFilenameFromUrl();
    if (fileName == null) {
        return;
    }

    // read from download file input stream into a string -> read from string -> write into storage file
    InputStream is = null;
    OutputStream os = null;
    try {
        final String content = Util.readToString(inputStream, "UTF-8"); // download files are encoded in UTF-8
        is = new ByteArrayInputStream(content.getBytes());
        os = Storage.getInstance().createOutputStream(getDestinationStorage());
        Util.copy(is, os);
        os.flush();
        lastFileSize = content.length();
        return;
    } catch (final Exception e) { // typically an IOException
        Logger.p("Exception in readResponse()", Log.ERROR);
        lastFileSize = -1;
    }

    // remove file in storage if download was killed while downloading
    if (isKilled()) {
        Storage.getInstance().deleteStorageFile(getDestinationStorage());
    }
}


@Override
protected void postResponse() {
    String fileName = getFilenameFromUrl();

    if (lastFileSize <= 0) { // file not downloaded?
        Logger.p("Download of [" + fileName + "] failed", Log.ERROR);
        logCookies();
        oneFileFailed(fileName);
    } else {
        Logger.p("Download of [" + fileName + "] finished, file size " + lastFileSize + " bytes", Log.INFO);
        logCookies();
        oneFileDone(fileName, lastFileSize);
    }
}


/**
 * Download of one file has finished successfully.<br/>
 * Will start to download the next file if this file wasn't the last one.
 * @param fileName name of file that has been downloaded
 * @see #oneFileFailed(String)
 */
private static void oneFileDone(final String fileName, final int sizeInBytes) {
    try {
        // download next file if it was't the last one
        final String nextFileName = getNextFileName(fileName);
        if (nextFileName != null) {
            downloadOneFile(nextFileName]);
            return;
        }

        // all files downloaded
        deleteDataFiles();
        activateNewFiles();

    } catch (final Exception e) {
        Logger.p("Exception in oneFileDone()", Log.ERROR);
    }

    inProgress = false; // clear progress flag
}

Who can help? Any idea what's wrong since iOS 18 or iOS 18.1?

本文标签: codenameoneCodename One Internet Access doing nothing on iPhone with iOS 18Stack Overflow