オムレツ工房

androidとかiPhoneとかの開発雑記

CentOS 6.xをVirtualBoxで動かすとCPU使用率が荒ぶる件とその対処法について

CentOSVirtualBoxで動かすとホストOS側のCPU使用率が荒ぶって、ゲスト上では無風状態なのに30%-40%くらい持っていかれるという現象はよく知られています。

ちなみに、CentOS 5.x系ではカーネルに割り込みオプションを指定することで荒ぶるOSを沈めることができます。

# vi /etc/grub.conf
kernel行の行末に「divider=10」を追加して再起動

で、本題の6.x系なのですが。上記割り込みオプションが「効きません」(少なくとも僕の環境下では)

で、色々探しまわった結果たどり着いた対処法。

VirtualBoxの設定を以下のように変更。

(1) IO APICを有効化のチェックを外す(設定 - システム - マザーボード
(2) 割り当てCPU数を1つにする(設定 - システム - プロセッサー)
(3) ネステッドページングを有効化のチェックを外す(設定 - システム - アクセラレーション)

以上でCentOS 6.x系でも荒ぶるCPUを沈めることができました。

※確認OS CentOS-6.4(64bit)

VirtualBoxでホストOS<->ゲストOS間 ゲストOS->インターネット間の通信を行う方法

基本的にWeb開発はバーチャル環境でやりたい派なのだけれど、バーチャルマシン上のWebサーバに接続したい時とか
SSHで操作したい場合なんかは、ホストOS・ゲストOS間での通信が必要になる。
けれど、開発中のWebシステムが外部のサービスを利用する場合や、リポジトリがインターネット上だったりする場合は、
ゲストOSからインターネットに出ていく通信が必要になる。

今回は、VirtualBoxでどういう設定をすれば良いかというお話。

先に、VirtualBoxが仮想OSに割り当てるネットワークアダプタにはいくつか種類があるので簡単に説明。

1.NAT 
アドレス変換。ゲストOS・インターネット間の通信ができる。
ホストOSからゲストOSへアクセスできない。
2.ブリッジアダプタ
ホスト端末が複数NICを備えている場合なんかに、一つのNICをゲストOSに割り当てる。
バーチャル上のWebサーバを外部に公開したい場合なんかはこれを使うのかな?
3.内部ネットワーク
ゲストOS「同士」で通信できるようになる。
ホストOSからゲストOSへはアクセス出来ない。
4.ホストオンリーアダプタ
ホストOS・ゲストOS間で通信可能。
ゲストOSからインターネットには出られない。

で、結論から言うと、

 ゲストOSにNATとホストオンリーアダプタの両方を設定する

ことで、解決できる。

つまり、ゲストOSがインターネットに出ていく時はNATアダプタで通信して、
ホストOSと通信する時はホストオンリーアダプタで通信するということ。

以下、手順。

1.VirtualBoxにホストオンリーネットワークを追加(実OS内部に仮想ネットワークを作るようなイメージかな?)

[VirtualBox] - [環境設定] から[ネットワーク]タブを選択。
右側にある「+」ボタンを押下。以上。IPアドレスとかを変えたければ、ドライバアイコンを押下すればOK。
だけど、基本的にはデフォルトのままでいいと思う。

2.ゲストOSにアダプタを設定する。
VirtualBoxマネージャーからゲストOSの[設定]タブを選択。
[ネットワーク]タブを選択。
アダプタ1に「NAT」を設定。
アダプタ2に「ホストオンリーネットワーク」を設定。

以上

雑記

一時、「ぼくのかんがえたさいきょうの仮想OSぐんだん」みたいな感じで、色んなディストリのLinuxを集めてはバーチャル化していたり
したなぁと思い出した。別に何に使うってわけでもないんだけど。
お気に入りはPuppy Linux。理由。軽い。可愛い。
ローカルで複数Linuxサーバを建てたい時に重宝した。

AndroidでJNIを利用する

今更感満載だけど、前に仕事でやった時は既に環境が出来上がった状態からのアサインだったので改めてまとめてみる。

動機としては、前回の投稿で書いた音声認識をオフラインでやりたくて、Cライブラリを探してみたところ、Juliusというオープンソースのエンジンがあったので試してみたかったから。
誰かAndroid用にポーティングしてくれないだろうか…。

まぁともかく、Androidからネイティブコードを実行する方法については以下。(ちなみに僕の開発環境はMacOSX 10.7.3)

1.Android NDKを導入する。
2.ネイティブコード呼び出しAndroidプロジェクトを実装する。
3.自前ネイティブライブラル(C言語)を実装する。
4.自前ネイティブライブラリをビルドする。
5.動かす

1.Android NDKを導入する。

下記から最新版のNDKをダウンロードする。
http://developer.android.com/sdk/ndk/index.html

適当なところに展開し、パスを通す。
(例)

export PATH=$PATH:/Users/xxxx/develop/android-ndk-r7c

パス通ったか確認。

$ which ndk-build
/Users/xxxx/develop/android-ndk-r7c/ndk-build

2.ネイティブコード呼び出しAndroidプロジェクトを実装する。

作るのは普通のAndroidプロジェクトと同様。ただし、ネイティブコードを実行したいActivityは以下のような格好で実装する。

今回は自作するライブラリの名前を「sampleJNI」として、呼びだすと文字列を返すgetNativeString
メソッドをネイティブで実装することにした。

package net.omu;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

public class SampleJNIActivity extends Activity {
	// ライブラリのロード。
	static {
		System.loadLibrary("sampleJNI");
	}

	// ネイティブメソッドの宣言
	public native String getNativeString();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		// ネイティブメソッド実行
		String nativeStr = getNativeString();

		Toast.makeText(this, nativeStr, Toast.LENGTH_SHORT).show();
	}
}

3.自前ネイティブライブラル(C言語)を実装する。

ヘッダファイル作成

連携用のヘッダファイルを作成する。
2.で作成したプロジェクトのディレクトリ直下で、以下のコマンドを実行する。

javah -classpath bin/classes -d jni net.omu.SampleJNIActivity

※最後のクラス名はネイティブコードを実行するクラスを指定する。

javahが、クラス中のnative指定子を元にヘッダファイルを作成してくれる。

実行後、jniディレクトリが作成され、配下に「net_omu_SampleJNIActivity.h」
というヘッダファイルが自動生成される。
このヘッダファイルに書かれた関数プロトタイプを元に、ネイティブライブラリを実装する。

ネイティブライブラリ実装

とはいうものの、一から書くのは面倒なので、NDKのサンプルを拝借する。
NDKのディレクトリ配下にサンプルプロジェクトがいくつかあるので一番簡単そうなのをパクりましょう。
(ndkのルート)/samples/hello-jni/jni
から、Android.mkとhello-jni.cを、自作プロジェクトのjniディレクトリにコピーする。
hello-jni.cは、適当にsampleJNI.cとかにリネーム。

で、出来上がったCのソースが以下。

#include <string.h>
#include <jni.h>

jstring 
Java_net_omu_SampleJNIActivity_getNativeString(JNIEnv* env, jobject this)
{
	return (*env)->NewStringUTF(env, "へろうわあるど");
}

4.自前ネイティブライブラリをビルドする。

makefile作成

ビルドする前に、makefileを編集する。
3.でコピーしたAndroid.mkを以下の通り修正。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := sampleJNI
LOCAL_SRC_FILES := sampleJNI.c

include $(BUILD_SHARED_LIBRARY)

LOCAL_MODULE ビルド後に出来上がるライブラリ名。Java側で実行しているSystem.loadLibraryの引数がこれ。
LOCAL_SRC_FILES ビルド対象のソース。

ビルド!!

現時点で、jniディレクトリ配下はこんな感じ。

$ ls
Android.mk			sampleJNI.c
net_omu_SampleJNIActivity.h

ここで、ndk-buildを実行する。

$ ndk-build 
Compile thumb  : SampleJNI_C <= sampleJNI.c
SharedLibrary  : libSampleJNI_C.so
Install        : libSampleJNI_C.so => libs/armeabi/libSampleJNI_C.so

正常にビルドが完了していれば、libs/armeabi配下にライブラリファイルが作成される。

$ ls ../libs/armeabi/
libSampleJNI_C.so

これでビルド完了。

5.動かす

あとは、Androidのプロジェクトをクリーンビルドして(eclipse上でF5とかrefleshをしないと
ライブラリが反映されないことがあるみたいなので注意)実行すればOK。

libs/armeabi配下のファイルも、apkに含まれるので、特にアプリのインストールに特別な手順は不要。

以下、実行結果。
f:id:omulette:20120418203818p:plain
ちゃんと出た!!ふしぎ!!
以上!

まとめ

実は5.の手順で「そんなライブラリねーよ」エラーが出て全然うまく行かず、2時間近くドン嵌りしてました。
原因は、エミュレータにあったみたいで、最初実行用のエミュレータがx86版のものを起動していたのでダメだったようだ。
ARM版のエミュレータ(通常はこれ)に変えたらあっさり動いた。実機でも動いた。
当然といえば当然なんだけど、全然気づかなくてマジまいっちんぐ。

ビルドするときの指定でアーキテクチャの指定とかがあるのかな?

Androidで音声認識

最近はSiriさんとかが流行ってるようなので、Android音声認識を試してみた。

Android音声認識は、googleのサーバー側で提供される音声検索機能を使用して

実現される。なので、3GでもWifiでもいいのでネットワークに接続されている必要が

あります。

一番シンプルなのは、RecognizerIntentを使用する形。

doc: http://developer.android.com/reference/android/speech/RecognizerIntent.html

サンプルソースとかは適当にググればいっぱい出てくるので割愛。

ただし、これは組み込み済の音声認識ダイアログが表示されるものなので、UI的に自由が効かない。

そこで、バックグラウンドで音声認識を行なってくれるSpeechRecognizerを使用してみた。

doc: http://developer.android.com/reference/android/speech/SpeechRecognizer.html

以下に実装例を示す。
起動するとひたすら音声認識を待ち受け続ける。
やっつけ仕事なので、あまり参考にしないように注意。

※そもそも実装の仕方間違ってると思う。

あと、前述のとおり音声認識はサーバーとの通信が発生するので、起動しているだけで
笑えるくらいバンバン通信します。Wifi推奨。

つうかスタンドアローンで動かしたいね。別にややこしい言葉とか認識しなくていいから。
どっかにAPIころがってないかな。

[サンプルソース]

package net.omu;

import java.util.ArrayList;

import android.app.Activity;
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.os.Bundle;
import android.os.Vibrator;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.widget.Toast;

public class SampleVoiceMemoActivity extends Activity {

	SpeechRecognizer mSpeechRec;
	SampleRecognitionListener mRecogListener = new SampleRecognitionListener();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}

	@Override
	protected void onResume() {
		super.onResume();
		// 音声認識開始
		mSpeechRec = SpeechRecognizer.createSpeechRecognizer(getApplicationContext());
		mSpeechRec.setRecognitionListener(mRecogListener);
		mSpeechRec.startListening(RecognizerIntent
				.getVoiceDetailsIntent(getApplicationContext()));
	}

	@Override
	protected void onStop() {
		super.onStop();
		mSpeechRec.destroy();
	}

	public class SampleRecognitionListener implements RecognitionListener {

		@Override
		public void onResults(Bundle results) {
			// 音声検索結果
			ArrayList<String> reslurtWordList = results
					.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);

			// 候補の一つ目を表示
			String resultWord = reslurtWordList.get(0);
			Toast.makeText(getApplicationContext(), resultWord,
					Toast.LENGTH_SHORT).show();

			// 名前を呼ばれると返事する
			if ("ダッフィー".equals(resultWord)) {
				ToneGenerator toneGenerator = new ToneGenerator(
						AudioManager.STREAM_SYSTEM, ToneGenerator.MAX_VOLUME);
				toneGenerator.startTone(ToneGenerator.TONE_PROP_BEEP);
				((Vibrator) getSystemService(VIBRATOR_SERVICE)).vibrate(1000);
			}

			// 再度音声認識開始
			mSpeechRec.setRecognitionListener(mRecogListener);
			mSpeechRec.startListening(RecognizerIntent
					.getVoiceDetailsIntent(getApplicationContext()));

		}

		@Override
		public void onError(int error) {
			// 再度音声認識開始 ※候補が見つからない時とかにここが呼ばれるっぽい
			mSpeechRec.setRecognitionListener(mRecogListener);
			mSpeechRec.startListening(RecognizerIntent
					.getVoiceDetailsIntent(getApplicationContext()));
		}

		<<省略>>
		
	}
}

【まとめ】

通信必須なのでバッテリーとかの面でもひたすら待受続けるのはいけてないっぽい。
ディズニー行きたい。ミッキーさんに会いたい。ダッフィーモフモフしたい。

Androidエミュレータが高速化

Androidエミュレータはとにかく重い。あれで開発しろっていうのは無茶すぎる。

ところが、つい先日x86ベースのAndroidイメージに限り、高速化できるように

なったそうな。

 

【参考】

以下の先人たちに感謝。

see: http://blog.makotoishida.com/2012/03/android-sdkrevision-17.html

 

 

【結】

試してみたところ、かなり高速化した。

ただ、参考サイトにもある通り、VirtualBoxと共存できなくなるらしいのでアウト。

ハイパーぬか喜びタイムでした。

 

ちなみに、アクセラレータのアンインストール方法は以下のコマンドを実行

sudo /System/Library/Extensions/intelhaxm.kext/Contents/Resources/uninstall.sh

 

※まぁ、Androidは実機があるからいいか。

ていうか、iOSエミュレータが素晴らしすぎる。Andoroidのエミュレータはもっと自省すべき。

 

AlertViewからActionSheetを呼び出すと、正常に復帰できない

AlertViewのボタン押下デリゲートからActionSheetを呼び出すと、

なぜかActionSheetのボタン押下後に正常に復帰できない。

(画面が操作を受け付けない状態になる)

 

【参考】

以下の先人たちに感謝。

 

ActionSheetの呼び出しを遅延させることで対処できるらしい。

see: http://semnil2.sblo.jp/article/53516005.html

 

メソッドの遅延実行

see: http://blog.fenrir-inc.com/jp/2010/09/afterdelay.html

 

Objective-C @selector SELクラス

see: http://akatukisiden.wordpress.com/2011/10/04/objective-c-selector-selector-imp/

 

【結】

※ActionSheet -> AlertViewっていう画面遷移は結構有りそうな気がするのに、

Web上に事例があんまりないのはなぜ?

iOSエミュレータのメモリ使用量を調べる

Instrumentsを使用する。(xcode付属のパフォーマンスモニタ)

 

XCode

[Run] - [Profile]を実行。

ここでAllocationsやLeaksを選択すれば良い。

※他にもあるんだろうけど試してない。