[課題] Terminalっぽいことを、してみたい。 私はxyzzyというEditorを愛用しています。xyzzyには、Editorからshellを操作する機能があります。 「KyoroTextにも、Shellモードが欲しいなぁ~」と考えていました。 ためしに実装してみたら、そこそこ動いたので簡単に紹介します。 [問題] Googleを多様して、以下のような問題を解決できました。 -A Androidで、CLIアプリを動作させめ方法は? -B cd した後の、現在参照しているフォルダを利用したい? [A] Androidで、CLIアプリを動作させる方法は? 自分以外のAndroidアプリを起動する場合、通常は、Intentを使用します。 「cd」「ls」dateといった。/system/bin配下にあるネイティブアプリを動作させるには、 どうすれば良いのでしょうか? Intentで実現できるのでしょうか? Runtimeクラス、ProcessBuilderクラスで実現できます。 細かな使い方は、Google先生に聞くと良いでしょう。 KyoroTextでは、以下のようなコードを書きました。 https://github.com/kyorohiro/KyoroHelloAndroid/tree/master/KyoroCommon/src/info/kyorohiro/helloworld/util/shell CLIAppKicker > > CLIAppKicker#start(String command) > startを呼ぶとCLIアプリを起動します。 [B] cd した後の、現在参照しているフォルダを利用したい? 「Aの問題」が解決しました。なので、「ls」使いたい時は、「CLIAppKicker#start("ls")」 とすれば、「ls」が呼べます。 しかし、うまくいかない場合があります。 「cd」です。 以下のようなシナリオを考えてみましょう。 1. "/" にいる。 2. cd mnt とする。 3. cd sdcard として、SDカード配下に移動したいとする。 1. CLIAppKicker#start("cd mnt") 2. CLIAppKicker#start("cd sdcard") のようなコードで実現できそうです。しかし、 子プロセスの情報は引き継がれません。 1. CLIAppKicker#start("cd mnt") ---> /mntい移動する。 2. CLIAppKicker#start("cd sdcard") --> /sdcardに移動する。 となり、思い通りの場所(/mnt/sdcard)に移動できません。 この問題は、CLIAppKicker#start("sh") とすることで解決できます。 KyoroTextでは、以下のようなコードを書きました。 https://github.com/kyorohiro/KyoroHelloAndroid/tree/master/KyoroCommon/src/info/kyorohiro/helloworld/util/shell CmdSession.java [気をつけた点] topコマンドなど、プロセスが動作し続けるアプリでは、「InputStream#available() == 0」の状態で、InputStream#read()すると、次へ進まなくります。データが入力されるまで待機するためです。 なので、必ず、「InputStream#available() > 0」ことを確認してから、InputStream#read()する ようにしました。 [次回] テスト自動化はできた!!どのくらいテストできたか定量的に客観的にしりたい!! どうすればよいでしょうか? 次回はEMMAというテストカバレッジツールを使用する方法について解説します。
2013年1月27日日曜日
KyoroText で使用されている技術 その11
2013年1月19日土曜日
KyoroText で使用されている技術 その10
[課題] File#listFiles(FileFilter filter) を中断できるようにしたい KyoroTextはテキストビューアなので、ファイルシステムからテキストファイルを選択して、開く機能が必要です。 解決すべきシナリオは、 アプリ : 1. ファイルシステム上のファイルの一覧を表示する。 ユーザー: 2. ファイルまたはフォルダーを選択する。 アプリ : 3. 選択されたものがファイルならば、テキストとして表示する。フォルダならば、1に戻る。 といった感じでとても単純です。 しかし、ちょっとした罠があります。それは、「一覧表示すべきファイルの数: 0~無限」ということです。 大量のファイルを表示する場合、以下の問題が発生します。 [A] 一覧を出すのに時間がかかる。 [B] 一覧表示すべきファイルの数は、ヒープに収まらないこともありえる。 たとえば、[A]については、端末起動直後などに10000件程度のファイル一覧を取得する場合、10秒以上待たされる場合もありました。 つまり、Thread#interrupt()が呼ばれても、最悪10秒以上は処理を返さないことがあるということです。 [Androidの作り] // // 実際のコードです。 // public File[] listFiles(FileFilter filter) { File[] files = listFiles(); if (filter == null || files == null) { return files; } Listresult = new ArrayList (files.length); for (File file : files) { if (filter.accept(file)) { result.add(file); } } return result.toArray(new File[result.size()]); } [A]、[B] の各々はJava側では解決する方法はなさそうですね!! [KyoroTextの解決方法] [A]に時間がかかることはあきらめて、中断できる機能を追加することにしました。 1. File#listFiesの呼び出す時は、専用のスレットを呼び出す。 2. 1の処理が終わるまで待つ。 3. もしも、Thread#interruptが呼ばれたら、File#listFilesの処理が終わるのを待つのをやめる。 具体的には、以下のような機能を用意しました。 #AsyncronousTaskクラス AsyncronousTask atask = new AsyncronousTask#AsyncronousTask(Runnable 作業完了を監視したいタスク) Thread runner = new Thread(atask); runner.start(); if(atask.syncTask()) { // タスクが完了した。 } else { // interruptされた } https://github.com/kyorohiro/KyoroHelloAndroid/blob/master/KyoroCommon/src/info/kyorohiro/helloworld/util/AsyncronousTask.java https://github.com/kyorohiro/KyoroHelloAndroid/blob/master/KyoroCommon/src/info/kyorohiro/helloworld/util/FileListGetter.java [PS] 次回、今回のAsyncronousTaskを使用する方法だと、使用する側は、AsyncronousTaskと監視対象の両方のクラスを制御しなくてはならない。これを一つにまとめると、使いやすさがUPします。 KyoroTextでおこなった方法について説明します。 キーワードは、Future Task
2013年1月14日月曜日
KyoroText で使用されている技術 その9
InputConnectionの使い方の説明は、家に帰って動作確認しながらでないと、 記事が書けないようです。 家に帰ったら、別の事がしたなって、全然ブログま更新ができていませんでした。 InputConnectionの使い方の説明が終わるまでは、次には進まないぞ!!と考えていましたが、 やめました。 ※ 通勤中に電車の中で書けるような事でないとダメみたい。 [課題] 走らせるスレッドを制限したい その1 同時に複数のスレッドごりごり走ると、低性能な端末だとダメダメな感じです。 なので、スレッド数をある程度管理化におく必要があります。 なおかつ、あまり、作業を作る側は、管理している側を意識したくない。 [KyoroTextでの解決] 同時にひとつだけ、Threadが動く事を保障したクラスを用意しておいて。 作業(Runnable)をそのクラスに渡す。 [パターン] いくつかパターンがあります。例えば、 - 1 指定された作業をキューにつんで置いて、順番にアクセスする。 - 2 作業が指定されたら、他の作業をすべて殺して、その作業を優先する。 - 3 等々 今回は、-2をとりました。 [作り] 使う側は、とりあえず、以下のメソッドに依頼したい作業を指定するだけ。 SingleTaskRunner#start(Runnable task) 管理側 1. 動作中のThreadを中断する。 2. joinして、Threadが終了するのを待つ 3. 新しい作業を開始する。 ちょっと工夫した点は、1.、2、3 の作業を、別スレッドで行うようにしたところ。 このstartメソッドを呼び出した側がロックを開放しないと、今動作中のThreadが終了しない といった事がおきると困のます。なので、別スレッドにしておくほうが安全です。 ※ 工夫したというよりは、上記が理由で、KyoroTextにて、デットロックを起こしてしまったので、直したという表現がただしい。 [中身] https://github.com/kyorohiro/KyoroHelloAndroid/blob/master/KyoroCommon/src/info/kyorohiro/helloworld/util/SingleTaskRunner.java [次回のネタ] - データ保存操作だとかを、バックグラウンドで実施したい。しかし、バックグラウンドへ移動すると、PFから、プロセスキルされる可能性が高くなります。(※例えServiceと動作していても) KyoroTextでの、プロセスキルへの対策を説明します。 キーワードは、 low memory killer, taskmanager, process kill
2013年1月1日火曜日
KyoroText で使用されている技術 その8
その7の続き... [小課題] SurfaceViewでEditorを作る。その1 - 未確定文字と確定文字について 確定文字と未確定文字の扱いについて説明します。 日本語を入力する場合、一旦、「ひらがな」を入力した後で、「漢字」に変換します。この変換される前の状態を 「未確定文字」、変換後の状態を「確定文字」と呼ぶことにします。 例えば、「お腹が空いた」と入力したい場合、 1. 「おなかがすいた」と未確定文字を入力 2. 「お腹が空いた」と確定文字に変換 となります。 APIの流れを見てみましょう。 1. IMEアプリ : フォーカスがあるViewから、InputConnectionを取得する。 2. IMEアプリ : InputConnection#setComposingText("おなかがすいた", 1); 3. Editor : 未確定文字をユーザーに表示 4. IMEアプリ : InputConnection#inputCommitText("お腹が空いた", 1); 5. Editor : 確定文字として、テキストを更新 [サンプル] - 上記シナリオをInstrumentationで記載しました。ログを仕込めば、APIの流れがわかります。 https://github.com/kyorohiro/KyoroSamples/blob/master/KyoroSampleSurfaceViewEditor/src/info/kyorohiro/samples/android/test/CheckForComposingText.java [PS] - setComposingText()、inputCommitText() で渡されるテキストについて setComposingText()、inputCommitText() で渡されるテキストはCharSequenceです。 Spannable装飾ありのテキストが渡さることがあります。 例えば、「<|>」をカーソルとします。「おなかがすいた」と入力した後で、 「おなか」だけを変換したい場合、以下のようなシナリオになります。 1.「おなかがすいた<|>」と入力 2.「おなか<|>がすいた」と移動 3.「お腹<|>がすいた」と変換 4.「お腹がすいた<|>」と確定する。 ほとんどの場合、この時のカーソル位置は、Editor側のカーソルではありません。 IMEのものです。装飾ありのテキストを使用して表現されます。 例えば、 「おなか<|>がすいた」は、「おなか」の部分の背景色をグレーにするとかしています。 ※ このような変換処理は、IMEアプリ側がやってくれるわけですが、 Editor側は、装飾情報を解析して、ユーザーに表示してあげる必要があります。 - setComposingText()、inputCommitText() で渡されるnewCursorPositionについて Editor側のカーソル位置を表しています。 * 0 の時、「<|>お腹がすいた」 * 1 の時、「お腹がすいた<|>」 となります。
登録:
投稿 (Atom)
mbedtls dart の 開発を始めた web wasm ffi io flutter
C言語 で開発した機能を、Dart をターゲットとして、Web でも サーバーでも、そして Flutter でも使えるようにしたい。 そこで、mbedtls の 暗号化の部分を Dart 向けのPackageを作りながら、 実現方法を試行錯誤する事にした。 dart...
-
[課題] Low Memory Killer を意図的に発生させたい Androidには、ヒープが涸渇すると使われていないアプリをKillする機能があります。 この記事では、意図的にヒープを枯渇させて、この状態をつくる方法について説明します。 単純にヒープを大...
-
UPnPを用いて、NAT越えできました。しかし、ルータがUPnPをサポートしていなかったり。UPnPだけでは越えられないNATがあります。 本文では、その代案として前回解説できなかった。「適当なサーバーに接続してみて、相手から見えているアドレスを返してもらう方法」について解...
-
Dart の Native Extensions を利用して、 SDL を用いたマルチプラットフォームのゲーム開発環境を作れるか検証してみた。 結論からいくと、 「Mac 上で動作する SDL x Dart の アプリは動作させることが難しいよ」 と言うことです。 Wind...