1

我正在使用cordova 2.2.0 在eclipse 上开发一个Android 应用程序。似乎获得了 Phonegap 的文件 API,但无法读取或写入文件。

我已经从 xcode 复制了脚本,在那里我完成了适用于 iOS 的应用程序,并且它可以工作。

这是我的脚本,使用控制台输出进行跟踪:

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));
}

运行此脚本会提供以下控制台输出:

  • 1:加载
  • 2:获取设置()
  • 3:fileSys-settings.txt-getContent
  • 4:获取文件系统,获取目录...
  • 5:得到目录。获取文件...
  • 6:得到文件。执行操作:getContent...
  • 7:读为​​文本...

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:

  • 1: onload
  • 2: getSettings()
  • 3: fileSys - settings.txt - replaceContent
  • 4: Got file system, get directory...
  • 5: Got directory. Get file...
  • 6: got file. Perform action: replaceContent...
  • 7: got file writer...

I have:

  • set the correct permissions/plugins in res/config.xml and in the android manifest.xml
  • Verified that the Phonegap API is included and working (with notifications)

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.

4

2 回答 2

2

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.

于 2012-11-18T19:21:07.733 回答
1

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.

于 2013-03-12T15:53:49.187 回答