[モバイル開発]リソースの外部化について

前回は、不具合の根本原因を特定する方法についてご紹介しました。
今回は、モバイルアプリでのリソース外部化についてご紹介します。

モバイルアプリを作成する際、画像・音といった様々なリソースを使用することになります。
iOS/Androidのストアには、アップロード可能な上限が決まっていますが、演出や見た目にこだわると、どうしてもリソースの数が多くなってしまい、これらをアプリに内包して作成したipa/apkのサイズが大きくなり、すぐに上限を超えてしまいます。
また、リソースの一部を修正したいとなった場合でも、再度アプリを作成しなおしてストアにアップロードしなければなりません。
iOSでは、アップロードする際に申請を行わなければいけないため、すぐに更新ということも出来ず、少しの変更を加えるだけでも非常に煩わしいですね。
こういった問題を解決するのが、リソースの外部化です。
アプリで使用するリソースをウェブサーバー上に配置しておき、クライアント側ではそれらを取得して画面に表示します。
こうすることで、アプリのサイズを抑えながら、見た目も凝ったアプリを作成することが出来ます。

モバイルアプリを作成する際、多くの方がライブラリもしくはゲームエンジン(以下、作成ツールと表記)を使用していると思います。
有名なものには、リソースを取得するための機能が備わっていることも少なくありませんが、それぞれの作成ツールによって使い方が異なったり、バージョンアップによって使い方が変わってしまった等、覚えるのが大変だと思っている方もいるのではないでしょうか。
そこで、今回はリソース外部化を自作してみようと思います。

仕組みといってもそんなに大層なものではなく、必要な作業はの下記の3つのみです。
1.「アプリ内保存パス」「サーバー上のパス」「リソースバージョン」の情報をもったjson形式の「リソース管理ファイル」を用意し、サーバーに配置
2.クライアントでは、起動時にサーバーからjsonファイルを取得し端末内に保存
3.取得したjsonファイルをパースして、「取得対象条件」と一致したリソースをダウンロード

上から順に説明します。
まず、jsonファイルの内容として、「アプリ内保存パス」「サーバー上のパス」「リソースバージョン」の3つを記載しましたが、具体的には下記になります。

{
    "version": バージョン値,
    "timestamp": 適当なタイムスタンプ,
    "resources": {
        アプリ内保存パス: {
            url: サーバー上のパス,
            v: リソースバージョン
        },
        //以下例
        "texture1": {
            url: "images/tex_sample1.png",
            v: 1
        },
        "sound1": {
            url: "sounds/bgm_sample1.png",
            v: 1
        },
    }
}

サーバーで管理しているリソース一覧を、resourcesキー下に書いておき、クライアントでは、ここにあるデータを元にサーバーへのリソース取得を行います。
保存パスや、サーバー上のパスをフルパスで指定していないのは、接続先が環境によってことなるということを考慮したものです。
例えば、一般的には開発環境・ステージ環境・本番環境ではドメインが異なりますが、ドメインまでこのファイルに入れてしまうと、環境ごとにファイルを用意しなければなりません。
リソースが少ないうちは全てのファイルに同じデータを追加して行けばよいですが、数が多くなってくると追加漏れや1ファイルだけ誤ったデータを記載等、様々な問題が発生する可能性が高くなってしまいます。
こうしたヒューマンエラーを防ぐためにも、あえてドメインは記載せず、共通でかける部分(※1)のみを管理ファイルに記載するようにします。
※1.全ての環境でサーバー内ディレクトリ構成が同じである前提

ここまでで、ファイルの内容から自動的にリソースを取得する部分までは出来ましたが、まだ不足していることがあります。
差分比較ですね。
ファイルに記載しているリソース全てを毎度取得しても問題はありませんが、ファイルに更新がないものは一度取得したものを使いまわせると、余計な通信を抑えることもできて、とても効率的です。
そのため、一度取得したリソースは、クライアント内部へファイル保存します。

ここまでの流れを一度整理してみましょう。

—————————————
1.クライアント起動

2.サーバーからリソース管理ファイルを取得

3.内容を解析

4.取得対象のファイルがクライアントに存在するかを確認
│ ※対象ファイルがクライアントに存在する場合は、6へ

5.通信でリソースを取得して、クライアントに保存

6.リソース管理ファイルを保存して終了

—————————————

これで基本的な仕組みは出来ました。
しかし、今の流れではリソースの取得有無をファイルが存在しない場合のみで判断しているため、「リソースの更新」に対応出来ていません。
サムネイルの修正/ボイスのセリフ変更等、アプリで使用するリソースというのは、様々な理由で更新が発生する可能性があるため、各リソースに「リソースバージョン」を持たせることで、差分更新を可能にします。

一度取得したリソース管理ファイルはクライアントに保存しているため、サーバーからリソース管理ファイルを取得した際、クライアントに前回取得したファイルが保存されている場合、それぞれの内容を比較して条件と一致するもののみを取得対象として扱います。
取得対象条件は下記の通りです。
・サーバーのリソース管理ファイルのみに存在するデータ(新規追加ファイル)
・サーバー・クライアントのリソース管理ファイル双方にデータが存在するが、サーバー側のリソースバージョン値のほうが大きい(ファイル更新)
・クライアントのリソース管理ファイルにデータが存在するが、対象のファイルがクライアント内部に保存されていない場合(何かしらの理由でデータが削除された)

このようにすることで、「新規取得」「更新取得」「データ破損時の取得」それぞれのパターンに対応できます。

最後、一番気を付けなければいけないのが、通信キャッシュです。
いくら差分更新の仕組みを用意しても、通信キャッシュが有効なことでサーバーの最新ファイルがDLできないことがあります。
その対策として、各リソースを取得するときにURLにランダムな文字列を付与することでキャッシュを無効化することが出来ます。
私は、jsonデータの中にランダム文字列を用意し、その値を通信時に付与しています。
例として記載したjsonデータの中にtimestampというキーがありますが、これがランダム文字列を格納するキーとなります。
リソース管理ファイルを更新する際にこちらの値を同時に変更しておくことで、リソースの更新があるたびに付与するランダム文字列を変えることが出来るため、更新のあったファイルは必ず最新のものを取得するようになります。

以上がリソース外部化を管理する仕組みの最低限の構成です。
外部化や管理といった単語が出てくると、それだけで複雑・難しいと思ってしまうかもしれませんが、それらに必要な項目や作業を明確にし、順序立てて組み立てていくと思っていたよりも簡単であるということが多いです。
各作成ツールの対象言語に合わせて処理を移植する必要はありますが、自ら仕組みを作ることで、使用する作成ツールに左右されずに同じロジックを使いまわすことが出来るため、新しく使い方を覚える手間がなくなり開発効率も上がります。
もちろん描画や通信自体等の複雑な処理が必要なものは専門的な知識も必要になるため、作成ツール側の機能を使用したほうが効率的であるため、全てを自作したほうがいいと思っているわけではありません。
どの部分を自前で自作するかは、製作者のスキルにも左右されるため、”これは自作したほうが効率的”と言い切ることは難しいです。
しかし、全ての機能を作成ツールの機能に頼るのではなく、応用が可能な部分を自作することで、開発がしやすさや選択肢の幅も変わってくるため、皆さんも挑戦してみてはどうでしょうか。


弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*