SharedObject类实现了客户端机器数据的持久性存储

简介
电影剪辑在运行时,大多数数据都存储在内容中,一旦电影剪辑关闭,那么这些数据也同时从
内存中清除掉,如果想存储数据或者让客户端的 两个电影剪辑共享数据该怎么办呢,要想办法
把数据存储在Flash播放器外面。
ActionScript中,SharedObject类 实现了客户端机器数据的持久性存储。有两种类型的共享对象:
本地和远程,这章集中讨论local shared objects (LSOs).
Local shared objects 很类似于浏览器中cookies,LSOs 的功能也和cookies 很类似,如存储用户
登陆网站的用户名, 这样不必每次登陆都要输入用户名了,不过LSOs 的功能不仅限于此,它
比cookies 要强大的多,他们不必在客户端和服务器端进行传输,且都是以ActionScript 数据类
型形式存储。与此对应的服务端有remote shared objects (RSOs), LSOs 不需要在客户端和服务
端额外的软件即可工作。
17.1.
创建,打开 Local Shared Object
问题
    我想当访问swf过程中存储一些信息
解决办法
使用LSO.
讨 论
如同这章的简介里描述的那样,Flash的LSOs 就如同Web浏览器中的cookies,它们被一些开发
者称为“超级 cookies”,因为LSOs可以存储大量数据,且存储和读取的都是原生的ActionScript
数据类型。
   LSOs 默认的空间大小100 KB,用户可以通过Flash Player's Settings Manager控制LSOs的使用空
间大小,来严 格限制被使用的空间。存储在客户端的LSO文件是一种二进制文件,扩展名为.sol。
同一个域的Flash电影通过 flash.net.SharedObject类读写.sol文件。
当.sol文件创建后,被放置在Flash播放器对应的应用程序数据目录,以 Windows为例,目录为
C:\Documents and Settings\[ username ] \\Application Data\\Macromedia\\Flash
Player\\#SharedObjects\\ [ random character directory name ] , 在Mac OS X 上, 目录为
/Users/[ username ] /Library/Preferences/Macromedia/Flash Player/#SharedObject/ [ random
character directory name ]。随机字符目录命名是为了安全考虑。一些恶意的swf文件可能试图
猜 测shared object(共享对象)的名称或目录,以便从文件系统中读取LSO,进而提升访问权限。
因此必须把路径随机化,这样猜路径几乎 是不太可能了。
LSOs 的静态方法getLocal( ) 用于创建或打开共享对象,它至少需要一个参数指明共享对象名
称:
var example:SharedObject = SharedObject.getLocal( "example" );
getLocal( )方法首先试图定位找到LSO,如果找不到则根据这个名字创建新的,否则则返回
SharedObject 实例。
17.2.
写 入数据到共享对象上
问题
   我想添加数据到LSO上.
解决办法
    给共享对象的data对象添加属性值
讨 论
共享对象(Shared objects)有个内建的属性data,data属性类型为object,因此可以添加任何信息
上去:
// 存储username值给example共享对象
example.data.username = "Darron";
和早期版本的 ActionScript不同,现在不能直接把属性值赋值给共享对象本身了,如果这样做了
编译器会直接报错:
example.variable = "This will cause a compile-time error.";
另外,直接把值赋值给data属性也不正确:
example.data = "This will cause another compile-time error.";
正确的写法应该这样:
example.data.variable = "This is the correct way to store data.";
可以直接存储ActionScript原生数据类 型:
example.data.exampleArray = new Array( "a", "b", "c" );
example.data.exampleDate = new Date();
example.data.exampleObject = { key1: "example", key2: -12, key3: null };
但需要注意的是,不能存储可视化对象(例如 MovieClips,Sprite,Buttons,TextFields)
17.3.
保存本地共享对象
问题
我想保存 LSO到客户机上
解决办法
使用SharedObject.flush( )方法
讨论
以下几种情况会导致Flash自动试 图保存LSO数据到硬盘上:当Flash播放器关闭时,当共享对象
被垃圾回收时,当调用SharedObject.clear( )方法时。但是自动保存功能并不很实用,因为还有
很多原因需要及时保存共享对象数据,因此我们可以显式调用 SharedObject.flush( )方法:
var flushResult:String = example.flush( );
flush( )方法有个可选的参数用于指定最小的硬盘空间,单位为字节,默认为0,指用最小的空间
正好存储本地共享对象。
当flush( )方法触发时,它试图把数据写到客户端上,调用结果有三种:
如果用户拒绝存储或Flash播放器因某种原因导致存储失败,该方法会抛出一个 Error。
如果本地存储空间不够导致数据不能保存,该方法返回SharedObjectFlushStatus.FLUSHED。
如果 用户没有分配足够的空间,该方法返回SharedObjectFlushStatus.PENDING。
三种情况中,当flush( )方法返回SharedObjectFlushStatus.PENDING常量时,用户可以选择授权或
拒绝保存数据,当用户做出选择 后,netStatus事件被激活,需要定义一个事件处理函数,当事
件处理函数被触发时,传递进一个类型为 flash.events.NetStatusEvent的事件,检查info.code 属性
值判断用户是同意 (SharedObject.Flush.Success)还是拒绝(SharedObject.Flush.Failed)
这里有个例子调用 flush( )保存数据,处理可能返回的结果:
var example:SharedObject = SharedObject.getLocal( "example" );
example.data.someData = "a value";
try {
var flushResult:String = example.flush( );
// If the flush operation is pending, add an event handler for
// netStatus to determine if the user grants or denies access.
// Otherwise, just check the result.
if ( flushResult == SharedObjectFlushStatus.PENDING ) {
// Add an event handler for netStatus so we can check if the user
// granted enough disk space to save the shared object. Invoke
// the onStatus method when the netStatus event is raised.
example.addEventListener( NetStatusEvent.NET_STATUS, onStatus );
} else if ( flushResult == SharedObjectFlushStatus.FLUSHED ) {
// Saved successfully. Place any code here that you want to
// execute after the data was successfully saved.
}
} catch ( e:Error ) {
// This means the user has the local storage settings to 'Never.'
// If it is important to save your data, you may want to alert the
// user here. Also, if you want to make it easy for the user to change
// his settings, you can open the local storage tab of the Player
// Settings dialog box with the following code:
// Security.showSettings( SecurityPanel.LOCAL_STORAGE );.
}
// Define the onStatus() function to handle the shared object's
// status event that is raised after the user makes a selection from
// the prompt that occurs when flush( ) returns "pending."
function onStatus( event:NetStatusEvent ):void {
if ( event.info.code == "SharedObject.Flush.Success" ) {
// If the event.info.code property is "SharedObject.Flush.Success",
// it means the user granted access. Place any code here that
// you want to execute when the user grants access.
} else if ( event.info.code == "SharedObject.Flush.Failed" ) {
// If the event.info.code property is "SharedObject.Flush.Failed", it
// means the user denied access. Place any code here that you
// want to execute when the user denies access.
}
// Remove the event listener now since we only needed to listen once
example.removeEventListener( NetStatusEvent.NET_STATUS, onStatus );
};
如果确切知道存储数据的大小,可直接给flush( )传参数:
// Request 500 KB of space for the shared object.
var flashResult:String = example.flush( 500 * 1024 );
17.4.
从共享对象中读取数据
问题
我想读取先前写入到 LSO的数据
解决办法
读取共享对象的data属性中的值
讨论
在客户端读取这些内容很简单,这些持久性数据都保存在共享对 象的data属性里,因此像下面
的语句这样读就可以了:
// Read the value of exampleProperty from the shared object,
// example, and display it in the Output window.
trace( example.data.exampleProperty );
通过读写数据,我们可以判定用户是不是头 一次访问swf文件:
// Create a shared object and store some data in it
var example:SharedObject = SharedObject.getLocal( "example" );
if ( example.data.previouslyViewed ) {
// The user has already viewed the .swf file before, perhaps
// we skip an introductory help screen here.
} else {
// This is the first time the user is viewing the .swf file
// because previouslyViewed has not yet been set to true.
// Set previouslyViewed to true so that the next time this
// code is run we know the user has been here before.
example.data.previouslyViewed = true;
example.flush( );
}

17.5.
删除共享对象中保存的数据
问题
我 想删除共享对象中的某个属性值或者干脆删除整个共享对象
解决办法
使用delete删除共享对象的data属性中的值,或使用clear( )方法清除整个共享对象
讨论
删除共享对象中的数据是很简单的,但是要注意方法,在ActionScript中我们经常看到删除对象
或 数组只要赋值为null或undefined即可,但是对于共享对象这样做却不行:
// 试图删除共享对象example的someVariable 值
// 这语句编译通过,但是并不是我们所期望的
example.data.someVariable = null;
因为共享对象中null和undefined都是有效的值,因此上面的代码并没有删除someVariable属性,
只 不过设置为null而已,正确的方法是使用delete命令,如:
// Remove someVariable from the example shared object.
delete example.data.someVariable;
clear( )方法删除整个共享对象,实际上就是删除硬盘中的.sol文件,看下面的代码:
// Create a shared object and store some data in it
var example:SharedObject = SharedObject.getLocal( "example" );
example.data.someData = "a value";
// Displays: a value
trace( example.data.someData );
// Remove the shared object from the disk
example.clear( );
// Displays: undefined
trace( example.data.someData );
需要注意的地方是清除数据后, 共享对象的引用仍然是有效的,这是还是可以重新添加数据进
行保存。

17.6.
序列化自定义类
问题
我想把 自定义类实例存储到LSO
解决办法
使用flash.net.registerClassAlias( )方法保留类型信息并把类实例添加到共享对象的data属性上。
讨论
LSOs 使用特殊的二进制格式,Action Message Format (AMF),当要在LSO中存储类实例时,实
例会被编码为包含属性的普通的object。这样当重新从共享对象中读取实例时,已经 不是原来的
类实例了,因为已不能根据类型信息解码回来。
flash.net包中的registerClassAlias( )方法就是为解决这个问题的,这个方法的使用是很简单的,
在AS1.0和AS2.0中写法是Object.registerClass( ),但是在AS3.0里已经被删除了,取而代之的是
flash.net.registerClassAlias( )。
registerClassAlias( )方法需要两个参数,第一个参数表示类的别名,可以用任意字符串表示别名,
比如modal包中有个Person类,别名可以是 modal.Person,第二个参数类引用。
registerClassAlias( "somePackage.ExampleClass", ExampleClass );
这个代码的作用是把这个类的信息存进LSO,当读取数 据时,Flash 播放器就知道这个object
到底是什么类。
下面的例子完整实现了类实例的保存,首先创建自定义类:
// Create a Person class in the model package
package model {
public class Person {
private var _firstName:String;
private var _age:int;
public function Person(firstName:String, age:int) {
_firstName = firstName;
_age = age;
}
public function toString( ):String {
return _firstName + " is " + _age + " years old";
}
}

}
接着,编写主 类读取和写入数据
package {
import flash.net.registerClassAlias;
import flash.net.SharedObject;
import model.Person;
public class Example {
public function Example( ) {
// Map "model.Person" to the Person class
registerClassAlias( "model.Person", Person );
// Create a shared object and store a Person instance in it
var example:SharedObject = SharedObject.getLocal( "example" );
// Test to see if the person instance has been saved already
if ( example.data.person == undefined ) {
trace( "first time, saving person instance" );
var person:Person = new Person("Darron", 24);
// Write the class instance to the local shared object
example.data.person = person;
} else {
trace( "person instance already saved, using stored values" );
}

trace( example.data.person.toString( ) );
}
这 里需要注意的是registerClassAlias( )必须在SharedObject.getLocal( )方法之前调用才有效。否则
的 话共享对象会把Person解释为普通的object类型进行存储。
17.7.
Flash
程序之间共享数据
问题
我 想要同一个域中的两个swf文件能访问同一个LSO。
解决办法
当创建或打开LSO时指定本地路径参数。
讨论
默认情况 下,LSOs存储的名称是唯一的,这主要是为了防止名称冲突,例如,在Windows XP,
如果电影剪辑名称为movie.swf,放在http://www.person13.com/ascb路径下,写入时LSO名称为
example,则保存的路径为:
C:\Documents and Settings\[user name]\Application Data\Macromedia\Flash
Player\#SharedObjects\[random directory name]\person13.com\ascb\movie.swf\example.sol
.swf文件的名称都包含 在路径中,这样的话其他的swf即使在同一个域和路径下也不会冲突,因
为各自都有自己相关的LSO,但是有时候,如果想让两个电影剪辑访问同一个 LSO,这时,调用
getLocal( )需传入一个可选参数:本地路径,来打开或创建LSO。
本地路径参数(getLocal( )第二个参数)为绝对或相对路径字符串,指定LSO的存储位置,例如:
var example:SharedObject = SharedObject.getLocal( "example", "/" );
如果movie.swf放在http://www.person13.com/ascb, 根据上面的代码那么LSO就会保存在:
C:\Documents and Settings\[user name]\Application Data\Macromedia\Flash
Player\#SharedObjects\[random directory name]\person13.com\example.sol
这个目录的不同点是缺少电影剪辑的信息,这样创建的LSO可以被同一个域的其他 flash电影所
共享访问:
var example:SharedObject = SharedObject.getLocal( "example", "/" );
正确理解绝对路径和相对路径是很重要的,这里举个例子,比如有两个swf文件:movieOne.swf 和
movieTwo.swf. 两个都放在同一个域(http:// www.person13.com) 但是在不同的路径下。
movieOne.swf 在http:// www.person13.com/ascb/firstGroup, movieTwo.swf 在http://
www.person13.com/ascb/secondGroup,这样movieOne.swf 能创建和读取的本地路径为:
/
/ascb
/ascb/firstGroup
movieTwo.swf 能创建和读取的本地路径为:
/
/ascb

/ascb/secondGroup
因此,如果要创建两个swf都能 访问的公共LSO,则getLocal ( ) 方法的第二个参数必须是(/ 或
/ascb)
为了展示如何共享LSO,我们看一下下面的 例子:
1.
创建新的swf文件,包含下列代码,命名为movieA.swf:
var count:SharedObject = SharedObject.getLocal( "count" );
// 第一次读取,默认值为0
if ( count.data.value == undefined ) {
count.data.value = 0;
} else {
// 每次读取,value自动加1
count.data.value++;
}
// 创建text field显示值
var output:TextField = new TextField( );
output.text = "count value: " + count.data.value;
addChild( output );
2.
在电脑上建个新目录,名称为LSOTest.
3.
在 LSOTest目录下创建两个子目录:movieAPath 和movieBPath.
4.
拷贝movieA.swf文件到两个的子目录
5.
重 名名movieBPath目录下的movieA.swf 为movieB.swf
6.
多打开及关闭movieA.swf几次,每次打 开,count值都会加1
7.
多打开及关闭movieB.swf几次,每次打开,count值都会加1,但是注意到初始值是从0开始,
也 就是说,movieA.swf和movieB.swf 使用各自的LSO,虽然LSO的名字相同,但是放在不同
的路径里。
8.
要 让两个swf使用同一个LSO,必须指定本地路径参数,像这样:
9.
var count:SharedObject = SharedObject.getLocal( "count", "/" );
10.
这将导致两个swf在同一个路径下寻找LSO,因 此他们使用了同一个文件.

17.8.
控制LSO
的容量大小
问题
我想控制LSO的硬盘占用空间
解 决办法
使用Security.showSettings( )方法或者访问Web站点的Flash Player Settings Manager.
讨论
默认LSO的大小为100 KB。在.第17.3节介绍了flush( )方法来申请获得一定的硬盘空间大小,如
果 请求的空间超出共享对象空间的最大值, 则会提示用户是否同意分配空间, 通过
flash.system.Security.showSettings( )方法可设置LSO空间的最大值。下面的代码打开了本地存储容
量的对话框界面。
Security.showSettings( SecurityPanel.LOCAL_STORAGE );
posted @ 2011-06-20 02:33 子福当自强 阅读(...) 评论(...) 编辑 收藏
悟道2012