아주 간단한 날씨앱을 만들어봤는데, urlLoader가 잘 작동하는지 테스트해봤습니다^^

Flash로 개발하게 되었을 때 이점은 역시나 플래시만의 장점인 쉽게 그래픽을 그리고, 조작할 수 있다는 것이겠죠^^ 게임개발에 유용한...ㄷㄷ
게다가 이번에 안드로이드air까지 지원하면서 한가지의 코드로 데스크탑, 안드로이드, 아이폰앱까지 만들 수 있는 형태가 될 수 있죠(사실 Android와 Desktop은 하나의 프로젝트긴 한데, 아이폰은 AIR프로젝트가 아닌 아이폰OS프로젝트로 생성해야 만들 수 있긴하죠ㅠ).
그리고, iPhone개발을 위해 값비싼 맥북을 사지 않아도 되지요-_-(하지만, 역시....그냥 xcode로 개발하는 게...)
하지만, 모바일 기기 특성상 각각 장단점을 살릴 수 있는 기능들은 일부제한이 됩니다. 그런 기능들을 다 포기한다면 그나마 할 수 있는 분야는 게임분야겠네요^^ 게임분야는 제작하면 좋을 듯 하지만, 또 여기서 퍼포먼스가 좀 걸리네요. 제가 bitmap을 통해 뭔가 그리기 테스트를 해봤는데, 역시나 뭔가 좀 느린 듯한 느낌을 떨쳐버릴 순 없군요ㅠㅠ

일단 뭐 간단한 URLLoader를 이용해 데이터 뿌려주는 것부터 해봤긔...ㅠㅠ

일단 Adobe에서 제공하는 문서에 보면 엄청 잘나와있습니다^^
http://help.adobe.com/ko_KR/as3/iphone/index.html

준비물은 Flash CS5 랑 인증서생성 해주고 발급해주는 OpenSSL만 있으면 될 듯!

1. 인증서발급 및 프로비저닝 프로파일 받기
맥에서는 KeyChain Access라는 프로그램으로 막 만들면 되는데, 윈도우즈에서는 그게 없어요. 그래서 하는 방법이 어도비공식문서 잘 나와있네요~ 여기에서는 윈도우즈에서도 할 수 있는 방법이 있으니까 참조하세요~
http://help.adobe.com/ko_KR/as3/iphone/WS789ea67d3e73a8b2-240138de1243a7725e7-7ffd.html
혹시나.... 99딸라짜리 개발자등록을 안하셨다면 할 수 없습니다ㅠㅠ
근데, 제 아이팟은 탈옥을 해서 그런지, 안드로이드용 .p12파일과 아무 .mobileprovision파일로 해도 잘되더라구요. 기기에 그냥 테스트가 하고 싶으신 분이라면(제가 그랬음!기기에 어떻게 돌아가는지만 궁금했을 뿐), 탈옥(?)을 하셔서 테스트를 해보시는게...ㅠㅠ

2. Flash CS5에서 개발하기

Flash CS5를 열고 그냥 개발하면 됩니다(응?).
일단 새로만들기에 iPhone OS라는 게 있는데, 그걸로 생성하면 320x480사이즈의 플래시문서형태가 나옵니다. 일단 초간단한 구글날씨api를 이용해서 날씨를 불러와서 문서에 보여주는 형태의 코드를 해봤습니다.
[code]
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.errors.IOError;
import flash.utils.Timer;
import flash.events.TimerEvent;

var urlLoader:URLLoader;
var urlRequest:URLRequest;
var timer:Timer;

function init():void
{
    trace("init");   
   
    timer = new Timer(5000, 0);
    timer.addEventListener(TimerEvent.TIMER, timerHandler);
   
    urlLoader = new URLLoader();
    urlLoader.addEventListener(Event.COMPLETE, completeHandler);
    urlLoader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
   
    requestWeather();
}

function timerHandler(event:TimerEvent):void
{
    requestWeather();
}

function requestWeather():void
{
    urlRequest = new URLRequest("http://www.google.co.kr/ig/api?weather=seoul&temp=" + Math.random());
    urlLoader.load(urlRequest);
    timer.reset();
    timer.start();
}

function completeHandler(event:Event):void
{
    var xml:XML = new XML(urlLoader.data);
    trace(xml);
    var currentConditions:XMLList = xml.weather.current_conditions;
    var condition:String = currentConditions.condition.@data;
    var tempC:int = currentConditions.temp_c.@data;
    var humidity:String = currentConditions.humidity.@data;
    var weatherIcon:String = currentConditions.icon.@data;
    var windCondition:String = currentConditions.wind_condition.@data;
    trace(tempC + " " + humidity + " " + weatherIcon + " " + windCondition);
   
    imageLoader.source = "http://www.google.co.kr" + weatherIcon;
    labelTemp.text = tempC + "도";
    labelCondition.text = condition;
    labelWind.text = windCondition;
}

function errorHandler(event:IOErrorEvent):void
{
    trace("error = " + event.text);
    labelCondition.text = "오류발생!";
}

init();[/code]
아오....오늘 하루종일 어둡더니 계속 안개네요... 13일의 금요일이 보통 이런 날씨인데...
사용자 삽입 이미지

잘보면 아이콘이 어도비 에어입니다. 뭐 serversocket도 된다니 air가 맞겠네요^^

3. iPhone용에 넣기위해 ipa파일로 제작하기
파일 -> iPhone OS 설정 에서 보면 셋팅하는 게 있고, 배포탭을 보면 인증서와 프로비저닝 프로파일이 있는데, 그건 아까 1번에서 설명한 대로 넣으면 됩니다.
장치 테스트용 빠른 제작으로 하고 제작을 누르면 ipa를 만들기 시작합니다... ipa 생성까지 백만년 걸리는 것 같았습니다ㅠㅠ 엄청 느립니다ㅠㅠ

ipa파일이 해당 프로젝트 폴더에 생성이 되면 그걸 itunes에 있는 응용프로그램에 넣고, 동기화 시키면 아이폰/아이팟에 들어가 있을겁니다.

4. 아이폰/아이팟에서 확인
사용자 삽입 이미지
아오...나 레티나액정인데, 고화질로는 못만드는건가....ㅠㅠ 그냥 흐리게 나오네요ㅠㅠ

5. 후기의 한말씀-_-
그냥 맥북사시고, objective-c배우세요.

 
Posted by 머드초보
,
 
사실....-_-
아이폰이나 아이패드, 아이팟터치는 html5의 audio로 재생하고 나머지는 flash로 재생하면 다되는거죠.
이건 그렇게 귀찮게 구분하는 것을 알아서 해주는플러그인이에요.
이런 종류의 플러그인이 SoundManager라고도 있는데, 얘는 재생할 때 UI같은 것이 재미있는 것이 많아서 좋긴한데, 조금 무거운 것 같습니다. min파일이 50k정도 하는 듯.
그에 반에 jPlayer는 물론 jquery기반이라서 jquery를 먹고 가지만, jPlayer만 16k정도 하네요.

현재 브라우저가 지원하는 오디오 코덱이 다 다른데요.
크롬은 mp3/ogg 지원하고, 사파리는 mp3만, opera와 firefox는 ogg만 지원을 해요.
그래서 mp3에 대한 재생을 할 때에는 opera와 firefox는 플래시로 재생시키도록 하게 되는 것이죠.

게다가 UI연동같은 것도 progress callback함수나 complate callback함수 등 재생, 일시정지, 정지 이런 것이 너무 함수로 잘되어있어서(마치 as3의 sound클래스를 보는 듯한...) 쉽게 쓸 수 있어요~

공식사이트

대충 써보니까 매우 좋네요. 저는 이렇게 문서화랑 demo가 잘되어 있는 사이트가 너무 좋아요!ㅠㅠ

[code]
$("#player").jPlayer({
            ready:function(){
                this.element.jPlayer("setFile", "http://mudchobo.tomeii.com/html5/jPlayer/aa.mp3").jPlayer("play");
            },
            swfPath:"/html5/jPlayer/"
        })
        .jPlayer("onSoundComplete", function(){
              // 다음곡 처리~
        })
        .jPlayer("cssId", "play", "btnPlay")
        .jPlayer("cssId", "pause", "btnPause")
        .jPlayer("cssId", "stop", "btnStop");
[/code]
이런식으로 jQuery문법에 충실하도록 제작되었네요~
게다가 css랑 연동도 저렇게 play버튼을 지정해주고 btnPlay태그가 재생버튼이 바로 되어버리죠!
최고인 듯.

암튼 잘 쓰세요~
 
Posted by 머드초보
,
 
많은 분들(?)이 제 블로그에 오셔서 질문을 해주셔서 간단한 예제를 통한 설명을....-_- 나중에 저도 참고하려고 기록용-_-

일단, Flash에서 CrossDomain에 걸리는 데이터를 요청할 때 Plicy File인 crossdomain.xml 파일을 root에 정의해둬서 해당 도메인이면 데이터를 허용하게 할 수 있습니다.

소켓도 마찬가지입니다. 해당 도메인에서 해당 포트로 들어온 요청은 받겠다는 정책파일을 작성할 수 있습니다.
최초 소켓이 정책파일을 요청하게 되는 포트는 843포트입니다. 만약 이포트가 열러있지 않다면 현재 연결하려고 하는 포트로 <policy-file-request/>를 날려서 정책파일을 요청하게 됩니다.
그러면 843이든, 해당포트든 간에 정책파일만 날려주면 됩니다.

[code]<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
   <allow-access-from domain="*" to-ports="10000-10001" />
</cross-domain-policy>[/code]
소켓에 대한 정책파일을 정의한 것인데, domain에는 허용할 도메인을 쓰고, to-ports에는 허용할 포트를 쓰면 됩니다.
이걸 날려주면 이제 연결할 포트로부터 데이터를 주고 받을 수 있습니다.

초간단 에코예제!
일단 서버는 자바로...(그나마 자신있는 언어라서ㅠㅠ)

일단 PlicyFileServer를 하나 돌릴 쓰레드를 만듭니다.
PlicyFileServer.java
[code]import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class PolicyFileServer extends Thread{
   
    private ServerSocket serverSocket;
    private String policyFile = "<?xml version='1.0'?>" +
                                "<!DOCTYPE cross-domain-policy SYSTEM '/xml/dtds/cross-domain-policy.dtd'>" +
                                "<cross-domain-policy>" +
                                "<allow-access-from domain='*' to-ports='10000' />" +
                                "</cross-domain-policy>";
   
    @Override
    public void run() {
        try {
            serverSocket = new ServerSocket(843);
            while (true) {
                final Socket socket = serverSocket.accept();
                new Runnable() {
                    @Override
                    public void run() {
                        try {
                            socket.setSoTimeout(10000);
                            InputStream in = socket.getInputStream();
                            byte[] buffer = new byte[23];
                            if ( in.read(buffer) != -1 && (new String(buffer)).startsWith("<policy-file-request/>") ) {
                                OutputStream out = socket.getOutputStream();
                                out.write(policyFile.getBytes());
                                out.write(0x00);
                                out.flush();
                                out.close();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            try { socket.close();} catch(Exception ex){}
                        }
                    }
                }.run();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
[/code]
내용을 보면 그냥 서버소켓하나 만들어서 요청이 들어오면 그 소켓으로 policy file을 전송하는 형태입니다. 파일은 만들기 귀찮아서-_- 그냥 String으로 선언-_- 보면 모든도메인에 한해서 10000포트를 열어주는 겁니다.

이제 메인서버!
SocketTest.java
[code]import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;


public class SocketTest {

    private ServerSocket server;
   
    public SocketTest() {
        try{
            server = new ServerSocket(10000);
            System.out.println("접속을 기다립니다.");
           
            while (true){
                final Socket socket = server.accept();
                Thread t = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
                            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                            String line = null;
                            while ((line = br.readLine()) != null) {
                                System.out.println("수신데이터 : " + line);
                                pw.println("Hello! " + line);
                                pw.flush();
                            }
                        } catch (Exception e) {
                            try { if(socket != null) socket.close(); } catch (Exception ex) {}
                        }
                    }
                });
                t.start();
               
            }
        } catch(Exception e){
            System.out.println("Error!");
        }
    }
   
    public static void main(String[] args) {
        new PolicyFileServer().start();
        new SocketTest();
    }
}[/code]
데이터를 받으면 다시 Hello!를 붙여서 다시 전송해주는 echo서버를 하나 만듭니다.
그리고 main함수에서는 PolicyFileServer쓰레드를 하나 시작하고, EchoServer를 돌립니다.

이제 Flex!
[code]<?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" minWidth="955" minHeight="600"
               applicationComplete="application1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import mx.events.FlexEvent;
           
            import spark.components.mediaClasses.VolumeBar;
           
            private var socket:Socket;
           
            protected function application1_applicationCompleteHandler(event:FlexEvent):void
            {
                socket = new Socket("127.0.0.1", 10000);
                socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
                socket.addEventListener(Event.CONNECT, connectHandler);
            }

            protected function btnSend_clickHandler(event:MouseEvent):void
            {
                // TODO Auto-generated method stub
                socket.writeUTFBytes(inputMessage.text + "\n");
                socket.flush();
            }
           
            private function connectHandler(event:Event):void
            {
                trace("접속완료!");   
                hbox.visible = true;
            }
           
            private function socketDataHandler(event:ProgressEvent):void
            {
                var message:String = socket.readUTFBytes(socket.bytesAvailable);
                trace("수신메세지 : " + message);
                textResult.text = message;
            }

        ]]>
    </fx:Script>
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
   
    <s:layout>
        <s:VerticalLayout />
    </s:layout>
   
    <mx:HBox id="hbox" visible="false" width="100%" horizontalAlign="center">
        <s:TextInput id="inputMessage" />
        <s:Button id="btnSend" label="송신하기" click="btnSend_clickHandler(event)"/>
    </mx:HBox>
   
    <mx:Text id="textResult" width="100%" textAlign="center"/>
</s:Application>
[/code]
Socket만들어서 10000포트로 연결합니다. 그러면 도메인이 다르게 되면 swf가 843포트로 "<policy-file-request/>"를 날려서 정책파일을 달라고하는데, 서버에서 만들어놓은 PolicyFileServer가 정책파일을 내려주면 받게되면 10000포트로 다시 연결해 연결을 시작하게 됩니다.

몬가 별거 없는데 장황하게 설명해놨네.

그리고, 이런식으로 PolicyFileServer를 서버어플에 통합하면 안되겠죠? 나중에 서버어플이 늘어난다면 계속 새로 추가해야하니, PolicyfileServer를 따로 만들어서 띄워놓으면 되겠죠?^^
일단, 여러 폴리시서버 예제는 구글링하면 많이 나와요~
여기 아래주소는 Java, PHP, C#, VB.NET, Python 등등 예제가 있어요.
http://code.google.com/p/assql/wiki/SecurityInformation
이건 c로 만든거!
http://panzergruppe.hp.infoseek.co.jp/fspfd.html
 
Posted by 머드초보
,
 
와....

안드로이드에서 Adobe AIR어플이 돌아가네요. 일단 포딩할만한 어플이 없어서(저는 대부분 Flex로 작업을 해서-_-) 공씨님이 전에 플래시액션스크립트 카페에서 발표했던 자료를 가지고 포팅해봤어요-_-
http://flashist.tistory.com/68

일단 하드웨어 가속 기능이 현재 pre-release버전에는 지원되지 않아서 그래픽 관련된 것에 퍼포먼스는 상당히 떨어진다고 얘기를 하는 듯 하네요. 그래서 공씨님이 만든 이 아트웍플래시 같은 건 느리게 돌아갈 수 밖에 없는 듯. 물론 최적화하는 방법이 있을 것 같긴 한데, 제가 몰라서..-_- 저 소개한 블로그에서 나온 게임들은 대체 어디서 받아서 해볼 수 있는건지... 동영상을 보면 굉장히 부드럽게 구현되어있는데, 코드를 보고 싶네요 ㅠㅠ

환경은 Android 2.1이상에서만 되는 듯.

방식은 이렇습니다. Adobe AIR 런타임을 안드로이드폰에 설치해야합니다. 그리고 Flash 등을 이용해 apk파일로 만든 파일을 설치하면 런타임이 설치가 되어있으면 실행이 됩니다. 이런방식인 듯.

소개한 블로그입니다.
http://blogs.adobe.com/air/2010/05/air_android_prerelease.html

그 어도비랩사이트 가면 실제 런타임과 Adobe AIR2.5 SDK를 받아서 개발할 수 있습니다.
http://labs.adobe.com/technologies/air2/android/

아...그리고 개발방식은 역시나 Flash CS5를 이용하는 방법과 Flash Builder를 이용하는 방법이 있는데요. 아직 flex프레임워크로는 안되는 듯 합니다. 디폴트로 WindowedApplication이 있어야하는데, 이 안드로이드는 윈도우 방식이 아니다보니...-_- 순수 ActionScript로 짤 수 있습니다.

하지만 Flash builder를 이용하게 되면 디버깅이나 테스트를 해볼 수 없습니다-_- 실제 에뮬레이터나 폰에 배포하기 전까지 모양을 볼 수 없어요. 정신 건강상 Flash CS5를 이용하시는 게 ^^ 일단 Flash CS5기준으로 설명을....


1. 런타임 설치

랩사이트가서 가입하고 로그인하면 파일들을 받을 수 있습니다.
근데, 런타임이 Device용이랑 Emulator용이 따로 있음. 어쨌든, 런타임을 받으면 6메가 정도인데, 설치하면 20메가 입니다(내 모토로이 용량도 없는데....ㅠㅠ).

2. Flash CS5 확장기능 설치

랩사이트에서 Flash CS5에서 AIR for Android Extension for Flash CS5를 받은다음에, Adobe Extension Manager CS5를 이용해서 설치를 합니다. 그런다음에 Flash CS5를 실행하면 새로운 템플릿이 하나 생깁니다.
AIR for Android에서 480x800Android 모냥이 하나 생깁니다.

3. 이제 셋팅 끝 개발-_-

이제 그냥 개발하면 되는 듯-_-

4. 배포 및 실행

오른쪽에 속성에 보면 AIRAndroid 설정이 있는데, 그거 편집누르면 다양한 publish옵션이 있습니다. 인증서 선택하고, 비밀번호 치고, 그다음에 Android SDK의 adb.exe파일 경로까지 잡아주면 배포후에 바로 실행도 할 수 있습니다.
사용자 삽입 이미지


5. 구동화면

사용자 삽입 이미지

물론 실제 Adobe AIR용 API는 사용해보진 않았는데, ActionScript로만 Android어플을 만들 수가 있는 것이네요. 근데 꼭 Adobe AIR런타임을 거쳐야하는지가 의문이 드네요. 그냥 apk파일로 만들어지는 거라면...... 아....안되겠구나..... 쓰다보니 사실 java에서 쓰이는거랑 Adobe AIR에서 쓰이는거랑 완벽하게 포팅을 할 순 없겠죠. 뭔 소린지....-_-

어쨌든, 그냥 신기...하지만 프리릴리즈기때문에, 뭐 아직 멀은 듯.

PS. Flash Player for Android도 나오긴 나왔는데, Android 2.2버전에서만 되어서 모토로이에서 테스트해볼 수 없네요. 넥서스원이 2.2업할 수 있다고 하는데, 한번 구동하는 걸 보고 싶네요^^

 
Posted by 머드초보
,
 
air어플을 만들어야할 것 같아서(근데 이제 취소된 듯-_-), ApplicationUpdater를 삽질해봤습니다.

일단 air 어플리케이션은 수정사항이 있으면 자동적으로 업데이트를 체크해서 어플을 업데이트하는 기능이 꼭 들어가야합니다. 그래서 ApplicationUpdater클래스를 이용해 삽질을 해봤습니다.

보면 ApplicationUpdaterUI라는 클래스도 있는데, 이건 ui적으로 기능이 이미 구현이 되어있는 것입니다. 사용자가 이런 ui를 새롭게 꾸미려면 ApplicationUpdater클래스를 이용해서 내부적으로 처리를 하고, 나머지 ui는 직접 꾸밀 수가 있습니다. 그래서 사용자가 직접 새로운 업데이트air파일을 받는 모습과 업데이트체크를 해서 현재 업데이트가 있는지 등을 구현할 수 있습니다.

사실 과정은 applicationUpdater.checkNow()한번 때려버리면 업데이트가 있으면, 논스톱으로 강제 업데이트를 시켜버리긴합니다만-_- 강제업데이트말고, 사용자에게 업데이트 과정을 알려주기 위해서 과정을 삽질해봤습니다.

강제 업데이트를 중지하려면 각각 프로세스별로 event 중지를 시켜야합니다.

[code]
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="vertical" 
    title="Sample Version Checker Application"
    applicationComplete="applicationCompleteHandler()">
    <mx:Script>
        <![CDATA[
            import air.update.events.DownloadErrorEvent;
            import air.update.events.StatusUpdateErrorEvent;
            import air.update.events.StatusUpdateEvent;
            import air.update.events.UpdateEvent;
            import air.update.ApplicationUpdater;
             
            private var applicationUpdater:ApplicationUpdater;
            
            private function applicationCompleteHandler():void
            {
                // 현재버전 찍기
                   var appXml:XML = NativeApplication.nativeApplication.applicationDescriptor;
                var ns:Namespace = appXml.namespace();
                var appVersion:String = appXml.ns::version[0];
                log.text += "현재 어플리케이션버전 : " + appVersion + "\n";
               
                // ApplicationUpdater초기화
                applicationUpdater = new ApplicationUpdater();
                applicationUpdater.configurationFile = new File("app:/update.xml");
               
                // initialize할 때
                applicationUpdater.addEventListener(UpdateEvent.INITIALIZED, initializedUpdateEventHandler);
                applicationUpdater.addEventListener(ErrorEvent.ERROR, errorErrorEventHandler);
               
                // checkNow할 때
                applicationUpdater.addEventListener(StatusUpdateEvent.UPDATE_STATUS, updateStatusStatusUpdateEventHandler);
                applicationUpdater.addEventListener(UpdateEvent.CHECK_FOR_UPDATE, checkForUpdateUpdateEventHandler);
                applicationUpdater.addEventListener(StatusUpdateErrorEvent.UPDATE_ERROR, updateErrorStatusUpdateEventHandler);
               
                // downloadUpdate할 때               
                applicationUpdater.addEventListener(UpdateEvent.DOWNLOAD_START, downloadStartUpdateEventHandler);
                applicationUpdater.addEventListener(ProgressEvent.PROGRESS, progressProgressEventHandler);
                applicationUpdater.addEventListener(UpdateEvent.DOWNLOAD_COMPLETE, downloadCompleteUpdateEventHandler);
                applicationUpdater.addEventListener(DownloadErrorEvent.DOWNLOAD_ERROR, downloadErrorDownloadErrorEventHandler);
               
                // installUpdate할 때
                applicationUpdater.addEventListener(UpdateEvent.BEFORE_INSTALL, beforeInstallUpdateEventHandler);
               
                // 초기화
                applicationUpdater.initialize();
            }
           
            private function initializedUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "initialized : " + applicationUpdater.currentState +"\n";
                applicationUpdater.checkNow();
            }
           
            private function errorErrorEventHandler(event:ErrorEvent):void
            {
                log.text += "error : " + applicationUpdater.currentState + "\n";
            }
           
            private function updateStatusStatusUpdateEventHandler(event:StatusUpdateEvent):void
            {
                event.preventDefault();
                log.text += "update_status : " + applicationUpdater.currentState +"\n";
                log.text += "version : " + event.version + "\n";
                log.text += "available : " + event.available + "\n";
               
                if (event.available)
                {
                    applicationUpdater.downloadUpdate();
                }
            }
           
            private function updateErrorStatusUpdateEventHandler(event:StatusUpdateEvent):void
            {
                log.text += "update_error : " + applicationUpdater.currentState + "\n";
            }
           
            private function checkForUpdateUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "check_for_update : " + applicationUpdater.currentState + "\n";
            }
           
            private function downloadStartUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "download_start : " + applicationUpdater.currentState + "\n";
            }
           
            private function progressProgressEventHandler(event:ProgressEvent):void
            {
                log.text += "progress : " + event.bytesLoaded + "/" + event.bytesTotal + "\n";
            }
           
            private function downloadCompleteUpdateEventHandler(event:UpdateEvent):void
            {
                event.preventDefault();
                log.text += "download_complete : " + applicationUpdater.currentState + "\n";
                btnInstall.visible = true;   
            }
           
            private function downloadErrorDownloadErrorEventHandler(event:DownloadErrorEvent):void
            {
                log.text += "download_error : " + applicationUpdater.currentState + "\n";   
            }
           
            private function beforeInstallUpdateEventHandler(event:UpdateEvent):void
            {
                log.text += "before_install : " + applicationUpdater.currentState + "\n";
            }
           
            private function clickInstallHandler(event:MouseEvent):void
            {
                applicationUpdater.installUpdate();
            }
        ]]>
    </mx:Script>
    <mx:TextArea id="log" width="100%" height="100%" />
    <mx:Button id="btnInstall" click="clickInstallHandler(event)" visible="false" label="설치"/>
</mx:WindowedApplication>
[/code]

air안에 있는 update.xml파일
[code]<?xml version="1.0" encoding="utf-8"?>
 <configuration xmlns="http://ns.adobe.com/air/framework/update/configuration/1.0" >
   <url>웹에있는update.xml파일 주소</url>
   <delay>1</delay>
</configuration>[/code]

web에 올려진 update.xml파일
[code]<?xml version="1.0" encoding="utf-8"?>
<update xmlns="http://ns.adobe.com/air/framework/update/description/1.0">
  <version>1.5</version>
  <url>업데이트할 air파일 주소</url>
  <description>
      <![CDATA[update! 1.5!]]>
  </description>
</update>
[/code]

과정은 initialize()한다음에 checkNow()를 호출하면....
현재 업데이트가 있는지 update_status가 발생하면, 그 event변수에 version과 available값이 오는데, 거기에 available이 true면 업데이트가 있는 겁니다.
downloadUpdate()를 호출하면 파일을 막 다운로드하는데, 파일 받는 과정을 progressevent로 받을 수 있습니다.
완료가 되면 downloadComplete이벤트가 발생해서 installUpdate()를 때려주면 어플이 종료되고 업데이트를 시작합니다.

사용자 삽입 이미지
사용자 삽입 이미지
사용자 삽입 이미지

PS. 오늘따라....시간이 엄청 안가네-_-
 
Posted by 머드초보
,