Androidでファイル名と行番号がログに出力する
これをやるとログを書いたところのファイル名と行番号がログに出力されます
多分どこぞの誰かがすでにやってると思いますが(検索したら大量に出てきて常識レベルだったっぽい!恥ずかしい。。)
import android.util.Log; public class Logger { public static void log(String msg) { StackTraceElement calledClass = Thread.currentThread().getStackTrace()[3]; Log.d(calledClass.getFileName() + ":" + calledClass.getLineNumber(), msg); } }
これだけです
あとは呼び出し側でこんなふうに呼び出せば
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Logger.log("Logging!!"); } }
こんな感じでわかる!!!
08-05 20:34:12.592: D/MainActivity.java:41(1431): Logging!!
takam
macでfontforgeをインストールするときにlibiconvがbrewから使えなくて困った
brew install fontforge
でインストールしようとして
ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation) make[1]: *** [../libgunicode.la] Error 1 make: *** [libgunicode] Error 2
こんな感じで怒られたので
brew install fontforge -v
したら
Undefined symbols for architecture x86_64: "_libiconv", referenced from: ....
んな感じで怒られてたので
libiconvをインストールしてみると。。。
brew install libiconv Error: No available formula for libiconv Apple distributes libiconv with OS X, you can find it in /usr/lib. Some build scripts fail to detect it correctly, please check existing formulae for solutions.
っていう感じで怒られたので、
ホントは/usr/libに入っているのを使えたら良かったんだけどやり方がよく分からなかったので
wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz tar xvfz libiconv-1.13.1.tar.gz cd libiconv-1.13.1 ./configure --prefix=/usr/local/Cellar/libiconv/1.13.1 make sudo make install
でとりあえず自力でインストール(http://bullrico.com/2012/07/12/installing-nokogiri-after-updating-homebrew/)を参考にしちゃったのですが、今は
http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
のほうがいいかもです。
このままだとまだ最初と同じエラーが出るので
brew link libiconv brew install fontforge
でいけましたという話
takam
JNIを使った 最小構成
OpenCVとか触っていきたいので、ちょっとずつ学んでいきます。
ほぼメモ書きです。
とりあえず
MainActivity.java
Android.mk
hello-jni.c
の3ファイルが有り、ちゃんとEclipseの設定がしてあれば動く模様です。
JNIから文字列を取得してトーストに出す例です。
ほぼサンプルから持ってきただけです。
src/Packageへのパス/MainActivity.java
package com.example.ndksample; import android.app.Activity; import android.os.Bundle; import android.widget.Toast; import com.example.ndk_sample.R; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toast.makeText(this, stringFromJNI(), Toast.LENGTH_LONG).show(); } public native String stringFromJNI(); static { System.loadLibrary("hello-jni"); } }
jni/Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY)
jni/hello-jni.c
#include <string.h> #include <jni.h> jstring Java_com_example_ndksample_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from JNI !"); }
takam
録音と再生のIOストリームで入力した音をリアルタイム再生する
録音の仕方
データストリームとして音声データを読み込む - Androidプログラマへの道 〜 Moonlight 明日香 〜 - Seesaa Wiki(ウィキ)
再生の仕方
音声を使って楽しげなものを作りたい。再生をする。 - 素人のアンドロイドアプリ開発日記
オーディオのオブジェクトの取得の仕方
android - AudioRecord object not initializing - Stack Overflow
一部意味などは分かっていませんが
この3つを組み合わせてできました
package com.example.recodeplay; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioRecord; import android.media.AudioTrack; import android.media.MediaRecorder.AudioSource; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { AudioRecord audioRec = null; Button btn = null; boolean bIsRecording = false; int bufSize; int samplingRate; private AudioTrack audioTrack; private void recodingAndPlay() { if (bIsRecording) { bIsRecording = false; } else { // 録音開始 Log.v("AudioRecord", "startRecording"); audioRec.startRecording(); bIsRecording = true; // 録音スレッド new Thread(new Runnable() { @Override public void run() { byte buf[] = new byte[bufSize]; // TODO Auto-generated method stub while (bIsRecording) { // 録音データ読み込み audioRec.read(buf, 0, buf.length); audioTrack.write(buf, 0, buf.length); // Log.v("AudioRecord", "read " + buf.length + // " bytes"); } // 録音停止 Log.v("AudioRecord", "stop"); audioRec.stop(); } }).start(); } } private static int[] mSampleRates = new int[] { 8000, 11025, 22050, 44100 }; public AudioRecord findAudioRecord() { for (int rate : mSampleRates) { for (short audioFormat : new short[] { AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT }) { for (short channelConfig : new short[] { AudioFormat.CHANNEL_IN_MONO, AudioFormat.CHANNEL_IN_STEREO }) { try { Log.d("TAG", "Attempting rate " + rate + "Hz, bits: " + audioFormat + ", channel: " + channelConfig); int bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat); if (bufferSize != AudioRecord.ERROR_BAD_VALUE) { // check if we can instantiate and have a success AudioRecord recorder = new AudioRecord( AudioSource.DEFAULT, rate, channelConfig, audioFormat, bufferSize); if (recorder.getState() == AudioRecord.STATE_INITIALIZED) bufSize = bufferSize; samplingRate = rate; return recorder; } } catch (Exception e) { Log.e("TAG", rate + "Exception, keep trying.", e); } } } } return null; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // AudioRecordの作成 audioRec = findAudioRecord(); audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, samplingRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, bufSize, AudioTrack.MODE_STREAM); audioTrack.play(); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { recodingAndPlay(); } }); } }
普通に出した声が耳の所で聞こえる!!
つまりこれができるってことはラインみたいな音声通話って結構簡単にできそう。。。
takam
HttpURLConnectionのdisconnect()を使うと他の通信が中断されてしまうことがあるので注意
AndroidでHTTPでデータをダウンロードしてこようとした。
2.09. ネットワーク通信 · mixi-inc/AndroidTraining Wiki · GitHubを参考に
private String downloadUrl(String urlString) { StringBuilder result = new StringBuilder(); HttpURLConnection urlConnection = null; try { URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); InputStream inputStream = urlConnection.getInputStream(); while (true) { byte[] data = new byte[1024]; int size = inputStream.read(data); if (size <= 0) { break; } result.append(new String(data, "utf-8")); } } catch (IOException e) { e.printStackTrace(); } finally { urlConnection.disconnect(); } return result.toString(); }
こんなように関数を書いていた。(通信するAsyncTaskのdoInBackground()からこの関数を呼び出す)
一回通信するにはちゃんと動いていたのだが、
このような通信をするボタンを連打した時、片方の通信では内容が途中までしか取得できなかった。(Android 4.2のエミュレータ内)
HttpURLConnection (Java Platform SE 6)のjavadocによると
要求後、HttpURLConnection の InputStream または OutputStream 上で close() メソッドを呼び出すと、そのインスタンスに関連付けられていたネットワークリソースが解放される可能性がありますが、共有されている持続接続への影響はまったくありません。disconnect() メソッドを呼び出した場合、持続接続がその時点でアイドル状態になっていれば、使用していたソケットがクローズされる可能性があります。
と書いており、共有されている接続へ影響がないInputStreamのclose()メソッドを利用することでこの問題は解決できた
最終的に以下のようなソースコードにすることにした。
またAndroid公式のトレーニングConnecting to the Network | Android Developersにもそのように記述されている。
private String downloadUrl(String urlString) { StringBuilder result = new StringBuilder(); HttpURLConnection urlConnection = null; try { URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.connect(); InputStream inputStream = urlConnection.getInputStream(); while (true) { byte[] data = new byte[1024]; int size = inputStream.read(data); if (size <= 0) { break; } result.append(new String(data, "utf-8")); } } catch (IOException e) { e.printStackTrace(); } finally { urlConnection.disconnect(); } return result.toString(); }
takam
Chromeアップデートが行われ、HTMLファイルのNotificationが動かなくなった話
Notification API の変更
先日、Google Chrome のアップデートが行われ、Google Chrome 28 になった。
レンダリングエンジンが Webkit から 新レンダリングエンジン Blink に移行したりと大きな変更があった様子。
新機能としてリッチ通知が追加された。
その関係からか、Notification API にも変更があったようだ。
具体的には、HTML ファイルを指定して Notification させる方法が利用できなくなっている。
基本的な Notification の使用
アイコン、タイトル、メッセージを指定して表示する方法
このコードはこれまで通り動く。
var n = window.webkitNotifications.createNotification(icon, title, message); n.show();
HTMLファイルを指定して Notification として表示する方法
このコードは動かなくなってしまっている。
var file = "notification.html" var n = window.webkitNotifications.createHTMLNotification(file); n.show();
createHTMLNotification() がそもそもなくなっているためだ。
createNotification() はこれまで通り存在する。
Chrome のコンソール上で、
console.log(window.webkitNotifications);
と実行してみればこれは確かめられる。
createHTMLNotification() がなくなっているおかげで、これを利用しようとするサイトやブラウザプラグインが軒並み死んでいる。
それでも createHTMLNotification() を使いたい場合はどうすればよいのでしょうか。
解決方法
結論から言うとよくわかりません。
調べてみたが、まだこの関係の情報は少なかった。
ボタンを設置できてたりするリッチな通知を出す方法として、
chrome.notifications
を使うやり方も出てきたが、まるで動かなかった。
正直良く分かりません。
console.log(chrome.notifications); // undefined
とコンソールに吐き出してみても、undefined だ。
暫定的な解決方法
HTML ファイルの Notification を少し使っていたためこの変更に気付いたのだが、そもそもなぜ HTML ファイルの Notification を使っていたかというと、通知をクリックされた際に任意の動作をさせたかったからであった。
つまり、通知される HTML ファイル内の JavaScript 内でクリックイベントを検知していたということだ。
このように単純に一つのクリックイベントを検知したいだけであれば、以下の方法で対応することができる。
var n = window.webkitNotifications.createNotification(icon, title, message); n.onclick = function(e) { console.log("clicked!"); // 任意のコード }; n.show();
結論
通知に対して単純に一つのクリックイベントを設定するだけなら上記コードのようにするとよい。
これまでのような HTML ファイルを指定した Notification をする方法はよく分からなかった。
また、chrome.notifications を利用したリッチ通知の利用方法もまだ未確認。
こちらに関してご存知の方は教えてくれるととても喜びます。
pywebsocketで80番ポート以外を使おうとするとHeader/connection port mismatch: 80/xxxxというエラーが出る
ブログに書くようなネタがありそうでなくて悩んでいました
pywebsocket - WebSocket server and extension for Apache HTTP Server for testing - Google Project Hosting
pywebsocketを使うとPythonでWebSocketを使うことができます。
80番ポートがApacheとかが動いてる時に他のポートを使いたくて、使ってみた結果タイトルにようなエラーが発生してしまっていました。
parse_host_header()関数で帰ってくるポート番号が80番になっているのがこれがなんで80番が帰ってきてしまうのかよく分からない。(調査不足)、もしかしたらhost_headerにポート番号が設定できて、その設定ができていない?
とりあえずは
https://code.google.com/p/pywebsocket/source/browse/trunk/src/mod_pywebsocket/handshake/hybi00.py#108:handshake/hybi00.py#108らへんのコードを
#if port != connection_port: # raise HandshakeException('Header/connection port mismatch: %d/%d' % (port, connection_port))
こんな感じでコメントアウトすればちゃんと動いてくれます
ただ本質的な解決になってないのであれですが、、