Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

45,006 views

Published on

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

Published in: Software
  • Be the first to comment

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

  1. 1. 絶対落ちないアプリの作り方 DroidKaigi 2015/04/25 株式会社マナボ 白山 文彦 (@fushiroyama)
  2. 2. サンプルコード https://github.com/srym/DroidKaigiSample
  3. 3. • 白山 文彦 (@fushiroyama) • 株式会社マナボ (http://mana.bo/) • Android • Ruby on Rails • DevOps • Full Text Search • 筋トレ
  4. 4. 今日は絶対に落ちないアプリについて 考察してみたいと思います。
  5. 5. だがその前に伝えておきたことがある…
  6. 6. _人人人人人人人人人人_ > そんなものはない <  ̄Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
  7. 7. なんでAndroidアプリは 落ちる運命にあるのだろうか?
  8. 8. • バージョンが多種多様 • 端末が多種多様 • ベンダによる カスタマイズ • 鬼門:カメラアプリ • http://alpha.mixi.co.jp/entry/ 2013/11572/ ※ここに苦労がまとまってます • プログラマの無知と怠慢
  9. 9. • バージョンが多種多様 • 端末が多種多様 • ベンダによる カスタマイズ • 鬼門:カメラアプリ • http://alpha.mixi.co.jp/entry/ 2013/11572/ ※ここに苦労がまとまってます • プログラマの無知と怠慢
  10. 10. プログラマの無知と怠慢は 何とかしたい!
  11. 11. 今日は短い時間なので テーマを絞って話したいと思 います。
  12. 12. クラッシュのないアプリ作りの スタートは、クラッシュの事実 に向き合うことから始まります。
  13. 13. https://crashlytics.com/
  14. 14. 弊社のCrashlyticsで 実際にクラッシュの多いミスを 元に資料を作りました!
  15. 15. 1. FragmentTransactionの取り扱いミス 2. ライフサイクルの終わったコントローラへの不正なアクセス 3. APIとの連携ミス 4. カメラアプリとの連携ミス 5. 大量の画像によるOutOfMemory 6. Activity/Fragmentの再生成・復元ミス などなど…
  16. 16. アジェンダ • ライフサイクルの理解 • 非同期処理とコールバック • Contextの正体 • Fragmentあれこれ • Handlerの本質 • 静的解析を活かす • 本当に落ちないことが本当に幸せか?
  17. 17. ライフサイクルの理解
  18. 18. ホントにホントに理解出来てる?
  19. 19. • Activityはユーザ操作等によって短期間 で頻繁に生き死にを繰り返す • Activityの生死はプログラマが手出し出 来ない • Activityが状態として死を迎えた時と、 実際にオブジェクトがGCされるタイミ ングは違う
  20. 20. 超・初級編: onSaveInstanceState onRestoreInstanceState
  21. 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. 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. 23. • 渡されたIntentの中身を取り出して次の Activityのfieldに保存しておくことは良くある と思うが、これをうっかり保存しわすれてその フィールドがある前提でアクセスしてクラッ シュ。誰もが初心者の時にやったことがある のではないでしょうか。
  24. 24. onSaveInstanceState 強制ギプス
  25. 25. これで漏れを検知できる
  26. 26. 初級編:非同期処理
  27. 27. Androidの非同期ライブラリ
  28. 28. • AsyncTask • Loader • Java’s Thread • Handler (!) • 3rd Party Libraries • OkHttp • Volley • Retrofit
  29. 29. これらに共通して言えること
  30. 30. 非同期処理はコントローラよ り寿命が長いことがままある
  31. 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. 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. 33. Activityより長生き… ここにクラシュの罠がたくさんある
  34. 34. まずはAsyncTask
  35. 35. 実はAsyncTaskは非推奨 (ワタシ的に)
  36. 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. 37. • もともと長くて数秒の非同期処理にのみ使うことを推奨されてい る • 基本やりっ放しなので処理を丸々無駄にする • cancel()すればいいのでは? • BitmapFactory.decodeStream()みたいなキャンセルできない 操作もある • バージョンによってシーケンシャルかパラレルかが異なる • 画面回転等で結果を引き継げない • onRetainNonConfigurationInstanceとか使えばいいけどそん なことするぐらいなら後述のLoader使ったほうがいい
  38. 38. どうしてもAsyncTaskを 使いたい場合…
  39. 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. 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. 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. 42. AsyncTaskはちょろっとした処 理をするのに便利なのも事実な ので上手に付き合おう
  43. 43. AsyncTaskLoader
  44. 44. • ActivityやFragmentのライフサイクルと非同 期処理を切り離すことができる • コールバックをコントローラ側に簡単に記述で きる。結果はメインスレッドで受け取れる。 • 一番重要な点は、画面回転等でも非同期処理を 引き継げるような書き方が簡単にできること。
  45. 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. 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. 47. • Loaderを管理するLoaderManagerはActivityやFragment ごとにひとつ。 • 画面回転でもLoaderManagerインスタンスは死なない。 • LoaderManager#initLoaderではコールバックをセットし 直しつつ処理を引き継ぐ。 • LoaderManager#restartLoaderでは既存の非同期処理を破 棄して新たに非同期処理を行う。
  48. 48. AsyncTaskとの最大の違い
  49. 49. • AsyncTaskの中にコールバックメソッドがある • →利用側にコールバックのロジックを持つのが 手間 • AsyncTaskLoaderはLoaderCallbacksを利用側 が実装するので使いやすい
  50. 50. EventBus
  51. 51. • Observerパターンのような感じでライフサイクルオブジェクト は自分のライフサイクルに合わせてイベントの購読/非購読する • 非同期処理が終わったら購読者に完了イベントを一斉通知する • コールバックが基本一対一なのに対し、一対多へ通知も可能。 • Loader同様、ライフサイクルオブジェクトより長命な処理が無 駄にならず、ライフサイクル側でregister/unregisterするので コールバック時にライフサイクルが終わっている危険性も無い。
  52. 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. 53. BusProvider.getInstance().post(new NewVersionAvailableEvent(latestVersion)); イベントを発火
  54. 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. 55. • square/otto • https://github.com/square/otto • greenbot/EventBus • https://github.com/greenrobot/EventBus • おれおれコールバック設計 • http://qiita.com/hnakagawa/items/984557b13aede61d05d0 • EventBusとは違うけど非常に参考になる
  56. 56. RxJava/RxAndroid
  57. 57. RxJava/RxAndroid
  58. 58. • ここ一年で物凄い勢いで注目を集めているライブラリ及びプ ログラミング手法 • Reactive Functional Programming • LINQのような集合操作メソッド群と遅延評価を合わせたよう な関数型的プログラミング手法 • コールバックヘルからの脱却! • これだけで長大な記事になるので機会があれば後日記事にし ます!!!(今回は泣く泣く断念)
  59. 59. Contextの正体を意識する
  60. 60. • Context…アプリケーションの様々な情報にアク セスするためのインタフェース • 画像、文字等の各種リソース • アプリケーションそのものの情報 • パーミッション等々
  61. 61. 概念が抽象的すぎてよくわからない
  62. 62. 大きく分けて ApplicationContext ActivityContext もうあと2つぐら いあるらしいけど
  63. 63. なぜContextの正体を知って いる必要があるか?
  64. 64. ActivityContextが長く参照さ れる=深刻なメモリリーク
  65. 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. 66. だいたいのケースで ApplicationContextが使える
  67. 67. じゃあ全部 ApplicationContext でいいの?
  68. 68. • Activity 外から startActivity する場合は Intent に FLAG_ACTIVITY_NEW_TASK が含まれている必要 がある。 • AlertDialog.BuilderにApplicationContextを渡すと WindowTokenの取得に失敗 • Toast時に意図しないテーマが出たりすることも
  69. 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. 70. やはり、ちゃんと理解して気 持ちよく使おう!
  71. 71. 原則:ActivityContextをActivity より長命などこかに渡す場面に遭 遇したら、設計を見なおせ!
  72. 72. Fragmentあれこれ
  73. 73. Fragmentは気をつけないと非 常にクラッシュの元になります
  74. 74. 原則その1: Fragmentインスタンスにsetしない
  75. 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. 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. 77. 原則その2:リスナもsetできない
  78. 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. 79. 原則その3:リスナは setSerializableで渡せない
  80. 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. 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. 82. • https://docs.oracle.com/javase/jp/1.4/guide/serialization/spec/serial- arch.doc10.html • (引用)ローカルクラスおよび匿名クラスを含む内部クラス (static メンバクラスでは ない入れ子のクラス) の直列化は、いくつかの理由により、使用しないことを強くお勧 めします。非 static コンテキストで宣言された内部クラスには、囲むクラスインスタン スへの暗黙的な非 transient 参照が含まれるので、そのような内部クラスインスタンス を直列化すると、関連する外部クラスインスタンスも直列化されることになります。内 部クラスを実装する javac (またはその他の JavaTM コンパイラ) によって生成された 合成フィールドは、実装に依存するので、コンパイラによって相違が生じることがあり ます。
  83. 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. 84. EventBusはActivity-Fragmentの インタラクションにも有効
  85. 85. FragmentTransactionに ご用心!
  86. 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. 87. • onSaveInstanceState以降にFragmentTransactionを伴う処理を行うと IllegalStateExceptionが発生する • これは、そのタイミング以降になにか変更をしても、もしインスタンスが再 生成された場合に元の状態に戻しようがないため • FragmentTransaction.commitAllowingStateLoss()すれば例外が上がらな いようにはできる
  88. 88. • 意外な盲点として、DialogFragment#show, dissmissはFragmentTransaction を伴う処理であるということ。 • 非同期通信の間何かダイアログを出し、完了後にdissmissするような処理は非 常にありがちなので、タイミング次第で発生するクラッシュになる。 • 弊社のクラッシュでもFragmentTransactionに起因する例外が非常に多かっ た!
  89. 89. Fragment#isResumed() isベンリ
  90. 90. if (isResumed()) {
 dialog.dismiss();
 }
  91. 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. 92. onResume/onPause間で 安全にFragmentTransactionする テクニック紹介
  93. 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. 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. 95. @Override
 public void processMessage(Message message) {
 if (message.what != WHAT_SHOW_DIALOG) {
 return;
 } dialog.show(getSupportFragmentManager(), "TAG");
 }
  96. 96. Handlerを理解する
  97. 97. Handlerとはなにか
  98. 98. mHandler.post(new Runnable() {
 @Override
 public void run() {
 Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
 
 }
 }); ワーカスレッドか ら呼び出し
  99. 99. ワーカスレッドからUIスレッドを 触るときに使う不思議なやつ…?
  100. 100. だけではないんです。
  101. 101. • 任意のThread(メッセージループ)にメッセージを送ったりメッ セージを取り出して処理したりする人 • AndroidではLooperがメッセージループ • Messageがメッセージ(そのまま!) • LooperはMessageを貯めておいて随時実行するキューを持ってい る(MessageQueue) • 単純なキューではない。遅延実行等を加味されてキューに積まれる。
  102. 102. なぜこれを知っておく必要があるか?
  103. 103. AndroidのUI =シングルスレッドモデル
  104. 104. UI更新はUIスレッドのメッセージ キューに積んで処理してもらう必要 がある。
  105. 105. new Handler()された場所のThreadに紐づく=UIス レッドでnewされたらUIスレッドにメッセージが送れ るというだけ! あるいは new Handler(Looper.getMainLooper());
  106. 106. AndroidではUIスレッド以外でUI操 作を試みた時点でクラッシュする
  107. 107. いままでなんとなくHandler使っ てませんでしたか?
  108. 108. Handlerとその奥に流れる思想を理解で きたらいよいよAndroid中級者です!
  109. 109. Handlerの応用例
  110. 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. 111. 静的解析のチカラを借りる
  112. 112. Javaはよくも悪くも強い静的型付 け言語である。
  113. 113. 型、アノテーション、いずれも強 力なのだから最大限利用すべき
  114. 114. @Nullable @NonNull nullチェックがないと警告 nullを渡すと警告
  115. 115. @BooleanRes @ColorStateListRes @DrawableRes @IntArrayRes @IntegerRes @LayoutRes @MovieRes @TextRes @TextArrayRes @StringArrayRes https://github.com/excilys/ androidannotations/wiki/Resources int型でも、対応するリソースの intでないとコンパイルエラー
  116. 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. 117. 絶対に落ちないアプリが 本当に幸せか?
  118. 118. • ゼロ除算の発生しうる場所だったの でガチガチのチェックをしてクラッ シュすることがないように初期値を 入れたりしたが、実はAPIのバグ以 外で0が返ることはありえない場所 • かえってAPIの不具合の発見を遅ら せる結果になった
  119. 119. 落ちるときは素直に落ちるのも手
  120. 120. お聞きいただきありがとうございました。
  121. 121. We're Hiring!
  122. 122. • 教育で世界を変える仲間を募集しています! • iOS技術者 • Android技術者 • http://mana.bo/corp/recruit/

×