Contenu connexe Similaire à 絶対落ちないアプリの作り方 (20) Plus de Fumihiko Shiroyama (10) 絶対落ちないアプリの作り方8. • バージョンが多種多様
• 端末が多種多様
• ベンダによる カスタマイズ
• 鬼門:カメラアプリ
• http://alpha.mixi.co.jp/entry/
2013/11572/ ※ここに苦労がまとまってます
• プログラマの無知と怠慢
9. • バージョンが多種多様
• 端末が多種多様
• ベンダによる カスタマイズ
• 鬼門:カメラアプリ
• http://alpha.mixi.co.jp/entry/
2013/11572/ ※ここに苦労がまとまってます
• プログラマの無知と怠慢
22. 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」
23. 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);
}
}
}
33. 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」
34. 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より
長生きする可能性
がある
38. • 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/
41. 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」
42. もうほんのちょっ
とマシな例
@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」
43. 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」
47. 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」
48. 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();
}
}
54. 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」
56. @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) {
}
67. 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;
}
}
}
70. • Activity 外から startActivity する場合は Intent
に FLAG_ACTIVITY_NEW_TASK が含まれている必要
がある。
• AlertDialog.BuilderにApplicationContextを渡すと
WindowTokenの取得に失敗
• Toast時に意図しないテーマが出たりすることも
77. 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();
どっちも
間違い!
78. 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);
}
80. 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();
もちろん
間違い!
82. 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);
}
一見行けそうだが
…
83. @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への
暗黙の参照
85. @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」
88. 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();
93.
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版を作る
と気休めになる
95. 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」
96. 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();
}
112. 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);
118. 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の代入を
コンパイル時に防げる