AIR/Flex: 2011年1月アーカイブ

flash(SWFファイル)から他のサーバ上のテキストファイルや XML ファイルなどをロードしようとすると、セキュリティエラーが発生する。いや、ま、そういう仕様なんだからしょうがないな。

[SecurityErrorEvent type="securityError" bubbles=false cancelable=false eventPhase=2 text="Error #2048"]

こんなエラーね。

で、こんな時はテキストファイルが置いてあるサーバのドキュメントルートにクロスドメインポリシーファイルを置けばいいのだが・・・

単に、お客さんのサーバでテストをするのがどうかなっと思ったので、SWFファイルだけうちのサーバに置いてテストしてたんだけど、クロスドメインポリシーファイルを新規に作って置くよりは、素直に SWF ファイルをテキストファイルと同じサーバに置くほうが何となくよさげなのでそうする。

しかし、Flex Builder 3 使って開発してるんで、ついつい AIR アプリ作ってる気になっちゃうけど、flash の方が随分セキュリティ上の制限があって、AIR アプリのつもりで見積もっちゃうと問題だな(笑)

Flex Builder 3 で作成する flash の中で、サーバ上の画像ファイルを読み込んで表示をしたいのだが、「Action Script 3.0 逆引きクイックリファレンス」などの書籍に載っているやり方だと上手くいかない。
例えば、こんな感じ。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    initialize="initProc()"
    layout="absolute" width="420" height="250"
    borderColor="#D7DBDD" color="#000000"
    backgroundGradientAlphas="[1.0, 1.0]"
    backgroundGradientColors="[#CAC8C8, #CAC8C8]">
   
    <mx:Script>
    <![CDATA[

    import flash.net.URLRequest;
    import flash.display.*;
    import flash.events.*;

    public var gLoader:Loader    = new Loader();

    private function initProc():void {

        var url:URLRequest    = new URLRequest("http://hogehoge.jp/images/qr_code.gif");
        gLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
        gLoader.load(url);

    }

    private function loadComplete(event:Event):void {

        var loaderInfo:LoaderInfo    = event.target as LoaderInfo;
        var bitmap:Bitmap    = loaderInfo.content as Bitmap;

        addChild(bitmap);

    }

    ]]>
    </mx:Script>

</mx:Application>

しかし、これで Flex Builder 3 で swf ファイルを作成しても、エラーも出ないが画像も表示されない。
ちゃんと画像は読み込まれ、Event.COMPLETE イベントも発生しているのだが、画像は表示されないのである。なんでぇ?

Web 上でググっても、だいたい、上のようなソースで上手くいっているようである。何で上手くいかないのかは謎。
FlexBuilder で吐く flash ファイルではこの書き方は駄目・・・とかいうことなんかなあ?識者の人、是非ご教示ください。

まあ、一応、UIComponent クラスを使ったやり方で表示じたいは出来ているので、問題はないのだが、何故に定番のやり方が上手くいかないのかは気になる。

ちなみに、上手くいっているソースは以下のとおり。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    initialize="initProc()"
    layout="absolute" width="420" height="250"
    borderColor="#D7DBDD" color="#000000"
    backgroundGradientAlphas="[1.0, 1.0]"
    backgroundGradientColors="[#CAC8C8, #CAC8C8]">

    <mx:Script>
    <![CDATA[

    import flash.net.URLRequest;
    import flash.display.*;
    import flash.events.*;
    import mx.core.UIComponent;

    public var gLoader:Loader    = new Loader();

    private function initProc():void {

        var url:URLRequest    = new URLRequest("http://hogehoge.jp/images/qr_code.gif");
        gLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);
        gLoader.load(url);

    }

    private function loadComplete(event:Event):void {

        var uic:UIComponent    = new UIComponent;

        uic.addChild(gLoader);
        this.addChild(uic);

    }

    ]]>
    </mx:Script>

</mx:Application>

いやぁ、ついつい同じ Flex Builder 3 で作る AIR アプリと同じ感覚で、「サーバ上の画像の表示? HTML コンポーネントを使えばすぐですよ」なんて簡単に見積もってしまったが、実際には flash 用には HTML コンポーネントなんて用意されてないし、よって UIComponent クラスなんかも使うの初めてだし・・・厳しいぃ~(^^;

WebService と通信しながら、シーケンシャルに処理したい案件があったので、

1.プロセス順(ProcIdx)に 0 をセットして、最初の WebService との通信用イベントを実行し、ProcIdx の値が更新されるまで無限ループで待つ。
(例)
ProcIdxOld  = ProcIdx;
while(ProcIdx <= ProcIdxOld) {
    // dummy
}

2.WebService との通信用イベントが終了(ResultEvent.RESULT 等)したら、ProcIdx の値を 1加算。

3.上記 2の処理で ProcIdx の値が加算されたので、1 の処理の while ループから抜けて次の処理へ・・・

という動きでシーケンシャルな処理を実現しようと思ったのだが・・・

駄目だった(^^;

どうも、1の while ループ処理が CPU を掴みっぱなしになってしまうようで、終了イベント(上記 2の処理ね)がまったく実行されないようだ。

ここで、VisualBasic なら sleep 命令を while ループの中に書いて終わりなのだが、Adobe ActionScript には sleep 命令はない。

つーことで、結局、Timer イベントを 500ミリ秒とかの単位で発生させ、

1.Timer イベントで、現在どこまで処理が進んでいるかチェック(プロセス番号(ProcIdx)や、そのプロセスが終了したかのフラグ(ProxEndFlg)を持ち、その値をチェックする)。前の処理が終わったら、次の処理をおこなう(WebService との通信用イベントを実行)。その際、ProcIdx を 1加算。

2.WebServer との通信用イベントが終了(ResultEvent.RESULT 等)したら、ProcEndFlg に 1(終了の意味)をセット。

3.Timer イベントで、現在どこまで処理が進んでいるかチェック。前の処理が終わったら、次の処理をおこなう(WebService との通信用イベントを実行)。その際、ProcIdx を 1加算。

・・・これが、全ての WebService との通信処理が終わるまで延々続く・・・

というプログラムを書かなきゃ駄目。
Timer イベント内で、処理がどこまで進んでいるかチェックする分だけプログラムは複雑になる。

まあ、sleep が無いのはイベント駆動型プログラミングとしては正当なのかもしれないが、やっぱ sleep あると便利だなあという思いは捨てきれない、お茶目で巨根な俺なのである。

ああ・・・面倒くせえ。(^^;

以前書いた、「AIRアプリでZIPファイルを解凍してみる」の続き。

FZip というフリーの zip ファイル操作用ライブラリを使って、AIR アプリで zip ファイルを解凍するという話で、実際、Windows 上では問題なく動いてたんだけど・・・

その AIR アプリを、Mac OS X 10.5 のマシンにインストールして使うと、全然 zip ファイルが解凍されない。

Flex Builder 上でデバッグしてみると、どうも、IOErrorEvent が発生してる。
エラーメッセージを出力してみると(trace("ERROR=" + evt.text);で)、

Error #2032: Stream Error. URL:app:/Users/shinoda/Library/Preferences/hogehogeApp/Local%20Store/data/cont/ds.zip

だって。

う~む・・・
パスは合ってると思うがなあ・・・頭に app: とか付いてるのが問題なのか?でも、こりゃ Flex が勝手に付けてるだけだからなあ。どうしようもないし。

結局、zip ファイル名を URLRequest の url プロパティに渡すところで、

Fzip = new FZip();

var request:URLRequest = new URLRequest();
var file:File = File.applicationStorageDirectory.resolvePath(ZipFileName);
request.url = file.nativePath;
~以下略

この、file.nativePath file.url にしてやったら、IOErrorEvent は発生しないようになり、Mac OS X の上でも AIR アプリで zip 解凍をおこなうことが出来た。

ちなみに、file.url の場合、実際には "app-storage:/data/cont/ds.zip" という値が渡される。

まあ、Windows でも Mac でも同じように使いたいなら、出来るだけ native なんて付いてるプロパティは使うなってことですな(^^;

HTMLコンポーネントでも、HTMLLoaderクラスでも表示は一緒」に書いたように、AIR アプリのHTMLコンポーネントでインターネット上のWebページを表示した時に、外部CSSファイルや画像ファイルを一切読み込まない(つまり、URLで指定したHTMLだけ読み込んでお終い)という状況が発生していた。

「なんやねん!こんなしょぼい仕様なんかい!?」と怒っていたのだが、原因は俺だった。ごめん・・・Adobe社。

結局、原因は「URLの最後に改行コードがついていた」からであった。

テキストファイルから URL を読み込んで、それをHTMLコンポーネントのlocationプロパティにセットするのだが、末尾の改行コードをとりわすれてた。

なので、

url  = (url.split("\r")).join("");
url  = (url.split("\n")).join("");

web.location   = url;

という具合に、locationプロパティにセットする前に CR/LF コードを削除するようにしてやればバッチリであった。

URL の後ろに改行コードが入ってると、怪しいURLと判断して、そこからのリンクを全部無視してしまうとか、そういう仕様なのかね?

URL の後ろに改行コードが入ってると、「外部の画像ファイルやCSSファイル等を一切読み込まない。その代わり、ローカルディスク上にある画像であれば表示できる」という動きをする。ローカルディスク上の画像ファイルは読み込めるということは、やっぱセキュリティ上の制限をかけてるのかなあ・・・

ま、単純に Flex にバグがあって、予想外の動きをしているという可能性も捨てきれんがなあ(笑)

Adobe AIR アプリ内で、HTMLコンポーネントを使ってインターネット上のWebページを表示させているのだが、何か、画像ファイルの表示がうまくいかない。
ちなみに、以下のようなHTMLのWebページである。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
<title>テスト</title>
<style type="text/css">
<!--
body {
    background-color: #f0f060;
    text-align:center;
    margin:0px;
    }
h1    {
    background:url(img/title-bar.jpg) repeat-x;
    text-align:center;
    margin:0px;
    }

-->
</style>

</head>

<body>
<h1>テストページ1です</h1>
<br />
これはテストページでありますよ。<br />
<br />
<img src="img/test1.jpg" alt="電気ブランとサンマの塩焼き缶" width="240" height="240" /></div>
</div>
</body>
</html>

で、HTML文自体はちゃんと解釈されているようだが、画像ファイルを受信していない(?)ようだ。
以下のような表示。ちゃんとセンター寄せされているので、CSS もちゃんと解釈されてるみたいだね。

20110121_air1.jpg

もしかして、HTMLコンポーネントは機能がしょぼしょぼで、正しくは Flash の HTMLLoaderクラスを使うべきなのかと思い試してみた。

以下のようなソース。左にHTMLコンポーネントを、右にHTMLLoaderクラスを使ったオブジェクトを配置してみた。

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"
    initialize="initProc()"  x="0" y="20"
    layout="absolute" height="462">
    <mx:Label x="114" y="20" text="HTMLコンポーネント" fontSize="13"/>
    <mx:Label x="436" y="20" text="HTMLコントロール" fontSize="13"/>
    <mx:HTML x="28" y="50" width="295" height="390" id="web1"/>
   
    <mx:Script>
    <![CDATA[

    import flash.net.URLRequest;

    private function initProc():void {

        //------------------------------------------
        // HTML コンポーネント
        //------------------------------------------
        web1.location    = "http://www.exsample.jp/sample/003/test1.html";

        //------------------------------------------
        // HTML コントロール
        //------------------------------------------
        var url:URLRequest    = new URLRequest("http://www.exsample.jp/sample/003/test1.html");

        var web2:HTMLLoader   = new HTMLLoader();
        web2.x    = 340;
        web2.y    = 70;
        web2.width    = 295;
        web2.height   = 390;

        web2.load(url);

        this.nativeWindow.stage.addChild(web2);

    }

    ]]>
    </mx:Script>

</mx:WindowedApplication>

さあ、表示がどう違うかな・・・

・・・て、同じやーーーーーん!!!

20110121_air2.jpg

どっちでも、まったく同じように表示されましたな。
ま、落ち着いて考えれば、Webレンダリング・エンジンは「Web kit」しか組み込まれてないわけで、どっちもそれを使っているわけで・・・

つーことで、表示が変なのは俺のプログラムの問題なのだな。

そうか、そうか。そういうことだな・・・(;_;)

Adobe AIRアプリから、某WebServiceのAPIをSOAP通信で叩くのだが、APIのエンドポイントにはBasic認証がかかってる。

ということで、エンドポイントへのアクセス時にヘッダに Basic 認証用の Authorization 情報を追加してやらんといかん。

具体的には、HTTP ヘッダに、

Authorization: Basic [Base64エンコードされた user:pass]

という情報を付けてやるのだが、もちろんいちいちヘッダ文字列の編集をしなくても、WebService クラスを使ってアクセスするのであれば、httpHeaders プロパティにセットしてやればいい。

    var encoder : Base64Encoder = new mx.utils.Base64Encoder();
    encoder.encode("ID文字列:パスワード文字列");
    ws.httpHeaders = {Authorization:"Basic " + encoder.toString()};

    ws.getHogehoge("");

みたいな感じ。
URLRequest クラスでは requestHeaders だけど、WebService では httpHeaders。それに、requestHeaders は配列型だけど、httpHeaders は Object 型なんだね~)

このアーカイブについて

このページには、2011年1月以降に書かれたブログ記事のうちAIR/Flexカテゴリに属しているものが含まれています。

前のアーカイブはAIR/Flex: 2010年12月です。

次のアーカイブはAIR/Flex: 2011年2月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。


月別 アーカイブ

電気ウナギ的○○ mobile ver.

携帯版「電気ウナギ的○○」はこちら