読者です 読者をやめる 読者になる 読者になる

woshidan's blog

そんなことよりコードにダイブ。

AndroidのNDKやABIについてのメモ

はじめに

C系の言語で動いているクロスプラットフォームのブリッジを作成するには、そのコンパイルなどの過程でNDKに関する知識があるとトラブルシューティングやテストケースの設計に役立つことがあります。

なので、ここではその概要をまとめておきます。はじめに用語からまとめようと思ったのですが、

https://developer.android.com/ndk/guides/index.html?hl=ja

Native Development Kit(NDK)は、Android アプリで C および C++ コードを使用できるようにするツールのセットのことです。NDK を使用して独自のソースコードをビルドしたり、事前にビルドされた既存のライブラリを活用したりできます。

https://developer.android.com/ndk/guides/concepts.html?hl=ja

アプリケーション バイナリ インターフェース(ABI)はアプリのマシンコードが実行時にどのようにシステムと連携するかを正確に定義したものです。 NDK はこれらの定義に対して .so ファイルをビルドします。

と言われてもよくわからなかったので、NDKがABIを使ってC, C++ソースコードから.soファイルをビルドする周辺で何をしているかを説明します。

.soファイルとは

と書いたものの、書いた本人は.soファイルについての事前知識が調査開始時点でなかったため、簡単に説明します。

soshared object fileの略です。

.soファイルは、UNIX系の環境下での共通ライブラリファイルのことで、Windowsでいうdllにあたります。具体的には、複数の実行ファイル間で共有して利用される処理の定義がまとめられています。

中身は実行形式ファイルですが、あくまで処理を含んでいるだけでエントリポイントがなく単体で起動することはできません。

各々の実行ファイルに含まれず、実行ファイルを起動した時に動的にロードとリンクが行われるため、動的(ダイナミック)リンクライブラリと呼ばれることがあり、Android Developers GuideのNDKについての記述では主に動的リンクライブラリ、と呼称されています。

Androidでは、アプリのコンパイル時にリンクされている方が.a(静的リンクライブラリ), アプリの起動時にロードされてリンクされる方が.soで、NDKを使ってABIに従って作成される方が基本的に.soという感じです。

自分が作成したライブラリが.so か、.aになるかはコンパイルの設定によりますが、Androidアプリのビルドの文脈だと、.soファイルの方しか、公式のドキュメントに記載がなかったので、こちらが多いかもしれません。

NDKを利用してビルドする際の流れ

https://developer.android.com/ndk/guides/concepts.html のフローをもとに適宜用語の説明を入れながら説明します。

  1. Androidプロジェクトを作成します
  2. Androidプロジェクト直下にjniディレクトリを作成し、ネイティブライブラリ(.aファイル, .soファイルなど.アーキテクチャごとにフォルダを用意したり…)とコンパイル対象のソースコード(CやC言語で書かれたソースコード)とそれらをコンパイルするモジュールにどう含むか記述していくAndroid.mkを配置
  3. 2のディレクトリに任意でターゲットABI, ツールチェーン, リリース/デバッグモード, STLを設定するApplication.mkファイルを作成(任意)
  4. デフォルト設定
  5. ABI: armeabi
    • ABIというのは、ネイティブライブラリをコンパイルする際、コンパイルした.so.aファイルがインストール対象の端末で動くことを保証するための規約みたいなものです。
  6. ツールチェーン: GCC 4.8(4.8 32bit / 4.9 64bit端末対応らしい)
  7. モード: リリース
    • リリースモードだと変数宣言が最適化されていたりして、C言語側のステップ実行が困難になったりします
  8. STL: system
  9. NDKツールを適切に利用するために作成されたシェルのラッパーであるndk-buildを使用して、Cのソースコードをネイティブ(.so, .a. 主に.so?)ライブラリへコンパイルしたり、静的ライブラリをリンクしたり
  10. Javaソースコードをビルドして、DVMで実行可能な.dexファイルを生成
  11. アプリの実行に必要な .so, .dex、その他ファイルをすべてapkファイルにパッケージ化

ところで

.soファイルやネイティブライブラリと.dexを一緒に処理していませんが、JNI用のヘッダとそれに対応した規則のメソッド名で作成していけば、JVM(DVM)上でメソッドテーブルが作成されると理解していて、それにしたがってJava<=>C,C++間でメソッドを利用し合うことが可能みたいです。

この辺を調べていると、CMakeといった言葉もでてきますが、ひとまずこの記事を書くにあたっての状況に限っては(Andorid Studioでビルドをしない場合は)関係なかったので省きます。

CMakeについては、また今度機会あればまとめます。

参考

https://developer.android.com/ndk/guides/concepts.html http://blog.katty.in/4347 http://www.peliphilo.net/archives/681 http://qiita.com/m1takahashi/items/3a3c9d2845e9b57aeda3 http://morado106.blog106.fc2.com/blog-entry-80.html

COUNT関数の引数にNULLが入る場合について

SQLで行数を数える時に利用する COUNT 関数で一番使い慣れているのは COUNT(*) なのですが、 COUNT(col) や、COUNT(1かNULLになる式)といった形を見かけたのでメモ。

見かけた時は面白いなと思ったのに短い。。

iOSのProvisioning Profile周りについてざっくり確認するシートを作りました

iOSのProvisioning Profile周りについて毎回ハマっているような気がしていたので、どのファイルをいつ用意して用意した後どうすればいいのか、といったことを手短に確認するために

  • 主になんて呼ばれているか
  • 代表的なファイル名
  • 主にいつ作成するか、どう作成するか
  • ファイルをDLするか、DLした後どうするか
  • XCode上でそのファイルに関連してどう設定するか
  • そのほか備考

をまとめて、作成する際に必要、など関連のあるところの線を引いた図を作ったので貼ります。

f:id:woshidan:20170331174938p:plain

上の図に千鳥足みたいなのありますが、一つのファイルに対して他方のファイルが複数存在するといった場合は複数存在する方が雑に鳥足みたいにしました。 なんかそういうツールや様式があった気がしますが、それは次の機会ということで。

参考

https://i-app-tec.com/ios/provisioning-profile.html http://macdays.hatenablog.com/entry/2013/10/11/172532 http://qiita.com/edo_m18/items/6f10e57f95b25d9dab4e http://qiita.com/fujisan3/items/d037e3c40a0acc46f618

注釈

※1 Apple Developer Centerに登録した1つのチームから複数の開発者を招待して、その開発者はそれ以外のチームに所属していないという想定。個人開発している人もいるのでいつも事実では無いですが会社アドレスのアカウントでは事実なこと多いのでは
※2 証明書周りがよくわからなくなってむやみやたらに再発行され各種ファイルの作り直しに励むのはよくあるので助けてください
※3 1つのアプリについてProductionとSand Boxというつもり。

気になるところあったらツッコミください。

ObjCで戻り値以外にNSErrorのポインタを出しているのが面白かった話

NSErrorの使い方というよりはNSErrorが使われる場面についての話で、 C言語をはじめとした関数やメソッドの戻り値は基本的に1つになります。

複数の値を返したい場合、構造体を作ったり、オブジェクトを使ったり、といった方法がありますが、基本的にメソッドの戻り値は1つの値か、関連する値をまとめた一つのオブジェクト、となります。

普段はそこまでこまらないのですが、本来の処理と全然関係ない情報を呼び出し元に返したくなった場合はどうしたらよいでしょうか。例えば、エラーが起きてしまって本来の値が返せない、といった場合。

JavaRubyなら上記のような想定外の事態が起きて処理が進められない場合には例外を投げてしまえばいいのでしょうが、メモリ管理にARCを利用している関係もあり、Objective-Cでは基本的にアプリがそのまま死ぬような場合以外は例外を投げたくありません。

では、どうするか、と言いますと、例外を表すNSErrorのインスタンスのポインタを渡して、何か起きた時にはNSErrorのポインタがさす変数にnil以外が入っているようにするのです。

// 引用元: http://www.inazumatv.com/contents/archives/9078
NSError *error = nil;
NSString *result = [ExampleError exampleMethod:&error]
 
if (error) {
    // error処理
}

// class ExampleError
+(NSString *)exampleMethod:(NSError **)didFailWithError
{
    NSError *error = nil;
    NSString *resultMessage = @"success message";
 
    [self innerMethod:&error];
    if (error) {
        *didFailWithError = error;
        return nil;
    }
 
    return resultMessage;
}

というのが面白かった、というお話。

参考:

iOSでファイルにオブジェクトの状態を保存する/.plistとは

ObjCのコードを読んでいた時に書いたメモを放流。

アーカイブとは

プログラムで使われている複数のオブジェクトを、その属性値や相互の関係も含めてファイルに保存したり、他のプロセスに渡したりしたい場合があります。 そのために、互いに関連するオブジェクトをバイト列に変換する機能をアーカイブといい、さらにバイト列から元のオブジェクトを復元する機能をアンアーカイブといいます。 また、オブジェクトを変換して得られたバイト列をアーカイブと呼ぶことも。

一方、シリアライゼーションはプロパティーリスト程度の単純な階層構造しか表現できません。

アーカイブができるオブジェクトは、NSCodingプロトコルに適合するように実装する必要があります。

// アーカイブ用
- (void)encodeWithCoder:(NSCoder *)coder
{
  [super encodeWithCoder:coder];
  // スーパークラスがNSCodingプロトコルに適合していない場合は不要

  [coder encodeObject:オブジェクト forKey:キー文字列];
  // またはencodedConditionalObject:forKey:を使う

  ...
  [coder encodeDouble:実数の変数 forKey:キー文字列];
  // いくつかの方に合わせたメソッドが用意されている
}
// オブジェクトがまた依存しているオブジェクト...とたどっていくと元のオブジェクトのencode...が
// 呼ばれそうだが、すでにアーカイブされていたら二度目はされないらしい
// アンアーカイブ用
- (id)initWithCoder:(NSCoder *)coder
{
  self = [super initWithCoder:coder];
  // スーパークラスがNSCodingプロトコルに適合していない場合は[super init]でよい

  変数 = [[coder decodeObjectForKey:キー文字列] retain];

  ...
  変数 = [coder decodeDouble:キー文字列];
}

NSKeyedArchiver

上記のencodeWithCoderを使って、対象となるオブジェクトをエンコードした結果をデータオブジェクトに書き込み。 初期化の際は、書き込み先のNSMutableDataのオブジェクトが必要となります。

一つずつアーカイブしていっても良いが、ルートオブジェクトをエンコードすると再起的にオブジェクトグラフがアーカイブ化される。全部完了したらfinishEncodingを読んで後処理をします。

書き込み先はデータオブジェクトなのでデータオブジェクトからファイルへ書き込んだり、他プロセスへ送信したりができるし、ファイルに書き込むまで一括で出来るメソッドもあります。

NSKeyedUnarchiver

上記のinitWithCoderを使って、アーカイブされたオブジェクトが書き込まれたデータオブジェクトからオブジェクトを復元します。 ファイルからデータオブジェクトを作って読み込んだりもできます。

.plist

https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html http://glassonion.hatenablog.com/entry/20110910/1315609950 http://qiita.com/hp0me/items/b619680611fd6667273f

Apple系が発祥のNSPropertyListSerializationクラスでパースできる*1、データ永続化などのために使われるファイル形式。 プロパティリストと呼ぶみたいです。

代表的なものはアプリの設定を保存している Info.plist だが、それ以外にも利用可能なようです。 ファイル形式としてはXMLの一種だと思います。*2

*1:NSArray や NSDictionary クラスでもまぁできる

*2:そういえば、AndoridのSharedPreferencesの実装もXMLだったはず http://qiita.com/ochim/items/dc4d77d478e87c1449ad

ideviceinstallerで「Could not connect to lockdownd. Exiting.」と出てアプリのインストールに失敗する

$ ideviceinstaller -u b93fd1bed1bbdf952070fa4160a34510efbe71ee -i /Users/woshidan/to/app/dir/iOSApp/build/sym/Release-iphoneos/iOSApp.app
Could not connect to lockdownd. Exiting.

github.com

Mac OS X El Captain以降のバージョンだと、iOSアプリのインストールに利用している ideviceinstaller が依存している libimobiledevice のバージョンが古い場合、iPhoneにアプリをインストールする際に操作する必要のある /var/db/lockdown の編集権限がない場合があります。

この場合は、libmobiledeviceから新しく入れ直してした後、古い /var/db/lockdownディレクトリ以下を削除してやり直すと自分の場合は解決しました。

brew uninstall ideviceinstaller -g
brew uninstall libimobiledevice -g
brew install --HEAD libimobiledevice -g // libimobiledeviceの新しいバージョンを入れ直す
brew install ideviceinstaller -g
sudo rm -rf /var/db/lockdown/*

NoSQLの種類とNoSQLのアプリケーションの例について

NoSQLは、一人のユーザーが書き込んでから他の人が参照するデータに反映される間には少し時差があるけどいいだろうというゆるい整合性(結果整合性)で動いているんだ、といった話をこちらあたりで読んでなんか面白かったので少しだけメモします。

RDBMSとNoSQL

RDBMSは即時での一貫性を求められる基幹システムや在庫管理システムなどをはじめとしてWebアプリケーションのデータベースとして広く採用されています。RDBMSはデータの整合性を保証する代わりにトランザクションのコミット前に通信や待ちが発生するため、アプリケーションがたくさんのリクエストに応える必要が生じるにつれ、パフォーマンス的に厳しいものが出てきました。

NoSQLはこのRDBMSの状況の対抗策として登場したものとなります。 ざっくりいうと、ネットワーク上でデータを分散して管理するシステムにおいて、RDBMSの整合性を保証する代わりに遅い、という部分の逆をいく、みたいな形で作られたものであって、じゃあ逆ってなんなのだ、という話になります。

CAP定理

ネットワーク上の分散システムの性質に関して「CAP定理」*1があります。CAPのそれぞれの文字は

  • Cが Consistency(整合性) = 複数のシステムのノード間で読み書き結果が矛盾しない
  • Aが Availability(可用性) = (特に更新)停止しない
  • Pが Partition Tolerance(ネットワーク分割耐性) = ネットワークの一部に障害が起きても動作可能(SPOF(単一故障点)を持たない)

から来ています。

CAP定理は、ネットワーク上の分散システムは、この三つのうち二つしか満たすことができないというものです。後に、提唱者のBrewer氏自身により、CAP定理の過度な単純化は問題であるとされ、割合やバランスの問題であるという具合に訂正されています *2

しかし、ここでは単純のため、RDBMSを含むデータベースがどの性質を満たすのか、どういったアプリケーションがあるのかを考えてみます。

C, A, P アプリケーションの例 データベースの種類
C(整合性), A(可用性) 銀行口座など,データの矛盾が許されないリアルタイム処理 RDBMS, Vertica(Column-Oriented)
C(整合性), P(ネットワーク分割耐性) 検索エンジンのインデックス処理など,データの矛盾が許されない非リアルタイム処理 MongoDB(Document-Oriented), memcached(K-V), redis(K-V)
A(可用性), P(ネットワーク分割耐性) ユーザーに近く、ダウンタイムがないビジネスで利用する。ショッピングカートやSony PlayStationネットワークなど Cassandra(Column-Oriented)

CA以外、正直、データベース=RDBMS脳の自分にとってはわかりにくいですが、たとえば、Google検索エンジンの更新処理やソシャゲのランキング処理がユーザーの私達から見て秒単位でリアルタイムである必要はなさそうです*3

また、APについては、整合性より、書き込めることが重視されていますが、ゲームのネットワークであるSony PlayStationネットワークが銀行のネットワークシステムより整合性を捨てて速さを優先する、と言われたら、そこ速さがなかったらゲームにならないもんなーと納得したりしますが、実際は個々のものについて触れたり触れなかったりしてその折に考えることになるのでしょう。

現場からは以上です。

参考

*1:数学的には定理ではない

*2:http://www.publickey1.jp/blog/13/capcap32.htmlhttps://www.infoq.com/jp/articles/cap-twelve-years-later-how-the-rules-have-changed

*3:ガチ勢じゃ無くてすみません。。