2013年11月20日水曜日

GE2ストーリー攻略記念かきこ

たまにはあらぬほうに走ってもいいじゃないですかー

というわけで全部攻略した。

【近接編】
出てくるボスに対して武器種を選ぶ感じ。基本は獣剣でホールド。
・ロングのクロガネ系(BA:ゼロスタンス/対象MOB:何でも)
・バスターのクロガネ系(BA:C.C系/対象MOB:ウロヴォロス系、ヴィーナス)
・ハンマー系(BA:ブーストラッシュ系/対象MOB:カムラン系、クアドリガ系、まれにグボ系)
・獣剣 陽→ホールド付(BA:□ボタン4段系/対象MOB:上記ハンマー系以外の大概のMOB)
・ショートのクロガネ系(BA:何でも/対象MOB:零号神機兵)

寸評:攻撃するだけならクロガネ系だけで十二分。スピアだけはスタイルに合わなかった。

【銃形態】
長くなりそうなので一番下に書く

【防具】
状況に応じて盾のスキルを使い分ける。特に必要がないと感じたらオウガテイル盾。
・オウガテイルの盾(スキル:アイテム効果↑)
・イェン・ツィーのバックラー(スキル:隠密集団スキル)
・ラーヴァナの盾(スキル:節約)

寸評:タワーの出番がなかった。隠密集団スキルまじ便利。

【制御ユニット】
・ソルジャー(序盤攻略)
・ディフェンダー(序盤・中盤攻略)
・アノマロ(ホールド中と併せて)
・スラッシャー(イケイケの時)

寸評:ディフェンダーがとても優秀だった。アノマロはホールド武器と組み合わせると吉。

【強化パーツ】
・弾受け渡しバースト化(メリット:任意でバースト可能)
・スタミナ強化(メリット:スタミナの減りが激しいから)
・近接強化A(メリット:コンボマスター付与)
・調合キットA(メリット:オウガテイル盾を持っていけない時)
・体力強化A(メリット:体力増強剤を買えない時)

寸評:弾受け渡しでの任意のバースト化が非常に便利。

【リンクサポートデバイス】
・被ダメ↓5~10

寸評:NPCの被ダメ↓2~5と自分の被ダメ↓5~10の組み合わせはガチ。

【連れてくNPC】
・キグルミ・・・言葉がいらないほど優秀。リンクバースト多目。回復弾まで撃ってくれる。
・ナナ・・・タフ。銃をあまり使わない。ハンマーを使う時に併せて連れて行く。回復が速い。
・シエル・・・正確な射撃が魅力。MOBのHPの状態がわかる。リンクバースト多目。回復が遅い。
・ギルバート・・・戦場では空気。攻撃力上昇スキルが魅力。回復が遅い。
・コウタ・・・ウロヴォロスのときに連れて行く。MOBのヘイト稼ぎで有能。
・エミール・・・ハンマーで騎士道が必要な時に連れて行く。
・カノン・・・撃たれたい時に連れて行く。
※他の人はそれほど実感が湧くほど連れて行ってない

寸評:NPCの特性を掴んだ上でのメンバー選択が大事。キグルミが最強。

ここから銃形態編。
先に寸評を示す。
攻略だけに限ればクロガネのブラストで十二分。
2周目はスナイパーやアサルト、ショットガンを使ってみたい。
レーザーの出番がない。というか必要性を感じなかった。

アサルトだとドローバックで任意で後退できるのが魅力。
スピアを選ぶと銃の如何に関わらず任意で後退が可能なのでおもしろそうだと思う。

というわけで持ってく弾の内容。ブラストでしか作ってません。

・モグラ
用途:捕食しに行ったMOBを釣れる
寸評:優秀。まれに当たらないことがあるので立ち位置を変えてみる。
op:14
【ボタンを押したら】S装飾弾丸:直進/極短【垂直-90】
□【1が地形に衝突時】S装飾弾丸:直進/短【垂直+65】
□□【2の自然消滅時】M制御:敵の方を向く/生存時間普通【】
□□□【3の自然消滅時】S装飾レーザー:直進/長【】
□□□□【4が何かに衝突時】S弾丸:直進/極短【】

・集中
用途:貫通属性の弾を撃ちたい時
寸評:あれば安心。もっと短時間で高威力なものを作りたい。
op:33
【ボタンを押したら】S弾丸:直進/短【】
□【1が敵に衝突時】M球:敵に貼りつく/生存時間短【BB充填】
□□【2の発生から0.2秒】M弾丸:直進/極短【BB減衰緩和】
□□【2の発生から0.5秒】M弾丸:直進/極短【BB減衰緩和】
□□【2の発生から1秒】M弾丸:直進/極短【BB減衰緩和】
□□【2の自然消滅時】M弾丸:直進/極短【BB減衰緩和】

・落下傘
用途:上空からの撃ち落し用
寸評:あれば安心。もっと短時間で高威力なものを作りたい。
op:57
【ボタンを押したら】S装飾弾丸:直進/短【垂直+76】
□【1の自然消滅時】M制御:敵の方を向く/生存時間短【垂直-100】
□□【2の発生から0.2秒】L弾丸:強ホーミング/全方向【BB抗重力弾】
□□□【3が敵に衝突時】M球:敵に貼りつく/生存時間短【BB充填】
□□□□【4の発生から0.2秒】M弾丸:直進/極短【BB減衰緩和】
□□□□□【5が敵に衝突時】M弾丸:直進/極短【BB減衰緩和】
□□□□【4の発生から0.5秒】M弾丸:直進/極短【BB減衰緩和】
□□□□【4の発生から1秒】M弾丸:直進/極短【BB減衰緩和】

・くっつきBOMB
用途:破砕が必要な時
寸評:あれば安心。破砕ダメージの安定化。改良の余地あり。
op:99
【ボタンを押したら】S弾丸:直進/短【】
□【1が敵に衝突時】M爆発:爆発/通常【BB減衰緩和】
□【1が敵に衝突時】M制御:その場で停止/生存時間短【】
□□【3の発生から0.2秒】M球:敵に貼りつく/生存時間短【】
□□□【4が敵に衝突時】M爆発:爆発/通常【BB減衰緩和】
□□□【4が敵に衝突時】M制御:その場で停止/生存時間短【】
□□□□【6の発生から0.2秒】M球:敵に貼りつく/生存時間短【】
□□□□□【7が敵に衝突時】M爆発/爆発/通常【BB減衰緩和】

・384
用途:MOBの体力をごっそり削りたい時
寸評:装飾レーザーで爆発の「発生点」を少し前に出してるイメージだが合っているか不明。
op:384
【ボタンを押したら】S弾丸:直進/短【】
□【1が敵に衝突時】M球:敵に貼りつく/生存時間極長【BB充填】
□□【2の自然消滅時】S装飾レーザー:直進/極短【】
□□□【3と同時に】LL爆発:爆発/通常【BB減衰緩和】
□□□【3と同時に】LL爆発:爆発/通常【BB減衰緩和】

2013年11月7日木曜日

javaで{}で括られたステートメントってなぁに?

TextureViewで参考にしてるコードで{}で括られてるステートメントを見かけました。

はて?これってなんだろうね。

if文とかwhileとかclassとかmethodとかって{}で括られてるのはよく見ますね。
でもコード中に単体で{}で括られてるのってあんまり見たことがなかった。

で、調べました。
これはブロックステートメントというものです。
コンパイル単位で見た場合、このブロックステートメントを使うと一つの処理単位として見なすそうです。

私見ですが、アトミックということなのかな?

最初は慣れなかったけど、なるほどこれを使うとどこからどこまでが1単位なのかわかりますね。
無名クラスならぬ無名メソッドみたいな感じかな。

2013年11月6日水曜日

4.4のテーマが変わった

づらーっと4.4の変更点を眺めました。
sdkにあるkitkatの紹介文

タイトルの内容に触れる前に個人的に注目した内容を簡単にご紹介。

注目したのは、
『Translucent system UI styling』
『Audio monitoring』

『Translucent system UI styling』は呼んで字の如し、
statusbarが透明になったよ、とのこと。
これはきれい。画像を見ると、statusbarにあるアイテムの色は白で表現されてますね。
機械色な感じが払拭されましたね。

『Audio monitoring』。これが個人的にはいいなーと感じました。
画像を見ていただけるとよくわかると思いますが、
イコイライザ(音の波形をグラフ化したもの)が表示されています。
今までもイコイライザの機能はありました。
が、見た目がちょっとアレだったので結局自作ということになっていました。
しかし、今回のイコイライザはきれいですねー。このまま使えそうです。

では、冒頭のタイトルに戻りましょう。
そうなんです。テーマが変わりました。
特にセレクターについては押さえておいたほうがいいでしょう。
今までは青(ライトブルー)でした。
今回からは黒白調になりました。
青だと他のアプリの色と競合しやすいからやめますねということらしい。
ソースはどっかで見かけたはずだけど、今手元にないのでとりあえずここまで。
探しておきますね。
ありました。
google+のAndroid Development - Japan(日本の Android 開発者のための公式コミュニティです)の中で、

 "Android Design in Action: New in Android 4.4" の概要を 日本語でまとめました。
というコメント?(記事?)があります。
リンク
この中で解説されています。


2013年11月3日日曜日

勉強中なう

TextureViewを扱うのに色々と勉強しています。

以下、参考にしているサイト様

・eaglesakuraの技術ブログ
-SurfaceTextureでMediaPlayerやカメラ映像をOpenGLテクスチャとして使う場合の注意点
-GLTextureViewを公開しました

・dalinaum / TextureViewDemo
-googleplay
-github

・SDKにある4.0のTextureViewと4.4にあるTextureView
・SDKにある4.0のGLSurfaceView

目指してる仕様
イベントがあった時にrunnableをぽんぽん投げ込んで処理する。

課題
回転などでViewが破棄されるときの処理。
onPause時の処理。たぶんwait,notifyでなんとかなりそう。
AcivityなりFragmentが終了するときの処理。

textureviewをじっくり眺めてみた

GLSurfaceViewのが楽じゃね?というのが素直な感想。

あぁしよう、こうしようと追加追加してるうちに、どんどんGLSurfaceViewに近づいている。
GLSurfaceViewとTextureViewの違いって、カメラとか動画とかの関連付けが楽なだけな気がする。

sdkの中にある両方のソースを見比べてみると納得していただけると思う。
実用レベルで見た場合、GLSurfaceViewの方が穴が少ない。
TextureViewでやるとどうしてもオレオレな管理になってしまう。

TextureViewは何もない代わりに非常に汎用性が高いけど、落とし穴も多い。
GLSurfaceViewはシステム側でめんどうな設定をしてくれている。が、それ以上はできない。

んーでも、やりたいことはListViewの中でのほうだからTextureView使わないといけない。
がんばろう。

2013年11月2日土曜日

android4.4が来ましたー

きましたねー4.4

様々なところで解説が出ています。
読むのが楽しみですねー

そんなわけでtextureviewの4.4版と4.0版を比較して読んでみました。
勉強になるわー。なるほどねー。

2013年11月1日金曜日

chromeのwindowが2枚復元された

たまたまPCをスリープにしようとして間違えてシャットダウンした。
シャットダウンを押してからやっちまったーと思った。

chromeって最後に開いているwindowが復元される。開いていたタブも。
なので、調査用から閉じてしまうとせっかく見つけてきたwebページが履歴のほうにいってしまい、
再度開くのがめんどいのなんの。

しょんぼりしながらPcを再度つけて、chromeを開いたらwindowが2枚表示された。
なんぞこれ?
バックアップから復元うんぬんというメッセージも見受けられなかった。
表示状態で電源オフだとデフォルトでそうなってるのか?

んーでも、電源切るときはちゃんと閉じた方がいいよね。

サーフェイスが関連付けられてるという前提の下で組んでみる。

必要なのは共通のGLのインスタンス。
drawの部分をrunnableにしてonSurfaceTextureAvailableで処理。
textureの有効無効をonSurfaceTextureUpdatedで書いてみる。

たぶんrunnableの実装はdrawに必要なパラメーターを渡して処理を依頼する感じ。
glコンテキストはsurfaceを通して使う感じかな。
surface,width,height
addしたtexureviewの幅や高さの設定もここかな?

TextureViewのHardwareLayer getHardwareLayer()

中でテクスチャーの初期設定やってるんじゃなかろうか。

2013年10月31日木曜日

TextureViewのライフサイクル(予想)

ところどころ違う箇所があるかもしれません。
なので予想です。表現が違っている箇所があるかもしれません。
例えばGLにtextureの作成を依頼するところとか。

肝はsurfaceを通じてnativeのGLの描画と関連付けすることです。
関連付けたsurfaceさえあればGLの描画スレッドとやりとりできるということです。
surfaceがHandlerみたいな感じですね。

Listenerは通知です。
AvailableはTextureViewが持つsurfaceをGLに関連付けするフェーズです。
UpdatedはGLからのsurfaceの受け取り完了通知を表しています。
DestroyedはGLに対して関連付けしたsurfaceの破棄を依頼することを表しています。


start
TextureViewがaddViewされる

TextureViewのonAttachedWindow
変更:getHardwareLayerが呼ばれて

TexureViewにSurfaceTextureListenerしたmListenerが呼ばれる


mListenerのonSurfaceTextureAvailableが呼ばれる

mListenerのonSurfaceTextureAvailableでsurfaceが用意されている

追加:Runnableに入る前にメンバーとしてGLインスタンスもっとかないと色々マズイ

surfaceのtextureを作るRunnable()を実行する。引数にsurfaceが必要。

RunnableでopenGLESの初期化と設定を行う。もしかしたらUI上で初期化がいるかもしれない。

surfaceをGLとバインドする。超大事。

初期化と設定が終わったらGLにsurfaceに描画するtextureの作成を依頼する

GL側でtextureができたらmListenerのonSurfaceTextureUpdatedが呼ばれる

textureがsurfaceに表示される
end

start
AcitivtyなりFragmentなり自身がisFinishing = trueなり、isRemoving = trueになり、detachedなり。

TextureViewのonDettachedWindow

onDettachedWindow内でmListenerのonSurfaceTextureDestroyedが呼ばれる。

surfaceをクライアントか「システム※1」かどちらが破棄するかのフラグをリターンする
※1.trueならonSurfaceTextureDestroyedが終わった後にonDettachedWindowでreleaseされる

追加:メンバーとしてGLインスタンスもっていれば、ここで破棄をGLに依頼できる※2
※2.onDettachedWindowに戻る前に破棄ができる。
onDettachedWindowでmListenerとのバインドが解除される

onDettachedWindowが終了。surface及びmListenerとの関連付けが全てなくなる。
end

2013年10月30日水曜日

Looperとやり取りするMessageQueの中身が見れない

TextureViewのsurfaceの破棄のところでひたすら詰んでます。


メッセージキューの状態ってどーやって見れるんだ。。。

メッセージのキャンセルもわからないし。
ブロッキングもわからん。

代価案としては、
自前でリストをもっておいて、
postして終わったかどうかどうかisAliveとかでオブジェクトを判定して、
終わっていたらremoveとかそんな感じなのかなぁ。

Looperの終了処理もあんまり見受けられないし、作りっぱなのかなぁ。
4.3のメソッドで安全にLooperを終了するよっていうメソッドもあったけど、
4.3ベースとかありえんし。

Looperおそろしい子っ。

2013年10月28日月曜日

Viewのサイズの取得できるタイミング

TextureViewでBufferとして使うBitmapをFragmentの開始あたりで取れないかなーと思った。
Loaderまわして、CacheにBufferとなるデフォルトを作ってからTextureViewをスタートさせたかった。

しかし、Viewのサイズは取得できない。
FragmentからだとViewのサイズが確定するタイミングを取得することができない。
ログを入れたところ、全て0だった。
ライフサイクル通りですね。ViewはWindowにアタッチされた時に自身のサイズを決定します。
ActivityならOnWindowFocusChangedで取得できる。

サイズ決め打ちなら作ることはできるけど、動的に取得するならそうもいかなかった。

どうしても欲しいなら、Interfaceを使ってActivityのonWindouFocusChangdeとやり取りすればいいと思う。ActivityのOnWindowFocusChangedでActivityのinterfaceからFragmentに通知する形です。

ActivityのOnWindowFocusChangedから、Fragmentでインプリメンツしたメソッド内で、現在アタッチされているViewの階層構造をたどって、お目当てのクラスなりidなりで引っ掛ければ取得できると思う。
でも、スマートじゃないというのが私見です。がっつり結びつくのって好きじゃないんですよ。

ややこしくなりそうなので、textureviewの中でやります。

他のアプリだとloading画面って何やってるんだろうなー

2013年10月25日金曜日

Contextの違い

色々気になってwebをちらほら。

contextの違いを要旨だけ。
getApplicationContext()
・・・アプリケーションのライフサイクルに沿った値が入っている。
this(=Activity)
・・・Acitivityのライフサイクルに沿った値が入っている。
getBaseContext()
・・・contextにラップされているものを1枚はがした場合の値が入っている。


よくまとまっているサイト様

Android:引数はthisか?getApplicationContextか?ActivityとApplicationの違い
http://yuki312.blogspot.jp/2012/02/thisgetapplicationcontextactivityapplic.html


上記サイト様でdialogを例として解説があります。
dialogの引数にappのほうのcontextをつっこむと、

引用ここから
ApplicationContextでは適切なWindowTokenが得られないためエラーが発生しました。
このことから、ContextインスタンスであってもActivityContextと、ApplicationContext
には違いがあり、APIによってはエラーとなることがわかりました。
引用ここまで

なるほど。たまに消えるダイアログのあるアプリがあるのはこういう理由なのかな。

で、getBaseContext()についても解説がありました。
使わないほうがいいよとgoogleのデベロッパーさんが言ってたそうです。

Viewの傾きでActivityの傾きはあてにならない

そりゃそうですよね。
世の中2pain以上のレイアウトもあるわけでして。
どんな風にどんな形でViewをつっこんでもいいわけでして。

というわけで、じゃあどう判断するのということで。

内部でgetWidthとかgetHeightの関係でboolean isSelfPortraitでも作りましょう。
んで、if文で

if (isSelfPortrait){
  処理A
}else {
  処理B
}
とか作りましょう。

2painとか実装によっては後から変更とかあり得るので、
Activityの傾きは使わないように。

とTextureViewでActivityの傾きであれこれしてしまった体験談。

2013年10月22日火曜日

ひじょーに悔しかったので書いておく

listfragmentってあるんですよ。ええ、すごく便利なやつ。
そいつにですね、ちょっとてこずったんですよ。30分くらい。

てこずった内容なんですけどね、
arrayadapterってあるじゃないですか。
そうそう、カスタムして使うやつ。
ぱらっと目次みたいなの作ろうと思ったんですよ。
クラスもつくってぺたりとしたわけですよ。
出てこないんですよ。目次が。

おっかしーなーとログ入れてみたんですよ。
getViewが呼ばれてないんですよ。意味不明でしたね。
なんかおかしいのかなーと思って、スタックオーバーフロー眺めましたよ。
それらしいのがないわけですよ。
んなあほな。と思いますよね。

煮詰まったので、クラスごとコメントアウトして新しく作ったんですよ。
出たんですよ目次が。
意 味 が 分 か ら な い

よーく見直しました。

「コンストラクタ内部で呼んでるsuperの引数の数があってない」

いつ消したんだおれ。。。

Fragmentのバックスタックを全て破棄

おれおれ逆引きTipsもしくはただの倉庫

gitにまとめて置こうかと思ったけど、
タグで探したり、タイトルで探せたりしたほうが便利かなと思った。
gitじゃピンポイントで探せないしなー。

なんかそういうサービスないからしら。
こいつにまんまあげるとえらく見難い。
かと言って、他のにあげるとコメント管理とかめんどくさい。

こいつでいっかー(

画面の縦横切り替えでActionBarを含んだ変更

2013/10/22追記
パッケージが異なると動作しない。
同じパッケージなら大丈夫。
原因については未調査。
調査中です。
暫定的にパッケージを分けない方向で回避してます。
intentのフラッグにニュータスクとかシングルトップとかやってみたけどだめだった。
マニフェストでlaunchmodeをシングルインスタンスとかもやってみたけどだめだった。
追記ここまで。

使用した端末のApiは14。Android 4.0です。

横画面にしたらフルスクリーンでタッチしたらActionBarが出るのを作りたい。
そう思わなければよかった。おかげでこんな時間だよ!泣

というわけで、盛り込んだ機能。

縦画面
ActionBar
・・・常に表示
レイアウトのサイズ
・・・ActionBarとStatusBarが含まれる
NavigationBar
・・・くっきり見える

横画面
ActionBar
・・・画面をタッチで表示/非表示。
レイアウトのサイズ
・・・ActionBarやStatusBarが含まれない
NavigationBar
・・・画面をタッチで表示/目立たない

以下ソース。



import android.app.Activity;
import android.app.Fragment;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;

public class RotateActivity extends Activity{
 public static final String TAG = RotateActivity.class.getSimpleName();
 private OnTouchListener mTouchListener = new OnTouchListener() {
  
  @Override
  public boolean onTouch(View v, MotionEvent event) {
   if(event.getAction() == MotionEvent.ACTION_UP){
    Log.d(TAG+"_onTouch_up", "in");
    if(getActionBar().isShowing()){
     getActionBar().hide();
     v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
    }else{
     getActionBar().show();
     v.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
    }
    return false;
   }
   return true;
  }
 };
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  int orientation = getApplicationContext().getResources().getConfiguration().orientation;
  if(orientation == Configuration.ORIENTATION_PORTRAIT){
   requestWindowFeature(Window.FEATURE_ACTION_BAR);
   getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
   getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
  }else{
   requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
   getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
   getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
   getWindow().getDecorView().setOnTouchListener(mTouchListener);
   getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
   getActionBar().hide();
  }
  String tag = RotateTextureFragment.TAG;
  Fragment fragment = getFragmentManager().findFragmentByTag(tag);
  if(fragment  == null){
   fragment = RotateTextureFragment.getInstance();
   int layoutId = android.R.id.content;
   getFragmentManager().beginTransaction()
   .replace(layoutId, fragment, tag)
   .commit();
  }
 }
}

2013年10月21日月曜日

スタックしたactivityにtextureviewをぶっこんだら怒られた

発生:Android 4.0
surportpackage利用してません

Activity1stからActivity2ndを呼んで、Activity2ndでTextureViewを入れてBackKey押下という流れです。


setContentView(R.layout.second_layout);
LinearLayout ll = (LinearLayout) findViewById(R.id.second_ll);
ll.addView(new MyRotateTextureView(this));

backkeyで2ndを終了したときに、logcatにuncaought exeptionとして出る。
で、errorとして赤字で出る。
call to OpenGL ES API with no current context (logged once per thread)

前後関係のないコンテキストで呼んでねはーと

ということらしい。

webで調べると、OpenGL ESのメソッドは専用のスレッドあるからそこで操作してねということらしい。
いやー、なのでpostしてるんですが。。。

というわけでエラーを再現するために最小と思われるプロジェクトをそそくさと作成。
TextureViewのコンストラクタに使う引数は、Activityならthis、FragmentならgetActivity()。

Activityが1枚。再現せず。
Fragmentをバックスタックに積んで、ForegroundのFragmentでやってみる。再現せず。
Activityがスタックされてる状態でForegroundのActivityに対してTextureViewを入れると再現した。

Fragment上でやってればこのエラーは回避できそうだ。
どういうのが根本的な原因なんだろうなー。
掴むルーパーが違うのかな?2ndのonCreateのあとに1stのonSavedInstanceStateが来るし。

2013年10月20日日曜日

2013年10月19日土曜日

TextureViewを使ってみた

SurfaceViewと同じような感じで使えると伺いましたので、使ってみた。

隅っこに円を描くだけのTextureViewを作ってみました。
public class SingleCircleTextureView extends TextureView implements SurfaceTextureListener
で宣言して、Runnableはメンバ変数として作成しました。

2013/10/30追記ここから
考察が足りてませんでした。まことに申し訳ありません。
アニメーションを作成している際にwindowの状態が変わる、ないしActivityなどを閉じた場合に、
エラーとしてcall to OpenGL ES API with no current context (logged once per thread)が表示されます。
どうやったらこのエラーがとれるのか悪戦苦闘中です。
試したこととしては、HandlerThreadで別looperとしてpostするなどです。

looperをぶつっと切ってもテストアプリ上では実害は見受けられないのですが見過ごせません。
がんばって調査します。

追記ここまで

以下は誤った内容だと思うので、ご注意ください。実際に動かしてみるとよくわかります。

SurfaceViewと異なる点。
・自前でスレッド管理しない。
RunnableはTextureViewクラスのメソッドのpost(Runnable())を使う。
自前でやってるとログでさわんなとopenGL(ES?)さんから怒られる。
call to OpenGL ES API with no current context (logged once per thread)

・lockcanvasをなどする場合は、TextureViewのlockcanvasを使う。
オートコンプリートで出てくるよ。(デフォルトだと左ctrl+shift)

・実行状態はonSurfaceTextureAvailable。
このメソッドでRunnableをpost()する。
ActivityとかFragmentとかTextureViewを継承したクラスでログを打ち込みまくった結果、
onWindowFocuschangedが終わってからTextureViewの処理に入った。
ログうつのだるかったっす。

destroyはMyTextureViewのメンバ変数をnullしたりcallbackをリムーブしたり。
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,int height)

public void onSurfaceTextureUpdated(SurfaceTexture surface) {
は、どう使うのかはまだ何もしてません。

2013年10月18日金曜日

使ってるアプリを晒してみる

デスクトップやスマホのアプリやツールって使ってるうちにどんどん増える。
使ってないものもいろいろあったりするので、整理しようと思った。
そんなわけでよく使っているものを書き出してみる。

PC編
chrome…汎用ブラウザ
bookmarksTagger…webページをタグで保存=bookmarkの代わり
evernote…アイディア帳
skitch…アイディア帳
googleDrive…汎用クラウドストレージ
Lhaplus…解凍圧縮ソフト
eclipse…開発でバリバリ
リサイズ超簡単!Pro…アプリのアイコン作成時によく使う
SQLiteDatabaseBrowser…SQLの確認に使う
サクラエディタ…androidのソースコードを見る時に使う
logicoolG700…eclipseのショートカットとかを登録してる多ボタンマウス
LibreOffice…ちょっとした書類とか表を作成
Gmail…汎用メール。タグわけとかしてる

スマホ(android)編
aixweather…温度や降水量を時間と温度を軸としたグラフで表示してくれるウィジェット
名言Everyday365…いろんな人の名言と呼ばれるものを集めたウィジェット
ビジネス書の名言…ビジネス書の名言と呼ばれるものを集めたウィジェット
Gmail…汎用メール
PuffinBrowser 有料版…ニコニコ動画のNsen01をよく観る。フラッシュなしで全部の機能が使える。
gReader…フィードリーダー。情報収集専用。そのうち自分で作る。
evernote…アイディア帳
skitch…アイディア帳
googleDrive…汎用クラウドストレージ
sleipnir…モバイル用のブラウザ。これが一番使いやすい。
アストロファイルマネージャー…スマホのファイル管理
Apex…ホームアプリ。テスト用のアプリもアイコン化するので軽いけどやたらと重い。
PVSTAR+…ニコニコのフォルダをインポートしてバックグラウンド再生。そのうち自分で作る。
Nicoro Try-α…ニコニコを観れる。そのうち自分で作る。
AdobeReader…pdfファイルを見るのに使う。
swippad…画面領域の下から引っ張り出して使ってる。
Hellostatusbar…スワイプパッドのショートカットに入れてる。全画面のときに良く使う。
Auto-RotateSwitch…スワイプパッドのショートカットに入れてる。全画面のときに良く使う。
麻雀android…とてもシンプルで遊びやすい。悪い点はあるけど良い点のほうがでかい。

よくアクセスするwebサイト
google
sdk…とてもお世話になってます。
weblio…翻訳。とてもお世話になってます。
Developers I/O…技術系サイト。お世話になってます。
techBooster…技術系サイト。EffectiveAndroid買いました。お世話になってます。
codeZin…検索するとちょくちょく行き当たる。ちょっとお堅い技術系。
このブログ
developersconsole
日本Androidの会メーリングリスト…お世話になってます。
StackOverflow…英語で書かれてるけどがんばって読む。
Yukiの枝折り…ちょくちょく行き当たる技術系ブログ。お世話になってます。

2013年10月6日日曜日

limitはどこで指定する?

sourceはAndroid14です。

SQLiteDatabaseの1481行目の
public Cursor query(String table, String[] columns, String selection,
            String[] selectionArgs, String groupBy, String having,
            String orderBy) {

        return query(false, table, columns, selection, selectionArgs, groupBy,
                having, orderBy, null /* limit */);
    }
あ、nullなんすか。そーすか。
前の記事でSQLはStringBuilderでappendして作られることがわかったから、
無理やりどっかにつけようと思えばできる。
できるけどもやらないほうがいいかもしれない。

不勉強だったので、queryの引数が違うものがあることを知らなかった汗
distinctやlimitを使いたい場合は、同じqueryメソッドの引数が違うタイプのものがあるので、
そちらを使いましょう。

start
db.query(String table, String[] columns, String selection,
            String[] selectionArgs, String groupBy, String having,
            String orderBy)
query・・・引数はそのまま。ここでdistinctはfalse指定。Limitはnull指定。
query・・・引数にdistinctのbooleanとLimit句のStringがある。
rawQueryWithFactory
end

ちなみにrawquery()だとすぐrawQueryWithFactoryに行く。

で、rawQueryWithFactoryで
 public Cursor rawQueryWithFactory(
            CursorFactory cursorFactory, String sql, String[] selectionArgs,
            String editTable) {
        verifyDbIsOpen();
        BlockGuard.getThreadPolicy().onReadFromDisk();

        SQLiteDatabase db = getDbConnection(sql);
        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(db, sql, editTable);

        Cursor cursor = null;
        try {
            cursor = driver.query(
                    cursorFactory != null ? cursorFactory : mFactory,
                    selectionArgs);
        } finally {
            releaseDbConnection(db);
        }
        return cursor;
    }
driver.queryの引数内の判定で、引数の違うタイプを使っている。
これはこれで便利だけど、書き方としてはちょっと。。。
流儀だと思うので不問ということで。

んー。
ここのフィールドの値はUNIQUEですよってのがわかれば、Limit判定ができるってことかな。

こつこつと独自プロバイダー作成でqueryって何やってるの?

こつこつつくってるとoffsetやらdistinctやらgroupbyやらlimitなどなど使いたくなってくる。
やりたい操作によって、select構文を変化させたい。

んで、どうやったら制御できるんだろうと思ったわけです。
queryは内部でどうやって組み立てられてるのかソースを見ました。
sourceはAndroid14のものです。

色々進んでSQLiteDatabaseの1441行目にあるqueryWithFactoryというメソッドで組み立て開始。
SQLiteQueryBuilder.buildQueryStringでSQLの組み立てを実行。
SQLiteQueryBuilderの198行目にbuildQueryStringがあります。

public static String buildQueryString(
            boolean distinct, String tables, String[] columns, String where,
            String groupBy, String having, String orderBy, String limit) {
        if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
            throw new IllegalArgumentException(
                    "HAVING clauses are only permitted when using a groupBy clause");
        }
        if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
            throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
        }

        StringBuilder query = new StringBuilder(120);

        query.append("SELECT ");
        if (distinct) {
            query.append("DISTINCT ");
        }
        if (columns != null && columns.length != 0) {
            appendColumns(query, columns);
        } else {
            query.append("* ");
        }
        query.append("FROM ");
        query.append(tables);
        appendClause(query, " WHERE ", where);
        appendClause(query, " GROUP BY ", groupBy);
        appendClause(query, " HAVING ", having);
        appendClause(query, " ORDER BY ", orderBy);
        appendClause(query, " LIMIT ", limit);

        return query.toString();
    }

2013年9月22日日曜日

プライベートなデータはどこでどんな風に保護するか

androidに限らずiosでもというか、ソフトウェア全般は割れるもんです。
割れるというのは、平たく言えば解析が可能ということです。
解析が可能ということは、第3者によるデータの取得ができるということです。
プライベートなデータ(誰からも見られる危険性がない)というのは存在できません。

データをとって全世界にばら撒いて、宣伝して、
自分は御用となっても目的が達成できればおkという人も存在します。
ばら撒かれたデータは使い道がある限り脅威は消えません。
脅威の排除は非現実的な手段をとるなら可能ではありますが、まずできません。

だから厳密に言えば、世で言うセキュリティに願うものというものは存在できません。
常に努力はしてます。しかしながら、それは善も悪もありません。

データアクセスはログイン機能を設けて機密性を高くしていますというのがベター。
もしくはセキュリティとしてログイン機能を利用しています。とかかな。
自動認証ならルート奪取された場合、如何なる責任も負いませんとか。

だから、公開をすることを前提とした設計をおすすめします。
どうやっても保護できねーもん。

とは言え、丸投げではあまりに無責任なので、
どのような機能を設けているのか提示して、納得したら開始するという形がいいと思います。

それは如何なるソフトウェアでもです。
だから、こういう経緯でネットワークアクセスが発生しますとか、ルート奪取されたら云々とか、
漏洩のリスクが高まることはちゃんと列挙して同意を得ましょう。
アプリを開始する際にそういった注意書きを出して、同意したらアプリ開始がベターです。
説明がなかった場合、訴訟になったらかなり旗色が悪いです。
初心者でして、とか言い訳になりません。

利用者は次のことを肝に銘じてください。これは最低限です。
安心はない。
常にデータ漏洩の危険性がある。
パーミッションにネットワークアクセスがないからといって、ネットワークアクセスが発生しないわけではない。
説明文の中にパーミッションがないからと言って、その動作ができないわけではない。

クレカの情報とか入れてる人はご愁傷様。good luck.

2013年9月19日木曜日

というわけでcontentproviderのお話

毒も吐き出した所で本題。

contentproviderは基本的にGlobalを想定して作ったほうがいいと思う。
リファレンスにも書いてあるけど、完全ローカルなら使う必要はありません。

では、なぜ今contentproviderというのが出てきたのか。
もっと柔軟なデータリソースにしませんか?という提案の形だと思う。

もっと言えば、今までにない情報の出し方をしませんか?と言うことだと思う。
すでに存在するデータを使って、じゃあそれを使ってこんなん作ってみましたとか楽しいと思う。
使う側としても色んなところから使える、引っ張り出せるっていうのは魅力的だと思います。

だから僕の作るcontentproviderは基本的にグローバルアクセスだったりする。
もちろん、ローカルで使うデータもあるのでそこはそれとしてローカル仕様にしています。
もちろん何でもかんでも受け入れるようなことはしてません。
手を取り合える状態を用意しておくということです。
ちょろっとコンテンツプロバイダでチェック処理いれてます。
マニフェストではグローバルアクセスです。

もうベンダーでは魅力あるものを作れない。
時代がそうさせない。
あらゆるネガティブな要素の影響がでかすぎる。
逆に言えば、その反動や、作った時間で個人、有志なら魅力あるものを作れると思う。
HowToはたんまり貯まってるんじゃないでしょうか?

だから、手を取り合える環境を「共有」していきませんか?
というcontentproviderのお話でした。

昨今の流行の「共有」と「誰かと繋がっている感」について

ばかじゃねーの。
何を誤訳したらそうなるの?

どっかの誰かさんが共有と誤訳したのをまんま日本語通りで受け取っているから困る。
正しくは、「最低限の存在を認めていること」を指す。
具体的には「そこにある、いる」を表しています。

無愛想な言い方をすると、そこにあるのでいかようにでもお使いください。
少し愛想の良い言い方をすると、これはあなたの力になるものかもしれません。
となる。
無機質な言い方をすると先述通り「そこにある」となる。

この表現の以下でも以上でもない。
日本語の妙と言えばそうなんだけど、もちっと何とかならんのかね~。

「誰かと繋がっている感」について。
残念ながら人はいつまでたってもどこまでいっても一人です。
感という言葉で濁していますが、それってとても都合の良い話だよね。
思いやりがないし、思いやりを受け止められる度量もない。

あなたが今一番つながっていると思わないといけないのは誰ですか?
時間を作って、一生懸命時間を作って、そのためだけに時間を何とか作って、
そうして作った時間を大切にしていますか?
あなたの時間にとってそれは大切な時間ですか?

人付き合いにおいて大事なことは縁を切らぬようにすること。これは最低限。
例え疎遠になったとしても、何かの時に連絡とってみるかーというのも大事でしょう。
こまめにツイッターやメールなどで些細だとしても継続して行うことは大事なことです。
が、気軽に簡単、短時間に「済ます」というのはちょっと違うと思う。

少なくとも僕は、大切な時間のために作った時間をもう少しだけ大きくできないか、
大切な時間を思いっきり楽しんでもらうためにもうちょっと何とかできないか、
そんな風にアプリやシステムの先にいる人のことを思ってがんばっている。

「ゆるいお付き合い」は例え疎遠になってもそれまで作った信頼があるからこそ成り立つ。
「お~久々~!元気にしてた?」とか言えるのがゆるいお付き合い。
即席のお付き合いなんてのはこの世にない。それはお互いを利用している関係だ。

まぁ、世の中にあるgoodとかいいね!とか言うのが気に入らないだけなんですけどね。
早いとこ機能不全であることを認めてこの世から抹消してほしいと切に願ってます。
人の思いはそんなもんで表現なんかできねーんだよ。
本当にいいと思ったらちゃんとレスがつく。そこに書いてあること、文章のタッチが全てだ。

2013年9月17日火曜日

ふと思った

SQLの発行は全部コンテントプロバイダに集約すればいいんじゃね?

なんでかっていうと、コンテントプロバイダは別プロセスの中のとあるスレッドで起動されるから。

集約っていうのは、あるSQLを発行、つまりselectやDMLに関する全てのケースを、
コンテントプロバイダ内で制御するということ。
より具体的にいうと、コンテントプロバイダ内でコーディングしたものだけを発行できる形にするということ。
こうすれば、アプリケーション上からstaticなコンテントプロバイダのオブジェクトを参照して、そのオブジェクトを通してSQLを発行するだけでいい。
ストアドプロシージャみたいな形だね。ついでにゲートキーパーの役割も持つ。

SQLiteのトリガーだと生SQLを発行することになるので、
プレースホルダー機能をもつcontentresolverクラスのメソッドが使えない。
簡単なSQLインジェクションをトリガーで行ってしまう可能性があるということ。
なので、テーブル毎にDMLの制御を作ればいいんじゃないだろうか。
どのテーブルにSQLを投げているかは引数のUriで判定できるし。

満たすべき仕様は以下。
1.常に同じコンテントプロバイダオブジェクトを使うこと。
2.常に同じSQLiteDatabaseオブジェクトを使うこと。
3.順次発行ができているか確認すること。
4.databaseの変更を受け取れるコールバックを実装すること。

これはコンテントプロバイダ内のonCreateでstaticなメンバとして扱えばいいのかな?
というわけでやってみる。

2013年9月13日金曜日

今、一番欲しいもの

3Dで表現できるプレゼン用ソフトウェア。

画像をぐるぐる回したり、コマを進めたりできるもの。

完全に立体として表現ができ、画像への操作ができるもの。

2013年9月10日火曜日

日本は「国」か「地域」か

NHKの世論調査(9月10日18時)で
「日本の未来は明るいか」という問いに対して、
そう思う、どちらかと言えばそう思うが19%前後。
一方、そう思わない、どちらかと言えばそう思わないが47%。

そりゃそうだよね(

僕の仕事は場所を選ばないし、生活するだけのお金もスキルもノウハウもある。
この国いるのはまだ終わってない義理があるからとしか言えない。

日本という国はここ100年くらいで様変わりするだろう。
進む亜熱帯化による農作物の変更。
国債乱発による信用不安。
箱物の占拠による土地の枯渇。
人口の年齢層の一極化。
税収の減少と税率の上昇。
日本市場の極端な縮小。
進まない政治改革。
ひたすら無駄なランニングコスト。
亜熱帯化による豪雨と日本特有の地震による災害増加。

正直、この「国」に未来なんてあるわけない。

だから若い人はどんどん世界に打って出た方が良い。
活躍できるフィールドは「日本という地域」には留まらないはずだ。

そういや、今の初老の方々は目指していた日本は作れたのだろうか。
目指していた将来は掴めたのだろうか。

今、日本が目指してる将来ってどんなもんだろうね。
人が目指してる将来ってどんな環境なんだろうね。

世界は変わる。それに伴い物の価値も変遷していく。そして人も変わる。
とても流動的な時代だ。
この荒波をサーフィンで楽しむくらいの目線は必要かもしれませんね。

どこかで誰かが言ってたけど、「楽しさ(これには非常に多くの意味が含まれる。hopeやinterestedやfineやcuteやchallengeやlook up やsearchやcreate)」=「子供が遊んで楽しかったという感想に含まれる楽しさ」を実行できる力が今必要だなと思う。

斜に構えるのは悪いというのが通例だけど、適当に斜に構えて生きたい。

2013年9月2日月曜日

色々作ったり見たりして思ったandroidのその先

基本的には内部にデータを置かない。

内部に置くべきデータは、デバイスの違いを吸収できるプライベートに強固に維持しなければならない設定。
例えば、web上の情報をそのままAndroid端末で読もうとするとあっさりメモリがいっぱいになるので、情報をしぼったり、情報は情報として置いておいて見せるものを減らすとか、そういったことをフィルタリングする設定ファイル。
アプリケーションの設定ファイルをローカルに置くのではなく、あくまでフィルタリングに必要な設定を置いておくのが内部設定ファイル。

外部に置くデータは、参照に対する権限を明記し、権限が許す限り参照を許可する。写真、音楽、記事、カレンダーなどなど。
こうすることで様々なデバイスから権限さえあればいつでもどこでも参照が可能となる。見せ方はデバイスにあるアプリケーションの設定による。

まーでもこれだとインターネット上で構築した環境がボンッしちゃうと終わるんだけどね(
それとデータを預かる側がやろうと思えば何でもできてしまう。記憶に新しいアメリカの機密情報リークとかまさにこれ。やってんだろうなーとは思ってたけど、まだアメリカしか顔を出してないのが恐いとこっすね。インターネットが自由だ自由だとは言うけれど、結局どこかの誰かさんにデータ預けてるだけなのにね。何でこうも危機意識のないネット環境ができたのか。

データに対して見せる見せないも大事だけど、そのデータの占有権が誰にあるのかそういうことも考えていきたいですね。

2013年9月1日日曜日

ん?Looper使うならry

Looper使うならServiceの意味なくね?

Looperの中のおれおれスレッドでイベントするなら、Service発行しなくてもいいような気がする。

まずはserviceで組んでみて、だめそうならLooperだけでやってみるか。

ふと思った

androidって、
コアプロセスを中心としたプライベートなローカルネットワークみたいな構造にしたいのかな?
外に情報を出したいときはコアプロセスで権限が認められてるものだけ外にいくような。

ローカルな一極集中タイプのサーバクライアント型とも言えるような気がする。
フォルダ構造ではなく、Uriでアクセスしてねってのもそういう意味なのかな。
そうなると外部ストレージとか確かにありえないよね。セキュリィティ的にも。

と、クラスとか構造とかから読み取れるような気がする。

SQLの発行場所と完了を受け取る場所

アプリのプロセスが発生している場所のスレッドプールってどこだろね?

っていうのがLooperというとこだったりする。
なので、そのLooper内で発行と結果を任せればいいのかな。
参考になりそうなクラスがあったりする。それがAsyncQueryHandlerだ。
中身をみると、ほうほうなるほどとなると思う。

もうちょい具体的にすると、
AsyncQueryHandlerというクラスをServiceとしてコアプロセス経由でContentProviderが動けるプロセスに投げる。で、Looper内で発生させたServiceで受け取り、必要があればAppプロセス内に通知する。でもAppプロセスがdeadしてる場合もあるから気をつけないとね。

プロセス間通信≠Service

先に述べたとおり、
ContentResolverを使ったSQLステートメントの実行はBinderを使ったプロセス間通信です。

Appプロセス⇔DB
ではなく、
Appプロセス→コアプロセス→DB→コアプロセス→Appプロセス
です。

これを排他的に非同期にキューイングされた状態を作り、且つプロセス間通信として確立したい。
いや、そんな別に意気込んでやんなくてもいいと思うんですよ。
でもね、どこでどういう方法で何をやっているかを明記するほうが僕はいいと思うんですよ。
流儀の問題だと思います。

まーでも、webにある情報ではだいたいUi上で書いてしまっているのが現状です。
間違った記述であると言わなければいけません。
スレッドに移して書いてる場合もあります。これも間違った記述であると言わなければいけません。

CursorLaoaderの実装を見ると、new ForceLoadContentObserver()というのをしています。
コールバックを内包しているわけですね。
これにより状態を検知しています。

なので、コールバック作りましょう。
それと起動しているActivityのスレッドプールではなく、Activityを起動しているスレッドプールを使いましょう。
そしてプロセス間通信となるサービスを発行してSQLの発行と結果の受け取りを行いましょう。

それいけBinder

ContentProviderはBinderをimportしている。

先の記事でちらっとでてきたかもしれないけど、
ContentProviderはContentProviderNativeをextendsしたTransPortというクラスをもっている。

ContentProviderNativeってのがBinderをextendsしてる。
で、ContentProviderクラスではTransportというクラスで内包している。
class Transport extends ContentProviderNative

はて?Binderってなんだっけ?
Bindっていうservice発行の仕組みはあったと思うけど、Binderではないんだよな。

ちょろっと検索したら以下の記事がありました。
AndroidのBinderによるプロセス間のメソッド呼び出し(メモ)
記事の日時は2011年03月17日なのでもしかしたらAndroid自体の内容が変わっている箇所があるかもしれません。

内容としては、IBinderというインターフェイスでActivityのプロセスが開始されているというものでした。
実行しているコアプロセスとは違うプロセスとしてActivityがスタートされ、IBinderが両プロセス間の通信を実現しているという感じですね。

なるほど。やはりサービス絡みでした。
となると、呼び出したプロセスのスレッドに対してサービスの結果を返してるということになるのかな。

んーとなると、UIスレッドに書けるけど、Activityの影響を受けにくいところに結果を出してあげるべきなのかな。ノーティフィケイションとか使ってね☆ミとSDKのどこかで言われてたような気がする。
でもノーティフィケイションに出すと通知が溜まりまくるような。。。

2013年8月31日土曜日

あれ?すっとばした

ContentResolver resolver = this.getActivity().getContentResolver();
これやらないと始まらんよね。

ContentResolverってなんぞ?というとandroid内のプロセス間通信を制御するものらしい。
「らしい」というのはまだあんまり調べてないから(ぁ

調べます。

SDKのAPI GuidesのProcesses and Threadの章によると

Similarly, a content provider can receive data requests that originate in other processes. Although the ContentResolver and ContentProvider classes hide the details of how the interprocess communication is managed, ContentProvider methods that respond to those requests—the methods query(), insert(), delete(), update(), and getType()—are called from a pool of threads in the content provider's process, not the UI thread for the process. Because these methods might be called from any number of threads at the same time, they too must be implemented to be thread-safe.


ContentProvider methods that respond to those requests—the methods query(), insert(), delete(), update(), and getType()—are called from a pool of threads in the content provider's process, not the UI thread for the process

おおまかに訳すと
ContentProviderは呼ばれる。content provider'sのプロセスの中のスレッドプールから。UIスレッドではなく。

日本語らしく言うと、コンテンツプロバイダはUIスレッドからじゃなくてcontentのプロバイダ達がいるところのプロセスの中のスレッドプールから呼ばれてんじゃごらぁ。

まともに訳すと
コンテンツプロバイダはcontentプロバイダー達のいるプロセス中で発生しているスレッドプールで呼ばれています。

つまり、そもそも発生しているプロセス自体が違うってことですね。
だから「UIスレッド」で呼んでも問題ない。
「UIスレッド」だろうがおれおれスレッドだろうが「Appのプロセス」に結果が返される。

えーまじでー。ソースは?ってかどこで受けとってんのよその結果は。
UIスレッド上で結果を使ってあーだこーだできるじゃん。
ってことはスレッドを何らかの形で捕まえてて、そのスレッド中に投げてんじゃないのー?

スレッドを発生させているのはわかった。で、結果はどこで渡してるの?

    /**
     * After being instantiated, this is called to tell the content provider
     * about itself.
     *
     * @param context The context this provider is running in
     * @param info Registered information about this content provider
     */
    public void attachInfo(Context context, ProviderInfo info) {
        /*
         * We may be using AsyncTask from binder threads.  Make it init here
         * so its static handler is on the main thread.
         */
        AsyncTask.init();

        /*
         * Only allow it to be set once, so after the content service gives
         * this to us clients can't change it.
         */
        if (mContext == null) {
            mContext = context;
            mMyUid = Process.myUid();
            if (info != null) {
                setReadPermission(info.readPermission);
                setWritePermission(info.writePermission);
                setPathPermissions(info.pathPermissions);
                mExported = info.exported;
            }
            ContentProvider.this.onCreate();
        }
    }

sdkのソースをちらちら

ContentProviderをいれたアプリケーションで、
ふつーのUIスレッド上でContentResolver.insertとかできる。
なんだろね、これ。
予想ではUIスレッドからHandlerでLooperでも呼んでるのか?

というわけでContentResolverくんのソース。

public final Uri insert(Uri url, ContentValues values)
    {
        IContentProvider provider = acquireProvider(url);
        if (provider == null) {
            throw new IllegalArgumentException("Unknown URL " + url);
        }
        try {
            long startTime = SystemClock.uptimeMillis();
            Uri createdRow = provider.insert(url, values);
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
            return createdRow;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            releaseProvider(provider);
        }
    }

IContentProviderというのは、調べてみるとinterfaceだった。
で、誰がインプリメンツして応答してるの?

あった。ContentProviderNativeくん。
abstract public class ContentProviderNative extends Binder implements IContentProvider
さらに中のfinalクラス
final class ContentProviderProxy implements IContentProvider

で、このNativeくんはどこで使われてるのかというと、ContentProviderクラス。
class Transport extends ContentProviderNative
この中でabstructのInsertを呼ぶようになっている。

続く
                                            かもしれない。

そういえば

初めてAndroidのSqlite触ったときにびっくりしたなぁ。

ロールバックないんすかw
外部キーないんすかw

逆の立場、sqliteから他のDBだと、
ロールバック?あぁ、キャベツで巻かれてるry
何で結合できねぇんだごらぁ。
制約とかめんどくせー
関数超便利

となるんだろうな(遠い目

googleから新しいNexus7が発売されました

というわけで、発売されましたね。

相変わらずSDカードスロットなしっすかー。

googleとしては、外部メモリを歓迎してないのかなぁ。
作成コストが削れるからなのか、システム的に歓迎していないのか気になるところ。

データを組んでて思ったこと

SQLiteを使用する場合です。
タグを入れるテーブルとコストを入れるテーブルの関係性をどうやろうか考えました。

t_cost
_id
_value
_timestamp
_tagname

m_tag
_id
_name
_timestamp
_count

DBのアップグレードでカラムを加えるような場合、
前のデータ+デフォルト値を加えた新しいカラムをテーブルにインサートという感じにしたい。
手間を考えると、copyして古いのDropして新しいのをrenameする形が良さそうだ。
この時、_idを外部キー扱いとすると、_idの値が変わったときに連鎖updateが必要になる。
SQLiteだとシステム的にそういった仕組みがないので、自前でこつこつupdateしないといけない。
これはめんどくさい(ぁ


mapタイプのデータ群でいいや。←手抜き感
アップグレード時の手間を減らしたい。←怠惰
copyして古いのDropして新しいのをrenameすればいいよねー。←簡単
だから関係性はnameでやろう。_idはがんがん変わるし。←言い訳
連鎖アップデートを手動でとかめんどくさいですしおすし。←本音
重複しないようにロジック組めばいいよね(震え声

※良い子のみんなは「_idの値を持つ_idとは別のフィールド」を外部キー扱いして関係のあるテーブルの値を更新するように。

他のDBだったら外部キー制約あるから、ルールに則って連鎖アップデートしてね。

以下蛇足。
でかい規模のサーバにアクセスするシステムってどうなってるんだろう。
例えば、戸籍とか新幹線の予約システムとか。

サーバ系は触ったことはもちろん見たこともないのでどういう処理してるのか一度見てみたい。
サーバに投げて終了の方しかやったことねーんす。今度勉強してみよう。

新幹線の予約システムはマルス(MARS)と読むらしいです。
以下、マルス(システム)より抜粋
元々は"Magnetic electronic Automatic seat Reservation System"(磁気的電気的自動座席予約装置)の略とされていたが、現在では"Multi Access seat Reservation System"(旅客販売総合システム)の略となっている。
抜粋以上。

2013年8月29日木曜日

MMD杯のマイリス投票がぼちぼち締め切りです

締め切りは9月2日(月)21時です。

テーマが8本あって、相変わらずのごった煮でございます。

大会のサブタイトルが「REBIRTH」ということもあり、
PV系に力を入れてる方が目立つなという印象です。
それなりに見たつもりですが、他の人のおすすめからマイリスしたりしてます。

どんな閉会式になるか楽しみですね。

2013年8月27日火曜日

マルチバージョニングはやっぱりおかしい

どこに違いがあるかわからない。
0..0.0刻みでもどこに違いがあるのかわからない。
対処療法が確実とは言え、
テスト状態でとりあえず出して反応を見るのがプロモーションとは言え、
やはりマルチバージョニングは危険すぎる。
surportpackageがあるとは言え、その中にも色々違いがあったりするし。
同じようには絶対に扱えない。

利用者がエラーを寛容できるのが先か、
企業がマルチバージョニングをやめるのが先か、
利用者がベータ版でのプロモーションを認めなくなるのが先か。

とは言え、どこにエラーがあるかわかりませんは口から出せない。
やってみないとわかりませんなエラーはほんと見つけるのに苦労する。

現実的な対処がUserからの
「強制終了が頻発します。最悪です。アンインストールします。」
を真摯に受け止めて、対応をできるだけ早くすることぐらいしかない。

レポート送ってください。お願いします。

googleさん、何か声明なり注意書きなり出してください(泣)

SQLiteかPreferencesか-ProcessかThreadか

アプリの設定画面ならPreferencesというのがお約束。
そんなwebの情報をちらほら見かける。

が、そんなPreferencesで大丈夫か。

私も設定画面はPreferencesで作るつもりでした。
久々に使うことになったので色々調べてみました。

今時のPreferencesはjsonObjectやArrayListとかつっこめるらしい。すげー
キーだけでやりたい放題じゃまいか。便利便利。

さらに調べる。

どーもPreferencesがきなくさい。
追記:Contextを引数に取る場合のものは全てあやしい。
対策としては全て同じpackageで行うっていうことかな?
この問題はandroidのversionが4.0.3以上だと発生するそうです。
2.xとか早く駆逐されてほしいと切に願います。

何がきなくさいって、commitしたのにcommitできてないとかあるらしい。
より正確に言えば、commitを記述したけど、ちがう値をもってくるというものだった。
設定した値を反映してフラグメントやアクティヴィティで使用するケースがほとんどだ。
OnSharedPreferenceChangeListenerで変更検知というのもあるけど、
処理の重たいもの、もしくは何らかの理由で重たくなった場合、
こちらが期待したとおりの値を取得できるのだろうか。

さらに厄介なことにプロセスやスレッド絡みでも発生する問題がある。
AからPreferencesにアクセス。変更。
BからPreferencesにアクセス。変更。
同じコンテキスとした場合、さてこれはどうなるか。
例えが若干違うような気がしたので追記。
同一プロセス内で複数のアプリの Context を扱う場合、
つまり複数のパッケージのコンテキストで ContextImpl#getSharedPreferences(String name, int mode) を呼んだとき、
より具体的にすると、
アプリの中に複数のpackageがあり、各packageで同一のpreferenceを扱う場合、データの整合性が取れなくなる。ということです。

原因はPreferenceManagerの元、つまりSDKのソースにある。
@Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
        SharedPreferencesImpl sp;
        synchronized (sSharedPrefs) {
            sp = sSharedPrefs.get(name);
            if (sp == null) {
                File prefsFile = getSharedPrefsFile(name);
                sp = new SharedPreferencesImpl(prefsFile, mode);
                sSharedPrefs.put(name, sp);
                return sp;
            }
        }


そうなるとAsyncQueryHandlerもあやしいな。
public AsyncQueryHandler(ContentResolver cr) {
        super();
        mResolver = new WeakReference<ContentResolver>(cr);
        synchronized (AsyncQueryHandler.class) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();

                sLooper = thread.getLooper();
            }
        }
        mWorkerThreadHandler = createHandler(sLooper);
    }

2013年8月24日土曜日

取り止めがなかったのでさらに追記

つまり、SQL発行から完了までの流れは、

SQLに使うパラメーターの準備

Acitivityの影響をできるだけ受けない場所=NoUiFragmentで発行準備

AsyncQueryHandlerをExtendsしたクラス

androidのworkerThreadを使ってContentResolver経由でSQLをContentProviderに発行

トランザクションどうしよう

通知を受け取る(NoUiFragmentがいいと思う)

UiFragmentの初期化とかやったりやんなかったり

Bundleの中でメッセージ出すフラグとか渡しておいて、UIFragmentが画面に出たらメッセージ

という流れになるんじゃないだろうか。

SQLの発行と完了の受け取り方

AsynQueryHandler。
別スレッド上でSQL発行。
終了通知はUIスレッド。
SQL絡みのエラーも通知できるように記述できる。メモリーカードいっぱいですよとか。

AsyncTaskLoader。
別スレッドを起こして、その中でSQLを発行。発行の仕方は生SQLでもServiceでもおk。
通知はUI上にくるけど、loader.destroyLoaderが走ればUI上にはこれない。

色々楽なのはServiceでSQLを投げること。

データ周りのアップデートは今後も続きそうだ。
serviceで投げた場合のトランザクションってどうなってるんだろう。

もうちょっとお勉強。

追記
トランザクションは特にないように見えた。
もともとSQLite自体がキャッシュした自身を公開してそれに対してアクセスする形だからなのかな。

contentResolverでエラーが発生するとプロセスKillされる。
つまり落ちる。
これはデータが常に静的に一意に使用可能なメモリが存在すること想定した動きと思われる。
つまり、エラーが起こった場合はandroidシステム上続行不可能なエラーとして扱うことを意味している。マウント解除とかありえねーという状況だと思う。

一方、AsynQueryHandlerの場合だとエラーでは落ちない。
これはContentResolverでのエラーをWorkerThread上でのエラーとしているからかな。
エラーであることを伝える実装が必要だが、メソッドが用意されている。
ついでにシンクロナイズドでスレッドを開始するので順次発行という形になっている。
public AsyncQueryHandler(ContentResolver cr) {
        super();
        mResolver = new WeakReference<ContentResolver>(cr);
        synchronized (AsyncQueryHandler.class) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();

                sLooper = thread.getLooper();
            }
        }
        mWorkerThreadHandler = createHandler(sLooper);
    }

生SQLを実行するなら色々工夫すればおk。だけどすんごい手間。

2013年8月22日木曜日

何をしているのか見てわかる

開始が始まったことがわかること。
処理中であることがわかること。
終わったことがわかること。

こいつらをもれなく満たすのがスピナーのダイアログだったりする。
しかし、ユーザーの操作はブロックされる。

Viewでもaddして触ったら内容書いてあるとかも考えたけど、画面回転とかであっさり逝きそうだ。

アニメーションで何とかできませんか。
俺みたいにアニメーションをデフォルトでオフとかされると意味なくなる。

んー困った。

DMLをどこで発行するのか

contentResolver使ってinsertやら何やらできる。
開発側で特にスレッドとか起こさなくても発行できる。
ちょーべんりやったねたえちゃんry

実はContentResolverのほうでIDつけたサービス発行してるだけなんすけどね(ぁ

何がいやかって、メインスレッド上で特にスレッドの切り分けもなく別スレッドが生成されて動いているのがなんかねー

じゃあ、どうやんのよということで色々考えた。

1.ぐるぐるダイアログだしてAsyncTaskLoaderなりThreadなり起こせばいいじゃん
A.こんなちょびっとしたのにダイアログ出して操作ブロックするとかやってられん

2.じゃ、トースト出せよwww
A.終わったか終わってねーかわかりずらいじゃねーかwww

3.もうサービスでいいよ
A.常駐とかおわっとる

4.じゃあ得意のNoUIFragmentでやれよ
A.えー何かコスト高そうでやだー

5.どうせキャンセル効かないんだしスレッドに丸投げでよかろう
A.下にあるNoUIFragmentでAsyncQueryHandlerとかやってみる?

6.モデルとして破綻してね?
A.そうだね

さて、どうしたもんか。

2013年8月21日水曜日

電子書籍化が進んでるそうです

先日のコミックマーケットで販売された「Effective Android」。
電子書籍化が進んでるそうです。ありがたや、ありがたや。

気になったのでざっと描いてみた

省電力、省メモリとなるとこんな感じなのかなぁ。
こうなるとNoUI_FragmentにごりごりCallBacksをつける形になる。且つ、もりもりメンバー保持。

UI_Fragmentがお飾りという結果に。まさにレイアウトのためだけに生きるUI_Fragment。
でもNewは少ない。再接続だけする。ひたすら参照。
実際のところ、UIに表示されてるもの、レイアウト以外はただの投影ないしミラーだったりする。
UI_Fragmentの初期値はそのままNoUI_Fragmentから持ってきてよし。

動的な変更はUI_FragmentとNoUI_Fragmentの間でInterfaceを使う。
DialogFragmentを使うなら、UI_Fragmentでとりあえず出す。
DialogFragmentのインスタンスや必要な情報もNoUI_Fragmentに持たせておいてもいい。
DialogFragmentでの一連の処理が完了したらNoUI_FragmentにInterfaceで値を渡す。
NoUI_Fragmentは必要ならLoader起こして、自身に値を確保して、その値をUI_FragmentにPost。
UI_Fragmentで処理を行う。
全部終わったらUI_FragmentからDialogFragmentを消す。

もしもUI_Fragmentがforegroundにないなら、そこで終了。
UI_FragmentのonActivityCreatedで必要なデータは全部格納する処理が必要。
すでにあるという状況をNoUI_Fragmentが作り出している。
UI_FragmentのonActivityCreatedの最後でDialogFragmentが生きているなら消す。

アキレス腱は、NoUI_Fragmentがシステムからkillされたらoutということ。
Activityよりも長生きだけど、消されるときはやっぱり消される。
どのインスタンスもそうだけどね。
違いは、いるならシステム以外から消させない。必要であることを主張しつづけることが可能。
いらないならさっさと退場していただくことが可能。
芋づる式に消す必要があるので、NoUI_FragmentのonDestroyにUI_FragmentのRemoveを行うようにしておけばいいのかな。

メモリはActivity.isFinishing()、もしくはFragment.isRemoving()で判定して消せばよし。
Backgroundに行くだけなら消す必要はないから残す。
画像に関しては画像を丸ごとキャッシュしてメンバで保持するのか、リサイズした画像のアドレスだけ持たせて再度読み込ませるか、仕様とよく相談。
個人的には丸ごとメンバでキャッシュは歓迎したくない。
ロードは早いけど、メモリではじゃまでしかない。
10件ぐらいないならいいでしょう?と言われても他に動いてるアプリあるんで^^;;;としか言えない。

ライフサイクル管理とメモリ管理が楽。いらなくなったら消せばいいし。
投げっぱなしのスレッドや張りっぱなしのサービスよりは行儀がいいと思う。

時間の限界値-追記

取り止めがない文章になっているような気がしたので追記。

時間をどこから取ってきて、
どんな端末を使って、
どんなフォーマットで、
何を使って、
どうやって表示しようとしているのかを考えましょう。

というわけでうちのアプリ君では
時間の取得はCaledarに一本化しています。
表示はandroid.text.format.DateUtilsに一本化しています。

時間の限界値

きっと誰もが通る道だと思われる日付や時間の処理。

今回作ってるものの場合、完全にローカル依存だからそれほど複雑ではないけれど。

ローカルなんだろ?好きなもん使えや。FA。
ということだと思うのですが、スマートフォンには扱える時間の範囲が決まっています。
UNIXベースというか、UNIX時間を使うものなら何でもそうらしいのです。

2038年問題のwikipediaから抜粋。2013/08/21 06:57時点
『1970年1月1日0時0分0秒から2,147,483,647秒を経過した、2038年1月19日3時14分7秒(閏秒を考慮しない場合)を過ぎると、この値がオーバーフローし、負と扱われる[2]ため、時刻を正しく扱えていることを前提としたコードがあれば、誤作動する。』
抜粋以上。

原因としては32bitで秒の計算を行う際にオーバーフローして負の数になるのが原因とのこと。

対策も同wikiより抜粋。
『対策としては、time_t型を符号つき64ビット整数型(一般にはlong long int型)にするという方法がある。符号つき64ビット整数型の場合、上限は9,223,372,036,854,775,807(263 - 1)である。これを秒数に用いるとおよそ西暦3000億年[4]まで使用できるので、事実上問題が発生することはない。最近のオペレーティングシステムや処理系では、time_t型は符号つき64ビット整数型で表されるようになってきている。』
抜粋以上。

だそうです(ぁ
ちなみにwindowsphoneだと3000年まではいけるそうです。

手持ちのスマホandroid4.0.4で時間をいじってみる。
機内モードにして、ネットワークから時刻を取得するチェックを外す。
タイムゾーンをグリニッジ標準時に手動で変更。
端末設定の日付と時刻の日付設定を変更。
2030年12月31日までは正しい日付が取れました。
2031年01月01日を設定すると設定が反映されませんでした。
うちのスマホ君はこれが限度値らしいです。

インターフェイスはDatePickerのスピナーの方でした。setMinとかsetMaxしてるってことかな。

長期なガントチャートやライフログの場合は注意が必要かと思います。

日付の取得や表示が何に依存しているのか理解した上で、
何をベースにやろうとしているのか確認することをおすすめします。
自前のUtilクラスで一本化しておくとメンテナンスも良さそうです。

2013年8月19日月曜日

時間や日付の 「カ タ チ」

UIというかUXというか、なんだろう、おれはこれがしたいという望みを叶える形について。

まず日付。
androidの3.0から標準でcalendarViewなるものが使える。
が、何か使いにくい。操作しづらいというか、何かイメージと合わない。
これは私のわがままなのです。直感というか何かそういうのを感じないのです。

次に時間。
androidの標準でtimepickerというのがあります。
確かにすぐ使える。間違いなく。
が、自分の中ではどうもこのUIがひっかかる。

もんもんとするが、作成コストが高そうなのでとりあえず上記2つで実装する。

ぱっと思いつくのが、
カレンダーは左右で月の変更。上下で年の変更。入力でも変更できるように入力箇所を設ける。
時計はアナログ時計を表示。入力でも変更できるように入力箇所を設ける。長針1回転が60分、短針1回転が12時間。AMなのかPMなのかわかるようなものがあるといい。

以上、アイディアだけ温めておくというメモ書きでした。

2013年8月16日金曜日

DateFormatよりもDateUtilsを使おう

日付の表示やらなんやらでDateFormatもしくはSimpleDateFormatを使っていたのですが、
DateFormatのほとんどがThis constant was deprecated in API level 18.非推奨とのこと。

というわけで、DateUtilsを使うことにしました。パースのほうはまだ調べてません。
DateUtilsは表示形式をintの値をflagとして指定します。

具体的には、今日の日時をlongで取得したとして、
2013/08/16(金)とStringで表示したい場合、
this.mBtnDate.setText(DateUtils.formatDateTime(
getActivity(),
justnow,
DateUtils.FORMAT_SHOW_YEAR|DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_SHOW_WEEKDAY)
);
こんな感じになります。|で区切って使う感じです。

時間を24時間表記で表示したい場合は、
this.mBtnTime.setText(DateUtils.formatDateTime(
getActivity(),
justnow,
DateUtils.FORMAT_SHOW_TIME|DateUtils.FORMAT_24HOUR)
);
こんな感じです。

というわけでApiLevel18以降もターゲットする人はDateUtilsに移行しましょう。
他にも便利な比較関数もあったので使ってみます。
インターネット越しの時間取得とかどうなるんだろうなー。

2013年8月14日水曜日

ひじょーーーに悔しかったので忘れないように書いておく

肝に銘じるともいう。

今回困ったのは、ListViewの1行をハイライト(強調)して維持することでした。
ListViewにsetchoicemode(ListView.singlechoice)
this.mListView.setItemChecked(position,true)※positionはハイライトしたい子のposition

起動。ハイライト(強調)されない。あれ?
ひたすら見直す。
Listのselectorの条件に引っかからないものでもあるのかと色々試した。
だめ。まったくだめ!

何が悪かったのか。rowのlayoutの親にセレクター当ててない。ふぁっきゅぅ。

そんなわけでrowのLinearLayoutに
android:background="?android:attr/activatedBackgroundIndicator"
を当てたらできました。

とてもくやしいです。

NavigationDrawerのサンプルを自分で改造したものを思い出しました。
コンテンツの部分をハイライトしてたなーと思い出してgithubから引っ張り出してきて血眼で調べました。

2013年8月12日月曜日

Effective Android かえなかったあああああああああああああああ

TechBooster様監修のEffective Androidを買いに有明に行きました。

開始30分で売り切れたとのこと。
再販については検討したいとのこと。

どんな形でもいいので再販おなしゃす!

2013年8月11日日曜日

色々考える

前提条件
データを表示するフラグメントがあるとする。
このフラグメントはデータの追加、更新、削除を行う機能をもつ。
レイアウトのケースはどうなるか。

ケース1
ListView置いて、CursorLoaderを接続する。
更新の場合は、裏でDB更新。更新完了時に表示されてるレイアウトを上書き。
追加と削除はDataSetChangeで表示をフル更新。
ActivityCreatedで常に最新のデータを取得する。


ケース2
FlameLayoutを置いて、LinearLaytouを持つSchrolViewを配置。AsyncTaskLoaderを接続する。
AsyncTaskLoaderは表示するデータのレイアウトを丸々キャッシュ。
更新の場合はケース1と同じ。
削除の場合は、裏でDB更新。更新完了時にキャッシュと表示の両方でremoveView。
追加の場合は、裏でDB更新。更新完了時にキャッシュと表示の両方でaddView。

ケース1のメリット
1高速。
2導入が簡単。
3作成時間も短い。
4ライフサイクルの管理も全部おまかせで大丈夫。

ケース2のメリット
1アニメーションの付与でリッチに。
2レイアウトのフル更新が少ない。
3レイアウトのキャッシュが効いてるのでインスタンスの取得だけでいい。

ケース1のデメリット
1表情にとぼしい。
2GCが多い。
3一からレイアウトを組み直す必要がある。

ケース2のデメリット
1キャッシュが通常の2倍以上。
2作るのがめんどくさい。
3ライフサイクルやシングルトンに疑問が残る。

ぱっと思いつくとこんなもん。
ケース2のメリットの2に関してはレイアウトの位置を変更するから、これはデータの全更新と同じような感じじゃないだろうか。
ケース2のキャッシュはかなり致命的な感じが。。。メモリの占有が多いってことは他のサービスがぶっちされるからユーザーとしてはうれしくない。スレッドだろうがサービスだろうが同じ。foregroundでもやだ。しかし、ローダーを使う以上何かしらキャッシュは効かしてるからしょうがないと割り切ればそんなもんだったりするのだろうか。

2013年8月10日土曜日

ListViewやめてみよっかな

LinearLayoutにしたほうが何かと自由が利きそうだ。
inflateしたViewをつこっむ。消す。更新する。したほうがいいような気がしてきた。
自前でアダプター組むようなもんだなー。
そうすりゃanimationはLayoutTransitionを親につけるだけでいいし。
どんな感じに仕上がるのやら。

2013年8月9日金曜日

ListViewをリフレッシュせずに表示だけ変えたい

なぜ表示だけ変えたいか。
1変えるのに全部変えるとかバカス。
DBだけ変わってりゃいいんだよ。どうせActivityCreatedで更新すんだしさー。

前提
CotentProvider実装済み。
select発行→cursor.setNotificationUri(getContext().getContentResolver(), uri);
DML発行→getContext().getContentResolver().notifyChange(returnUri, null);
2013/08/13追記
DML発行→contentProviderコマンド投げたら特に何もしないに変更してました。
_idやらなんやらはView.GoneしてるTextviewのhintにこっそり設定してそれで掴んでます。
追記ここまで。

ListFragmentを使用。
getListView.setDivider(null)でデフォルトのディバイダーを消してます。
adapterはCursorAdapterをカスタムしたもの。
cursorloaderの戻り値をそのまんまadapterにセット。newでViewholdr。bindでv.getTag()。

更新の場合
更新するViewを取得してv.getTag()でViewHolderをゲット。
ViewHolderから内容を書き換える。
notifyとかdatasetchangedとか使ってない。

削除の場合
削除するViewをVisivle.GONEに設定。
v.getLayoutParams().height = 0にする。←力技です
notifyとかdatasetchangedとか使ってない。

GONEだけでは行が詰まらなかった。height = 0だとちゃんと行が詰まる。
height = 0 でViewのレイアウトの更新なのか、adatpterviewでの更新なのか不明。
もし、viewだけの更新ならView.Goneしてからそのトリガー呼べばできるか?

アニメーションつけて下のViewを上に上げるのが理想かな。
グルーピングできるなら一斉に動かせるのだろうけど、
たぶんできないと思うから連動かな。
LayoutTransition使えると楽そうなんだけどなー。ダミーでもつっこむか?

追加の場合
adapter自体を更新しないとだめみたい。
ぽいっといれられないのか?
ためしに無理やりCursor取得してそいつでnewView→bindViewを読んでみた。
CursorLoaderがおかしくなった。エラーにはならないけども。
リフレッシュしないといけないなら、
WHERE field1 = 'hoge' ORDER BY id Desc LIMIT 10 OFFSET 0 とかか?

getViewとかで何とかなりませんか。

他のadapterならどうなんだろうなー

2013年8月8日木曜日

alertdialog.builderを使ったdialogfragmentのthemeを変更して表示したい

やってみた。

ケース
あるFragmentからDialogFragmentを呼び出して、
themeをholo.lightからholo(白から黒へ)にする。

方法1
onCreateでthis.setStyle(style,theme)
onCreateDialogで自前のレイアウトいれる。
結論
だめだった。白いまんま。


方法2
onDialogCreateでContextWrapperを作成し、
そいつにsetStyleしたものでAlertDialog.builder。
白から黒になった。やったねたえちゃんry
結論
実はだめだったりする。
ListFragmentのonListItemClickから呼び出したのだけど、
DialogFragmentが表示されている状態で画面回転すると、
ListFragment内のアダプター内のViewの表示がおかしい。
ログで確認するとDialogFragmentで当てたContextWrapperの影響を受けていた。
再度回転すると元の表示に戻るんだけどね。
調査がめんどくさかったので、それ以上調べてません。
何やかんややればできるだろうけど、設定がオンリーワンすぎて使いにくい。

方法3
onListItemClickで別Activityを起動。
マニフェストで背景を透明に設定。
styleをtheme_holo_noactionbarに設定。
表示したいものをIntentで渡して、タグなりなんなりでDialogFragmentを出す。
Activityを使うと他のサービスとかからも呼び出せて便利かなー。
でも、ListView扱うときにちょっと難儀かもしれない。

方法4
そもそもAlertDialogを使わない。Dialogでいい。
onCreateViewでレイアウトいれるなら白から黒になる。

2013年7月30日火曜日

ソフトキーボードが出てくるので閉じる

edittextが画面開く度にソフトキーボードが開くのでぺちっと閉じる。

手段はコード内、もしくはマニフェスト。

activityからだと、
this.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN)
このときのLayoutParamsはwindowmanagerのものをimport。且つsetContentViewする前。

fragmentなら、
前述のthisの部分をgetActivity(もしくはonAttachでの引数)。
うちはonActivityCreatedに書きました。

マニフェストなら、
activityの定義の所で、android:windowSoftInputMode="stateAlwaysHidden"

忘れないようにメモ。

ついでにEditTextで1行表示かつEnterで「完了」にしたい場合は、
android:imeOptions="actionDone"
android:singleLine="true"

contentProviderの解読にひたすら難儀しましたが、できました。
がんばるどー

あ、それとListViewは使わずにListFragmentとして部品かしたものをAttachした方が何かと楽なんじゃないかと思う今日この頃です。

2013年7月24日水曜日

うちのeclipse君でcalendarviewの表示でクラスねーよって言われる

2013年7月24日時点のお話です。

API17にするとcalendarviewがねーよ。変えてくれてと言われる。
The following classes could not be found:
- CalendarView (Change to android.widget.CalendarView, Fix Build Path, Edit XML)

何を仰るっているのかわからん。
というわけで時間を置いて調査。なんだろねーこれ。

2013年7月20日土曜日

なん・・・だと・・・

起きる。
朝食をとる。ニュースを見る。
ぽつぽつプログラミング。
一応ニコレポを見る。それほどの追加はなかった。
昼食。テレビを見る。
午前中に気になっていた箇所と格闘する。
ごりごり作る。
夕食。プログラムではまっている箇所に悩む。
お風呂。「そういや予選どうなってるかなー」

「Angel-CUP」 
上記サイト様でチェックチェック。

昨日それなりに深夜&支援生放送も含めてがんばって見たファイル数180前後くらい。

2013/07/20/2348
477

477・・・

477@@

参議院選挙をテレビで見ながらPCで予選見ます。

まだだ まだ明日がある・・・っ

予選開始初日のファイル数はだいたい350前後。

21時から見始めてだいたい3時間くらいで32×6ページは見た筈。
1ファイル45秒以内とはいえ、けっこう大変。


全部見るつもりで見てるけど、締め切りを考えるとこれは大変だ。
全部見れるかなぁ。。。

ちなみに第10回では予選は550±20だったと思います。
今回はどれぐらいなのか。。。

2013年7月18日木曜日

祭りが始まる

ニコニコ動画にて第11回MMD杯が始まります。

スマホを使い出して約1年。
何かいいコンテンツないかと探して生き残ったのがニコニコ動画でした。
nsenも解析がユーザー間で始まったから、スマホで見れるようになったらいいね。

では第11回MMD杯をお楽しみください。
以下、MMD杯のコミュニティからの抜粋です。

****************************************************************

第11回MMD杯―REBIRTH―
テーマ
1.「東北行きに乗って」 2.「侍」 3.「闇」 4.「再会」
5.「夏」 6.「CM」 7.「えーっ!?」 8.「大根」
日程
予選動画投稿   2013年7月19日(金) 21時~ 7月22日(月) 21時
予選マイリス投票 2013年7月19日(金) 21時~ 7月29日(月) 21時
本選開幕前夜祭  2013年8月15日(木) 21時
本選動画投稿   2013年8月16日(金) 21時~ 8月19日(月) 21時
本選マイリス投票 2013年8月16日(金) 21時~ 9月02日(月) 21時
閉会式 一部二部 2013年9月07日(土) 21時~
※詳細は公式HP http://mmdcup.org/ をご覧ください。 

*****************************************************************
MMD杯 作品支援システム
「Angel-CUP」 http://angel-cup.ch2.cc/
『↑をブックマーク推奨!』

  詳細・情報等は
  MMD杯公式ホームページ :http://mmdcup.org/
  MMD杯運営委員会Facebook:http://mmdcup.org/fb/
  にてご確認ください

2013年7月16日火曜日

RelativeLayoutとGridLayout

目いっぱいがんばったらこうなった。

幅240dpで1コンポーネントサイズが48dp×48dpだとすると、
5マス使用可能となる。

が、パディングやらマージンやらを考えるとうまくいかなかった。

最初はGridLayoutでやろうとおもったのだけど、どーもうまくいかない。
1コンポーネント48dpとしていたのだけど、どうしても32dpを使いたいところが出てきてしまった。

結局、RelativeLayoutで囲んで余白はEditTextにまわすという形になりました。

やっとコーディングできるけど、それはまた別のお話。

2013年7月13日土曜日

追記

東北ずん子様、ボカロ化決定おめでとうございます。

デザインとは一体うごごごごご

煮詰まった。
れいあうとむずかしいあるね。

煮詰まってるレイアウトの数々
http://ibank-rui17sei.sqale.jp/users/98

勉強になったこと。
・基本的にコンポーネントは48dp構成
・googleから推奨している色がある
デザインガイドラインはよく読みましょう。猛省。

何が煮詰まってるって、片手操作が煮詰まってる。

開いたらボイス入力。まぁこれはいい。コード上で実現してる。

が、レイアウトを色々組んでみたが、色々問題がある。

「Version タイトルと入力部分だけのシンプルなレイアウト」
いい点。状態が一目でわかる。
悪い点。多々・・・。
1 ぱぱっと入力して、あとはおkボタン押すだけ!が伝わらない。
2 何をどれだけ入力したらいいのかわからん。
3 ボタンが3つも縦に並んで何かダサい。
4 EditTextで入力するタイプとダイアログから入力するタイプとが混在してる。区別がない。

「Windows8スタイル」
いい点。カラフル!めっちゃカラフル!
悪い点。目にまぶしい。そこのレイアウトだけやたら鮮やか。ショートカットとは思えない。
いけてない(死語)。何をどうしたらいいのかがまったく伝わらない。

気になってるのは、
デフォルト操作、登録操作、もう一回ボイス操作の3点。
ボタンにするとなんか見た目が重い上にダサい。
縦に置いても、横に置いても、縦横混合に置いてもダサい。なんなん?このぽちぽち。

それと付帯する情報と費目。
この2つは別にいれなくてもいい。
この「別にいれなくてもいい」が伝わるレイアウトが作りにくい。
「Add Information」とかタイトルつければいいのか?

2013年7月10日水曜日

それっぽいレイアウト

縦はこんな感じ
横はこんな感じ

目に痛い(笑)

もっと弱いトーンにしないとね。
字の色もそれに合わせて変えないとね。

そこにあるものが横や縦に変えることで変わらないというのがいいところ。
悪いところは中の縦横バランスが変わること。

テキスト2行なら耐えるか?
イメージビューを横にいれたいのだけど、どうなんだろう。

2013年7月7日日曜日

DrawerLayoutのサンプルをアップデート

というわけで前にアップしたDrawerLayoutのサンプルをアップデートしました。
googleplay(DrawerLayoutで検索かけるとひっかかります)

どんなことができているかというと、
ActionBarが場面場面によって変わります(どやぁ

ソースコードが少しましになっ・・・てないかも。
リストぽちったときにフラグメントが変わってなかったので修正しました(ぁ

あとは色々触ってみてください。hayakuabustractnisitaiyo

それと追加したダイアログフラグメントで参考にしたサイトの著者様。ありがとうございます。
この場を借りてお礼申し上げます。

2013年7月6日土曜日

家計簿アプリのレイアウトを考えるその2

どこかのサイトか本で見た覚えがあるのだけど、
アプリを作るうえでまずやることはユーザーモデルを定義の一部として設けること。
バリバリ向けには作らない。ほっといても自分でやるから。むしろ、いいもの作ってどんどん周りを巻き込んでほしい。
今回は自分向け(記録は参考にしたいがめんどくさい派)をターゲットとします。
ハイパーめんどくさがりなので、記録しとこうと思ったときにできるものを作る。
ただ入力するだけなら意味がないので、入力の前に現在の状態を確認するというステップを踏ませる。

上記をより具体的に。
ドロワーを左右に持つアプリで左は項目選択、真ん中がコンテンツ、右が入力用のショートカット。
コンテンツどーん、仕方ないから入力しよう→右ドロワーを開いて音声入力。内容が違えば手入力。たぶんこの場ではしないがね。
で、項目とかさっぱりだから、代わりにデータに対する任意のプライマリタグをつける。費目内訳ってやつか。そして、必要であれば付帯情報として任意のタグをつける。5個くらいに留めておこう。
もちろん音声入力で。
データに紐づくプライマリタグには後からでも改変可能な費目を結びつける。分析対象が費目ではなく、何に使ったかということになる。無論、あとでつけた費目でもアナライズ可能にしておく。

それと色なんよねー。色。ピッカーはgoogleさんのも有志の方のもあるけれど、めんどくさがりがダイアログ開くわけがない。やっぱ変えようとか確たる意志がある時しか使わないはず。一応ダイアログを開くアイコンは置いておこう。基本は入力が終わったら色を自動でつける。一応下にパレットを8個くらい置いておく。使った色は除外して新しい色を補充する感じ。

費目もつけたいというのがあるかもしれないので、これもダイアログがひらけるやつをいれる。あとは設定からどうぞ。というか透明なActivityでいいような気がする。バックキー押せば戻る(元の画面の階層に上がる)。

そういやドロワーのレイアウトファイルでドロワーになる部分をコンテンツ部分より上にしたらどうなるんだろう。気が向いたらやってみます。レイヤー周りが変わるのだろうか。


家計簿アプリのレイアウトを考える

収支アプリを作ってるのだけども、色々困ってます。
参考にしようとあれこれとgoogleplayにある無料アプリを叩いてみました。

以下、人によっては不快に感じるものがあると思いますので白字にしてます。

当たったのが悪かったのか、ほとんどのアプリはアクションバーがないように見えた。
これは私のスマホがハードキーにメニューボタンを持ってるせいかもしれません。
どこで設定をすればいいのかさっぱりだった。

Q知ってるでしょ?A知らんがな。な表示の仕方が多い。これじゃ初見さんお断りだよ!
世帯のお金として扱うなら世帯全員がわかるものであってほしい。
というか収支を気にしなきゃいけないのって、収支に疎い人だよね。

費目が漢字で何言ってるのかわからん。生活費ってなんだ?食費じゃないの?

それとエラーにはならないけど、ダイアログがよく消える。なんぞこれ。。。

反面教師として大変参考になりました。

とは言え、こうなるにはそれなりの事情があると思う。
叩いたアプリが古ければそりゃアクションバーが出る前に作ったものの可能性が高い。
アクションバーを導入する前に、既存のバグなり使い続けてもらうためのアップデートが優先される。なくても機能には問題なければ後回しだよね。
それと既存のレイアウトを崩すことになるのでアクションバーを入れられないというのもあると思う。

あとは誰向けに作ったのかというのもある。
家計簿ばりばりつかってます!な御方から、家計簿やってんの?バロスwwwな人もいるだろう。一応記録にとって参考にはしたいけどめんどくさいという人もいるだろう。
バリバリな人向けに出せば、おーいいねーという評価は来るだろう。が、それ以外はついていけない。かといって、バロス向けに出したところで見向きもされない。一応君向けに出した場合、バリバリ層からあれやってこれやってとリリース後の対応次第で旗色がかなり変わる。
つまりある程度のお客を獲得したいとすれば、計算できるのはバリバリ向けのミニマム値にならざるをえない。レビューも落ち着くし、アプリとしての評価も定まりやすい。アプリのプロモーションとしては計算できる層がバリバリなのだ。
が、家計簿である以上世帯が意識して使わないと効果というか協力は得られない。バリバリ向けに作ったことでプロモーションとしては成功しても、アプリとしての使用価値が大幅に減退している。それが狙ってる評価です。と言われればそうですかとしか言えませんが、道具である以上役に立ってもらわないともったいないよ。心血注いだのにもったいない。

費目は費目でやっぱり使用用途がわからん。自分の気持ち次第ってことだと思うのだけれど。
あとは馴染みにくいね。やっぱり。何に使ったのかわからん。付帯情報や備考として入力する欄があるけど、その情報の集積はどこでアナライズできるの?

それとダイアログ。これはまぁ、googleが悪い(笑)。もっと手軽に使いたいです。お願いします。

こんなところか。これをもとにレイアウト要素を構築してみる。

2013年6月29日土曜日

左右にドロワーをもつDrawerLayout。できました。
左にNavigation Drawer。真ん中にコンテンツ。右にShortcutな使い方を考えました。

制御ばっちりのはずだよ!(泣

ポイントは
dispatchTouchEventでした。

今回の構造は、DrawerLayoutを親レイアウトとして上から順に、
メインコンテンツ用のFrameLayout、
左ドロワーをいれるFrameLayout(gravity:start)、
右ドロワーをいれるFrameLayout(gravity:end)となっています。

それぞれのFrameLayoutにFragmentをいれています。interfaceなどでイベントをFragmentとActivityでやり取りしてます。

これはActivityにDrawerLayoutをsetContentViewしているためです。そのためActivityで各ドロワーの制御を行うことになります。

それと右ドロワーのイベントは、左ドロワーのイベントが先に起こってしまいイベントが完結してしまうので、Activityから必要なdispatch系のメソッドを呼ぶことになります。

以上です。

2013年6月28日金曜日

というわけで左右に2つのドロワーがあるDrawerLayoutができました。
サンプル(masterブランチ)

バグ発見。右ドロワーが開いている時にドロワー内のViewがある領域をタッチイベントで拾えない。。。ちょこちょこ直します。

コードをきれいにしたいところですが、自分のアプリに取り込んで機会があればこちらで紹介します。
DialogFragmentを使う場合は、DialogFragmentのイベントをinterfaceでActivityに伝えてドロワーのopen/closeを制御してください。lockモードをいじればできるはずです。
DialogFlagmentを使う場合は、DialogFragmentのイベントをinterfaceでActivityに伝えてください。受け取った先でDrawerLayoutからRightDrawerの開閉を行う処理を付加すればいいはずです。Loaderの処理中、完了時も同じようにinterfaceでイベント通知する形になると思います。

2013年6月27日木曜日

左右に2つのドロワーをもつDrawerLayoutを作ってみました。
サンプル

可読性がアレです。
中身は次の通り。
ActivityやFragmentでイベントを共有したいときはinterfaceの引数に持たせたい値をいれて渡す。
右のドロワーはActivityで動作を制御。
ActionBarは各ドロワーの状態を確認して制御。
右ドロワーは直に、この場合だとFrameLayoutにonTouchで制御。
ドロワーの開閉時にActionBarDrawerToggleの中でロックモードを切り替えて制御。

ということをやっております。

課題としては、コードが汚い。きっとおれしかわからん。
右のドロワーが表示された時にactionbarの動作を変えたい。
右のドロワーの内容もぺちぺち変えたい。変えないならそのまま。
右のドロワーのタッチできる範囲を描画している範囲に変更したい。

以上です。

2013年6月25日火曜日

追記
DrawerLayoutを2つActivityにaddContentViewしても機能する。

左DrawerにAppコンテンツ。
右Drawerによく使うもの=actionbarに詰め込んでいるよく使う機能。
こういうこともできたりすると思う。
ちょろっとやったけど、制御がめんどくてやめた(ぁ

具体的には、android.r.id.content(Framelayoutだったりする)に対して、
左にドロワーをもつlayoutをaddContentView。
右にドロワーをもつlayoutをaddContentView。
メインコンテンツのFragmentを入れるFramelayoutをもつlayoutをaddContentView。

またぽつぽつやってみよう。
引き続きNavigation Drawerのお話。
ドロワーをFragment化、コンテンツもFragment化、actionbarのvisibleを制御なものを作ってみました。
サンプル(ブランチ:sample20130625)
2013/06/29追記
2013/06/29の記事にあるサンプルをお使いください。

interface万歳。これに尽きる。
actionbarのハンドリングもdrawerのイベントもinterfaceで受け渡し。
drawerから選択したitemを開くイベントもinterfaceでActivityへ。

画像サンプル
open時
close時

2013年6月20日木曜日

NavigationDrawerのleftDrawerの部分をFlameLayoutにしてListFragmentをいれてみました。

結果としては問題なし。

色々わかったこととしては、
1 menuの判定がめんどくさい
2 ドロワーレイアウトが開いているかいないかを常に確認する必要があった
3 ドロワー部分は単一のレイアウトグループの方がよさじ
4 Activityにもたせるものがわかりにくい

判定ではViewのDrawerLayoutやLeftDrawerをstaticにして参照させていたけど、
DrawerLayoutのリスナーでopen/close時にBundle投げたほうがいいかもしれない。

それとActivityにあるものをやたらめったら参照する必要があるので、staticになりがち。
それと可読性がちょっとアレ。でもAcitvityのクラスにまとめて入れておかないと何かと不便。

actionbarでひたすら難儀した。
最初は、LeftDrawerにいれたListFragmentのactionbarの状態をデフォルトにしたかったのだけど、
考えてみればドロワーが開いたときのメニューってアプリのデフォルトのメニューだった。
「アプリの」ってことはActivityに持たせたほうがいいかなということで変えました。
onPrepareOpitionsMenu()とかinvalidateOptionsMenu()とか初めて使いました。
menuのsetVisibleがあまり効かなかったので、結局menu.clear()になりました。

2013年6月18日火曜日

navigationdrawerをつかったサンプルを作ってみた。
デモの通りだと左だから右にしようと思ってやってみた。
LayoutファイルのListViewのGravityをendにすると右から出てくる。ほんとそれだけ。
サンプル

でもtopはだめだった。bottomもきっとだめなんだろう。

ListViewのGravityをjavaで動的に変更するやり方をご存知の方がいらっしゃいましたら、
コメントでこっそり教えてください。

2013年6月10日月曜日

ほむぺからブログへ

ブログってやったことないけどとりあえず書けばいいのよね。

Navigation Drawerってのがでたのでそれをつかったアプリを作ってます。

・・・以上。いいのかこれで。