Your SlideShare is downloading. ×
絶対落ちないアプリの作り方
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

絶対落ちないアプリの作り方

147
views

Published on

絶対落ちないアプリの作り方(DroidKaig 2015)

絶対落ちないアプリの作り方(DroidKaig 2015)

Published in: Software

0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
147
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
2
Comments
0
Likes
3
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. 絶対落ちないアプリの作り方 DroidKaigi 2015/04/25 株式会社マナボ 白山 文彦 (@fushiroyama)
  • 2. サンプルコード https://github.com/srym/DroidKaigiSample
  • 3. • 白山 文彦 (@fushiroyama) • 株式会社マナボ (http://mana.bo/) • Android • Ruby on Rails • DevOps • Full Text Search • 筋トレ
  • 4. 今日は絶対に落ちないアプリについて 考察してみたいと思います。
  • 5. だがその前に伝えておきたことがある…
  • 6. _人人人人人人人人人人_ > そんなものはない <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
  • 7. なんでAndroidアプリは 落ちる運命にあるのだろうか?
  • 8. • バージョンが多種多様 • 端末が多種多様 • ベンダによる カスタマイズ • 鬼門:カメラアプリ • http://alpha.mixi.co.jp/entry/ 2013/11572/ ※ここに苦労がまとまってます • プログラマの無知と怠慢
  • 9. • バージョンが多種多様 • 端末が多種多様 • ベンダによる カスタマイズ • 鬼門:カメラアプリ • http://alpha.mixi.co.jp/entry/ 2013/11572/ ※ここに苦労がまとまってます • プログラマの無知と怠慢
  • 10. プログラマの無知と怠慢は 何とかしたい!
  • 11. 今日は短い時間なので テーマを絞って話したいと思 います。
  • 12. クラッシュのないアプリ作りの スタートは、クラッシュの事実 に向き合うことから始まります。
  • 13. https://crashlytics.com/
  • 14. 弊社のCrashlyticsで 実際にクラッシュの多いミスを 元に資料を作りました!
  • 15. 1. FragmentTransactionの取り扱いミス 2. ライフサイクルの終わったコントローラへの不正なアクセス 3. APIとの連携ミス 4. カメラアプリとの連携ミス 5. 大量の画像によるOutOfMemory 6. Activity/Fragmentの再生成・復元ミス などなど…
  • 16. アジェンダ • ライフサイクルの理解 • 非同期処理とコールバック • Contextの正体 • Fragmentあれこれ • Handlerの本質 • 静的解析を活かす • 本当に落ちないことが本当に幸せか?
  • 17. ライフサイクルの理解
  • 18. ホントにホントに理解出来てる?
  • 19. • Activityはユーザ操作等によって短期間 で頻繁に生き死にを繰り返す • Activityの生死はプログラマが手出し出 来ない • Activityが状態として死を迎えた時と、 実際にオブジェクトがGCされるタイミ ングは違う
  • 20. 超・初級編: onSaveInstanceState onRestoreInstanceState
  • 21. public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 startActivity(AnotherActivity.newIntent(this, "hello next intent"));
 }
 } サンプルコード 「saveinstancestate」
  • 22. public class AnotherActivity extends Activity {
 private static final String KEY_TEXT = "key_text";
 
 private String text;
 
 public static Intent newIntent(Context context, String text) {
 Intent intent = new Intent(context, AnotherActivity.class);
 intent.putExtra(KEY_TEXT, text);
 return intent;
 }
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_another);
 
 Intent intent = getIntent();
 if (intent != null && intent.hasExtra(KEY_TEXT)) {
 text = intent.getStringExtra(KEY_TEXT);
 TextView textView = (TextView) findViewById(R.id.text);
 textView.setText(text);
 }
 }
 }
  • 23. • 渡されたIntentの中身を取り出して次の Activityのfieldに保存しておくことは良くある と思うが、これをうっかり保存しわすれてその フィールドがある前提でアクセスしてクラッ シュ。誰もが初心者の時にやったことがある のではないでしょうか。
  • 24. onSaveInstanceState 強制ギプス
  • 25. これで漏れを検知できる
  • 26. 初級編:非同期処理
  • 27. Androidの非同期ライブラリ
  • 28. • AsyncTask • Loader • Java’s Thread • Handler (!) • 3rd Party Libraries • OkHttp • Volley • Retrofit
  • 29. これらに共通して言えること
  • 30. 非同期処理はコントローラよ り寿命が長いことがままある
  • 31. public class MainActivity extends Activity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 new MyTask().execute();
 }
 
 private class MyTask extends AsyncTask<Void, Void, Void> {
 @Override
 protected Void doInBackground(Void... params) {
 // heavy task return null;
 }
 
 @Override
 protected void onPostExecute(Void aVoid) {
 // do something on Activity
 }
 }
 } サンプルコード 「asynctaskbadexample」
  • 32. public class MainActivity extends Activity {
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 new MyTask().execute();
 }
 
 private class MyTask extends AsyncTask<Void, Void, Void> {
 @Override
 protected Void doInBackground(Void... params) {
 // heavy task return null;
 }
 
 @Override
 protected void onPostExecute(Void aVoid) {
 // do something on Activity
 }
 }
 } Activityより 長生きする可能性 がある
  • 33. Activityより長生き… ここにクラシュの罠がたくさんある
  • 34. まずはAsyncTask
  • 35. 実はAsyncTaskは非推奨 (ワタシ的に)
  • 36. • The dark side of AsyncTask • http://bon-app-etit.blogspot.jp/2013/04/ the-dark-side-of-asynctask.html • AsyncTask is bad and you should feel bad • http://simonvt.net/2014/04/17/ asynctask-is-bad-and-you-should-feel- bad/
  • 37. • もともと長くて数秒の非同期処理にのみ使うことを推奨されてい る • 基本やりっ放しなので処理を丸々無駄にする • cancel()すればいいのでは? • BitmapFactory.decodeStream()みたいなキャンセルできない 操作もある • バージョンによってシーケンシャルかパラレルかが異なる • 画面回転等で結果を引き継げない • onRetainNonConfigurationInstanceとか使えばいいけどそん なことするぐらいなら後述のLoader使ったほうがいい
  • 38. どうしてもAsyncTaskを 使いたい場合…
  • 39. public class MainActivity extends Activity {
 private static final String TAG = MainActivity.class.getSimpleName();
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 new MyTask(new MyTask.Callback() {
 @Override
 public void onFinish() {
 Log.d(TAG, "finish");
 }
 }).execute();
 }
 
 private static class MyTask extends AsyncTask<Void, Void, Void> {
 private Callback callback;
 
 private MyTask(Callback callback) {
 this.callback = callback;
 }
 
 @Override
 protected Void doInBackground(Void... params) {
 // do something
 return null;
 }
 
 @Override
 protected void onPostExecute(Void aVoid) {
 callback.onFinish();
 }
 
 private interface Callback {
 void onFinish();
 }
 }
 } ほんのちょっと マシな例 サンプルコード 「asynctaskbetterexample」
  • 40. もうほんのちょっ とマシな例 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 new MyTask(new MyTask.Callback() {
 @Override
 public void onFinish() {
 Log.d(TAG, "finish");
 }
 }).execute();
 }
 
 private static class MyTask extends AsyncTask<Void, Void, Void> {
 private WeakReference<Callback> callbackRef;
 
 private MyTask(Callback callback) {
 callbackRef = new WeakReference<Callback>(callback);
 }
 
 @Override
 protected Void doInBackground(Void... params) {
 // do something
 return null;
 }
 
 @Override
 protected void onPostExecute(Void aVoid) {
 Callback callback = callbackRef.get();
 if (callback != null)
 callback.onFinish();
 }
 
 private interface Callback {
 void onFinish();
 }
 } サンプルコード 「asynctaskmorebetterexample」
  • 41. private MyTask task;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 task = new MyTask(new MyTask.Callback() {
 @Override
 public void onFinish() {
 Log.d(TAG, "finish");
 }
 });
 task.execute();
 }
 
 @Override
 protected void onDestroy() {
 super.onDestroy();
 task.cancel(true);
 }
 
 private static class MyTask extends AsyncTask<Void, Void, Void> {
 // 前略
 
 @Override
 protected Void doInBackground(Void... params) {
 while (taskRemaining()) {
 if (isCancelled()) {
 Log.d(TAG, "canceled");
 return null;
 }
 doHeavyTask();
 }
 return null;
 }
 
 // 後略
 } もうほんの あと少しマシな例 サンプルコード 「asynctaskmuchmorebetterexample」
  • 42. AsyncTaskはちょろっとした処 理をするのに便利なのも事実な ので上手に付き合おう
  • 43. AsyncTaskLoader
  • 44. • ActivityやFragmentのライフサイクルと非同 期処理を切り離すことができる • コールバックをコントローラ側に簡単に記述で きる。結果はメインスレッドで受け取れる。 • 一番重要な点は、画面回転等でも非同期処理を 引き継げるような書き方が簡単にできること。
  • 45. public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks<String> {
 private static final String TAG = MainActivity.class.getSimpleName();
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 LoaderManager manager = getLoaderManager();
 manager.initLoader(0, null, this);
 }
 
 @Override
 public Loader<String> onCreateLoader(int id, Bundle args) {
 return new MyLoader(this.getApplicationContext());
 }
 
 @Override
 public void onLoadFinished(Loader<String> loader, String data) {
 Log.d(TAG, data);
 }
 
 @Override
 public void onLoaderReset(Loader<String> loader) {
 // NOP
 } サンプルコード 「loadersimpleexample」
  • 46. private static class MyLoader extends AsyncTaskLoader<String> {
 private String mCachedData;
 
 private MyLoader(Context context) {
 super(context);
 }
 
 @Override
 public String loadInBackground() {
 return "fetched";
 }
 
 @Override
 public void deliverResult(String data) {
 if (isReset()) {
 if (mCachedData != null) {
 mCachedData = null;
 }
 return;
 }
 mCachedData = data;
 if (isStarted()) {
 super.deliverResult(data);
 }
 }
 
 @Override
 protected void onStartLoading() {
 if (mCachedData != null) {
 deliverResult(mCachedData);
 return;
 }
 
 if (takeContentChanged() || mCachedData == null) {
 forceLoad();
 }
 }
 
 @Override
 protected void onStopLoading() {
 cancelLoad();
 super.onStopLoading();
 }
 
 @Override
 protected void onReset() {
 onStopLoading();
 super.onReset();
 }
 }

  • 47. • Loaderを管理するLoaderManagerはActivityやFragment ごとにひとつ。 • 画面回転でもLoaderManagerインスタンスは死なない。 • LoaderManager#initLoaderではコールバックをセットし 直しつつ処理を引き継ぐ。 • LoaderManager#restartLoaderでは既存の非同期処理を破 棄して新たに非同期処理を行う。
  • 48. AsyncTaskとの最大の違い
  • 49. • AsyncTaskの中にコールバックメソッドがある • →利用側にコールバックのロジックを持つのが 手間 • AsyncTaskLoaderはLoaderCallbacksを利用側 が実装するので使いやすい
  • 50. EventBus
  • 51. • Observerパターンのような感じでライフサイクルオブジェクト は自分のライフサイクルに合わせてイベントの購読/非購読する • 非同期処理が終わったら購読者に完了イベントを一斉通知する • コールバックが基本一対一なのに対し、一対多へ通知も可能。 • Loader同様、ライフサイクルオブジェクトより長命な処理が無 駄にならず、ライフサイクル側でregister/unregisterするので コールバック時にライフサイクルが終わっている危険性も無い。
  • 52. import com.squareup.otto.Bus;
 
 public final class BusProvider {
 private static final Bus BUS = new Bus();
 
 public static Bus getInstance() {
 return BUS;
 }
 
 private BusProvider() {
 }
 } サンプルコード 「eventbusexample」
  • 53. BusProvider.getInstance().post(new NewVersionAvailableEvent(latestVersion)); イベントを発火
  • 54. @Override
 protected void onResume() {
 super.onResume();
 BusProvider.getInstance().register(this);
 }
 @Override
 protected void onPause() {
 super.onPause();
 BusProvider.getInstance().unregister(this);
 }
 
 @Subscribe
 public void onEventReceived(HogeEvent event) {
 }
  • 55. • square/otto • https://github.com/square/otto • greenbot/EventBus • https://github.com/greenrobot/EventBus • おれおれコールバック設計 • http://qiita.com/hnakagawa/items/984557b13aede61d05d0 • EventBusとは違うけど非常に参考になる
  • 56. RxJava/RxAndroid
  • 57. RxJava/RxAndroid
  • 58. • ここ一年で物凄い勢いで注目を集めているライブラリ及びプ ログラミング手法 • Reactive Functional Programming • LINQのような集合操作メソッド群と遅延評価を合わせたよう な関数型的プログラミング手法 • コールバックヘルからの脱却! • これだけで長大な記事になるので機会があれば後日記事にし ます!!!(今回は泣く泣く断念)
  • 59. Contextの正体を意識する
  • 60. • Context…アプリケーションの様々な情報にアク セスするためのインタフェース • 画像、文字等の各種リソース • アプリケーションそのものの情報 • パーミッション等々
  • 61. 概念が抽象的すぎてよくわからない
  • 62. 大きく分けて ApplicationContext ActivityContext もうあと2つぐら いあるらしいけど
  • 63. なぜContextの正体を知って いる必要があるか?
  • 64. ActivityContextが長く参照さ れる=深刻なメモリリーク
  • 65. public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 new MyTask(getApplicationContext()).execute();
 }
 
 private class MyTask extends AsyncTask<Void, Void, Void> {
 private Context context;
 
 private MyTask(Context context) {
 this.context = context;
 }
 
 @Override
 protected Void doInBackground(Void... params) {
 context.getString(R.string.hello_world);
 return null;
 }
 }
 }

  • 66. だいたいのケースで ApplicationContextが使える
  • 67. じゃあ全部 ApplicationContext でいいの?
  • 68. • Activity 外から startActivity する場合は Intent に FLAG_ACTIVITY_NEW_TASK が含まれている必要 がある。 • AlertDialog.BuilderにApplicationContextを渡すと WindowTokenの取得に失敗 • Toast時に意図しないテーマが出たりすることも
  • 69. new  AlertDialog.Builder(getApplicationContext()); E/AndroidRuntime: Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
  • 70. やはり、ちゃんと理解して気 持ちよく使おう!
  • 71. 原則:ActivityContextをActivity より長命などこかに渡す場面に遭 遇したら、設計を見なおせ!
  • 72. Fragmentあれこれ
  • 73. Fragmentは気をつけないと非 常にクラッシュの元になります
  • 74. 原則その1: Fragmentインスタンスにsetしない
  • 75. Fragment fragment = new BlankFragment();
 fragment.setParam(param);
 getFragmentManager().beginTransaction()
 .replace(android.R.id.content, fragment)
 .commit(); —————————————————————————————————————————————————————— Fragment fragment = new BlankFragment(param1, param2);
 getFragmentManager().beginTransaction()
 .replace(android.R.id.content, fragment)
 .commit(); どっちも 間違い!
  • 76. public class BlankFragment extends Fragment {
 private static final String ARG_PARAM1 = "param1";
 private static final String ARG_PARAM2 = "param2";
 
 private String mParam1;
 private String mParam2;
 
 public static BlankFragment newInstance(String param1, String param2) {
 BlankFragment fragment = new BlankFragment();
 Bundle args = new Bundle();
 args.putString(ARG_PARAM1, param1);
 args.putString(ARG_PARAM2, param2);
 fragment.setArguments(args);
 return fragment;
 }
 
 public BlankFragment() {
 // Required empty public constructor
 }
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 if (getArguments() != null) {
 mParam1 = getArguments().getString(ARG_PARAM1);
 mParam2 = getArguments().getString(ARG_PARAM2);
 }
 }
 
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container,
 Bundle savedInstanceState) {
 // Inflate the layout for this fragment
 return inflater.inflate(R.layout.fragment_blank, container, false);
 }
  • 77. 原則その2:リスナもsetできない
  • 78. BlankFragment fragment = BlankFragment.newInstance("param1", "param2");
 fragment.setListener(new BlankFragment.OnFragmentInteractionListener() {
 @Override
 public void onFragmentInteraction(Uri uri) {
 // interaction
 }
 });
 
 getFragmentManager().beginTransaction()
 .replace(android.R.id.content, fragment)
 .commit(); もちろん 間違い!
  • 79. 原則その3:リスナは setSerializableで渡せない
  • 80. public class BlankFragment extends Fragment {
 private static final String ARG_PARAM1 = "param1";
 private static final String ARG_PARAM2 = "param2";
 private static final String ARG_LISTENER = "listener";
 
 private String mParam1;
 private String mParam2;
 private OnFragmentInteractionListener mListener;
 
 public static BlankFragment newInstance(String param1, String param2, OnFragmentInteractionListener listener) {
 BlankFragment fragment = new BlankFragment();
 Bundle args = new Bundle();
 args.putString(ARG_PARAM1, param1);
 args.putString(ARG_PARAM2, param2);
 args.putSerializable(ARG_LISTENER, listener);
 fragment.setArguments(args);
 return fragment;
 }
 
 public BlankFragment() {
 // Required empty public constructor
 }
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 if (getArguments() != null) {
 mParam1 = getArguments().getString(ARG_PARAM1);
 mParam2 = getArguments().getString(ARG_PARAM2);
 mListener = (OnFragmentInteractionListener) getArguments().getSerializable(ARG_LISTENER);
 }
 } public interface OnFragmentInteractionListener extends Serializable {
 public void onFragmentInteraction(Uri uri);
 } 一見行けそうだが …
  • 81. @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 BlankFragment fragment = BlankFragment.newInstance("param1", "param2", new BlankFragment.OnFragmentInteractionListener() {
 @Override
 public void onFragmentInteraction(Uri uri) {
 // interaction
 }
 });
 
 getFragmentManager().beginTransaction()
 .replace(android.R.id.content, fragment)
 .commit();
 } Activityへの 暗黙の参照
  • 82. • https://docs.oracle.com/javase/jp/1.4/guide/serialization/spec/serial- arch.doc10.html • (引用)ローカルクラスおよび匿名クラスを含む内部クラス (static メンバクラスでは ない入れ子のクラス) の直列化は、いくつかの理由により、使用しないことを強くお勧 めします。非 static コンテキストで宣言された内部クラスには、囲むクラスインスタン スへの暗黙的な非 transient 参照が含まれるので、そのような内部クラスインスタンス を直列化すると、関連する外部クラスインスタンスも直列化されることになります。内 部クラスを実装する javac (またはその他の JavaTM コンパイラ) によって生成された 合成フィールドは、実装に依存するので、コンパイラによって相違が生じることがあり ます。
  • 83. @Override
 public void onAttach(Activity activity) {
 super.onAttach(activity);
 try {
 mListener = (OnFragmentInteractionListener) activity;
 } catch (ClassCastException e) {
 throw new ClassCastException(activity.toString()
 + " must implement OnFragmentInteractionListener");
 }
 } Activityで implement サンプルコードの 「fragmentexample」
  • 84. EventBusはActivity-Fragmentの インタラクションにも有効
  • 85. FragmentTransactionに ご用心!
  • 86. BlankFragment fragment = BlankFragment.newInstance("param1", "param2");
 fragment.setListener(new BlankFragment.OnFragmentInteractionListener() {
 @Override
 public void onFragmentInteraction(Uri uri) {
 // interaction
 }
 });
 
 getFragmentManager().beginTransaction()
 .replace(android.R.id.content, fragment)
 .commit();
  • 87. • onSaveInstanceState以降にFragmentTransactionを伴う処理を行うと IllegalStateExceptionが発生する • これは、そのタイミング以降になにか変更をしても、もしインスタンスが再 生成された場合に元の状態に戻しようがないため • FragmentTransaction.commitAllowingStateLoss()すれば例外が上がらな いようにはできる
  • 88. • 意外な盲点として、DialogFragment#show, dissmissはFragmentTransaction を伴う処理であるということ。 • 非同期通信の間何かダイアログを出し、完了後にdissmissするような処理は非 常にありがちなので、タイミング次第で発生するクラッシュになる。 • 弊社のクラッシュでもFragmentTransactionに起因する例外が非常に多かっ た!
  • 89. Fragment#isResumed() isベンリ
  • 90. if (isResumed()) {
 dialog.dismiss();
 }
  • 91. 
 public class BaseActivity extends Activity {
 private volatile boolean mIsResumed = false;
 
 @Override
 protected void onResume() {
 super.onResume();
 mIsResumed = true;
 }
 
 @Override
 protected void onPause() {
 super.onPause();
 mIsResumed = false;
 }
 
 protected boolean isActivityResumed() {
 return mIsResumed;
 }
 
 protected boolean isActivityPaused() {
 return !mIsResumed;
 }
 
 }
 Activity版を作る と気休めになる
  • 92. onResume/onPause間で 安全にFragmentTransactionする テクニック紹介
  • 93. public abstract class PauseHandler<T> extends Handler {
 private final List<Message> messageQueueBuffer = Collections.synchronizedList(new ArrayList<Message>());
 
 private T obj;
 public final synchronized void resume(T obj) {
 this.obj = obj;
 
 while (messageQueueBuffer.size() > 0) {
 final Message msg = messageQueueBuffer.get(0);
 messageQueueBuffer.remove(0);
 sendMessage(msg);
 }
 }
 
 public final synchronized void pause() {
 obj = null;
 }
 
 @Override
 public final synchronized void handleMessage(Message msg) {
 if (obj == null) {
 final Message msgCopy = new Message();
 msgCopy.copyFrom(msg);
 messageQueueBuffer.add(msgCopy);
 } else {
 processMessage(obj, msg);
 }
 }
 
 protected abstract void processMessage(T obj, Message message);
 
 } サンプルコードの 「pausehandler」
  • 94. private static class ActivityPauseHandler extends PauseHandler<BaseActivity> {
 @Override
 protected void processMessage(BaseActivity activity, Message message) {
 activity.processMessage(message);
 }
 } public ActivityPauseHandler mPauseHandler = new ActivityPauseHandler(); @Override
 protected void onResume() {
 super.onResume();
 mPauseHandler.resume(this);
 } @Override
 protected void onPause() {
 super.onPause();
 mPauseHandler.pause();
 }
  • 95. @Override
 public void processMessage(Message message) {
 if (message.what != WHAT_SHOW_DIALOG) {
 return;
 } dialog.show(getSupportFragmentManager(), "TAG");
 }
  • 96. Handlerを理解する
  • 97. Handlerとはなにか
  • 98. mHandler.post(new Runnable() {
 @Override
 public void run() {
 Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
 
 }
 }); ワーカスレッドか ら呼び出し
  • 99. ワーカスレッドからUIスレッドを 触るときに使う不思議なやつ…?
  • 100. だけではないんです。
  • 101. • 任意のThread(メッセージループ)にメッセージを送ったりメッ セージを取り出して処理したりする人 • AndroidではLooperがメッセージループ • Messageがメッセージ(そのまま!) • LooperはMessageを貯めておいて随時実行するキューを持ってい る(MessageQueue) • 単純なキューではない。遅延実行等を加味されてキューに積まれる。
  • 102. なぜこれを知っておく必要があるか?
  • 103. AndroidのUI =シングルスレッドモデル
  • 104. UI更新はUIスレッドのメッセージ キューに積んで処理してもらう必要 がある。
  • 105. new Handler()された場所のThreadに紐づく=UIス レッドでnewされたらUIスレッドにメッセージが送れ るというだけ! あるいは new Handler(Looper.getMainLooper());
  • 106. AndroidではUIスレッド以外でUI操 作を試みた時点でクラッシュする
  • 107. いままでなんとなくHandler使っ てませんでしたか?
  • 108. Handlerとその奥に流れる思想を理解で きたらいよいよAndroid中級者です!
  • 109. Handlerの応用例
  • 110. HandlerThread thread = new HandlerThread(NON_UI_HANDLER_THREAD_NAME);
 thread.start();
 mNonUiHandler = new Handler(thread.getLooper()); Canvas canvas = mHolder.lockCanvas();
 canvas.drawColor(Color.WHITE, PorterDuff.Mode.SRC);
 canvas.concat(mMatrix);
 drawImages(canvas);
 canvas.restoreToCount(saveCount);
 mHolder.unlockCanvasAndPost(canvas);
  • 111. 静的解析のチカラを借りる
  • 112. Javaはよくも悪くも強い静的型付 け言語である。
  • 113. 型、アノテーション、いずれも強 力なのだから最大限利用すべき
  • 114. @Nullable @NonNull nullチェックがないと警告 nullを渡すと警告
  • 115. @BooleanRes @ColorStateListRes @DrawableRes @IntArrayRes @IntegerRes @LayoutRes @MovieRes @TextRes @TextArrayRes @StringArrayRes https://github.com/excilys/ androidannotations/wiki/Resources int型でも、対応するリソースの intでないとコンパイルエラー
  • 116. private ConfabDetailState(int statusId, @StringRes int statusStringId, @StringRes int tutorLabelId, @ColorRes int borderColorId, @ColorRes int backgroundColorId) {
 mStatusId = statusId;
 mStatusStringId = statusStringId;
 mTutorLabelId = tutorLabelId;
 mBorderColorId = borderColorId;
 mBackgroundColorId = backgroundColorId;
 } 意図しないintの代入を コンパイル時に防げる
  • 117. 絶対に落ちないアプリが 本当に幸せか?
  • 118. • ゼロ除算の発生しうる場所だったの でガチガチのチェックをしてクラッ シュすることがないように初期値を 入れたりしたが、実はAPIのバグ以 外で0が返ることはありえない場所 • かえってAPIの不具合の発見を遅ら せる結果になった
  • 119. 落ちるときは素直に落ちるのも手
  • 120. お聞きいただきありがとうございました。
  • 121. We're Hiring!
  • 122. • 教育で世界を変える仲間を募集しています! • iOS技術者 • Android技術者 • http://mana.bo/corp/recruit/

×