androidの内部ストレージにファイルを書き込む

android4.3(APIlevel18)で、内部ストレージにファイルを書き込もうとするとエラーになるが、以下をAndroidManifest.xmlタグの外に追記すると書き込めるようになった。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

adb shellで保存された場所を確認すると、/sdcard以下にあることを確認。

eclipseのコンテンツアシストで候補が表示されない場合の対処

eclipse MarsでPythonのコーディングをしているときはAlt+/を押すと、コンテンツアシストが起動(Emacsキーバインドにしているため)して候補が表示されるのですが、android開発をしているときに候補が表示されなかったので調べてみました。

すると同じ問題を解決してそうな記事を発見。

Eclipseでコンテンツ・アシスト(デフォルトプロポーザル)が表示されない - yuu_nkjm blog(2010-03-11)

上記のページの通り、
[設定]->[Java]->[エディタ]->[コンテンツ・アシスト]->[拡張] のプロポーザル選択をすべてチェックしたら直りました。

AndroidNDKでHelloWorld

基本的な実装方法を忘れがちなのでメモ。
開発環境はEclipse Mars(4.5)を想定。

プロジェクト作成

とりあえず今回はプロジェクト名をHelloWorldNdkとして作成

jniディレクトリ作成

プロジェクトを作成したら、プロジェクトを右クリックして、[新規]->[フォルダ]でjniディレクトリを作成

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オブジェクト内の文字列を一括エンコード

Pythonjsonモジュールを使ってJson形式のファイルを読み込むと、その中の文字列はUnicode文字列として読み込まれる。

ただ、それだと困る時があって、例えばPythonからあるWindowsAPIを叩いて、そこに読み込んだ文字列を渡したいけど、そのAPIUnicode対応してないレガシーなものだった時とか(ニッチすぎる...)

プラットフォーム非依存にするためには、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ファイルのヘッダが、書き換えたテンプレート通りになっていることを確認できれば問題無い。

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にて詳しく議論されている。