2012-01-26
■[android] ICS の AsyncTask の挙動を Gingerbread と同じにする(AsyncTask#executeOnExecutor) その2
前回のエントリで AsyncTask#execute の挙動がAPI Levelによって違う、とわかりました。
では、どこで切り替えているのか。
参考
日本Androidの会 - API LEVEL 11以上のAsyncTaskの振る舞いについて
@zaki50さん から情報(↑)をいただいたので
ICSの ActivityThread.java を読んでみました。
(@zaki50さん、ありがとうございます!)
該当箇所はココ。
// android.app.ActivityThread private void handleBindApplication(AppBindData data) { // 抜粋 // If the app is Honeycomb MR1 or earlier, switch its AsyncTask // implementation to use the pool executor. Normally, we use the // serialized executor as the default. This has to happen in the // main thread so the main looper is set right. if (data.appInfo.targetSdkVersion <= 12) { AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } }
思いっきり書いてあります。
ココで切り替えてたのですねー。。
このソース内、他にも Honeycomb以前だったら判定とか StrictMode設定だったりとか。
いろんなことやってるのがチラチラ見えたのでもっと細かく読んでみようと思います。
恥ずかしい話、ActivityThread は Looperの起動 くらいしか思っていなかったですw
2012-01-25
■[android] ICS の AsyncTask の挙動を Gingerbread と同じにする(AsyncTask#executeOnExecutor)
AsyncTask の挙動が Gingerbread と ICS で違う、という話を聞いて調べてみました。
複数AsyncTask実行=バックグラウンドで並行処理、と考えてると
実は並行処理になってなくて処理が遅い、ってことですね。。
参考
@findup さんのエントリ。
きままな日記帳 - GingerbreadとICSでのAsyncTaskの挙動の違い
結論から言うと、ソース修正はこれだけでOKです。
修正方法
- AsyncTask#execute を AsyncTask#executeOnExecutor に変更する
- AsyncTask#executeOnExecutor の引数に AsyncTask.THREAD_POOL_EXECUTOR を指定する
1行で済みそうな修正ですね。よかったw
とはいえ気になったので、AsyncTask#execute の挙動の違いについてまとめてみます。
↓はSDKのバージョンを変えて検証した結果です。
検証結果(※範囲を修正)
複数AsyncTask の AsyncTask#execute を実行した場合
- API Level
1012まで- 複数バックグラウンド処理がパラレルで実行される
- API Level
1113以降- 複数バックグラウンド処理がシリアルで実行される
3.2 から変わった様子。
ちなみに検証はこんなかんじで行いました。
検証方法
- ビルドには ICS(API Level14) を使用
- 以下のパターンのapkを作成
- AndroidManifest.xml の android:minSdkVersion を11〜14の間で変更したapkを作成
- emulator(API Level14) 上で実行
では、GB相当の AsyncTask と同等の動きをさせるにはどうしたらいいか。
AsyncTask#execute のJavadoc見るとこう書いてます。
After HONEYCOMB, it is planned to change this back to a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the executeOnExecutor(Executor, Params...) version of this method with THREAD_POOL_EXECUTOR; however, see commentary there for warnings on its use.
Honeycomb以降では 複数のAsyncTackのバックグラウンド処理 をパラで実行したいなら
AsyncTask#executeOnExecutor + THREAD_POOL_EXECUTOR を使用してください、と。なるほど。
今回使用したサンプル。
(ボタン押したら AsyncTask#execute か AsyncTask#executeOnExecutor を実行するだけ)
コード
https://gist.github.com/1674289
実行結果(emulator(API Level14))
AsyncTask#execute 01-25 02:58:37.899: D/AsyncTaskTest(613): [doInBackground]start 78 01-25 02:58:39.950: D/AsyncTaskTest(613): [doInBackground]end 78 01-25 02:58:39.950: D/AsyncTaskTest(613): [doInBackground]start 79 01-25 02:58:41.970: D/AsyncTaskTest(613): [doInBackground]end 79
AsyncTask#executeOnExecutor 01-25 02:59:11.889: D/AsyncTaskTest(613): [doInBackground]start 80 01-25 02:59:11.929: D/AsyncTaskTest(613): [doInBackground]start 81 01-25 02:59:13.919: D/AsyncTaskTest(613): [doInBackground]end 80 01-25 02:59:13.989: D/AsyncTaskTest(613): [doInBackground]end 81
AsyncTask#executeOnExecutor の方はちゃんとパラで動いてますね!
よかったよかった!
検証方法間違ってたらツッコミお願いします!
追記
ICS の AsyncTask の挙動を Gingerbread と同じにする(AsyncTask#executeOnExecutor) その2
2011-12-20
■[android] ListViewの高速スクローラまわりをカスタマイズする
前回 のエントリに引き続き、さらに ListView の高速スクローラまわりをカスタマイズしてみました。
(ついでにタイトル変更)
環境
2.3.3(API Level10)
before
after
スクローラもいじったりましたw
ここで疑問。
ICSではどうなるのだろうか。
とりあえずソースそのままで API Level だけ変えて実験。
環境
4.0(API Level14)
before
after
ガイド(overlay)部分が3Dぽい感じに、スクローラは細身な感じに。
ソースはこちら。(前回エントリで使用した部分も合わせてます)
https://gist.github.com/1384757
2011-12-18
■[android] ListViewの高速スクロール時のガイドを編集する
Android Advent Calendar 12/18 担当の @daichan4649 です。
豪華メンバーがものすごく有意義なネタを記載されている中で、
空気を無視して ListView について誰得なネタを書いてみるとします。
ListViewには項目が多い場合に 「高速スクローラ」 を表示する機能がありますよね。
このとき 「スクロール中に項目のどのあたりを表示しているか」 を示す、
ガイドみたいなヤツを表示することができたりもします。
高速スクローラ、ガイド表示の実装に必要なのはこの2つ。
- AbsListView#setFastScrollEnabled(true)
- 1のListViewに設定するAdapterに SectionIndexer をimplementsする
1で高速スクローラを有効にし、
2でガイド表示内容を実装する、と。
実装してみると実際のイメージはこんなかんじになります。
初期実装
この例ではガイドに 「画面内先頭要素のindex値」 をそのまま表示してます。
右側に表示されているスクローラをドラッグすると、
リスト先頭項目に合わせてガイド表示内容も変わっていく、という感じです。
本題に入ります。
このガイド。
普通にいじろうとすると 指定した文字列を表示する、くらいしかできない。
濃いグレーの正方形 は編集できない。
ないわ!!!
ということで、これを自分の好きなようにカスタムする方法ないかなー、
とframeworkソース読んで試してみました。
とりあえず今回はこのグレーの正方形を変えるところを実験してみるとします。
いつものドロイド君アイコン画像に差し替えてみたり。
カスタム実装
変更OK!!
では、どんなかんじで差し替えたかを書いてみます。
まず、基本から。
AbsListView#setFastScrollEnabled(true) を呼び出すと
「高速スクローラとガイド」 を管理している FastScrollerインスタンス が
AbsListView内で生成されます。
しかし、ガイドのインスタンスが FastScrollerクラス 内に隠蔽されていて、
外部から編集できないようになっていたりします。
↓の FastScrollerインスタンス の中をいじりたいのですが。。。
// AbsListView.java public void setFastScrollEnabled(boolean enabled) { mFastScrollEnabled = enabled; if (enabled) { if (mFastScroller == null) { mFastScroller = new FastScroller(getContext(), this); } } else { if (mFastScroller != null) { mFastScroller.stop(); mFastScroller = null; } } }
とまぁ、どうしようもなかったのでリフレクションでいじりましたw
リフレクションでいじるポイント、タイミングは以下。
- AbsListView#setFastScrollEnabled(true) を呼び出す
- その直後にリフレクションで内部のFastScrollerインスタンスを差し替える
実際のソースはこんな感じになります。
https://gist.github.com/1384757
// gistコードはりつけがうまくいかないので、 // 今回のポイントになるリフレクション部分だけ貼りつけてみます @Override protected void onCreate(Bundle savedInstanceState) { (抜粋) final ListView listView = (ListView) findViewById(R.id.listview); // 高速スクローラ有効化設定 listView.setFastScrollEnabled(true); // AbsListView#setFastScrollEnabled 直後に実行 Drawable overlay = getResources().getDrawable(R.drawable.ic_launcher); customizeFastScroller(listView, overlay); } private void customizeFastScroller(AbsListView listView, Drawable overlay) { try { // 新FastScrollerインスタンス生成 Class<?> clazz = Class.forName("android.widget.FastScroller"); Constructor<?> constructor = clazz.getConstructor(Context.class, AbsListView.class); Object newFastScroller = constructor.newInstance(this, listView); // ガイド用drawable(グレーの四角)を上書き Field fieldOverlay = clazz.getDeclaredField("mOverlayDrawable"); fieldOverlay.setAccessible(true); fieldOverlay.set(newFastScroller, overlay); // FastScrollerインスタンス(オリジナル)を上書き Field orgFastScroller = AbsListView.class.getDeclaredField("mFastScroller"); orgFastScroller.setAccessible(true); orgFastScroller.set(listView, newFastScroller); } catch (Exception e) { e.printStackTrace(); } }
見てわかるとおり、なんとまぁ強引な手法ですw
この方法を使えば 「高速スクローラ画像」 自体も差し替え可能ですね。
それについてはまた後日書こうと思います。(gistにはコード載せてます)
最後に。
このような素晴らしい企画に参加させていただきまして、
@youten_redo さん、本当にありがとうございました!
明日(12/19)の担当は @patorash さんです!
追記
2011-11-27
■[android][community] Android Make Days in 明星和楽 にスタッフとして参加してきました
11/11金 - 12土 で行われた Android Make Days in 明星和楽。
今回自分はスタッフとして参加させていただきました。
メイン作業は司会。
部屋が複数あるので3人で交代しつつ、とのことだけども。
こんなでかいイベントで司会とか!!不安でガクブル。
と、自分のことはさておき、まずはスタッフ視点を含めつつレポート書いてみようかと。
各セッションについての素晴らしいエントリはもういくつもあるし。
というわけで早速。
(写真多めですー)
11/10(木)
翌日に迫ったイベントに向けて最終打ち合わせ。
各担当の役割確認とか。
当日に配るステッカーを皆で切ったりとか。

11/11(金)
24Makeコンテスト。
場所は GuildCafe Costa。
金曜夜から24時間でアプリ作って土曜に発表、というクレイジーなイベントw
仕事終わって皆様の頑張る様子をチラ見しにきてみた。
来年は作る側で参加しようかなー。すごく楽しそうだった。
参加者の皆様の様子。
11/12(土)
9:00
スタッフメンバー集合。
場所は 福岡県Rubyコンテンツ産業振興センター。
自分は間違えて金曜の会場、GuildCafe Costa へ行ってしまうという醜態をさらすw
会場設営やったり。
スタッフミーティングやったり。

11:00
ホモ弁に予約していたスタッフメンバー分の弁当を受け取り行ったり。
司会させていただくセッションの講師の皆様と打ち合わせしたり。
弁当食べながら司会メンバーで打ち合わせの図。
twicca作者の @R246氏(青山さん) とパシャリ。
あんざいゆきさん との打ち合わせ。
自分はあんざいさんの大ファンなのでサインいただいちゃいました。
うん、自分きもいですねw
イベント開始前にみんなで交代で弁当タイム。
鹿。どや。
世界の みよしさん 発見!
13:00
開場。
参加者が続々。
すぐにメイン会場が埋まる。
受付の様子
美女ぞろい。
ステッカーたくさん。
オープニング、基調講演。
ブースの様子。





Arduino体験講座。

レッドブル様との交流w
LT会場。大盛況。
個人的にどストライクだったのが、
ブリリアントサービス様のNFCクエスト体験。
@R246氏、サイバーエージェント三島木さん と超強力なパーティ組んで挑んだものの。。
敗北('A`)
魔王強い。
ブースの皆様と。
19:00
閉会式。
全員で手を繋いでスタッフ一同挨拶。

19:30 - 21:00
懇親会。
乾杯。
わいわい。

三島木さんとあんざいさん
後片付け後、残っていたメンバーで集合写真。
終了後。
スタッフ数名で一緒に明星和楽Gates会場へ行ったあと、途中離脱して屋台へ移動。
屋台の後は酔っぱらってひとりで中洲をうろうろして川沿いで寝てた。(ひどい)
で、結局タクシーで5時すぎに帰宅。
イベントを終えて。
正直なところ、スタッフメンバーはイベント屋ではないので普段の自分の業務もある。
そんな状況下でも各メンバーが責任もってタスクをちゃんとこなし、
結果、イベントは大成功っていうのはやっぱりすごいことじゃないかと。
イベント終了後になんだかイロイロと考えてしまった。
いやー!!!本当に楽しかった!!
スタッフとして参加できて本当によかった!!
次回もぜひスタッフとして参加したいし、何かを発信する側にもなりたい。
改善すべき、反省すべき点はもちろんたくさんあるけれども、
イベント運営の難しさと楽しさを味わうことができて、
すごく貴重ないい経験ができた、参加できてよかった、楽しかった、というのが本音。
運営スタッフの皆様、講演者の皆様、参加された皆様、
本当にお疲れさまでした!!
2011-11-20
■[雑記]12歳の頃
12歳の子どもが独学でプログラミングをおぼえ, ついにiPhoneアプリの会社まで設立
12歳。。だと。。。
自分が12歳の頃を思い出してみた。
「unk!unk!」言ってキャッキャしてただけのただのガキンチョだった。
正直今もあまり変わってません。
がんばります。
2011-10-14
■[android]Intentの作り方
Activity/Service起動用のIntentを作るとき、
皆どうやってるのだろう、とふと疑問。
例えば Activity#onCreate 内で
「Service(test.intent.TestService)起動用のIntent」 を作るとする。
基本的な作り方はこんなかんじになるのかな?
@Override public void onCreate(Bundle savedInstanceState) { 〜 // コンストラクタのみ で作る場合 Intent intent1 = new Intent(this, TestService.class); Intent intent2 = new Intent(getApplicationContext(), TestService.class); // コンストラクタ(引数なし)+setClassName で作る場合 String packageName = getPackageName(); String className = TestService.class.getCanonicalName(); Intent intent3 = new Intent(); intent3.setClassName(packageName, className); }
自分の場合は intent1、2 みたいにコンストラクタ1行で済ませることが多い。
web上のサンプル見てもこのパターンが多い気がする。
通常はこれでいいはず。楽だし。
ここで出た疑問。
ApplicationContext を渡す必要があるのだろうか。
Intent のソースを確認してみたところ、
本来は ApplicationContext を引数に渡す必要などなく、
最低限必要なのはこれ↓↓だけだった。
Intent作成に必要な情報
- アプリケーションのパッケージ名
- 起動するクラス名
1は AndroidManifest.xml の <manifest>タグのpackage属性指定値。
起動するクラス(TestService)のパッケージ名ではない。
2は 起動するActivity/Service等のクラス名(パッケージ名まで含む)。
引数に渡された ApplicationContext は、内部で
「アプリケーションのパッケージ名」 を取得するために使ってるだけ。
まーつまり intent1、2、3 はどれも同じなので、
作りたい作り方でおk、ということでしたw
Intent.java(抜粋)
public Intent() { } public Intent(Context packageContext, Class<?> cls) { mComponent = new ComponentName(packageContext, cls); } public Intent setClassName(Context packageContext, String className) { mComponent = new ComponentName(packageContext, className); return this; }
ComponentName.java(抜粋)
public ComponentName(String pkg, String cls) { if (pkg == null) throw new NullPointerException("package name is null"); if (cls == null) throw new NullPointerException("class name is null"); mPackage = pkg; mClass = cls; } public ComponentName(Context pkg, String cls) { if (cls == null) throw new NullPointerException("class name is null"); mPackage = pkg.getPackageName(); mClass = cls; } public ComponentName(Context pkg, Class<?> cls) { mPackage = pkg.getPackageName(); mClass = cls.getName(); }
話変わって。
intentって文字列定義みたいなもんだから、
できれば 「static final」 定義で、static初期化子 内で作っておきたいなー、
なんて自分は思ってたりする。後から編集しないようなintentは特に。
でも 「アプリケーションのパッケージ名」 は
static初期化子 内で動的に取得できない。
ということは、こんなかんじで固定値定義するしかないのか??
private static final Intent intent; static { String packageName = "test.intent"; String className = TestService.class.getCanonicalName(); intent = new Intent(); intent.setClassName(packageName, className); }
うーん。微妙。
2011-10-04
■[android]別スレッドでキュー管理(HandlerThread)
非同期処理を組んでると、
「別スレッド上でも Handler みたいなキュー管理がしたい」
って時があったりしますよねー。(きっと)
そんなとき、HandlerThread を使うとわりと簡単に実装できたりする。
HandlerThread(公式)
HandlerThread は java.lang.Thread を継承したクラスで、
android標準の Handler と組み合わせて使うための仕組みが入ってたりする。
具体的な使い方。
まずは Handler(defaultコンストラクタ) を使う場合。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); }
実行すると当然 「main」 と出力される。
当然、mainスレ上でキューが実行される。
これは問題ないですね。
次に Handler+HandlerThread の場合。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 別スレ生成 -> 開始 HandlerThread handlerThread = new HandlerThread("other"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()); handler.post(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); }
実行すると 「other」 と出力される。
キューが実行されてるスレッドが mainスレ から 別スレ に変更されます。
もちろんこの Handlerインスタンス は別スレ上で使用してOK。
手順をまとめるとこんなかんじ。
- 別スレッド(HandlerThread)を生成
- 別スレ開始
- HandlerThreadインスタンス から Looperインスタンス 取得
- Handlerインスタンスを生成(3で取得した Looperインスタンス を 引数指定)
- 4 で作成した Handlerインスタンス を使用(Handler#post 等)
今まで PriorityBlockingQueue とか使って、
自前のキュー管理クラス作ってた自分涙目。
キューをFIFOで実行するだけのキュー管理であれば、
このやり方でまったく問題なさそうですね。
framework ではどうやってるのかなーと思い、
Looper、Handler、HandlerThread のソースを読んでみた。
ポイントはこのあたり。
- コンストラクタ Handler(default) は「mainスレッド上で作成された」 Looperインスタンス を使用
- コンストラクタ Handler(Looper) は「引数で渡された」 Looperインスタンス を使用
- Looperインスタンスは Looper#prepare を呼び出すと生成される(ThreadLocal保持)
- HandlerThread は Thread を継承したクラス
- HandlerThread#run の中で Looper#prepare を呼び出している
Looperインスタンスの生成場所 の違い。
なるほど。
2011-09-12
■[雑記]東日本大震災から半年経って思うこと
実は母親と祖父が福島に住んでいたりする。
原発から半径数十キロとかそのレベル。
直接的な津波の被害にはあっていないのだが、
震災が起きた際にはもちろん電話も通じず、物流も止まってた。
ただ、わりと早い時間で電気/ガスも復旧し、
今は普段どおりの生活はおくれているみたい。
震災直後。
電車も不通、道路もどうなっているかわからない。
祖父の家までたどりつけるかもわからない。
けど、このままだとどうなるかわからないから直接迎えに行く、
と言ったが、親からは絶対に来るな、と言われた。
私らは寿命まであと20数年。あんたは今からまだ長い。
今から結婚して子供生んだり育てたりするだろう。
原発の状況がわからない、どうなるかわからない場所には
絶対に来るな、来るのは親不孝な行為だ、と。
まーそんなこと言われても子の立場としては心配で仕方ない。
でもどうにもできず、ヤキモキしながら今を過ごしている。
ついこの間。
何かを見てて考えさせられることがあった。
両親とはあとどれくらい会えるのだろう、話せるのだろう、と。
両親は二人とも去年還暦を迎えて。
考えたくないけど、平均寿命とかイロイロ考えるとあと25年くらいなのか?
自分の場合、実際のところ年間何日会っているだろう。
月1回会うとしても年間12回。
12日×25=300日。
この数字を見て考えてしまった。
1年もないのか、と。
会える時間をもっと大事にしないといかんなー、と。
いろんなことを後悔しないように、と。
東日本大震災から半年。
メディアからは震災関連情報がだいぶ減ってきた。
震災直後ほど頭の中がそのことでいっぱいではない自分もいる。
若干麻痺してきているというか慣れつつあるというか。
でもやっぱり、自分の身内が被災しているので余計に他人ごととは思えない。
復興に関してもし自分にできることがあれば、
これからも少しでも協力できたらいいなと考えている。
ありがたいことに、@kuneen 作曲の被災地復興応援ソングプロジェクトの
バックコーラスに参加させていただきました。
元気が出るとてもいい曲なので、
たくさんの方に聴いていただけたら、と思ってます。
Pray〜明日を生きる
震災で亡くなった方々のご冥福を心よりお祈りいたします。

