아주 간단한 날씨앱을 만들어봤는데, 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 머드초보

댓글을 달아 주세요

 
와....

안드로이드에서 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 머드초보

댓글을 달아 주세요

 
ServerSocket이라는 것이 Adobe AIR 2에서 생겼는데요.
뭐 특별히 제약사항같은 것은 없는 것 같군요. 그냥 서버소켓 만들어서 쓰면 됩니다.

우선 Adobe AIR 2 셋팅Flash Builder를 다운로드

서버소켓 생성하는 방법은
new ServerSocket();
serverSocket.bind(포트, "아이피");
serverSocket.listen();
하면 서버소켓이 생성됩니다.

Event.Connect를 이벤트 추가하면 상대방이 연결해왔을 때 호출이 됩니다.
보통 Java에서는 Thread를 통해서 하게 되는데, Actionscript3는 특성상 이벤트기반이기에... 그냥 여러개가 연결이 되도 이벤트가 발생하고, 그 발생한 이벤트객체에서 socket이 들어있어서 그걸 이용하면 되구요.

그 소켓에 다시 이벤트를 걸어주면 됩니다. 그 소켓은 client와 연결된 소켓! 기존 client소켓처럼 쓰면 됩니다^^
데이터는 read, write로 주고 받으면 되죠.

아래는 간단한 예제를....-_-
ServerSocketTest.mxml
[code]<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/halo"
                       applicationComplete="windowedapplication_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import flash.events.Event;
            import flash.events.IOErrorEvent;
            import flash.events.ProgressEvent;
            import flash.events.ServerSocketConnectEvent;
            import flash.net.ServerSocket;
            import flash.net.Socket;
           
            import mx.events.FlexEvent;
           
            private var socketPolicyUtil:SocketPolicyUtil;
           
            private var serverSocket:ServerSocket;
            private var listSocket:Vector.<Socket>;
           
            protected function windowedapplication_applicationCompleteHandler(event:FlexEvent):void
            {
                socketPolicyUtil = new SocketPolicyUtil();
               
                // 소켓리스트 초기화
                listSocket = new Vector.<Socket>;
               
                serverSocket = new ServerSocket();
               
                serverSocket.addEventListener(Event.CONNECT, connectHandler);
                serverSocket.addEventListener(Event.CLOSE, onClose);
       
                serverSocket.bind(10000, "127.0.0.1");
               
                serverSocket.listen();
                trace("Listening on " + serverSocket.port);
            }

            private function connectHandler(event:ServerSocketConnectEvent):void
            {
                // The socket is provided by the event object
                var socket:Socket = event.socket as Socket;
                listSocket.push(socket);
               
                socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
                socket.addEventListener(Event.CLOSE, onClientClose);
                socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
               
                socket.writeUTFBytes("Connected.");
                socket.flush();
               
                trace("Sending connect message");
            }
           
            private function socketDataHandler(event:ProgressEvent):void
            {
                var socket:Socket = event.target as Socket;
               
                // Read the message from the socket
                var message:String = socket.readUTFBytes(socket.bytesAvailable);
                trace("Received: " + message);
               
                // 등록된 소켓에 모두 전송
                for each (var s:Socket in listSocket)
                {
                    s.writeUTFBytes(message);
                    s.flush();
                }
            }
           
            private function onClientClose(event:Event):void
            {
                trace("Connection to client closed");
                var socket:Socket = event.target as Socket;
               
                // 등록된 소켓 삭제
                var i:int = 0;
                for each(var s:Socket in listSocket)
                {
                    if (s == socket)
                    {
                        trace("같음");
                        listSocket.splice(i, 1);
                        return;
                    }
                    i++;
                }
            }
           
            private function onIOError(event:IOErrorEvent):void
            {
                trace("IOError: " + event.text);   
            }
           
            private function onClose(event:Event):void
            {
                trace("Server socket closed by OS.");       
            }
        ]]>
    </fx:Script>
   
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
</s:WindowedApplication>
[/code]
그냥 코드만 봐도 그리 어렵지 않아요.
서버소켓을 생성하고, 연결되는 소켓들은 list(Vector)에 추가해서 관리하게 되고, 끊어지면 list에서 빼버리고, 대화요청이 들어오면 받아서 모두에게 뿌려주면 돼요.

여기서 제가 만든 클래스가 하나 있는데요. SocketPolicyUtil클래스인데, 크로스 도메인을 위한 클래스입니다. 아래에서 설명을....-_-


크로스 도메인 설정 클래스
여기서 제가 보안샌드박스를 위한 클래스를 하나 만들었는데요. 크로스도메인에서 socket요청이 들어온 경우에는 crossdomain.xml을 넘겨줘야하는데, 그것도 air에서 다 할 수 있습니다.
타 도메인에서 요청하는 경우, 클라이언트 측에서는 843포트를 통해 crossdomain.xml을 요청하게 되어있습니다. 물론 클라이언트에서 843이 아닌 다른 포트로 요청을 원하면 바꿀 수 있지요.
 클라이언트 측에 이런 코드를 넣으면 되죠. 그러면 10001로 포트를 요청하죠.
[code]Security.loadPolicyFile("xmlsocket://127.0.0.1:10001");[/code]
SocketPolicyUtil.as
[code]package
{
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.ServerSocketConnectEvent;
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.net.ServerSocket;
    import flash.net.Socket;

    public class SocketPolicyUtil
    {
        private var serverSocket:ServerSocket;
       
        public function SocketPolicyUtil(port:int = 843)
        {
            serverSocket = new ServerSocket();
            serverSocket.bind(port, "127.0.0.1");
            serverSocket.listen();
            serverSocket.addEventListener(Event.CONNECT, connectHandler);
            serverSocket.addEventListener(Event.CLOSE, onClose);
        }
       
        private function connectHandler(event:ServerSocketConnectEvent):void
        {
            var socket:Socket = event.socket as Socket;
           
            socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
            socket.addEventListener(Event.CLOSE, onClientClose);
            socket.addEventListener(IOErrorEvent.IO_ERROR, onIOError);
           
            trace("Policy Connected.");
        }
       
        private function socketDataHandler(event:ProgressEvent):void
        {
            var socket:Socket = event.target as Socket;
           
            var message:String = socket.readUTFBytes(socket.bytesAvailable);
            trace("Policy received : " + message);
           
            var file:File = new File(File.applicationDirectory.nativePath + File.separator + "policy.xml");
            var stream:FileStream = new FileStream();
            stream.open(file, FileMode.READ);
            var data:String = stream.readUTFBytes(stream.bytesAvailable);
            trace("policy data = " + data);
            socket.writeUTFBytes(data);
            socket.writeByte(0);
            socket.flush();
        }
       
        private function onClientClose(event:Event):void
        {
            trace("Policy close");
            removeClientSocketEvent(event.target as Socket);
        }
       
        private function onIOError(event:IOErrorEvent):void
        {
            trace("ioerror = " + event.text);   
        }
       
        private function onClose(event:Event):void
        {
            trace("Server socket closed by OS.");
        }
       
        private function removeClientSocketEvent(socket:Socket):void
        {
            socket.removeEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
            socket.removeEventListener(Event.CLOSE, onClientClose);
            socket.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);   
        }
    }
}[/code]
중요한 점은 policy.xml파일 날릴 때 마지막에 writeByte(0)으로 끝을 맺어줘야한다는...
이 클래스를 보면 policy.xml파일은 File.applicationDirectory에서 찾고 있으니 이걸 src폴더에다가 넣어버리고 패키지할 때 같이 묶어버리면 됩니다. 그리고 그냥 843포트로 열어서 crossdomain.xml파일을 그냥 전송해주기만 하면 되죠. *는 권장사항이 아니니...-_- 해당 도메인이랑 포트번호를 정확히 입력하는게...-_-
policy.xml
[code]<?xml version='1.0' encoding='UTF-8'?>
<cross-domain-policy>
    <allow-access-from domain='*' to-ports='*'/>
</cross-domain-policy>[/code]

아래는 클라이언트 코드
ClientSocketTest.mxml
[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/halo" minWidth="1024" minHeight="768"
               applicationComplete="application1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.FlexEvent;

            private var socket:Socket;
           
            protected function application1_applicationCompleteHandler(event:FlexEvent):void
            {
                //Security.loadPolicyFile("xmlsocket://127.0.0.1:10001");
            }

            protected function btnDisconnect_clickHandler(event:MouseEvent):void
            {
                if (inputId.text.length < 1)
                {
                    Alert.show("아이디를 입력하세요");
                    return;
                }
                socket = new Socket("127.0.0.1", 10000);
                socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
                currentState = "connect";
                labelId.text = inputId.text;
            }
           
            protected function btnConenct_clickHandler(event:MouseEvent):void
            {
                socket.close();
                currentState = "disconnect";
            }
           
           
            private function socketDataHandler(event:ProgressEvent):void
            {
                var message:String = socket.readUTFBytes(socket.bytesAvailable);
                trace("message = " + message);
                taChat.text += message + "\n";
            }

            protected function inputChat_enterHandler(event:FlexEvent):void
            {
                if (inputChat.text.length < 1)
                {
                    return;
                }
                socket.writeUTFBytes("[" + labelId.text + "] : " + inputChat.text);
                socket.flush();
                inputChat.text = "";
            }

            protected function btnChat_clickHandler(event:MouseEvent):void
            {
                if (inputChat.text.length < 1)
                {
                    return;
                }
                socket.writeUTFBytes("[" + labelId.text + "] : " + inputChat.text);
                socket.flush();
                inputChat.text = "";
            }
        ]]>
    </fx:Script>
    <s:states>
        <s:State name="disconnect"/>
        <s:State name="connect"/>
    </s:states>
   
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>
   
    <s:TextInput id="inputId" x="231" y="10" includeIn="disconnect"/>
    <s:Button id="btnConenctClose" x="367" y="11" label="접속" label.connect="종료"
              click.connect="btnConenct_clickHandler(event)" click.disconnect="btnDisconnect_clickHandler(event)"/>
    <s:Label id="labelId" includeIn="connect" x="211" y="10" width="159" height="21"/>
   
    <s:TextArea id="taChat" includeIn="connect" x="10" y="49" width="621" height="402"/>
    <s:TextInput id="inputChat" includeIn="connect" x="10" y="459" width="507" enter="inputChat_enterHandler(event)"/>
    <s:Button id="btnChat" includeIn="connect" x="526" y="459" label="Button" click="btnChat_clickHandler(event)"/>
</s:Application>[/code]

아래 프로그램 설치하고 실행시켜놓은 뒤, 아래사이트를 접속해서 테스트할 수 있어요. 아이피는 127.0.0.1로 하드코딩되어있기 때문에 자기 pc에서 띄워놓고 자기가 접속한 클라이언트에서만 가능해요^^
클라이언트 접속 http://mudchobo.tomeii.com/swf/ClientSocketTest.swf


 
Posted by 머드초보

댓글을 달아 주세요

 
Adobe AIR 2에서는 제한적으로 프로세스를 실행할 수 있는 기능이 추가되었습니다. NativeProcess클래스인데요. 이 기능을 제공하는 이유는 그런 것 같습니다. AIR에서 안되는 거 C로 짠거랑 데이터 통신할 수 있는 게 주 목적인 듯.
Java처럼 그냥 exe파일 실행하는 것은 안되구요. 샌드박스에 위배되지 않는 파일만 실행할 수 있습니다.
즉, document폴더나 application폴더, 또는 applicationstorage 등과 같은 폴더에 있는 것은 자동으로 실행시킬 수 있지만, 그 외에 파일들은 사용자가 선택한 것만 실행할 수 있습니다. 즉, 열 수 있는 파일과 샌드박스안에 있는 파일만 실행할 수 있다는 얘기죠^^
안그럼 이런 에러가 뜨는 것 같습니다.
NativeProcessStartupInfo.executable does not specify a valid executable file.

뭐 사용자가 exe파일 선택만 해준다면야 모든 실행파일을 실행할 수 있네요.

우선 플래시 빌더를 다운로드를..ㅠㅠ

아래 예제는 그냥 실행파일 선택하면 그 실행파일을 실행해주는 예제입니다.
[code]<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/halo">
    <fx:Script>
        <![CDATA[
            import flash.events.Event;
            import flash.events.MouseEvent;
            import flash.net.FileFilter;
           
            import mx.controls.Alert;
           
            private var nativeProcess:NativeProcess;
           
            protected function btnRun_clickHandler(event:MouseEvent):void
            {
                if (NativeProcess.isSupported)
                {
                    var file:File = new File();
                    file.addEventListener(Event.SELECT, selectHandler);
                    file.browseForOpen("실행파일을 선택하세요", [new FileFilter("exe파일", "*.exe")]);
                }
                else
                {
                    Alert.show("NativeProcess를 지원하지 않음.");                       
                }
            }
           
            private function selectHandler(event:Event):void
            {
                var file:File = event.currentTarget as File;
               
                var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                //var args:Vector.<String> = new Vector.<String>;
                //args.push("/root,c:\\");
                //nativeProcessStartupInfo.arguments = args;
                nativeProcessStartupInfo.executable = file;
                nativeProcess = new NativeProcess();
                nativeProcess.start(nativeProcessStartupInfo);
               
            }
        ]]>
    </fx:Script>
   
    <s:Button id="btnRun" label="실행파일 선택"  click="btnRun_clickHandler(event)"/>
</s:WindowedApplication>[/code]
예제의 핵심은 NativeProcess클래스랑 NativeProcessStartupInfo클래스입니다.
NativeProcessStartupInfo클래스에 해당 file이랑 arguments를 넣어주고, NativeProcess로 실행하면 됩니다^^
그리고 Descriptor파일에 한줄 추가해줘야 하는 것 같은데요.
[code]<supportedProfiles>extendedDesktop</supportedProfiles>[/code]
왜 추가 안해도 잘 되지-_- 깜빡하고 추가안했는데, 잘 돼요. 이거 추가안하면 통신이 안되나...암튼 잘 모르겠네요.
근데, 저거 추가하고, Flash Builder에서 제공하는 빌더로 export하면 에러납니다-_-
[code]Error creating AIR file: NativeProcessTest-app.xml: error 306: Descriptor must support the profile desktop, mobileDevice, or extendedMobileDevice.[/code]
이거 웬지 Builder에서 제공하는 것은 1.5로 패키징하는 것 같음-_-
암튼, 저거 빼고 해서 air파일을 만들어도 실행하면 설치가 안됩니다. 이런 에러메세지가 나는군요.
[code]This application has been designed for a different device profile and will not run on your system. Contact the application author to see if a compatible version is available.[/code]
그래서 adt를 이용해서 command line으로 직접 패키징을 해야합니다. 아.....난 콘솔이 싫은데.....

우선 amxmlc로 컴파일합니다.
[code]amxmlc NativeProcess.mxml[/code]
그러면 NativeProcess.swf파일이 생김.
이제 패키징을 하면 됩니다.
[code]adt -package -storetype pkcs12 -keystore c:\Users\mudchobo\Documents\mudchobo.p12 -target native NativeProcessTest.exe NativeProcessTest-app.xml NativeProcessTest.swf[/code]
-keystore값에는 자기인증서 경로를 적구요. -target은 native로 하면 exe파일로 생성됩니다.
그리고 포함할 파일을 적으면 되구요.
그리고 빌더로 만든 *-app.xml파일을 보시면 <content>태그가
[code]<content>[This value will be overwritten by Flash Builder in the output app.xml]</content>[/code]로 되어있는데, 이거 swf파일 경로 적어 줘야합니다.
[code]<content>NativeProcess.swf</content>[/code]
비밀번호 입력하면 NativeProcessTest.exe파일이 생성되는데, 그거 실행하면 설치화면이 뜹니다.
이 설치파일은 기존 air파일로 패키징한거랑 틀리게 배경이 흰색이네요-_-

사용자 삽입 이미지
암튼, 설치하고 나면 실행 잘 됩니다.

※참고로 제가 탐섹히를 실행하려고 해봤는데, 이런 기능이 필요한 이유가 어떤 다운로드프로그램에서 사용자가 받은 파일이 있는 폴더를 손쉽게 열 수 있게 하는 것이 목표인데, 그럴려면 탐색기 실행이 필요합니다.
그래서 그냥 패키징할 때 탐색기 프로그램을 같이 패키징해버리면 보안샌드박스에 있기 때문에 실행할 수 있습니다-_-
c:\Windows\explorer.exe파일을 복사해서 패키징해버리고, 실행하면 탐색히가 뜹니다.
해당 폴더를 여는건 explorer.exe /root,c:\\로 하면 된다는.......-_-

마지막으로 다시한번 플래시빌더를 다운로드를-_-


참고자료
http://www.adobe.com/devnet/air/flex/quickstart/interacting_with_native_process.html
 
Posted by 머드초보

댓글을 달아 주세요

 
Adobe AIR 2.0 Beta가 Release되었네요.
Adobe AIR Team 블로그 글 - http://blogs.adobe.com/air/2009/11/adobe_air_2_beta_now_available.html

릴리즈노트는 여기서 - http://labs.adobe.com/wiki/index.php/AIR_2:Release_Notes#Overview

저의 짧은 실력의 릴리즈 노트를 보면......-_-

1. 해당 파일에 대한 기본 애플리케이션으로 지정된 프로그램을 실행할 수 있습니다. 이게 뭔 얘기냐면 doc파일이 word에 지정되어있으면 doc파일을 열어서 word를 실행할 수 있는 기능인 듯-_-

2. 마이크 데이터 엑세스. 마이크 데이터를 저장하고 뭐 삽질할 수 있나봅니다.

3. 대용량 저장소에 대한 발견이 가능해졌는데요. 예를 들어 USB를 연결하게 되면 그걸 AIR프로그램에서 인식할 수 있습니다.

4. 업데이트된 WebKit version. air에서 사용하는 브라우저엔진이 그냥 업데이트 됐다는 것 같은.....

5. 글로벌 에러 핸들링은 뭐지.....

6. 새로운 네트워크 지원. AIR프로그램이 서버프로그램이 될 수 있게 ServerSocket을 지원하네요. 그 외에 UDP랑 TLS/SSL도 지원한다고 써있지만, UDP빼곤 잘 몰라서-_-
아.....네트워크 인터페이스 목록이라는 부분은 그거 같은데, 네트워크 정보를 얻어올 수 있는 그런거. 맥어드레스같은 거 AIR에서 못 읽어왔는데, 읽어올 수 있나봅니다.

7. EXE파일의 PACKAGING이 가능. 원래 AIR파일이 나오는데, 이제 exe파일로도 패키징을 할 수 있습니다.

8. native process API. 이건 다른 프로그램을 AIR프로그램이 실행할 수 있는 API인데, 조금 삽질해보니 제약이 좀 있음. 패키징할 때 지정한 것만 되는 것 같음. 좀 더 삽질을 해봐야할 듯 ^^ 암튼, 프로그램을 제약적이지만 실행할 수 있어요.

9. 데이터베이스 트랜젝션 어쩌구 하는데 잘 모르겠음

다 적으려니까 디게 많네.....-_-
그냥 그외에, 향상된 IPv6을 지원하고, Native Window의 최대사이즈를 증가시켰고, 멀티터치지원하고, IME향상시켰고....영어공부해야겠다.

구축하기에 앞서 Flash Builder 4 Beta 2 다운로드

1. 개발환경 구축
사실 개발환경 구축이라고 하기에는 너무나 간단하지만....-_- 그냥 AIR SDK받아서 Flash Builder 4가 설치된 폴더 안에 있는 sdk에 덮어 씌우면 됩니다.
우선 Adobe AIR 2 Beta SDK와 Runtime 다운로드 - http://labs.adobe.com/downloads/air2.html
받은 sdk파일 (air2_b1_sdk_win_11709.zip)을 Flash Builder 2 sdk폴더에 압축을 덮어씌워서 풀어버립니다. 저의 경로는 아래와 같군요.
C:\Program Files (x86)\Adobe\Adobe Flash Builder Beta 2\sdks\4.0.0

2. 개발하면 돼요-_-

3. 예제 작성
[code]<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/halo"
                       width="500" height="500"
                       applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    <fx:Script>
        <![CDATA[
            import flash.events.StorageVolumeChangeEvent;
            import mx.events.FlexEvent;

            protected function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void
            {
                StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_MOUNT, mountHandler);
                StorageVolumeInfo.storageVolumeInfo.addEventListener(StorageVolumeChangeEvent.STORAGE_VOLUME_UNMOUNT, unmountHandler);
            }
           
            private function mountHandler(event:StorageVolumeChangeEvent):void
            {
                trace(event.rootDirectory.nativePath);   
                taInfo.text += "drive = " + event.storageVolume.drive + ", type = " +
                    event.storageVolume.fileSystemType + ", name = " + event.storageVolume.name + "\n";
            }
           
            private function unmountHandler(event:StorageVolumeChangeEvent):void
            {
                taInfo.text += event.rootDirectory.nativePath + " 제거됨\n";
            }
        ]]>
    </fx:Script>
    <s:TextArea id="taInfo" x="6" y="10" width="484" height="480" verticalScrollPolicy="on"/>
</s:WindowedApplication>[/code]

핵심은 StorageVolumeInfo클래스랑 StorageVolumeChangeEvent클래스입니다. 그냥 MOUNT, UNMOUNT이벤트 걸어주면 지가 알아서 다함^^
사용자 삽입 이미지
참조사이트
http://www.adobe.com/devnet/air/flex/articles/exploring_file_capabilities.html
 
Posted by 머드초보

댓글을 달아 주세요