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();
    }