アシアルブログ

アシアルの中の人が技術と想いのたけをつづるブログです

Onsen UIが生まれたきっかけ



本記事はOnsen UI Advent Calendar 2016のエントリーです。Onsen UIが生まれたきっかけについて、簡単に紹介したいと思います。

当時PhoneGapといわれていたCordovaに足りなかったもの



2009年頃からWebViewを用いた、今でいうHTML5ハイブリッドアプリ開発をしていた訳ですが、当時のデバイスはパフォーマンスも低く、ブラウザーの機能が貧弱だったことも相まって、満足のいくアプリをHTML5「だけ」で作ることがほぼ不可能と言ってもいい状況でした。

思い返すとiOS 4Android 1.6の時代です。まだposition:fixedも使えず、CSSトランジションも怪しい動きをしていました。

一方で、コンテンツをHTML5で記述することのメリットは多く、アプリのマルチプラットフォーム対応や、サーバーからの動的配信で内容をアップデートすることができるなど、この分野の可能性を感じていました。

それがPhoneGap/Cordova/Monacaにつながっていくわけですが、とはいえUIWebViewだけでは、iOSAndroidのネイティブUIにかなう表現ができなかったのは事実です。

そこでネイティブUIフレームワーク



そこで2010年からMonacaを開発するに当たり、どのようにハイブリッドアプリでUI部分でブレークスルーを実現するかが特に大きな課題だと考えていました。その結果、私たちのチームが開発したのは「ネイティブUIとUIWebViewを組み合わせる」というアプローチです。

具体的には、コンテンツの中身はUIWebViewで記述しつつも、ナビゲーターやタブといったネイティブなトランジションが圧倒的に有利な部分はネイティブで描画する、という仕組みでした。

PhoneGapの拡張エンジンとして、オープンソースでリリースしました(メンテナンスはしていませんが、いまでも公開は続けています)。



このフレームワークでは、複数ページをスムーズに遷移するため、マルチWebView構成になっていました。ようするに、次のページに遷移した場合には別のWebViewが作られ、ページスタックが作られます。

ネイティブUIフレームワークの課題からOnsen UIへ



ただしこの仕組みには一つ大きな問題がありました。それは、WebViewが複数に分かれるため、JavaScriptでスコープを管理するのが大変難しくなったということでした。新しいページが全く別のJavaScriptインスタンスとして表示されるため、同じアプリであるにもかかわらず、ページ間でリソースを共有することが難しかったのです。

また、JSON形式でUI定義を記述できる仕組みとしていましたが、その結果デザインのカスタマイズに弱く、多様な表現をしたいアプリにとって制約になってしまうことも問題でした。

一方、iOS 7やAndroid 4が登場しフラットデザインが主流になるなか、WebViewでフルにUIを実装することの現実味が帯びてきました。そこで、社内で議論を行った末、これまで作っていたネイティブUIフレームワークを捨て、Onsen UIに移行するという決断を行いました。

これからのOnsen UI



最初はAngular 1のディレクティブ機能を使って実装したOnsen UIですが、当初は日本語の文献も全くなく、Angularは難しすぎるのではないか?という危惧もありました。しかし一方で、Angular以上にうまく部品をコンポーネント化できる仕組みがなく、Angularを選定したという経緯があります。

昨今ではCustom Elementsも安定したことから、Onsen UI 2.0よりAngularへの依存をなくし、ピュアなWeb Componentsフレームワークとなりました。そのメリットを生かし、Angular 2やReactへの対応とともに、Vue 2といったメジャーなフレームワークのサポートを進めていきたいと考えています。

ぜひ進化するOnsen UIにご期待ください。そして、日本発の世界的フレームワークの挑戦に向けて、GitHubのスターで応援ください!

Using Leap Motion and PhoneGap (Cordova)

Hi there. Today I want to introduce one of my new digital gadget, Leap Motion. Maybe many of the gadget-lovers already know, Leap Motion is a small stick-size device that captures your hands motion and send it to the host computer. It is like a Kinect, but have simpler SDK, and more affordable.

The usual case to use Leap Motion is to plug it to your PC, and drive some apps that supports it. Leap Motion allows third party developers to build apps that support Leap Motion. It includes, traditional C++ based SDK for Windows and Mac, and a JavaScript SDK for Web app developers.

JavaScript? Yes. It means, combined with PhoneGap, you can develop a mobile app to access to this new device with your favorite technology. So, this article will explain the overview and some sample application with Leap Motion.

How can a PhoneGap app connect to Leap Motion?



Leap Motion will be connected via USB to the host computer. So you are actually connecting to the PC from your PhoneGap app. When you install Leap Motion driver, it will automatically setup a daemon program running on your PC that opens and listens to its WebSocket protocol. Then, your app will connect using HTML5 WebSocket client to the host, via Leap Motion JavaScript API.

Since WebSocket client is required for the connection, Android devices cannot meet this requirement. Therefore, throughout this article I am using my iPad to run sample apps.

Leap Motion SDK



Leap Motion JavaScript SDK is open-source, and can be found in official website. I suggest you to read through the getting started guide, since it describes important aspect of how it is communicating with your app. In a nutshell, through WebSocket connection, it will send user’s interaction data at the specified frequency (default: 60fps), including the raw coordinates and the gesture commands calculated by the hardware. The recognizable gestures include Key Tap (like tapping to your keyboard), Screen Tap, Swipe, and Circle. Also, the device is smart enough to detect each individual fingers and its movement (like velocity).

Okey, now you are ready to create a new Leap Motion enabled app. In this article, I use Monaca for development environment. If you first hear about Monaca, it is a browser-based development environment for PhoneGap, which provides all-in-one IDE and cloud services. It is free to use, so please register an account if you don’t have one yet.

Hello Leap Motion!



Now let’s create very first example. Create a new project from “Minimum Template”, and IDE will display like below.



Then change your index.html as follows:



<html>
  <head>
    <script src="plugins/plugin-loader.js"></script>
    <link rel="stylesheet" href="plugins/plugin-loader.css">
    <style>
      #motion {
        box-sizing: border-box;
        position: absolute;
        top: 30%; left: 20%; width: 60%;
        opacity: 0.9;
        background-color: #fff;
        padding: 30px;
        border: 1px solid #ccc;
        border-radius: 20px;
        text-align: center;
        font-size: 30px;
      }
    </style>
    <script src="http://js.leapmotion.com/0.2.0-beta1/leap.min.js"></script>
    <script>
    monaca.viewport({"width": "640"});
    Leap.loop({
      enableGestures: true,
      host: '10.0.5.130' // CHANGE HERE!!
    }, function(obj) {
      if (obj.gestures) {
        obj.gestures.forEach(function(gesture) {
          if (gesture.state == "start") {
            $("#motion").text(gesture.type).show();
            lastGestureType = gesture.type;
            addList(gesture);
          } else if (gesture.state == "stop") {
            $("#motion").fadeOut();
          }
        });
      }
    });
    function addList(json) {
      var $list = $("#gestures");
      $list.prepend("<li class='ui-li ui-li-static'><b>Starting " + json.type + "</b> <div style='font-size: 80%'>" + json + "</div></li>")
    }
    </script>
  </head>
  <body>
    <div data-role="page">
      <div data-role="header" data-position="fixed"><h1>Leap Motion Gestures</h1></div>
      <div class="ui-listview" id="gestures"></div>
    </div>
    <div id="motion" style="display: none"></div>
  </body>
</html>


Now, please change the host address (mentioned as // CHANGE HERE!! in the source code) to your computer’s IP address. The server should be running if Leap Motion driver is installed.

The program code should be self-explanatory. The function “Leap.loop” is the most important function, where the callback function will be triggered at maximum of 60 times per second, and display the output of the gesture found.

If you are first-comer to Monaca, you just download Monaca Debugger via App Store to execute this program. Please try.

Here is the screenshot of the result of this program. It dumps out the raw data, and displays motion while detecting in front.



More Complex Example



Now, let’s create a more complex example. Replace index.html with following code:



<html>
  <head>
    <title>DOM Visualizer - Leap</title>
    <script src="http://js.leapmotion.com/0.2.0-beta1/leap.min.js"></script>
    <script>

      function moveFinger(Finger, posX, posY, posZ, dirX, dirY, dirZ) {
        Finger.style.webkitTransform = "translateX("+posX+"px) translateY("+posY+"px) translateZ("+posZ+"px) rotateX("+dirX+"deg) rotateY(0deg) rotateZ("+dirZ+"deg)";
      }

      function moveSphere(Sphere, posX, posY, posZ, rotX, rotY, rotZ) {
        Sphere.style.webkitTransform = "translateX("+posX+"px) translateY("+posY+"px) translateZ("+posZ+"px) rotateX("+rotX+"deg) rotateY(0deg) rotateZ(0deg)";
      }

      var fingers = {};
      var spheres = {};
      Leap.loop({
        enableGestures: true,
        host: '10.0.5.130' // CHANGE HERE!!
      }, function(frame) {
        var fingerIds = {};
        var handIds = {};
        if (frame.hands === undefined ) {
          var handsLength = 0
        } else {
          var handsLength = frame.hands.length;
        }

        for (var handId = 0, handCount = handsLength; handId != handCount; handId++) {
          var hand = frame.hands[handId];
          var posX = (hand.palmPosition[0]*3);
          var posY = (hand.palmPosition[2]*3)-200;
          var posZ = (hand.palmPosition[1]*3)-400;
          var rotX = (hand._rotation[2]*90);
          var rotY = (hand._rotation[1]*90);
          var rotZ = (hand._rotation[0]*90);
          var sphere = spheres[hand.id];
          if (!sphere) {
            var sphereDiv = document.getElementById("sphere").cloneNode(true);
                sphereDiv.setAttribute('id',hand.id);
                sphereDiv.style.backgroundColor='#'+Math.floor(Math.random()*16777215).toString(16);
                document.getElementById('scene').appendChild(sphereDiv);
                spheres[hand.id] = hand.id;
          } else {
            var sphereDiv =  document.getElementById(hand.id);
            if (typeof(sphereDiv) != 'undefined'  & & sphereDiv != null) {
              moveSphere(sphereDiv, posX, posY, posZ, rotX, rotY, rotZ);
            }
          }
          handIds[hand.id] = true;
        }
        for (handId in spheres) {
          if (!handIds[handId]) {
            var sphereDiv =  document.getElementById(spheres[handId]);
            sphereDiv.parentNode.removeChild(sphereDiv);
            delete spheres[handId];
          }
        }

        for (var pointableId = 0, pointableCount = frame.pointables.length; pointableId != pointableCount; pointableId++) {
          var pointable = frame.pointables[pointableId];
          var posX = (pointable.tipPosition[0]*3);
          var posY = (pointable.tipPosition[2]*3)-200;
          var posZ = (pointable.tipPosition[1]*3)-400;
          var dirX = -(pointable.direction[1]*90);
          var dirY = -(pointable.direction[2]*90);
          var dirZ = (pointable.direction[0]*90);
          var finger = fingers[pointable.id];
          if (!finger) {
            var fingerDiv = document.getElementById("finger").cloneNode(true);
                fingerDiv.setAttribute('id',pointable.id);
                fingerDiv.style.backgroundColor='#'+Math.floor(Math.random()*16777215).toString(16);
                document.getElementById('scene').appendChild(fingerDiv);
                fingers[pointable.id] = pointable.id;
          } else {
            var fingerDiv =  document.getElementById(pointable.id);
            if (typeof(fingerDiv) != 'undefined'  & & fingerDiv != null) {
              moveFinger(fingerDiv, posX, posY, posZ, dirX, dirY, dirZ);
            }
          }
          fingerIds[pointable.id] = true;
        }
        for (fingerId in fingers) {
          if (!fingerIds[fingerId]) {
            var fingerDiv =  document.getElementById(fingers[fingerId]);
            fingerDiv.parentNode.removeChild(fingerDiv);
            delete fingers[fingerId];
          }
        }
        document.getElementById('showHands').addEventListener('mousedown', function() {
          document.getElementById('app').setAttribute('class','show-hands');
        }, false);
        document.getElementById('hideHands').addEventListener('mousedown', function() {
          document.getElementById('app').setAttribute('class','');
        }, false);
      });

    </script>
    <style>
      *,*:before,*:after {
        margin: 0;
        padding: 0;
        border: 0;
        -webkit-box-sizing: border-box;
      }
      button {
        padding: .5em;
      }
      #app {
        position: absolute;
        width: 100%;
        height: 100%;
        font-size: 200%;
        overflow: hidden;
        background-color: #101010;
        -webkit-perspective: 1000;
      }
      #scene,
      #scene:before {
        position: absolute;
        left: 50%;
        top: 50%;
        width: 40em;
        height: 40em;
        margin: -20em 0 0 -20em;
        border: 4px solid #A0A0A0;
        background-color: rgba(255,255,255,.1);
        background-image:
        -webkit-linear-gradient(rgba(255,255,255,.4) .1em, transparent .1em),
        -webkit-linear-gradient(0deg, rgba(255,255,255,.4) .1em, transparent .1em),
        -webkit-linear-gradient(rgba(255,255,255,.3) .05em, transparent .05em),
        -webkit-linear-gradient(0deg, rgba(255,255,255,.3) .05em, transparent .05em);
        background-size: 5em 5em, 5em 5em, 1em 1em, 1em 1em;
        background-position: -.1em -.1em, -.1em -.1em, -.05em -.05em, -.05em -.05em;
        -webkit-transform-style: preserve-3d;
        -webkit-transform: rotateX(75deg);
      }
      #scene {
        -webkit-transform: rotateX(75deg);
      }
      #scene:before {
        content: '';
        -webkit-transform: rotateX(90deg) translateZ(19.5em) translateY(20em);
      }
      .cube {
        background-color: red;
        -webkit-transform-style: preserve-3d;
        -webkit-transform: translateX(19.5em) translateY(19.5em) translateZ(0em);
      }
      .finger,
      .sphere {
        position: absolute;
        left: 50%;
        top: 50%;
        width: 1em;
        height: 1em;
        margin: -.5em 0 0 -.5em;
        -webkit-transform-style: preserve-3d;
        -webkit-transform: translateX(14.5em) translateY(14.5em) translateZ(0);
      }

      .finger {
        opacity: .8;
        height: 3em;
      }

      .sphere {
        opacity: .3;
        display: none;
        font-size: 100px;
      }

      .show-hands .sphere {
        display: block;
      }

      .face {
        position: absolute;
        width: 1em;
        height: 1em;
        background-color: inherit;
        -webkit-transform-style: preserve-3d;
        -webkit-transform-origin: 0 0;
        -webkit-box-shadow: inset 0 0 0 1px rgba(255,255,255,.9);
      }
      .cube .face.tp { -webkit-transform: translateZ(1em); }
      .cube .face.ft { -webkit-transform: rotateX(90deg) translateZ(-1em); }
      .cube .face.bk { -webkit-transform: rotateX(90deg); }
      .cube .face.lt { -webkit-transform: rotateY(90deg) translateX(-1em); }
      .cube .face.rt { -webkit-transform: rotateY(90deg) translateX(-1em) translateZ(1em); }

      .finger .face.tp { -webkit-transform: translateZ(1em); height: 3em; }
      .finger .face.ft { -webkit-transform: rotateX(90deg) translateZ(-3em); }
      .finger .face.bk { -webkit-transform: rotateX(90deg); }
      .finger .face.lt { -webkit-transform: rotateY(90deg) translateX(-1em); height: 3em;}
      .finger .face.rt { -webkit-transform: rotateY(90deg) translateX(-1em) translateZ(1em); height: 3em;}

    </style>
  </head>
  <body>
    <div id="app" class="show-hands">
      <button id="showHands">Show Hands</button>
      <button id="hideHands">hide Hands</button>
      <div id="scene">
        <div id="cube" class="cube">
          <div class="face tp"></div>
          <div class="face lt"></div>
          <div class="face rt"></div>
          <div class="face ft"></div>
          <div class="face bk"></div>
        </div>
        <div id="finger" class="cube finger">
          <div class="face tp"></div>
          <div class="face lt"></div>
          <div class="face rt"></div>
          <div class="face ft"></div>
          <div class="face bk"></div>
        </div>
        <div id="sphere" class="cube sphere">
          <div class="face tp"></div>
          <div class="face lt"></div>
          <div class="face rt"></div>
          <div class="face ft"></div>
          <div class="face bk"></div>
        </div>
      </div>
    </div>
  </body>
</html>



I post the example movie taken by this sample. You will notice Leap Motion is detecting five individual fingers seamlessly.



This sample app is a slightly modification version of this page.

From Here



It is up to you to make this cool technology integrated with your mobile apps. The only pitfall is that you need a PC running as a server. I hope next Leap Motion will have its own WiFi connection and a WebSocket server.

You can actually go far beyond the examples here. If you have made a Leap Motion PhoneGap app with Monaca, please inform us so that we can feature on our blog.

Happy coding!

成功するPhoneGapアプリを開発するための高速化&UXテクニック

興味深いブログ記事が海外で掲載されていました。拙訳で恐縮ですが紹介したいと思います。

内容はPhoneGapアプリを高速化するための手法の解説で、具体的な事例とともに、いくつかのテクニックの紹介が行われています。少し長い記述になりますが、是非PhoneGapやMonacaを用いた開発の参考にしてください。


成功するPhoneGapアプリを開発するための高速化&UXテクニック


Performance & UX Considerations For Successful PhoneGap Apps



PhoneGapアプリを開発する方から、下記のような質問をよく尋ねられます。

  • ・アプリを高速化する方法は?
  • ・どうやってネイティブアプリのような質感を出せるか?
  • ・プラットフォームに違和感のないアプリを作るためのテクニックは?
  • ・OSのルック&フィールとマッチさせるためには?


この記事では、素晴らしいPhoneGapを開発するためのテクニックを紹介しつつ、「interweb(Webを取り込んだアプリ)」にまつわる疑問や迷信を明らかにしたいと考えています。

アプリ速度とは、エンドユーザーが感じるパフォーマンスであり、アプリの反応性を意味します。PhoneGapアプリはHTMLとWebViewで構成されているため、各OSプラットフォームが持つWebViewの速度に依存することになります。これは決してHTMLベースのアプリが本質的に遅いという意味ではありません。事実、HTMLベースのUIで成功したアプリは数多くあります。また、WebView自体を高速化することは困難ですが、HTML実行を高速化することは可能です。

一方で、もしWebViewが十分に高速でないと感じる場合は、その部分だけネイティブで組み合わせることが可能です。こうすることで、ネイティブUIを用いながら、必要に応じてPhoneGapによるHTMLとJavaScriptのUIを組み合わせることが可能です。

HTMLとWebViewのパフォーマンスについて



まずは、モバイルにおけるHTMLとWebのパフォーマンスについて紹介します。ここには多くのテクニックが蓄積されており、そのなかには大きく効果を発揮する内容が多くあります。

ハードウェアアクセラレーション


多くの記事で、可能な限りハードウェアアクセラレーションを強制することを推奨する記述があります。その具体的な方法は、下記のようにCSSのtransformのスタイルにtranslate3dを用いるというものです。

transform: translate3d(0, 0, 0);


こうすることでHTML DOMの描画にGPUが用いられるようになります。一方で、この設定がアプリケーションにどの程度のパフォーマンス向上を与えるか、理解しておく必要があります。アプリケーションの速度が大きく向上することもあるでしょう。しかしながら、時としてパフォーマンスに問題が発生し、その原因を突き止めるのに非常に苦労する場合もあります。そのため、あまり考えずに本設定をすべてのHTML DOM要素に対して適用しないことが重要です。

translate3dを用いた場合、GPU上のメモリーに描画内容が格納されます。過度にHTML DOM要素の描画にGPUを用いると、GPU上の利用可能メモリーが埋まってしまう可能性があります。こうなると、突然アプリケーションがエラーメッセージも表示せずクラッシュしてしまうか、GPUのメモリースワップが端末メモリー記憶媒体に対して発生してしまいます。いずれの場合も、GPUを用いない場合と比べて大幅にパフォーマンスが低下してしまいます。

また、translate3dを用いたtransform操作を適用するたびに、HTML DOM要素の描画データがGPUに転送されます。通常この処理にかかる時間は微々たるもので、気にする必要はありません。しかし、大きく複雑なHTML DOM要素に対してtranslate3dを使用した場合は、最新の端末とOSを使った場合においても、体感できる程度の遅延が発生することになります。HTML DOM要素が複雑になるほど、この遅延は大きくなります。その結果、転送中はWebViewのUIスレッドは完全にロック状態となります。これまで私が経験したなかで1秒を超えるケースはありませんが、500ミリ秒のUIロックでも深刻なマイナスの印象を与えてしまいます。さらに、DOM要素に変更を加えた場合、その部分が再度GPUに転送されることになります。

translate3dを用いる場合には、DOM要素をネストさせることも控えましょう。たとえば、
要素内に大量のDOM要素が配置されており、先頭の
要素を含めたすべての要素がtranslate3dを用いてGPU描画を有効にしている例を考えます。この場合、先頭の
要素を描画するためには、その子供の要素が描画される必要があり、さらにその子供が描画され・・・という具合に連鎖していきます。要するに、親要素を描画する前に、すべての子要素の内容がGPUにアップロードされる必要があります。その結果として、描画に時間がかかるだけでなく、GPUモリー使用量の肥大化や最悪アプリケーションをクラッシュさせる原因となってしまいます。

また、GPUの最大テクスチャーサイズも考慮する必要があります。もしDOM要素がGPUのサポートする最大テクスチャーサイズ(横幅・縦幅のいずれか)よりも大きい場合、パフォーマンスの低下や品質の低下が発生します。translate3dを用いたアニメーションやスクロールの際に、HTML DOM要素の描画がちらついた経験はありませんか?これはDOM要素の縦横サイズがGPUのサポートしているテクスチャーサイズよりも大きいことが原因です。多くのモバイル端末の最大テクスチャーサイズは1024x1024ですが、OSにより異なります。iPad 2の場合は2048x2048で、iPad 3/4の場合は4096x4096となります。もし最大テクスチャーサイズが1024ピクセルで、DOM要素の高さが1025ピクセルだった場合、画面がちらついたり速度低下が発生したりします。

参考: OSによっては、ブラウザーの実装が原因で描画がちらつくことがあります。この場合、backface-visibilityというCSSプロパティーを設定することで解決する場合があります。詳しくはこちらの記事(英語)を参考にしてください。

最後に、モバイル端末の特徴を再確認することをお勧めします。一般的に、デスクトップPCと比べて、より低速なCPU、バス転送速度、そしてメモリー容量に制限されています。デスクトップPCでtranslate3dが問題なく動作しても、モバイル端末では同じように動作するとは限りません。また、プラットフォームによってハードウェアアクセラレーションの対応度に違いがあり、そのパフォーマンス結果はまちまちです。

translate3dを効果的に用いると、必ずパフォーマンス向上にとってプラスとなるでしょう。一方、使い方によってはマイナス要因となる可能性もあります。上手に活用し、必ず・必ず・必ず、実機でのテストを行いましょう。

コンテンツのリフローが与える影響


「リフロー」という言葉を初めて聞いた方は、本項目に注目してください。リフローとはブラウザーエンジンの処理内容の一つで、HTML DOM要素の位置や座標を計算する処理となります。たとえば、各DOM要素の横幅・縦幅の計算、テキストの改行処理、相対的な位置計算などがリフロー処理の例となります。

リフローの計算は負担が大きいものなので、効果的にパフォーマンス向上を実現するためには、リフロー処理の頻度を最小限に止めることが重要です。HTML DOM要素の横幅を変更するようなアニメーションを作成し、フレームレートが5fps程度となってしまうのは、このリフロー処理が原因と考えられます。

リフロー処理は、DOMの内容を変更する、DOM要素をリサイズする、CSSによる位置変更や余白の変更を行う、といった場合に実行されます。デスクトップPCでは気にならない程度の処理時間ですが、モバイル端末においてはパフォーマンス低下の大きな要因となります。しかし、静的なページでない限り、動的に変更する際のリフロー処理を完全に無くすことはできません。そのため、リフロー処理の負担を軽減する方法について紹介します。

リフロー処理に関する詳細は、下記の記事を参照するといいでしょう。



PhoneGapアプリ開発のテクニックを紹介する多くの記事で、「DOM要素を最小限にとどめること」や「深くネストしたHTMLを避ける」こと、CSSアニメーションや画像のプリロードを行うこと、などが推奨されています。これらはすべて、リフロー処理を最小限にとどめるためのテクニックとなります。

・DOM要素の数を減らす

DOM要素の数が減るほど、リフロー処理において測定・計算する対象要素が減ります。

・深くネストされたHTML DOM構造を避ける

HTML構造が深くなるほど、リフロー処理はより複雑になり、計算量が増大します。さらに、末端の要素における変更がすべての親の階層に至るリフロー処理を発生させ、より多くの計算量が必要になります。

CSS transformを用いる

先述のハードウェアアクセラレーションに関するテクニックに加え、CSS3のtransformを用いるとリフロー処理を行わずにHTML DOM要素を変更することができます。たとえば、X・Y・Z軸に対する変換や拡大・縮小処理、回転処理などです。

CSS AnimationとCSS Transitionを用いる

CSS AnimationとTransitionを用いると、一気に高速化されます。しかし、すべての場合で有効である訳ではありません。リフロー処理を引き起こすCSS操作を行った場合(たとえばwidthやheightプロパティーの変更など)パフォーマンスの低下が発生します。こういう場合は、上述のCSS transformを用いることで、リフロー処理を抑制できます。

・DOM要素に対して固定の幅と高さを設定する

コンテンツのサイズを変更しない場合には、リフロー処理は実行されません。これは<div>要素だけでなく、画像を読み込む際にも該当します。画像サイズが固定されず、画像の読み込みが行われた場合は、読み込み完了のタイミングでリフロー処理が実行されてしまいます。複数の画像が用いられている場合は、その都度リフロー処理が実行されます。

CSSスタイルで用いられる画像を事前に読み込む

これには2つのメリットがあります。まず、画像が必要になった際に既に利用可能となります。これにより、表示遅延やちらつきを抑えることができます。そして、事前に画像などを読み込んでおくと、画像などが読み込まれた後に実行される2回目のリフロー処理(1回目はDOMが最初に計算された際、2回目は読み込みの際に発生します)を回避できます。

・HTML DOM要素を賢く使う

たとえばJavaScriptの配列で格納されたデータに対して、<table>要素を作成することを考えてみます。事前に%lt;table>要素を設置し、毎回のループで既存のDOMに各行を追加する処理は、非常にコストの高い処理となります。こういった場合は、まずはJavaScriptの配列から、%lt;table>要素内のHTML DOM要素を作成してしまいます。次に、そのループ完了後に%lt;table>要素を既存のHTML DOMに追加します。こうすることで、リフロー処理を最小限に抑えることができます。

このように、DOM要素のレイアウト計算や位置計算の処理を減らすことが、パフォーマンス向上につながります。

グラフィックはシンプルに



デザイナーの方はすばらしい見栄えのモックアップを作成しますが、それらのデザインに忠実に従ったアプリはパフォーマンス低下の原因となります。CSSシャドゥやCSSグラデーションを使いすぎると、プラットフォームやブラウザーによっては速度低下が発生します。たとえば、これらの効果はiOS端末と比較して、Android端末のパフォーマンス低下が顕著です。

タッチ操作



「マウスイベントは遅くタッチイベントは速い」という内容を聞いたことがあるでしょう。これは事実で、「mousedown」「mousemove」「mouseup」もしくは「click」イベントを使わず、「touchstart」「touchmove」そして「touchend」イベントを利用しましょう。

モバイル端末では、OSレベルでマウスイベントの低下が発生します。OSはジェスチャーが発生したかどうかを識別します。そしてジェスチャーが発生していない場合、マウスイベントとしてそのイベントをWebViewに渡します。タッチイベントを用いると、OS側の遅延なくWebViewにイベントが伝わります。

ただし「click」に相当するタッチイベントが欠如しています。自作することも可能ですし、「taps」というイベントを発生させるライブラリーを用いることも一考です。下記のようなライブラリーが対応しています:ZeptoFastClickHammer.jsiScroll

JavaScriptの最適化



本質的に効率的なコードを記述すると、UIスレッドをブロックすることはありません。JavaScriptの最適化に関する記事をお読みいただくといいでしょう。



アプリを実装する際は、いろいろな最適化手法を考慮する必要があります。友達のプログラマー仲間にコードレビューを依頼するなどして、より良いコードになるよう努力してください。

ネイティブのパフォーマンス



ネイティブUIを用いたアプリ開発を行いたいけれども、HTMLでその内容を記述したいとしましょう。実は、PhoneGapがネイティブアプリのサブビューとして利用できます。

この手法はCordovaViewをネイティブアプリのサブビューとして使うものですが、ネイティブコードの開発経験が要求されます。これによりPhoneGapやHTMLのUIを活用しつつ、ネイティブコンポーネントを利用することもできます。

HTMLを用いたカスタムUIの実装はとても簡単です。CordovaViewを用いる方法は、各ネイティブとHTMLの強みを組み合わせることができます。

UIとUXに関して



開発者は優れたユーザー体験を提供することに努力したいと思っているはずです。そのなかで、よくある質問に対する私の回答を紹介します。その質問とは「私のアプリをどうやって、ネイティブアプリのルック&フィールと同一のものにできますか?」というものです。

私のこれに対する回答は、同一にしない、です。

これは、特定のプラットフォームに対して「近い動きをする」アプリを開発してはいけないという意味ではありません。私が伝えたいのは、すべての細かい挙動までOSに合わせる努力をしない方がいいということです。これは、1)とても困難であるため2)OS側での変更が発生すると、あなたのアプリケーションの不一致がより大きなものとなるため、という理由からです。

不気味の谷」現象という言葉をご存知でしょうか。PhoneGapアプリを開発する際、ネイティブアプリと同一の挙動を作りこむほど、細かい差異が「何か違う」「何かがおかしい」と思うようになってしまいます。これがユーザーにとってマイナスのイメージにつながります。

そのため、アプリが固有のUIを実装することを推奨します(App Storeガイドラインに適合する範囲である必要があります)。たとえば、ルック&フィール、ボタンのスタイル、ナビゲーションなどが該当します。すべての側面からネイティブUXを目指すのではなく、あなたのアプリが提供する固有のブランドをUXとして提供しましょう。利用者はあなたのアプリに対して唯一の評価とし、ネイティブOSとの比較は行わないでしょう。

一方で、もしネイティブのルック&フィールを実現する場合は、CSSスタイルを用いることで完全に再現することができます。

実機でテストする



非常に重要なことです。常に、実機でテストを行いましょう。必ず、です。私は旧機種のテストを行います。それは、旧機種で速度が出せた場合、新機種ではより高速に動作するためです。対象となるすべての端末に対して、テストを行いましょう。私の場合、iOSではiPhone 4(4Sではありません)とiPad 2を対象としています。AndroidではMotorola Atrix、Nexus 7タブレットKindle Fire(第一世代)、そしてSamsung Galaxy 10.1(第一世代)でテストを行います。デバイスを借りてほかのプラットフォームに対するテストを行うこともあります。さらに、店舗に出向き展示端末にアプリをインストールして、アプリの見た目を確認することもあります。




翻訳した感想



日本語におけるPhoneGapアプリ開発のノウハウが、英語での情報と比べてまだまだ少ないと実感しています。今後、目にとまったテクニックを随時紹介していきたいと感じました。

記事に触れられている「ネイティブUIの活用」ですが、弊社が提供するPhoneGap開発環境であるMonacaでは実装されています。興味のある方は、こちらのドキュメントを是非ご覧ください。

Monacaのインスペクターはモバイル版Webデベロッパーツール

最近たびたび登場する田中です。本日はMonacaが持つインスペクター機能を紹介したいと思います。

先に宣伝を。PhoneGapで学ぶマルチスクリーン対応ハイブリッドアプリ開発はPhoneGapの概要から始まり、Monacaを使いながら実際のアプリを作っていくという連載になっています。ぜひご覧ください!



本題です。Monacaでは「インスペクター」という機能がありまして、実機上で動作しているアプリの中身をMonaca IDE上で閲覧することができます。

インスペクターには、下記のような機能が搭載されています:
1.表示されているページのDOMを表示し、その内容を書き換える
2.ローカルストレージやセッションストレージの内容を操作する
3.グローバル変数の内容を確認する
4.JavaScript関数をリモートから実行する

ここでは、簡単にこれらの使い方を紹介します。

○ どうやって使うの?



端末にインストールしたMonacaデバッガーで、IDE上と同じプロジェクトを実行してください。Monaca IDEにある「インスペクター」にて、自動的に端末上で表示されているページ情報が出力されます。

では使い方を見ていきます。

○ 表示されているページのDOMを表示し、その内容を書き換える



「Elements」タブをクリックすると、DOMツリーを確認できます。閲覧だけでなく、スタイルを動的に記述したり、HTMLの内容を変更することも可能です。



○ ローカルストレージやセッションストレージの内容を操作する



「Resources」タブでは、そのページが持つリソース情報を参照できます。



例として、今回のサンプルでは、下記のようにしてローカルストレージに値をセットしています。



window.localStorage.setItem("test1", "value1");
window.localStorage.setItem("test2", ["val1", "val2"]);


画面の通り、実際にセットしている値を閲覧するだけでなく、値の削除や変更も可能です。

グローバル変数の内容を確認する



「Console」タブでは、JavaScriptコンソール機能を利用できます。



この例では、getElementsByTagNameを用いてDOMの情報を読み取っています。

JavaScript関数をリモートから実行する





このタブでは、関数を実行することもできます。

例として、window.device.platformというPhoneGapが持つ変数にアクセスし、端末の種類を取得しています。

jQueryなどの関数も実行できますので、手軽にグローバル変数の値を表示できます。

このように、Monacaで開発している際のJavaScriptデバッグは、インスペクターを用いると便利なケースが多いと思います。ちなみにインスペクターの機能改良も予定していますので、そのうち新しいバージョンのインスペクターをお披露目できる予定です。

モバイル開発でChromeなどのWebデベロッパーツールやFirebugのような機能を使ってみたい方は、ぜひMonacaを試していただければと思います。

PhoneGap 2.3.0での変更点

田中です。PhoneGapブログにて、近日リリースされるiOS版PhoneGap 2.3.0の機能ハイライトが紹介されていたので紹介したいと思います。

1. Cordova.plistはconfig.xmlに変更となります

これまでAndroid版ではxmlという形式で、iOS版ではplist形式でPhoneGapに関する設定を記述していました。PhoneGap 2.3.0からは、Androidと同じconfig.xmlのフォーマットに切り替わるようです。

2. ChildBrowserと同じ機能を有する「InAppBrowser」という機能が追加されます

window.openに、下記のような指定方法が可能となります。イベントも受け付けます。



var ref = window.open('http://google.com', '_blank');
ref.addEventListener('loadstart', function(event) { alert(event.type + ' - ' + event.url); } );
ref.addEventListener('loadstop', function(event) { alert(event.type + ' - ' + event.url); } );
ref.addEventListener('exit', function(event) { alert(event.type); } );


なお、targetは_blankだけでなく、_selfと_systemという指定も可能です。_systemとすると、システムのWebブラウザーで開きます。Monacaでいうmonaca.invokeBrowserですね。

3. ホワイトリストの仕様変更

PhoneGapの設定で指定するホワイトリストURLは、今後アプリのメインWebViewのみ適用されるようになります。その結果、プラグインから接続をした場合は対象外となります。

4. デバイスAPIの仕様変更

device.platformが"iPhone"や"iPad"と返していたものが、"iOS"と返されます。一方、device.nameにて"iPhone"などの表記が返されます。

それ以外にも、LocalNotificationイベントをプラグインが受け取る仕組みの追加やCLIコマンドの改良などが加わっています。

最近は少しリリースのペースも落ちてきたかなと思いましたが、まだ仕様変更が含むアップグレードが続きそうです。Monacaでは今のところPhoneGap 2.2をサポートしていますので、現時点でアップデートを行う予定はありませんが、皆さまの参考になれば幸いです。

シリコンバレー研修2012 第1班 報告記

こんにちは、たびたびの田中です。

昨日のブログ記事にも記述しましたが、先週一週間、アシアル社員のシリコンバレー研修を行っていました。

シリコンバレー研修は、毎年、希望者に対して実施しているもので、今年は人数が多いため2班に分けることになりました。その最初の班が無事日本に帰国しました。

シリコンバレー研修とは?

2007年から毎年行われている、会社の社員研修となります(ただ去年は忙しくて実施できませんでした)。目的は、シリコンバレーで仕事をする人々の考え方を知り、交流を深めることです。

言うまでもなく、ITの中心はシリコンバレーです。アシアルの事業である「PHP」「HTML5」「スマートフォン」「JavaScript」などのテクノロジーは、例外なくシリコンバレーで育まれ、世界に広がっています。ITの世界で有名な会社の本拠地もいっぱいです。

そのシリコンバレーでは、当然ながら大勢のエンジニアが仕事をしています。彼らも私たちアシアルのエンジニアも、毎日会社でコードを書き、デバッグし、リリースをし、サーバーの運用をしています。決してシリコンバレーにいるエンジニアが特別な訳ではありません。

○ アシアルにとってシリコンバレー研修がなぜ必要か?

その仕事ぶりを見ると、私たちと価値感や考え方が違うなぁと感じることがあります。前回の記事にあったプロダクトマネジメントもそうですが、自分たちの仕事をより多くの人に伝えることを重要視しています。

私たちのようにITの世界で生きており、否が応でもグローバル目線が求められる仕事をしている人たちは、そのような文化や仕事の取り組みの違いについて、理解する必要があります。

約1週間程度の研修の中では、企業訪問を中心として、人々のライフスタイルや仕事の考えかたについて学びます。実際の内容は、参加者がプランを立てて、行動に移します。

今回は、私を含めて5名の人間が参加しました。明日から4日間、参加者による記事が続きますのでお楽しみください。

☆ 参考リンク: アシアルの過去のシリコンバレー研修

Inspired輪読会

お久しぶりです、田中です。

5月はGW明けから昨日まで、中国に行ったりサンフランシスコに行ったり、激しく動き回っておりました。おかげで素晴らしい出会いがあったり、新しい知見を得ることができたり、非常に充実した1ヶ月を過ごすことができました。

そのなかの1つに、素晴らしい書籍との出会いがありました。Evernoteの方とお話した際にでてきた「Inspired」という本です。日本語訳が近々でるということで、今回は簡単に紹介したいと思います。

○ プロダクトマネージャーのための本

シリコンバレーの会社では「プロダクトマネジメント」という役割があります。

そもそも日本には、このプロダクトマネージャーが不在だよね、という話から、この書籍の紹介を頂きました。たとえばAppleであれば、すべての製品の意志が統一されており、ユーザーに対してその製品の考え方が非常に明瞭です。一方、日本のメーカーの場合、その機能がその製品にとって、どういった意味があるのか、よく分からないものも多くあります。

これはひとえに、プロダクトマネジメントが機能していない結果です。書籍にはこう記されています:

プロダクトマネージャーとは、その組織が作る製品を定義する責任を負う人である

○ 書籍「Inspired」について

下記リンクをご覧頂くとおわかり頂くと思いますが、非常に高い(5点満点中4.8点)評価の書籍です。シリコンバレーで数々の製品・Webサービスに携わったMarty Cagan氏による書籍で、とても具体的かつ明瞭なアドバイスに溢れています。



○ なぜInspiredを読む必要があるか?

アシアルの売り上げの過半は受託開発によるものです。この場合、そもそもWebサイトや製品の定義は私たちで行う訳では無く、先方(クライアントさん)による仕様を実装することがメインの役割となります。もちろん、私たちが要件定義を行ったり、仕様策定を行うこともありますが、それは、クライアントさんのその役割を肩代わりしているに過ぎず、そのWebサイトの成功/失敗が本質的にアシアルにとって影響を与えることはありません。

しかし、Monacaのように、自分たち自身のサービス、なおかつグローバル展開を前提としたサービス、を推進していくにあたり、このプロダクトマネジメントの役割は非常に重要です。まずは、その役割を認識し、実践する必要があるということで、特にMonacaチームがプロダクトマネジメントについて学ぶこととなりました。

○ 輪読会

Kindle版では9.99ドルという事で、洋書ですがそんなに難しい文体もなく、良い輪読教材です。

手始めにアシアルでは毎週木曜日、午前10時から、輪読会を行います。
輪読に興味あるかたは、是非、私までコンタクトしてください!

Amazon.comへのリンク

screen(だけ)の時代は終わり。tmuxでリモートコンソールを便利に使うTips

※ コメントをいただきましたので、一部本文を修正させて頂きました。
1.サーバー・クライアントモデルというのはネットワーク接続なイメージになってしまいますので、取り外しました
2.mouse-utf8に関してコメントを頂いたので補足しました
3.タイトルを「screenの時代は終わり。tmuxでリモートコンソールを便利に使うTips」から「screenだけの時代は終わり。tmuxでリモートコンソールを便利に使うTips」に変更しました(変な誤解を受けられた方がいらっしゃったため)

お久しぶりの田中です。最近、ずっと大型案件に携わっていた関係で、あまりブログ等でのアウトプットができていませんでした。これからはドンドン書いていきますので、どうぞよろしくお願いします。本日はGNU screenと同様の機能を持つtmuxを紹介します。

GNU screenやtmuxは、1つのターミナル画面上に複数のコンソールウィンドウを表示するためのソフトです。デファクトスタンダードであるGNU screenと比べて、tmuxは比較的最近、注目を浴びています。私も1年ほどtmuxを常用し、便利に使わせていただいています。ここでは、tmuxを便利に使うために僕が行なっている設定を紹介します。



○ tmuxの特徴

screenとは異なり、tmuxはクライアント・サーバーモデルとなります。tmuxサーバーが常時起動し、tmuxクライアントのattachを待ちます。そのため、複数のPCで同じ画面を表示することができます。

・また、tmuxでは1つの画面(window)を複数のコンソールに分割する(複数pane)ことができます。その分、tmuxでは、client、window、paneという概念があり、screenよりは複雑になっています。

コマンドラインも充実しています。ウィンドウ分割やバッファ操作などの命令は、キーバインドだけでなく、tmuxコマンドを用いても指示を出せます。これを使って各サーバーの自作管理ツールを作っていますが、その話はまたどこかで言及したいと思います。

○ tmuxを起動する

Debianの場合、



apt-get install tmux


でサクっとインストールできます。tmuxコマンドをタイプすると、コンソールが表示されると思います。

○ よく使うtmuxコマンド

私は下記のコマンドをよく使います:



tmux attach - すでに開いたセッションにアタッチする
tmux list-windows (C-b w) - ウィンドウの一覧を取得する
tmux new-window (C-b n) - 新しいウィンドウを作る
tmux detach-client (C-b d) - クライアントをデタッチする
tmux list-keys (C-b ?) - キーバインドの一覧を表示する
tmux next-window (C-b n) - 次のウィンドウを表示する
tmux previous-window (C-b p) - 前のウィンドウを表示する
tmux kill-window (C-b k) - ウィンドウを強制的に閉じる


tmux attach以外は、すべてキーバインドアサインされています。デフォルトはCtrl-bですが、.tmux.confを編集することで変更可能です。

○ tmux.confでカスタマイズ

ホームディレクトリの.tmux.confを作成して編集することで、キーバインドの変更や、オプションの設定を行えます。ここでは、私が設定している設定について説明します。

1.バックスクロール行数を増やす



set-option -g history-limit 10000


この例では、tmuxのヒストリサイズを1万行に設定しています(デフォルトは2000行)。tmuxではウィンドウごとにバッファを管理しており、さかのぼって検索するだけでなく、コピー&ペーストを行うことも可能です。

2.ステータスラインの見た目変更



set-option -g status-utf8 on
set-option -g status-interval 5
set-option -g status-bg black
set-option -g status-bg white
set-window-option -g window-status-current-bg blue
set-window-option -g window-status-current-fg white


この辺の設定はお好みで。マニュアルを見ながらカスタマイズすると良いでしょう。

3.マウス操作に対応する



set-option -g mouse-select-pane on
set-option -g mouse-select-window on
set-option -g mouse-resize-pane on
set-option -g mode-mouse on
# PuTTYを使う場合は、下記の設定はoffを推奨(コメントを参照してください)
set-option -g mouse-utf8 on


上記設定により、私が普段使っているPuTTYにて、マウスが使えるようになります。マウスの範囲選択でバッファへのコピー&ペーストを行ったり、ステータスラインでマウスクリックによるウィンドウ選択などが可能になります。PuTTYの場合、デフォルトのマウス動作(Windows標準クリップボード)を使いたい場合は、Shiftキーを押しながら操作します。

4.キー割り当てのカスタマイズ



bind -n C-Space next-window
bind -n M-Space previous-window


私はフルスクリーン作業を中心にするため、頻繁にウィンドウ切り替えを行います。そのため、Ctrl-スペース、およびAlt-スペースをウィンドウ操作に割り当てています。

5.画面に表示されている内容を自分宛にメールで送る



bind S run "tmux capture-pane -S -10000; tmux show-buffer | /usr/sbin/sendmail masahiro@example.com"


よく、今見えている画面を残しておきたい(ログがわりに取得しておきたい)ことがあります。そういう時には、上記設定が便利です。Ctrl-Sをタイプすると、バッファの1万行分の画面表示が、自分のメールアドレスに送信されます。

上の例のように、tmuxではバインドキーに対してシェルスクリプトを実行できます。使い方次第では、他にも応用例が考えられるでしょう。

○ tmuxの注意点

ウィンドウを多数開いている場合、スクロールバッファを増やしすぎると、tmuxサーバープロセスのメモリ使用量が非常に大きくなり、固まってしまうことがあります。tmuxの安定性に対して疑問に思うことはありませんが、あまりスクロールバッファを大きくすることは控えたほうが賢明です。

GlusterFSでファイルシステムクラスタを簡単作成

最近アシアルのファイルサーバーに大規模障害が発生し、改めてリカバリ工数の必要性について考えさせられました。今回の主原因がRAIDアレーがデグレードするようなHDDの物理故障であったため復旧まで時間がかかりましたが、これがきっかけとなりサーバー同士でファイルをミラーし合うような仕組みが構築できないかと探した結果、GlusterFSを用いたクラスタ構成を評価する事にいたしました。

クラスタファイルシステムとは?

クラスタファイルシステムとは、ファイルシステムクラスタリング機能を持つものとなります。たとえば、RAID 1RAID 5といった仕組みでディスクの冗長性を確保するのとは異なり、ファイルシステムクラスタリングを用意します。

このようなクラスタファイルシステムでは、下記のような仕組みが有名です。
・DRBD → 特別なブロックデバイスを用いて、複数台のマシンでクラスタリングを行う仕組み。
ZFSファイルシステム自体がRAID同様の機能を有する。複数台のマシンでクラスタリングを行うことはできない。
ただし、これらは、OSのレイヤーで動作することから、万一論理故障などによる障害が発生した場合にはその復旧が難しく、導入を見送っておりました。

今回紹介するGlusterFSは、ユーザーレベルでのファイルシステムクラスタを実現する仕組みとなります。具体的には、LinuxベースのOSと、XFSやEXT4といったファイルシステムで構成されたディスクを用いて、複数台のマシン間でディスクの同期を取ることが可能となります。これは便利、かつシンプル、という事で、この記事ではGlusterFSのインストールまでを説明します。

○ GlusterFSのインストール

この記事では、現在公開中のバージョン3.2を対象としています。

インストールといっても、CentOSDebianの場合、専用のパッケージが用意されています。まずは、公式サイトより、ディストリビューションにあったパッケージをダウンロードしてください。

Debianの場合:


wget http://download.gluster.com/pub/gluster/glusterfs/LATEST/Debian/glusterfs_3.2.3-1_amd64.deb
dpkg -i glusterfs*.deb


インストールすると、GlusterFSのデーモンであるglusterdと、その管理ツールであるglusterコマンド、そしてFUSEを用いてマウントするmount.glusterfsがインストールされます。

GlusterFSのインストールは以上です。非常に簡単ですね。

○ GlusterFSはNFSサーバー

GlusterFSは、共有ストレージをボリュームという単位で管理します。ボリュームはGlusterFSが持つNFSサーバー機能を用いて、各クライアントからマウントすることができます。また、FUSEを用いた専用のglusterfs形式でのマウントも可能です。

たとえ自分のサーバーがglusterdを動作している場合でも、GlusterFSのボリュームにアクセスを行うためにはlocalhostでのマウントを行う必要があります。

○ GlusterFSことはじめ

GlusterFSでは、次の流れでクラスタを作成します
1.ピアの作成
2.ボリュームの作成
3.ボリュームの開始
順を追って説明します。これらはGlusterFSが提供するCLIであるglusterコマンドを通じて操作します。glusterコマンドを実行し、プロンプトを表示してください。


$ fileserver-1:~$ gluster
gluster >


1.ピア(peer)の作成

GlusterFSでは、まずクラスタを構成するサーバーの情報を登録します。


gluster> peer probe [fileserver-2]


ここで、[fileserver-2]には、クラスタを構成する別のサーバーを登録します。登録されたファイルサーバー側には、自動的にピア情報が転送され、ピア情報の共有が開始されます。

2.ボリュームの作成

複数のピアにまたがってボリュームを作成します。ボリュームは下記のモードで選択できます。
1.ストライピング(stripe)
2.レプリカ(replica)
の2種類のモードのいずれか、もしくは組み合わせでボリュームを作成できます。

今回は、冗長化を目的として、サーバー2台のディスクで複製された、1つのボリュームを作成します。


gluster> volume create [VOLUME-NAME] replica 2 transport tcp [fileserver-1:/path/to/brick] [fileserver-2:/path/to/brick]


内容は下記の通りです。
- VOLUME-NAME: ボリューム名、NFSFUSEでマウントする際の名前に利用されます。
- fileserver-1:/path/to/brick: 1台目のマシンのホスト名と、そのホスト名上の共有スペース置場
- fileserver-2:/path/to/brick: 2台目のマシンのホスト名と、そのホスト名上の共有スペース置場
ここで注意が必要なのは、/path/to/brickで指定されたパスは、実際のデータの格納に用いられるもので、新たなディレクトリを指定する必要があります。

3.ボリュームの開始

作成したボリュームは、volume startコマンドで開始する必要があります。これをしないでFUSENFSでマウントすると、エラーが発生したり、マウントは成功するもののファイルにアクセスできないといった現象になります。


gluster> volume start [VOLUME-NAME]


実際に開始したことの確認は、


gluster> volume info all

Volume Name: VOLUME-NAME
Type: Replicate
Status: Started
Number of Bricks: 2
Transport-type: tcp
Bricks:
Brick1: fileserver-1:/path/to/brick
Brick2: fileserver-2:/path/to/brick


で確認が可能です。

○ マウントする

先述の通り、マウントはNFSFUSEクライアントを利用できます。先ほど例では、それぞれ下記のようなコマンドとなります。


# mount -t nfs fileserver-1:/VOLUME-NAME /mnt/hoge
# mount -t glusterfs fileserver-1:/VOLUME-NAME /mnt/hoge


マニュアルには、速度やフェイルオーバー対策のために、FUSEを用いたマウントが推奨されています。CIFS(Windowsのファイル共有形式)にも対応していますが、こちらは用いたことがありません。

○ パフォーマンスは?

きちんとしたパフォーマンスについては、近々検証してみたいと思います。ざっと確認した限り、Duplicateモードでボリュームを作成した場合、各サーバーの同期が取られるため、ネットワーク速度がボトルネックとなる様子です。

○ その他良いところ

GlusterFSでは、Geo-Replication機能も備わっています。これは、定期的にrsyncコマンドを自動発行する形で、SSH経由などで外部サーバーへのレプリケーションを実施してくれるものです。

GlusterFSの所感としては、非常にシンプルで分かりやすい仕組みだと感じました。DRBDのようなOSレベルのレイヤーがなく、万が一glusterdに問題が発生した場合も、OSのファイルシステムレベルではファイルが残っている安心感があります。インストールや管理が簡単なのもポイント高です。

別のエントリとなりますが、パフォーマンス検証の結果についても報告したいと思います。

ウィンドウを捨て、フルスクリーンUIに切り替えたWindows 8

お久しぶりです、田中@LAです。現在、マイクロソフトが開催中のBuild Windowsカンファレンスに参加しています。

最近、弊社ではMonacaというモバイル端末向けのプラットフォームの開発に勤しんでいます。iPhoneに始まった本当の「モバイル端末」の流れは、スマートフォンだけでなくタブレット、そして今後はPCにも影響を及ぼすことは必至です。Monacaは、そういった各種デバイスを統合的に開発・運用するためのプラットフォームを目指し、クラウド時代のアプリ開発を提案したいと思います。

と、Monacaの宣伝はここまでにしておき、実際にWindowsタブレットが登場する近い将来、そのWindows 8が提供するタッチ操作を中心とした新しいUI(Metro UI)に非常に興味があったため、今回Build Windowsに参加しています。今日はその1日目が終わり、いろいろなサプライズと共にWindows 8の考える将来を垣間見ることができました。その中から、私が感じたことを綴っていきたいと考えています。

タブレットやUltra Book構想に向けて、超低消費なアーキテクチャを採用+ARMに対応

興味深かったのは、Metro Styleを採用したアプリはバックグラウンド移行時に「サスペンド」状態になることでした。これは、iOSなどと同じ考え方で、これにより、非常に低い電力でアプリを起動し続けることができます。メモリが足りなくなった際に古いアプリから自動的にプロセスが落とされるのも、Androidなどと同じ考え方です

また、Windows 8ではARMにも対応します。ARMは、iPhoneiPad、ほぼすべてのAndroid端末やWindows Phone端末に搭載されている、消費電力あたりの処理速度が非常に高いCPUです。このことから、低コストで長時間駆動可能なWindows PCあるいはWindowsタブレットが数多く登場することでしょう。

○ Metro Styleにより、従来と全く異なるWindows経済圏が誕生

今回のカンファレンスで大きく取り上げられているMetro Style。UIを含む新しいAPIセットであるWinRTアーキテクチャの上に、HTML+JSやXAMLC#でアプリ層を記述するということからも、完全に過去の互換性は切り捨てています。ポイントは、ARM版でも再コンパイル無しに動作することと、完全にこれまでと異なるUIを持つことです。

スタートボタンをクリックすると、Windows 8では従来のスタートメニューではなく、Metro UIのスタート画面が表示されます。Metro Styleが持つMetro UIでは、ウィンドウベースのUIから、タイルベースのUIに大きく変わりました。タブレットなどのタッチパネル搭載端末で、非常に操作性の高いものとなっています。

このMetro UIはタイルベースのUIです。これ実現するWinRTプラットフォームでは、「重ねた状態のウィンドウ(Cascaded Window)」を排除するため、そういったAPIを一切無くしたそうです。なんとMessageBoxも廃止されており、実際にVisual Studioのデモで「MessageBox」関数を使おうとしてエラーになっている例が印象的でした。

このWindows 8を簡単に触ってみましたが、とても可能性のあるOSだと感じました。OSとしては地道に進化をしており、デスクトップUIのタスクマネージャーやエクスプローラなどが機能的にも改良されていて、使いやすくなったと感じます。

一方、Metro UIは既存と全く異なるUIとなるため、どの程度浸透するのかが不明なところではあります。WordやExcelなどはMetro UIではなく、従来のDesktop UIでの提供となることでしょうから、ユーザーは両方のUIを使い続けることになると思います。ただ以前の「サイドバー」と異なり、本格的なAPIセットが提供されているMetro Styleが今後のアプリケーションの中心になる可能性は十分に高いと感じています。

○ 時代はHTML5+JavaScriptの流れへ

Metroアプリは、従来通りのC++C#Visual Basicに加え、HTML5JavaScript+CSS3で開発することができます。Monacaのように、JavaScriptでクライアントアプリを開発できるプラットフォームがほとんど存在しない中、これは非常に画期的なことです。Webブラウザーレンダリングのために生まれた技術が、このようにクライアントアプリでも十分なパフォーマンスとUIを表現できることを実証できたことから、ますますHTML5+JavaScriptの流れは加速すると確信しました。

まだ初日が終わったばかりで、カンファレンスはまだまだ続きます。サムソン製のWindowsタブレットが無料で配られたり、どうやら明日はVisual Studioに関するプレゼントが用意されていたりと、非常に充実した内容です。そこからも、マイクロソフトの対AndroidiOSの意気込みが感じられました。

キーノートスピーチはBuild WindowsのWebサイトから閲覧できますし、Windows 8 Developer Preview版もダウンロードできます。興味のある方は、是非試してみてはいかがでしょうか。