androidの内部ストレージにファイルを書き込む
android4.3(APIlevel18)で、内部ストレージにファイルを書き込もうとするとエラーになるが、以下をAndroidManifest.xmlの
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
adb shellで保存された場所を確認すると、/sdcard以下にあることを確認。
AndroidNDKでHelloWorld
基本的な実装方法を忘れがちなのでメモ。
開発環境はEclipse Mars(4.5)を想定。
プロジェクト作成
とりあえず今回はプロジェクト名をHelloWorldNdkとして作成
Cソースコード実装
以下のコードを実装してjniディレクトリ以下に作成。今回はhelloworldndk.cとして作成する。
関数名は、Javaを接頭辞にして
パッケージ名_クラス名_メソッド名として定義する。パッケージ名はcom.example.hogeならJava_com_example_hogeになる。クラス名はデフォルトだとMainActivityなので、そのまま用いることにする。
#include <jni.h> #include <android/log.h> void Java_com_example_hellondk_MainActivity_sayHello(JNIEnv *env, jobject thiz) { __android_log_write(ANDROID_LOG_INFO, __FILE__, "Hello World!") }
Android.mkの作成
以下のコードを実装して、jniディレクトリ以下に配置。
- CLEAR_VARS
- 変数のクリア
- LOCAL_PATH
- Android.mkが存在するパスを格納する変数。
- LOCAL_MODULE
- 生成される共有ライブラリ(.so)名
- LOCAL_SRC_FILES
- 生成元ソースファイル
- LOCAL_LDLIBS
- 動的リンクするライブラリを指定
- BUILD_SHARED_LIBRARY
- 共有ライブラリとしてビルドすることを指定
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := helloworldndk LOCAL_SRC_FILES := helloworldndk.c LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
共有ライブラリのビルド
jniディレクトリに移動して以下のコマンドをたたく。
パスが通ってない場合は、環境変数であらかじめndkライブラリのパスを通しておく。
ndk-buildビルドするとlibs/armeabi以下に共有ライブラリが生成される。
[armeabi] Compile thumb : helloworldndk <= helloworldndk.c
[armeabi] SharedLibrary : libhelloworldndk.so
[armeabi] Install : libhelloworldndk.so => libs/armeabi/libhelloworldndk.so
Javaコード実装
共有ライブラリの呼び出し・宣言・メソッド呼び出しのコードをJavaコードに追加する。
package com.example.helloworldndk; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends Activity { static { System.loadLibrary("helloworldndk"); } public native void sayHello(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sayHello(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } }
実行
コンソールにHello World!と表示されればOK
Jsonオブジェクト内の文字列を一括エンコード
Pythonのjsonモジュールを使ってJson形式のファイルを読み込むと、その中の文字列はUnicode文字列として読み込まれる。
ただ、それだと困る時があって、例えばPythonからあるWindowsAPIを叩いて、そこに読み込んだ文字列を渡したいけど、そのAPIがUnicode対応してないレガシーなものだった時とか(ニッチすぎる...)
プラットフォーム非依存にするためには、Unicode形式に統一するのがよいのだけど、内部処理する際に困ることがありまして...
また、Jsonの構成がネスト状になっていた時に、含まれているオブジェクトに対して一括でエンコードするにはどうしたらよいか調べてみたら、StackOverFlowで議論されてました。
How to get string objects instead of Unicode ones from JSON in Python?
jso.load()を呼ぶ際、object_hookに関数を渡すとコールバックされるので、これを利用する。
以下のコードでは、パースされるとdecode_dictが呼ばれる。また対象のオブジェクトがリストの場合はdecode_dictからdecode_listが呼ばれ、その値がUnicode形式ならエンコードします。
object_hookについては、Pythonの公式ドキュメントに詳細が書かれてます。
object_hook は、もし指定されれば、全てのデコードされた JSON オブジェクトに対して呼ばれその返値は与えられた dict の代わりに使われます。
http://docs.python.jp/2.7/library/json.html
この機能は独自の脱直列化 (たとえば JSON-RPC クラスヒンティングをサポートするような) を提供するのに使えます。
def _decode_list(data): rv = [] for item in data: if isinstance(item, unicode): item = item.encode('utf-8') elif isinstance(item, list): item = _decode_list(item) elif isinstance(item, dict): item = _decode_dict(item) rv.append(item) return rv def _decode_dict(data): rv = {} for key, value in data.iteritems(): if isinstance(key, unicode): key = key.encode('utf-8') if isinstance(value, unicode): value = value.encode('utf-8') elif isinstance(value, list): value = _decode_list(value) elif isinstance(value, dict): value = _decode_dict(value) rv[key] = value return rv def deserialize_json(json_str): try: # encode the json object 'unicode' string to 'str' string json_obj = json.loads(json_str, object_hook=_decode_dict) except IOError as (ecode, emsg): d = os.path.split(path)[0] print('[Error](%d)%s [%s]' % (ecode, emsg, d), file=sys.stderr) sys.exit(1) def load_json(path): b = os.path.exists(path) if b == False: return None try: with open(path, 'r') as f: json_str = f.read() json_obj = deserialize_json(json_str) except IOError as (ecode, emsg): json_obj = None print ('Error: (%d)%s' % (ecode, emsg), file=sys.stderr) return json_obj
munin-strapで使われるjqueryをローカル環境からロードする
あるPCからmuninを閲覧すると読み込みが遅いと言われたのですが、自分のPCから見ると特段遅くなく普通に閲覧できる。
ブラウザのdeveloper toolでプロファイルしてもらうと、jqueryを本家からダウンロードしようとするために遅かったようだ。
対象のPCは外部のインターネットに接続できない環境なので、そもそもダウンロードできない。
そこでjquery本体をサーバに置いて、htmlファイルで参照されているアドレスをサーバ上のパスに書き換えるようにした。
サーバ上にjqueryの本体を置いておこうと思ったのは良いのだけど、muninがリクエストに応じて吐き出すhtmlのテンプレートはどこにあるのか..
片っ端からgrepをかけていくと、/etc/munin/temprates/partial/head.htmlに定義されているのを発見。
head.htmlで定義されている
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
のsrc内を
<TMPL_VAR_NAME="R_PATH">/static/jquery-1.10.2.min.js
と書き換える。TMPL_VAR_NAME="R_PATH"はどうやら相対パスに置換してくれるマクロっぽいので、そのまま用いる。
あとはjquery本体を/var/www/munin/static以下に配置しておけば良い。
動的に生成されるhtmlファイルのヘッダが、書き換えたテンプレート通りになっていることを確認できれば問題無い。
vncserverのメモ
vncserver起動
$ vncserver :1 -geometry 1280x720
vncserver停止
$ vncserver -kill :1
パスワード変更
$ vncpasswd
muninのグラフズーム機能が動作しない場合の対処
グラフズーム機能が動作しなかったので、その対処をメモ。
環境
Munin 2.0.17
Ubuntu 13.10
対処**
FastCGIをインストール
$sudo apt-get install libcgi-fast-perl
a2enmodでCGIモジュールを有効化
$sudo a2enmod cgi
ここまで行うと、/var/log/munin/munin-cgi-graphにCould not draw graphなどのエラーが出る
munin-cgi-graphとcgi-tmpのパーミッションと所有者設定
$sudo chmod 755 /usr/lib/munin/cgi/munin-cgi-graph $sudo chown -R munin:munin /usr/munin/cgi $sudo chown -R www-data:www-data /var/lib/munin/cgi-tmp
なお本件は、StackOverflowのMunin Dynazoom not working on Ubuntuにて詳しく議論されている。