Upload
danny-preussler
View
346
Download
2
Tags:
Embed Size (px)
Citation preview
Backrground © Tsahi Levent-Levi, flickr.com/photos/86979666@N00/8161660138
More Android Code Puzzles
Danny Preussler Johannes Orgis proudly presents
Backrground © Tsahi Levent-Levi, flickr.com/photos/86979666@N00/8161660138
background © Howard Lake, flickr.com/photos/howardlake/5439888760
How it works….
Mub
arak
ALT
hani
, ⓘS
AW
†he
dirt
y so
ul ☠
Par
t I☠
, CC
by
2.0,
flic
kr.c
om/p
hoto
s/al
than
i-co
rner
ston
e/23
7099
0198
background © Howard Lake, flickr.com/photos/howardlake/5439888760
When I close the last activity on my phone:a. We see the log output immediatelyb. We see the log output only after GC ranc. We only see the log output when user closes
via task managerd. We never see the log output
I’ll be backclass MyApplication extends Application {
public void onTerminate() {super.onTerminate();Log.i("puzzle", "terminate");
}}
background © Howard Lake, flickr.com/photos/howardlake/5439888760
When I close the last activity on my phone:a. We see the log output immediatelyb. We see the log output only after GC ranc. We only see the log output when user closes
via task managerd. We never see the log output
I’ll be backclass MyApplication extends Application {
public void onTerminate() {super.onTerminate();Log.i("puzzle", "terminate");
}}
background © Howard Lake, flickr.com/photos/howardlake/5439888760
This method is for use in emulated process environments. It will never be called on a production Android device, where processes are removed by simply killing them; no user code (including this callback) is executed when doing so.http://developer.android.com/reference/android/app/Application.html
Android is [..] tricky [..], the concept of exiting the application is not like any other operating system [..] When press back from the last activity the user gets a feeling that the application is closed, but the app still remain in the memory ready to quick launch when you start next time.http://www.maxters.net/2012/11/android-last-activity-stack/
I’ll be back
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Did you know?
Did you know about requestWindowFeature(
FEATURE_INDETERMINATE_PROGRESS)that on android 4 progress is visible by default
and on 4.1 and higher hidden by default?
background © Howard Lake, flickr.com/photos/howardlake/5439888760
the way of life
What is the trace output here?a. “onCreate”, “finished”, “onStart”, “onResume”, “onPause”, “onStop”,
“onDestroy”b. “onCreate”, “finished”, “onPause”, “onStop”, “onDestroy”c. “onCreate”, “finished”, “onDestroy”d. “onCreate”, “onDestroy”e. an exception because of calling finish from onCreate()
protected void onCreate(Bundle arg0) {super.onCreate(arg0);trace("onCreated");finish();trace("finished");}
protected void onPause() {super.onPause();trace("onPaused");}
protected void onStart() {super.onStart();trace("onStarted");}….
background © Howard Lake, flickr.com/photos/howardlake/5439888760
the way of life
What is the trace output here?a. “onCreate”, “finished”, “onStart”, “onResume”, “onPause”, “onStop”,
“onDestroy”b. “onCreate”, “finished”, “onPause”, “onStop”, “onDestroy”c. “onCreate”, “finished”, “onDestroy”d. “onCreate”, “onDestroy”e. an exception because of calling finish from onCreate()
protected void onCreate(Bundle arg0) {super.onCreate(arg0);trace("onCreated");finish();trace("finished");}
protected void onPause() {super.onPause();trace("onPaused");}
protected void onStart() {super.onStart();trace("onStarted");}….
background © Howard Lake, flickr.com/photos/howardlake/5439888760
the way of life
● finish() will stop the lifecycle creation at current point and starts the shutdown procedure from that method on
● The current method will finish so be careful when using fragments: a simple super.onCreate() of Activity might start a lot of other lifecycles methods before finish will do it’s job
● Use isFinishing() if needed
background © Howard Lake, flickr.com/photos/howardlake/5439888760
but daddy said ...<style name="MyTextViewStyle" parent="android:Widget.TextView">
<item name="android:textColor"> #F00</item>
<item name="android:textSize"> 50dp</item> </style>
<FrameLayout ... android:layout_width=" match_parent"
android:layout_height=" 25dp">
<TextView … " wrap_content" android:text="Hello World"/></FrameLayout>
public View getView(int position, View convertView, ViewGroup parent) { ...
View v = mInflater.inflate( R.layout.row, null, false); …}
What do the rows look like:a. “Hello World” in 50dp red, row height wrapb. “Hello World” in 50dp red, row height 25dpc. “Hello World” in normal size, grey , row height wrapd. “Hello World” in normal size, grey , row height 25dpe. UnsupportedOperationException
Part of Application Theme
background © Howard Lake, flickr.com/photos/howardlake/5439888760
but daddy said ...<style name="MyTextViewStyle" parent="android:Widget.TextView">
<item name="android:textColor"> #F00</item>
<item name="android:textSize"> 50dp</item> </style>
<FrameLayout ... android:layout_width=" match_parent"
android:layout_height=" 25dp">
<TextView … " wrap_content" android:text="Hello World"/></FrameLayout>
public View getView(int position, View convertView, ViewGroup parent) { ...
View v = mInflater.inflate( R.layout.row, null, false); …}
What do the rows look like:a. “Hello World” in 50dp red, row height wrapb. “Hello World” in 50dp red, row height 25dpc. “Hello World” in normal size, grey , row height wrapd. “Hello World” in normal size, grey , row height 25dpe. UnsupportedOperationException
background © Howard Lake, flickr.com/photos/howardlake/5439888760
but daddy said ...
Inflate with parent if possible
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Whats the log output when clicking the menu item?a) “in activity”, “done in activity”, “in fragment”b) “in activity”, “in fragment”, “done in activity”c) “in fragment”, “in activity”, “done in activity”
Fragment with setHasOptionsMenu(true);...
public boolean onOptionsItemSelected(MenuItem item) {
Log.i("menu", "in fragment");
return super.onOptionsItemSelected(item); }
Activity: public boolean onOptionsItemSelected(MenuItem item) {
Log.i("menu", "in activity");
boolean b = super.onOptionsItemSelected(item);
Log.i("menu", "done in activity");
return b;}
fun with menus...
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Whats the log output when clicking the menu item?a) “in activity”, “done in activity”, “in fragment”b) “in activity”, “in fragment”, “done in activity”c) “in fragment”, “in activity”, “done in activity”
Fragment with setHasOptionsMenu(true);...
public boolean onOptionsItemSelected(MenuItem item) {
Log.i("menu", "in fragment");
return super.onOptionsItemSelected(item); }
Activity: public boolean onOptionsItemSelected(MenuItem item) {
Log.i("menu", "in activity");
boolean b = super.onOptionsItemSelected(item);
Log.i("menu", "done in activity");
return b;}
fun with menus...
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Did you know?
● Did you know that Cursor did not implement Closeable before ICS?
● Not found in JavaDoc● Will lead to crash in sth like:
Closeables.closeQuietly(...)● https://code.google.
com/p/android/issues/detail?id=67052
background © Howard Lake, flickr.com/photos/howardlake/5439888760
What’s the output when tasks returns?
a) “frag1”, “frag2”, “activ1”, “activ2”b) “activ1”, “activ2”, “frag1”, “frag2”c) “activ1”, “frag1”, “frag2”, “activ2”
Fragment:startActivityForResult(...);
public void onActivityResult(int req, int res, Intent dt) {
Log.e("handle", "frag1");
super.onActivityResult(req, res, dt);
Log.e("handle", "frag2");}
Activity: protected void onActivityResult(int req, int res, Intent dt) {
Log.e("handle", "activ1");
super.onActivityResult(req, res, dt);
Log.e("handle", "activ2");}
waiting for the end
d) “frag1”, “frag2”e) “activ1”, “activ2”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
What’s the output when tasks returns?
a) “frag1”, “frag2”, “activ1”, “activ2”b) “activ1”, “activ2”, “frag1”, “frag2”c) “activ1”, “frag1”, “frag2”, “activ2”
Fragment:startActivityForResult(...);
public void onActivityResult(int req, int res, Intent dt) {
Log.e("handle", "frag1");
super.onActivityResult(req, res, dt);
Log.e("handle", "frag2");}
Activity: protected void onActivityResult(int req, int res, Intent dt) {
Log.e("handle", "activ1");
super.onActivityResult(req, res, dt);
Log.e("handle", "activ2");}
waiting for the end
d) “frag1”, “frag2”e) “activ1”, “activ2”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Toast<style name="MyTextViewStyle" parent="android:Widget.TextView">
<item name="android:textColor"> #F00</item>
<item name="android:textSize"> 50dp</item> </style>
Toast.makeText(myActivity, "Hello World", Toast.LENGTH_LONG).show();
What does the Toast look like:a. “Hello World” in 50dp, redb. “Hello World” in standard size, redc. “Hello World” in 50dp, grey“d. “Hello World” in standard size, greye. AndroidRuntimeException
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Toast<style name="MyTextViewStyle" parent="android:Widget.TextView">
<item name="android:textColor"> #F00</item>
<item name="android:textSize"> 50dp</item> </style>
Toast.makeText(myActivity, "Hello World", Toast.LENGTH_LONG).show();
What does the Toast look like:a. “Hello World” in 50dp, redb. “Hello World” in standard size, redc. “Hello World” in 50dp, grey“d. “Hello World” in standard size, greye. AndroidRuntimeException
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Toast
● use Toast always with the applicationcontext
● use setView for custom views
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Did you know?
OnFocusChangeListener added in the Fragment’s onCreateView()
method won’t get called if you request focus in the layout XML
background © Howard Lake, flickr.com/photos/howardlake/5439888760
part of the queue...protected void onCreate(Bundle arg0) {
super.onCreate(arg0);trace("before");runOnUiThread(new Runnable() {
public void run() {trace("delayed");}
});trace("after");
}
What is the trace output here:a. “before”, “after”, “delayed”b. “before” and then random orderc. “before”, “delayed”, “after”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
part of the queue...protected void onCreate(Bundle arg0) {
super.onCreate(arg0);trace("before");runOnUiThread(new Runnable() {
public void run() {trace("delayed");}
});trace("after");
}
What is the trace output here:a. “before”, “after”, “delayed”b. “before” and then random orderc. “before”, “delayed”, “after”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
part of the queue...// from android open source:public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {mHandler.post(action);
} else {action.run();
}} use Handler with post (delay) when you
want to queue
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Did you know?
Did you know that what happens when you set an onItemClickListener
on a Spinner? :) java.lang.RuntimeException:
setOnItemClickListener cannot be used with a spinner.
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Comin’ up from behind...protected void onCreate(Bundle arg0) {
super.onCreate(arg0);new Thread(new Runnable() {
public void run() { getFragmentManager()
.beginTransaction()
.add(android.R.id.content, new SomeFragment())
.commit();}}).start();
}
What happens when running this code?a. exception on beginTransaction
as we are not on main threadb. exception on commit as we are not on main threadc. transaction will be done and fragment shownd. transaction will be done but nothing happens
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Comin’ up from behind...protected void onCreate(Bundle arg0) {
super.onCreate(arg0);new Thread(new Runnable() {
public void run() { getFragmentManager()
.beginTransaction()
.add(android.R.id.content, new SomeFragment())
.commit();}}).start();
}
What happens when running this code?a. exception on beginTransaction
as we are not on main threadb. exception on commit as we are not on main threadc. transaction will be done and fragment shownd. transaction will be done but nothing happens
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Comin’ up from behind...
● “After a FragmentTransaction is committed with FragmentTransaction.commit(), it is scheduled to be executed asynchronously on the process's main thread.”http://developer.android.com/reference/android/app/FragmentManager.html
● use executePendingTransactions to do the transaction immediately but then be sure you are on main thread
background © Howard Lake, flickr.com/photos/howardlake/5439888760
pick me, pick me<item android:drawable="@drawable/ thumbsup" android:state_enabled=" true"
android:state_focused=" false"
android:state_pressed=" false"/>
<item android:drawable="@drawable/ thumbsdown" android:state_pressed=" true"/>
<ImageButton … "wrap_content"
android:src="@drawable/ mythumbsselector"
android:focusable=" true" android:focusableInTouchMode=" true"
android:background=" #00000000"/>
What do you see:a. “thumbsup”b. “thumbsdown”c. nothing
background © Howard Lake, flickr.com/photos/howardlake/5439888760
pick me, pick me<item android:drawable="@drawable/ thumbsup" android:state_enabled=" true"
android:state_focused=" false"
android:state_pressed=" false"/>
<item android:drawable="@drawable/ thumbsdown" android:state_pressed=" true"/>
<ImageButton … "wrap_content"
android:src="@drawable/ mythumbsselector"
android:focusable=" true" android:focusableInTouchMode=" true"
android:background=" #00000000"/>
What do you see:a. “thumbsup”b. “thumbsdown”c. nothing
background © Howard Lake, flickr.com/photos/howardlake/5439888760
pick me, pick me<item android:drawable="@drawable/ thumbsup" android:state_enabled=" true"
android:state_focused=" false"
android:state_pressed=" false"/>
<item android:drawable="@drawable/ thumbsdown" android:state_pressed=" true"/>
<item android:drawable="@drawable/default" />
Have a default state as last item
background © Howard Lake, flickr.com/photos/howardlake/5439888760
something borrowed, something new
protected void onNewIntent(Intent intent) {trace(intent.getStringExtra("KEY") +
":" + getIntent().getStringExtra("KEY"));super.onNewIntent(intent);trace(intent.getStringExtra("KEY")+
":"+getIntent().getStringExtra("KEY"));}
If this activity is FLAG_ACTIVITY_SINGLE_TOP and we restart it with KEY=value what does it trace?
a) “value:null”, “value:value”b) “value:value”, “value:value”c) “value:null”, “value:null”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
something borrowed, something new
protected void onNewIntent(Intent intent) {trace(intent.getStringExtra("KEY") +
":" + getIntent().getStringExtra("KEY"));super.onNewIntent(intent);trace(intent.getStringExtra("KEY")+
":"+getIntent().getStringExtra("KEY"));}
If this activity is FLAG_ACTIVITY_SINGLE_TOP and we restart it with KEY=value what does it trace?
a) “value:null”, “value:value”b) “value:value”, “value:value”c) “value:null”, “value:null”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
something borrowed, something new
getIntent() always returns you the original intent!
Suggested solution on stackoverflow: use setIntent() in onNewIntent()
Let’s try!
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wind of change protected void onResume() {
super.onResume();
Intent intent = getIntent();
intent.putExtra("KEY", 1 + intent.getIntExtra("KEY", 0));
setIntent(intent);
showToast(valueOf(getIntent(). getIntExtra("KEY", 0));
}
What is the Toast message after background and foreground switch?a. 1,2,3…b. 1,1,1...c. 0,0,0...d. sometimes a, sometimes b
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wind of change protected void onResume() {
super.onResume();
Intent intent = getIntent();
intent.putExtra("KEY", 1 + intent.getIntExtra("KEY", 0));
setIntent(intent);
showToast(valueOf(getIntent(). getIntExtra("KEY", 0));
}
What is the Toast message after background and foreground switch?a. 1,2,3…b. 1,1,1...c. 0,0,0...d. sometimes a, sometimes b
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wind of change
● Depends on if activity get recreated● When using “don’t keep activities” developer
option this will always be: 1,1,1….
// from android open source project:public void setIntent(Intent newIntent) {
mIntent = newIntent;}
Try to avoid setIntent()!Always persist your state!
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Lost in transition<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/ transparent"/>
<item android:drawable="@android:color/ black"/>
</transition>
<ImageButton ..."wrap_content" android:paddingLeft=" 100dp"
android:src="@android:drawable/ btn_star_big_off"/>
public void doBackgroudTransition(Imagebutton button){
button.setBackgroundResource(R.drawable.transitionbackground);
}
What does the Button look like:
a)
b)
c)
d)
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Lost in transition<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/ transparent"/>
<item android:drawable="@android:color/ black"/>
</transition>
<ImageButton ..."wrap_content" android:paddingLeft=" 100dp"
android:src="@android:drawable/ btn_star_big_off"/>
public void doBackgroudTransition(Imagebutton button){
button.setBackgroundResource(R.drawable.transitionbackground);
}
What does the Button look like:
a)
b)
c)
d)
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Lost in transition<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/ transparent"/>
<item android:drawable="@android:color/ black"/>
</transition>
<ImageButton ..."wrap_content" android:paddingLeft=" 100dp"
android:src="@android:drawable/ btn_star_big_off"/>
public void doBackgroudTransition(Imagebutton button){
button.setBackgroundResource(R.drawable.transitionbackground);
}
What does the Button look like:
a)
b)
c)
d)
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Lost in transition
● check your padding when using TransitionDrawable
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wanna be lonely?
"singleTask"The system creates the activity at the root of a new task and routes the intent to it. However, if
an instance of the activity already exists, the system routes the intent to existing instance
through a call to its onNewIntent() method, rather than creating a new one.
Let’s try
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wanna be lonely? protected void onResume() {
super.onResume();…
startActivityForResult(new Intent(....);trace("started"}; }
} protected void onActivityResult(int req, int res, Intent data){
super.onActivityResult(req, res, data);trace("done"};
}
What is the log output when we run this activity with android:launchMode="singleTask"?a. nothing, an exception is thrown in startActivityForResultb. “started” and “done” if started returnsc. “started” and never “done” ignoring resultd. “started” and immediately “done”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wanna be lonely? protected void onResume() {
super.onResume();…
startActivityForResult(new Intent(....);trace("started"}; }
} protected void onActivityResult(int req, int res, Intent data){
super.onActivityResult(req, res, data);trace("done"};
}
What is the log output when we run this activity with android:launchMode="singleTask"?a. nothing, an exception is thrown in startActivityForResultb. “started” and “done” if started returnsc. “started” and never “done” ignoring resultd. “started” and immediately “done”
background © Howard Lake, flickr.com/photos/howardlake/5439888760
wanna be lonely?
● in single-task mode you can not use startActivityForResult. Will immediately return with: Activity.RESULT_CANCELED
● “singleTask and singleInstance — are not appropriate for most applications, since they result in an interaction model that is likely to be unfamiliar to users and is very different from most other applications.”http://developer.android.com/guide/topics/manifest/activity-element.html
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Did you know?
public static final Pattern EMAIL_ADDRESS
= Pattern.compile(
"[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" +
"\\@" +
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
"(" +
"\\." +
"[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" +
")+"
);
● ...that Android’s email pattern does not see new international addresses like юзер@екзампл.ком as valid?
background © Howard Lake, flickr.com/photos/howardlake/5439888760
new Thread(
new Runnable() {
public void run() {
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(new Intent(ACTION));}
}).start();
public void onReceive(Context context, Intent intent) {
((ArrayAdapter) getListAdapter()). notifyDataSetChanged();
}
to all the people in the world
What happens:a. error in run() because of applicationcontextb. error in run() because not in MainThreadc. error in onReceive() - notifyDatasetChanged needs MainThreadd. no error, everything is fine, kkthxbye
background © Howard Lake, flickr.com/photos/howardlake/5439888760
to all the people in the worldnew Thread(
new Runnable() {
public void run() {
LocalBroadcastManager.getInstance(getApplicationContext())
.sendBroadcast(new Intent(ACTION));}
}).start();
public void onReceive(Context context, Intent intent) {
((ArrayAdapter) getListAdapter()). notifyDataSetChanged();
}
What happens:a. error in run() because of applicationcontextb. error in run() because not in MainThreadc. error in onReceive() - notifyDatasetChanged needs MainThreadd. no error, everything is fine, kkthxbye
background © Howard Lake, flickr.com/photos/howardlake/5439888760
to all the people in the world 2static final String[] data = new String[]{"A","B","C"};
private void sendBroadcast(){
Intent intent = new Intent();intent.setAction(ACTION);
intent.putExtra("EXTRA",data );
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
data[0] = "D";}
public void onReceive(Context context, Intent intent) {
String[] mydata = intent.getStringArrayExtra("EXTRA");
mydata[2] = "F";
}
What is the content of data after onReceive:a. [A, B, C]b. [D, B, C]c. [D, B, F]d. Exception: ConcurrentModificationExceptione. Compiler Error: data is final and cannot be changed
background © Howard Lake, flickr.com/photos/howardlake/5439888760
to all the people in the world 2static final String[] data = new String[]{"A","B","C"};
private void sendBroadcast(){
Intent intent = new Intent();intent.setAction(ACTION);
intent.putExtra("EXTRA",data );
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(intent);
data[0] = "D";}
public void onReceive(Context context, Intent intent) {
String[] mydata = intent.getStringArrayExtra("EXTRA");
mydata[2] = "F";
}
What is the content of data after onReceive:a. [A, B, C]b. [D, B, C]c. [D, B, F]d. Exception: ConcurrentModificationExceptione. Compiler Error: data is final and cannot be changed
background © Howard Lake, flickr.com/photos/howardlake/5439888760
to all the people in the world 2
● With LocalBroadcastManager you get the original Object reference, no serialisation
● this is not the case for Context#sendBroadcast(Intent i)
background © Howard Lake, flickr.com/photos/howardlake/5439888760
Thanks● plus.google.com/+DannyPreussler, github.com/dpreussler
● plus.google.com/+JohannesOrgisgithub.com/joorg
● Check Wunderlist & Groupon Engineering blogs
● Please Download: enviQ ESA satellite based Air Quality check app