Pjottur
Pjottur

Reputation: 642

phonegap/cordova filewriter and reader not working

I'm developing an Android app on eclipse with cordova 2.2.0. It seems that gets Phonegap's file API, but can't read or write on the files.

I've copied the script from xcode, where I'm finished with the app for iOS, and it works.

Here's my script, traced with console outputs:

window.onload = function (){
    console.log('1: onload');
    document.addEventListener("deviceready", getSettings, false);
}
function getSettings(){
    console.log('2: getSettings()');
    fileSys('settings.txt', 'getContent', null);
    //fileSys('settings.txt', 'replaceContent', 'new settings');
}
function fileSys(fileName, action, data){
    console.log('3: fileSys - '+fileName+' - '+action);
    var directory = (fileName == 'sidur') ? 'appin/sidur':'appin';
    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
    function gotFS(fileSystem) {
    console.log('4: Got file system, get directory...');
    fileSystem.root.getDirectory(directory, {create: true}, gotDir, fail);
    }
    function gotDir(dirEntry) {
        console.log('5: Got directory. Get file...');
        dirEntry.getFile(fileName, {create: true, exclusive: false}, gotFileEntry, fail);
    }
    function gotFileEntry(fileEntry){
        console.log('6: got file. Perform action: '+action+'...');
        if(action == 'getContent') readAsText(fileEntry);
        if(action == 'replaceContent') fileEntry.createWriter(gotFileWriter, fail);
    }
    function gotFileWriter(writer){
        console.log('7: got file writer...');
        writer.write(data); //function variable of fileSys();
        writer.onwriteend = function(evt) {
        console.log('8: file written');
        };
    }
    function readAsText(file) {
        console.log('7: read as text...');
        var reader = new FileReader();
        reader.readAsText(file);
        reader.onloadend = function(evt) {
            console.log('9: done reading file');
            init(evt.target.result);
        }
    }
    function fail(error){
        console.log('fail: '+error.code);

    }
}
function init(settings){
    console.log('Init. Settings: '+JSON.stringify(settings));
}

Running this script gives the following console output:

And there it stops. reader.onloadend is never called, and no error is specified. If I run again, but instead call fileSys('settings.txt', 'replaceContent', 'new settings'); and outcomment the other call to fileSys, the console outputs:

I have:

I am new to app development as well as eclipse, so this could very well be some basic thing I've missed. Any suggestions and pointers are most welcome.

Upvotes: 1

Views: 5249

Answers (2)

Horace S
Horace S

Reputation: 26

Actually, it is NOT the order of your statements, because both Android and iOS will interpret them in the same sequential order. What is different is the speed at which the readAsText completes, because its work happens asynchronously in another thread. Here's an example of what happened on iOS:

reader.readAsText - this starts the read process in another thread
reader.onloadend = function... - you set up your handler
-- on separate thread, readAsText finally completes and sees your handler and calls it

this is what happened on Android:

reader.readAsText - this starts the read process in another thread
-- on separate thread, readAsText completed quickly but your handler has not been set yet so it does not get called
reader.onloadend = function... - you set up your handler too late, the read already completed in its own thread

In asynchronous calls, you can't guarantee when other tasks complete. It's not a difference between Android and iOS, it's just the nature of multi-threaded operations. There are all kinds of ways to deal with asynchronous calls (ordering and nesting callbacks properly, using jquery deferred, some type of semaphore mechanism, and many more, I'm sure). The main thing is, never, ever, rely on or assume that tasks will complete in a certain time. Reliance on timing can bite you big time and is VERY difficult to debug.

Upvotes: 1

Pjottur
Pjottur

Reputation: 642

Alright, I figured this one out. The problem was in the structure of my code.

This works fine on ios:

function readAsText(file) {
    var reader = new FileReader();
    reader.readAsText(file);
    reader.onloadend = function(evt) {
        console.log('9: done reading file');
        init(evt.target.result);
    };
}

But somehow, phonegap for Android requires you to declare the variable for the reader's onloadend method above the reader's readAsText method. Like so:

function readAsText(file) {
    var reader = new FileReader();
    reader.onloadend = function(evt) {
        console.log('9: done reading file');
        init(evt.target.result);
    };
    reader.readAsText(file);
}

In retrospect this makes perfect sense to me. It seems strange that iOS has allowed the other way.

Upvotes: 2

Related Questions