当前位置: > > > Flex4 - 多线程讲解及开发实例

Flex4 - 多线程讲解及开发实例

1,从Flash Player11.4开始,adobe终于提供了这个众望所归的api - 多线程。
通过使用AS3 Workers让创建真正的多线程应用变得非常简单,只需要几行代码即可。worker的原理是在你主SWF里运行的另一个SWF程序,从而实现多线程效果。

2,使用案例
代码在做某些工作时花了很长时间不说,还因为它让你的用户界面暂停了或是运行的非常卡时,就可以把他当做多线程了。尤其是如果某些代码你仅需要在后台执行。下面有些具体例子:
(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 ---
<?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");
			}
		}
	}
}
运行结果:


5,接收多个消息,多个数据(主线程发送add命令及两个参数,子线程获取后相加返回)

--- 主线程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);
    }
}

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。
示例如下:
--- 主线程 ---
//创建共享的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");
}

7,新的BitmapData.copyPixelsToByteArray方法,可以用来快速转换bitmapData到ByteArray。
比如一些费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");
 
    }
}
评论0