3

I'm new to mobile app dev, but I'm trying to build a mobile RPG game character sheet app. Instead of using SQLite, Im trying to use PersistenceManager to persist the character data when the app is interrupted or shutdown, but Im not sure if Im using it properly. Heres what I have so far.

Main.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
                        xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.CharactersView" 
                        applicationDPI="160" persistNavigatorState="true">

</s:ViewNavigatorApplication>

CharactersView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" creationComplete="creationCompleteHandler(event)">

<fx:Script>
    <![CDATA[
        import mx.collections.ArrayCollection;
        import mx.events.FlexEvent;

        protected function creationCompleteHandler(event:FlexEvent):void
        {
            var o:Object = new Object();
            o.name = "Aragorn";
            o.type = "Ranger";
            o.strength = 97;
            o.speed = 7;
            Database.insertCharacter(o);
            trace(Database.characters.length);
        }

    ]]>
</fx:Script>

<s:List id="list" width="100%" height="100%"/>

Database.as:

import spark.managers.PersistenceManager;

public class Database
{
    private static var data:Array = [];
    private static var pm:PersistenceManager = new PersistenceManager();

    public static function get characters():Array
    {
        if (!pm.load() || pm.getProperty("characters") == null)
        {
            Database.data = [];
        }
        else
        {
            Database.data = pm.getProperty("characters") as Array;
        }
        return data;
    }

    public static function insertCharacter(o:Object):void
    {
        Database.data.push(o);
        pm.setProperty("characters", Database.data);
        pm.save();
    }

}

In the creationComplete handler of CharactersView.mxml, I'm creating a one-off character with some arbitrary info just to test if inserting characters works. Every time I run this, completely as-is, the trace statement (length of my array) returns 1. Shouldn't it go up by one every time I run it? This is telling me something isn't right, but is it the persistencemanager not saving the data properly? Do I need to check if pm.save() returns true, and if it does, should I dispatch an event and only THEN run the trace statement to get the proper length? That seems overly complicated just to save some data, so I'm not entirely sure whats going on or how to fix it.

In any case, what I'm asking is if my current setup is correct, and if its not, does anyone have a simple example, or could create a simple example based on my code for how to properly use persistencemanager?

Once I get this technique down I can create several other apps as well, so Im really hoping to get this nailed down. Thanks in advance!

EDIT: It seems I'm using everything correctly regarding the PM and the answer I received did help me get my app working. However, when moving forward my app started acting funny. For instance, my list seems to react really slowly to interaction, as do most of the other components. New code below.

Main.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
                        xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.CharactersView" 
                        applicationDPI="160" persistNavigatorState="true">

</s:ViewNavigatorApplication>

CharactersView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" creationComplete="creationCompleteHandler(event)">

<fx:Script>
    <![CDATA[
        import mx.collections.ArrayCollection;
        import mx.events.FlexEvent;

        protected function creationCompleteHandler(event:FlexEvent):void
        {
            update();
        }

        private function update():void
        {
            title = "D&D Characters (" + Database.characters.length + ")";
            list.dataProvider = new ArrayCollection(Database.characters);
        }

        protected function button1_clickHandler(event:MouseEvent):void
        {
            var vo:CharacterVO = new CharacterVO();
            vo.name = "Aragorn";
            vo.className = "Ranger";
            vo.race = "Human";
            vo.level = 6;
            vo.gender = "Male";
            vo.alignment = "Unaligned";
            vo.hp = 25;
            vo.xp = 100;
            Database.insertCharacter(vo);
            update();
        }

    ]]>
</fx:Script>

<s:actionContent>
    <s:Button label="+" click="button1_clickHandler(event)"/>
</s:actionContent>

<s:List id="list" width="100%" height="100%"/>

</s:View>

Database.as:

import spark.managers.PersistenceManager;

public class Database
{
    private static var data:Array = [];
    private static var pm:PersistenceManager = new PersistenceManager();

    public static function get characters():Array
    {
        return data;
    }

    public static function insertCharacter(o:CharacterVO):void
    {
        Database.data.push(o);
        pm.setProperty("characters", Database.data);
        pm.save();

        if (!pm.load() || pm.getProperty("characters") == null)
        {
            Database.data = [];
        }
        else
        {
            Database.data = pm.getProperty("characters") as Array;
        }
    }

}

CharacterVO.as:

import flash.utils.IDataInput;
import flash.utils.IDataOutput;
import flash.utils.IExternalizable;

public class CharacterVO implements IExternalizable
{
    public var name:String;
    public var className:String;
    public var race:String;
    public var gender:String;
    public var alignment:String;
    public var level:Number;
    public var hp:Number;
    public var xp:Number;


    public function writeExternal(output:IDataOutput):void {
        output.writeUTF(name);
        output.writeUTF(className);
        output.writeUTF(race);
        output.writeUTF(gender);
        output.writeUTF(alignment);
        output.writeFloat(level);
        output.writeFloat(hp);
        output.writeFloat(xp);
    }

    public function readExternal(input:IDataInput):void {
        name = input.readUTF();
        className = input.readUTF();
        race = input.readUTF();
        gender = input.readUTF();
        alignment = input.readUTF();
        level = input.readFloat();
        hp = input.readFloat();
        xp = input.readFloat();
    }
}

As you can see most of the code is the same as what I first posted. I added the CharacterVO class, I moved a few things around in CharactersView.mxml, and I moved the PersistenceManager loading code to the insertCharacter function (this way, I'm only loading the PM whenever a new item is added, not everytime I call Database.characters).

Again, my app seems to react real slow to interaction, sometimes it doesnt react at all. Something just isnt right, Ive never had this issue with a mobile app before. Could it be that storing the array in the PM and then attempting to set it as the List's dataProvider is causing an issue?

blkhwks19
  • 503
  • 5
  • 13

1 Answers1

0

I'm not too familiar with using this however from the docs it looks like you probably can't use a generic Object since I don't believe it implements IExternalizable, according to the docs this doesn't cause an error for some reason but it can't actually read/write those objects.

http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/spark/managers/PersistenceManager.html

When storing values using the manager, it is important that all values can be properly be written to a shared object. Complex objects that store classes or non-standard flash primitives must implement flash.net.IExternalizable interface to work properly. Saving incompatible objects does not cause an RTE, but creates undefined behavior when the data is read back.

When using this myself, one time helping another SO poster on a project I don't recall really having any issues except if we changed properties of the model object we were storing/retrieving then we had to manually blow away the local shared object it was creating otherwise it couldn't de-serialize it (makes sense but just something to be aware of). I don't actually recall implementing IExternalizable but it's possible the other guy did it.

Try this:

[CharacterVO.as]

public class CharacterVO implements IExternalizable
{
    public var name:String;
    public var type:String;
    public var strength:Number;
    public var speed:Number;


    public function writeExternal(output:IDataOutput) {
        output.writeUTF(name);
        output.writeUTF(type);
        output.writeFloat(strength);
        output.writeFloat(speed);
    }

    public function readExternal(input:IDataInput) {
        name = input.readUTF();
        type = input.readUTF();
        strength = input.readFloat();
        speed = input.readFloat();
    }
}

Then instead of creating a Generic object create instances of that VO. I believe Array will serialize automatically by serializing all it's elements in order.

shaunhusain
  • 19,630
  • 4
  • 38
  • 51
  • So if storing complex objects/arrays is not allowed, then it sounds like I need to rethink my whole data storage strategy. Looks like I may be migrating it to use SQLite... – blkhwks19 Jul 31 '12 at 19:57
  • I don't think that's the case you just need to implement IExternailzable is how it reads to me, which is just a matter of writing out your objects to a stream and reading them back in the same order, the methods for IExternalizable get an IDataInput and IDataOutput passed to them that you can writeObject or readObject (or other methods for primitive types). http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/IExternalizable.html I think you just may not be able to use a generic object so you have to make a Class for that "value object" type. – shaunhusain Jul 31 '12 at 21:03
  • This seems to work great. 2 things I noticed, for anyone else that may be reading: 1) The IExternalizable package is flash.utils.IExternalizable, not flash.net like previously mentioned. 2) Both the writeExternal and readExternal functions need return values (:void). Other than that it seems to work great, the main array holding all these objects seems to serialize automatically. This allows me to easily create multiple VO objects and shove them into an array and save it, and then read it back later and still be able to access everything I need to. Thanks for the help! – blkhwks19 Aug 01 '12 at 14:52
  • Your solution seemed to work for simply tracing out the results of my saved data. When I tried to set the data as the List's dataProvider, I seem to get weird behavior from the entire app. More details in my edited question above. – blkhwks19 Aug 02 '12 at 17:01
  • @blkhwks19 I don't see anything in the code you posted that would seem to slow things up. Some things to try, make a function that creates some "mock" data objects puts them in a collection and put them in the List instead of using the DB object based one, see if there's a performance change. If not go break point crazy and see if any methods are getting called repeatedly also can try using the profiler to get an idea of what's going on. – shaunhusain Aug 02 '12 at 17:59
  • Thanks for taking a look for me, appreciate the responses! – blkhwks19 Aug 02 '12 at 19:24