Flex4 - 多线程讲解及开发实例
1,从Flash Player11.4开始,adobe终于提供了这个众望所归的api - 多线程。
通过使用AS3 Workers让创建真正的多线程应用变得非常简单,只需要几行代码即可。worker的原理是在你主SWF里运行的另一个SWF程序,从而实现多线程效果。
通过使用AS3 Workers让创建真正的多线程应用变得非常简单,只需要几行代码即可。worker的原理是在你主SWF里运行的另一个SWF程序,从而实现多线程效果。
2,使用案例
代码在做某些工作时花了很长时间不说,还因为它让你的用户界面暂停了或是运行的非常卡时,就可以把他当做多线程了。尤其是如果某些代码你仅需要在后台执行。下面有些具体例子:
(1)文字/数据处理(例如:幻灯片加载一个Xml文件,循环一遍,遍历出所有子节点创建图形。)
(2)可视/图像/音频数据处理(例如:任何一个不需要用PixelBender的图片处理算法。)。
(3)在后台运行物理引擎。
代码在做某些工作时花了很长时间不说,还因为它让你的用户界面暂停了或是运行的非常卡时,就可以把他当做多线程了。尤其是如果某些代码你仅需要在后台执行。下面有些具体例子:
(1)文字/数据处理(例如:幻灯片加载一个Xml文件,循环一遍,遍历出所有子节点创建图形。)
(2)可视/图像/音频数据处理(例如:任何一个不需要用PixelBender的图片处理算法。)。
(3)在后台运行物理引擎。
3,开发环境
(1)flash player版本最低建议选择11.5.0
(2)编译参数增加 -swf-version=17
(3)flash builder建议选择4.7版本,因为从4.7版本开始,编辑器提供新建“ActionScript Worker”的功能。
比如我们新建一个叫“MyWorker”的worker,编辑器会自动创建如下图的文件:
4,简单的例子(主应用发消息给子线程,子线程收到消息回复,主线程把回复消息打印出来)
--- 主应用 Main.mxml ---
5,接收多个消息,多个数据(主线程发送add命令及两个参数,子线程获取后相加返回)
--- 主线程interval函数 ---
6,使用byteArray.shareable
传输数据最快的方式就是根本不传输它!Adobe给我们提供可以直接共享ByteArray对象的途径。这是非常强大的,因为我们几乎可以在ByteArray里存储任何数据。
要共享一个byteArray对象,你需要设置byteArray.shareable=true,然后调用messageChannel.send(byteArray)或者worker.setSharedPropert(“byteArray”, byteArray)方法来共享它。
一旦你的byteArray对象是共享的,你可以直接对它写入数据,然后在另一端从它上面直接读取
要把一个对象转换成byteArray,最简单的是使用byteArray.writeObject(myArrayOfObjects)方法即可。不幸的是,这个方法非常慢,最快的方式是手动封装你的数据到ByteArray,我们直接写入Number和String的值到ByteArray对象,而不是写入每个Object。
7,新的BitmapData.copyPixelsToByteArray方法,可以用来快速转换bitmapData到ByteArray。
比如一些费cpu的图像处理操作,我们可以把BitmapData转换成ByteArray来给子线程进行处理。例子如下:
--- 主线程 ---
(1)flash player版本最低建议选择11.5.0
(2)编译参数增加 -swf-version=17
(3)flash builder建议选择4.7版本,因为从4.7版本开始,编辑器提供新建“ActionScript Worker”的功能。
比如我们新建一个叫“MyWorker”的worker,编辑器会自动创建如下图的文件:

--- 主应用 Main.mxml ---
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" creationComplete="init(event)"> <fx:Script> <![CDATA[ import mx.events.FlexEvent; protected var worker:Worker; protected var mainToWorker:MessageChannel; protected var workerToMain:MessageChannel; protected function init(event:FlexEvent):void { //创建worker worker = WorkerDomain.current.createWorker(Workers.MyWorker); //为两个方向通信分别创建MessagingChannel mainToWorker = Worker.current.createMessageChannel(worker); workerToMain = worker.createMessageChannel(Worker.current); //注入MessagingChannel实例作为一个共享对象 worker.setSharedProperty("mainToWorker", mainToWorker); worker.setSharedProperty("workerToMain", workerToMain); //监听来自Worker的事件 workerToMain.addEventListener(Event.CHANNEL_MESSAGE, onWorkerToMain); //启动worker(重新实例化文档类) worker.start(); //设置时间间隔定时worker发送消息 setInterval(function():void{ mainToWorker.send("HELLO"); textArea.text +="[Main] HELLO" +"\n"; }, 1000); } //从worker线程接收信息 protected function onWorkerToMain(event:Event):void { //打印输出worker里接收到的任何消息 textArea.text +="[Worker] " + workerToMain.receive() +"\n"; } ]]> </fx:Script> <s:TextArea id="textArea" width="100%" height="100%"/> </s:Application>--- 子线程 MyWorker.as ---
package { import flash.display.Sprite; import flash.events.Event; import flash.system.MessageChannel; import flash.system.Worker; public class MyWorker extends Sprite { protected var mainToWorker:MessageChannel; protected var workerToMain:MessageChannel; public function MyWorker() { super(); //在worker内部,我们可以使用静态方法获取共享的messgaeChannel对象 mainToWorker = Worker.current.getSharedProperty("mainToWorker"); workerToMain = Worker.current.getSharedProperty("workerToMain"); //监听来自主线程的事件 mainToWorker.addEventListener(Event.CHANNEL_MESSAGE, onMainToWorker); } //从主线程接收信息 protected function onMainToWorker(event:Event):void { var msg:* = mainToWorker.receive(); //当主线程给我们发送"HELLO"时,我们返回"WORLD" if(msg == "HELLO"){ workerToMain.send("WORLD"); } } } }运行结果:

--- 主线程interval函数 ---
//设置一个时间间隔让Worker线程做一些数学计算 setInterval(function(){ mainToWorker.send("ADD"); mainToWorker.send(2); mainToWorker.send(2); trace("[Main] ADD 2 + 2?"); }, 1000);--- 子线程监听函数 ---
protected function onMainToWorker(event:Event):void { var msg:* = mainToWorker.receive(); if(msg == "ADD"){ //接收到两个值,然后把它们相加 var val1:int = mainToWorker.receive(); var val2:int = mainToWorker.receive(); //返回计算结果给主线程 workerToMain.send(val1 + val2); } }
传输数据最快的方式就是根本不传输它!Adobe给我们提供可以直接共享ByteArray对象的途径。这是非常强大的,因为我们几乎可以在ByteArray里存储任何数据。
要共享一个byteArray对象,你需要设置byteArray.shareable=true,然后调用messageChannel.send(byteArray)或者worker.setSharedPropert(“byteArray”, byteArray)方法来共享它。
一旦你的byteArray对象是共享的,你可以直接对它写入数据,然后在另一端从它上面直接读取
要把一个对象转换成byteArray,最简单的是使用byteArray.writeObject(myArrayOfObjects)方法即可。不幸的是,这个方法非常慢,最快的方式是手动封装你的数据到ByteArray,我们直接写入Number和String的值到ByteArray对象,而不是写入每个Object。
示例如下:
--- 主线程 ---
//创建共享的ByteArray对象 positionBytes = new ByteArray(); positionBytes.shareable = true; worker.setSharedProperty("positionBytes", positionBytes); /**接收worker发过来的消息**/ protected function onMessageFromWorker(event:flash.events.Event):void { var msg:String = channelToMain.receive(); if(msg == "complete"){ //从byteArray对象里读取更新后的位置信息 var s:Crate; var ba:ByteArray = positionBytes; positionBytes.position = 0; var id:String; while(positionBytes.bytesAvailable){ //从byteArray对象里读取SpriteID id = ba.readUTFBytes(ba.readInt()); //更新在屏幕上的Sprite位置 s = spritesById[id]; s.x = ba.readInt(); s.y = ba.readInt(); s.rotation = ba.readInt() * Math.PI / 180; } } }--- 子线程 ---
//获取共享的ByteArray对象 positionBytes = Worker.current.getSharedProperty("positionBytes"); /** * 复制NapeSprites的位置信息到byteArray **/ protected function onEnterFrame(event:Event):void { var ba:ByteArray = positionBytes; ba.position = 0; for(var i:int = 0, l:int = sprites.length; i < l; i++){ ba.writeInt(sprites[i].id.length); ba.writeUTFBytes(sprites[i].id); ba.writeInt(sprites[i].x); ba.writeInt(sprites[i].y); ba.writeInt(sprites[i].rotation); } //通知主线程COMPLETE channelToMain.send("complete"); }
比如一些费cpu的图像处理操作,我们可以把BitmapData转换成ByteArray来给子线程进行处理。例子如下:
--- 主线程 ---
//转换位图数据并存储到共享的byteArray对象里,与worker线程共享。 imageBytes = new ByteArray(); imageBytes.shareable = true; origImage.copyPixelsToByteArray(origImage.rect, imageBytes); worker.setSharedProperty("imageBytes", imageBytes); //给worker传递初始化图像宽高尺寸 worker.setSharedProperty("imageWidth", origImage.width); worker.setSharedProperty("imageHeight", origImage.height); /**worker的完成任务时的响应函数**/ protected function onBackToMain(event:Event):void { var msg:String = backToMain.receive(); if(msg == "SHARPEN_COMPLETE"){ imageBytes.position = 0; image.bitmapData.setPixels(image.bitmapData.rect, imageBytes); } }--- 子线程 ---
//从共享属性缓存池里获取位图数据。 imageBytes = worker.getSharedProperty("imageBytes"); var w:int = worker.getSharedProperty("imageWidth"); var h:int = worker.getSharedProperty("imageHeight"); imageBytes.position = 0; imageData = new BitmapData(w, h, false, 0x0); imageData.setPixels(imageData.rect, imageBytes); /**响应主线程的请求**/ protected function onMainToBack(event:Event):void { if(mainToBack.messageAvailable){ //获取消息类型 var msg:* = mainToBack.receive(); //锐化位图并复制它到byteArray对象里 var data:BitmapData = ImageUtils.SharpenImage(imageData, currentSharpen); imageBytes.length = 0; data.copyPixelsToByteArray(data.rect, imageBytes); //通知主线程锐化操作已经完成 backToMain.send("SHARPEN_COMPLETE"); } }