使用 phonegap/cordova 文件插件的问题第 2 部分 - 同步性

Posted

技术标签:

【中文标题】使用 phonegap/cordova 文件插件的问题第 2 部分 - 同步性【英文标题】:Problems using phonegap / cordova file plugin part 2 - synchronicity 【发布时间】:2014-09-04 15:03:38 【问题描述】:

我想为我的 cordova 应用添加一些简单的日志记录功能。

于是我添加了文件插件,实现了一个超级简单的日志方法并进行了测试。

我的配置:

    $ cordova --version
    3.5.0-0.2.7

    $ cordova plugins
    org.apache.cordova.file 1.3.0 "File"

测试设备是华为u8850,运行android 2.3.5

记录者:

window.MyLog = 
    log: function(line)
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(FS) 
            FS.root.getFile('the_log3.txt', "create":true, "exclusive":false,
                function(fileEntry) 
                    fileEntry.createWriter(
                        function(writer) 
                            console.log(line);
                            writer.seek(writer.length);     // append to eof
                            writer.write(line + '\n');      // write the line
                        , fail);
                , fail);
        , fail);
    
;

测试:

    MyLog.log('A ' + new Date().toLocaleTimeString());
    MyLog.log('B ' + new Date().toLocaleTimeString());
    MyLog.log('C ' + new Date().toLocaleTimeString());

我启动了应用程序 3 次,意料之中。喜欢

A 16:03:47
B 16:03:47
C 16:03:47
A 16:04:34
B 16:04:34
C 16:04:34
A 16:04:41
B 16:04:41
C 16:04:41

但是得到了这个

C 16:03:47
C 16:04:34
A 16:04:41

有两个问题:

    并非所有行都在日志中 未定义恰好写入哪一行

我认为问题的原因是文件方法的异步性质。但这只是猜测。

问题是:

    如何实现将行追加到文本文件的方法,以确保不丢失任何行并且行的顺序与对该方法的调用相同? 我必须使用文件方法的同步版本吗? 如果是:是否有可用的文档/示例代码?

【问题讨论】:

【参考方案1】:

可能您在之前的写入完成之前尝试写入,并触发了onwriteend 事件。

正如我在回答您的第一个问题时所发布的,实现caching 函数是一个好主意。

因此,您的所有日志都将存储在临时cache 中。每次你向这个cache 添加一些东西时,你都会检查它的大小,一旦达到你定义的限制,就调用logDumping 方法,这将是实际的write 到日志文件。

当您调用dump 方法时,您可以将日志内容传递给您的编写器并清空您的缓存,这样您就可以在其中存储新内容并且它不会与已经记录的内容重叠内容。

我会做的是:

    cache日志 检查cache 是否达到大小限制 将cache 的内容传递给tmp_var 并清除cachetmp_var 写入日志文件 检查onwriteend > 如果成功清除tmp_var,如果出现错误,您可以将tmp_var 写回您的实际cache(因此不会丢失任何数据),然后尝试写入@ 的内容987654338@ 再次添加到您的日志文件中。

【讨论】:

【参考方案2】:
#include "stdio.h"

void appendLine(char* path_to_file, char* line) 
    FILE* fp = fopen(path_to_file, "a");
    if (fp) 
        fprintf(fp, "%s\n", line);
        fclose(fp);
    


int main()
    appendLine("logfile.txt", "One Line");
    appendLine("logfile.txt", "Another Line");
    return 0;

这是用一种叫做“C”的古老语言编写的。

只需 4 行代码:

    fopen : 打开一个现有文件或创建一个新文件,文件位置在末尾(“a”表示追加) if (fp) : 检查文件对象是否被创建 fprintf:去做吧! fclose : 关闭它(并刷新内容)

可以用这个命令编译:

gcc -Wall addline.c -o addline

就这样开始了:

./addline

输出将是这样的:

One Line
Another Line

不幸的是,在使用 phonegap / cordova 编写基于 JS 的应用程序时,这是无法做到的。

由于未知原因,用于基本文件操作的简单同步接口不是 phonegaps 核心实现的一部分,也不包含在“文件”插件中。

所以我花了一些时间来实现一个简单的 TextFile 包装器来完成这项繁琐的工作,因此客户端应用程序可以使用如下简单的命令:

// a single object is the wrapper:
var textFile;

function init() 
    // ...

    // initialize the (log) file
    // the file will be created if doesn't exists
    // when sth. goes wrong, the callback has an error message
    textFile = new TextFile('logfile.txt', function(ok, msg)
        if (!ok) 
            alert('logging not available' + (msg ? '\nError: ' + msg : ''));
            textFile = null;
         else 
            textFile.writeLine('start logging ...');
            start();
        
    ,
    
        // optional options, currently just one property 'clear' is supported
        // if set to true, the file will be emptied at start
        clear: true
    
);


// later, use methods

// append some lines
textFile.writeLine('a line');
textFile.writeLine('another line');

// get content, callback needed since it is not synchronous
textFile.getContent(function(text)
    // show text
);

// empty file
textFile.clear();

// remove file
textFile.remove();

我使用 jquery 来合并选项 - 如果 jquery 不合适,只需替换 $.extend 方法。

代码如下:

/**
 * Created by martin on 9/3/14.
 *
 * requires the file plugin:
 *
 * cordova plugin add org.apache.cordova.file
 *
 * implemented and tested with
 *   cordova-3.5.0-0.2.7
 * on
 *   Android 2.3.5
 */



(function()
    'use strict';

    // these values are NOT part of FileWriter, so we had to define them:
    const INIT = 0;
    const WRITING = 1;
    const DONE = 2;

    function errMessage(code)
        var msg = '';

        switch (code) 
            case FileError.QUOTA_EXCEEDED_ERR:
                msg = 'QUOTA_EXCEEDED_ERR';
                break;
            case FileError.NOT_FOUND_ERR:
                msg = 'NOT_FOUND_ERR';
                break;
            case FileError.SECURITY_ERR:
                msg = 'SECURITY_ERR';
                break;
            case FileError.INVALID_MODIFICATION_ERR:
                msg = 'INVALID_MODIFICATION_ERR';
                break;
            case FileError.INVALID_STATE_ERR:
                msg = 'INVALID_STATE_ERR';
                break;
            default:
                msg = 'Unknown Error';
                break;
        ;

        return msg;
    

    /**
     *
     * @param fileName  : the name of the file for which the TextFile is created
     * @param cb        : function, is called when
     *                    - TextFile is created, parameter: boolean true
     *                    - TextFile is not created, parameters: boolean false, string error_message
     * @param options   : object with single property
     *                    - boolean clear - truncates the file, defaults to false
     * @constructor
     */
    window.TextFile = function(fileName, cb, options)
        this.writer = null;
        this.queue = [];

        this.fileEntry = null;

        this._initialize(fileName, cb, options);
    

    var files = ;

    TextFile.prototype = 
        // pseudo private method, called form constructor
        _initialize: function(fileName, cb, options)

            this.options = $.extend(startMsg: null, clear: false, options)

            if (files.fileName) 
                cb(false, 'TextFile[' + fileName + '] already in use');
            
            files.fileName = true;
            var that = this;
            window.requestFileSystem(
                LocalFileSystem.PERSISTENT,
                0,
                function(FS) 
                    FS.root.getFile(
                        fileName,
                        
                            'create': true,
                            'exclusive': false
                        ,
                        function(fileEntry) 
                            that.fileEntry = fileEntry;
                            fileEntry.createWriter(
                                function(writer) 
                                    that.writer = writer;
                                    writer.seek(writer.length);
                                    writer.onwriteend = function()
                                        if (that.queue.length > 0)
                                            // sth in the queue
                                            var item = that.queue[0];
                                            switch (item.type) 
                                                case 'w':
                                                    writer.write(item.line + '\n');
                                                    break;
                                                case 't':
                                                    writer.truncate(0);
                                                    break;
                                                case 'g':
                                                    that._readContent(item.cb);
                                                    break;

                                                default:
                                                    throw 'unknown type ' + item.type;
                                            

                                            // get rid of processed item
                                            that.queue.splice(0, 1);
                                        
                                    
                                    if (that.options.clear) 
                                        that.clear();
                                    
                                    cb(true);
                                
                            );
                        ,
                        function(err)
                            cb(false, errMessage(err.code))
                        
                    );
                ,
                function()
                    cb(false, errMessage(err.code));
                
            );
        ,

        // internal use
        _readContent: function(cb)
            this.fileEntry.file(function(file) 
                    var reader = new FileReader();

                    reader.onloadend = function(e) 
                        var res = this.result;
                        cb(res);
                    ;

                    reader.readAsText(file);
                ,
                function(err) 
                    cb('got an error: ' + errMessage(err.code));
                
            );
        ,

        // reads whole file, content is sent to client with callback
        getContent: function(cb)
            if (this.writer.readyState !== WRITING) 
                this._readContent(cb);
             else 
                this.queue.push(type: 'g', cb:cb);
            
        ,

        // set file size to zero
        clear: function() 
            if (this.writer.readyState !== WRITING) 
                this.writer.truncate(0);
             else 
                this.queue.push(type: 't');
            
        ,

        // removes file
        remove: function(cb)
            this.fileEntry.remove(
                function()
                    if (cb) 
                        cb(true);
                    
                ,
                function(err)
                    if (cb) 
                        cb(false,errMessage(err.code));
                    
                
            );
        ,

        // append single line to file
        writeLine: function(line)
            if (this.writer.readyState !== WRITING) 
                this.writer.write(line + '\n');
             else 
                this.queue.push(type: 'w', line: line);
            
        
    ;
)();

也许这对其他人有用,正在努力解决同样的问题......

【讨论】:

以上是关于使用 phonegap/cordova 文件插件的问题第 2 部分 - 同步性的主要内容,如果未能解决你的问题,请参考以下文章

Cordova/Phonegap OpenEars 语音识别

PhoneGap/Cordova 2.2.0 Facebook 插件在 IOS FB.init 上失败

无法在 Phonegap / Cordova 3.5.0-0.2.4 上运行联系人插件

桌面/移动标准网页中的phonegap / cordova社交共享插件

如何使用cordova命令行创建好一个工程

使用 Phonegap/Cordova 相机插件从相机或图库中选择照片