Android Code
Puzzles
droidcon.nl
● Danny Preussler:
● Lead Engineer Android
ebay Kleinanzeigen, Germany
● Path to the green side:
C++ -> Java SE -> JME -> BlackBerry -> Android
● My current Droids:
Galaxy Nexus, Nexus 7, Logitech Revue, I'm watch
● My 1st Droid was a:
Motorola Droid/Milestone
● Johannes Orgis:
● Software Development Manager
Cortado Mobile Clients
Cortado AG, Germany
● Path to the green side:
Java EE -> Java SE -> BlackBerry -> Android
● My current Droids:
Galaxy Nexus, Nexus 7
● My 1st Droid was a:
HTC Magic
● We show code snippets that does something or maybe not
● Look out for hidden pitfalls
● All Puzzles are run with Android 4.1 Jelly Beans
There should be no platform depended puzzles
● Test yourself!
● Raise your hands when we ask you!
● Guess if you're unsure!
● Learn something if you were wrong!
● Have fun! :D
public class DroidConPuzzle extends Activity {
@Override
protected void onCreate(Bundle saved) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.add(android.R.id.content, new Fragment() {
@Override
public View onCreateView(LayoutInflater inf,
ViewGroup view, Bundle saved)
{
View v = inf.inflate(R.layout.fragment_default, null);
return null;
}
});
trans.commit();
super.onCreate(saved);
}
}
For your eyes only...
a) NullPointerException in Inflater.inflate()
b) NullPointerException after onCreateView()
c) llegalStateException in super.onCreate()
d) llegalStateException in Transaction.commit()
e) shows an empty screen
public class DroidConPuzzle extends Activity {
@Override
protected void onCreate(Bundle saved) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.add(android.R.id.content, new Fragment() {
@Override
public View onCreateView(LayoutInflater inf,
ViewGroup view, Bundle saved)
{
View v = inf.inflate(R.layout.fragment_default, null);
return null;
}
});
trans.commit();
super.onCreate(saved);
}
}
For your eyes only...
a) NullPointerException in Inflater.inflate()
b) NullPointerException after onCreateView()
c) llegalStateException in super.onCreate()
d) llegalStateException in Transaction.commit()
e) shows an empty screen
public class DroidConPuzzle extends Activity {
@Override
protected void onCreate(Bundle saved) {
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.add(android.R.id.content, new Fragment() {
@Override
public View onCreateView(LayoutInflater inf,
ViewGroup view, Bundle saved)
{
View v = inf.inflate(R.layout.fragment_default, null);
return null;
}
});
trans.commit();
super.onCreate(saved);
}
}
For your eyes only...
e) shows an empty screen BUT after rotation it will crash:
● Fragments need to be public:
not possible as inner or anonymous class
● Fragments must have a public empty
constructor
● RTFW: read the fucking warnings:
●
<Button ... android:onClick="clicked" />
public class OnClickToastFragment extends Fragment {...
public void clicked(View v)
{
Toast.makeText(getActivity(),getClass().getName(),Toast.LENGTH_SHORT).show();
}
}
public class OnClickToastFragmentActivity extends FragmentActivity{
public void clicked(View v)
{
Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show();
}
}
public class MyMainActivity extends Activity{
public void clicked(View v)
{
Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show();
}
}
Licence to click
What happens when I click the Button?
a) Toast : MyMainActivity
b) Toast : OnClickToastFragmentActivity
c) Toast : OnClickToastFragment
d) Nothing
e) IllegalStateException - Could not find a method ...
<Button ... android:onClick="clicked" />
public class OnClickToastFragment extends Fragment {...
public void clicked(View v)
{
Toast.makeText(getActivity(),getClass().getName(),Toast.LENGTH_SHORT).show();
}
}
public class OnClickToastFragmentActivity extends FragmentActivity{
public void clicked(View v)
{
Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show();
}
}
public class MyMainActivity extends Activity{
public void clicked(View v)
{
Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show();
}
}
Licence to click
What happens when I click the Button?
a) Toast : MyMainActivity
b) Toast : OnClickToastFragmentActivity
c) Toast : OnClickToastFragment
d) Nothing
e) IllegalStateException - Could not find a method ...
android:onClick is an easy way to trigger "click" events in xml
but remember: the calling Activity MUST implement the xxx(Viev v) method otherwise a
IllegalStateException will be thrown at runtime
be careful if you are using Fragments in multiple Activities
Every 2.2. Device has the "unique" ANDROID_ID
9774d56d682e549c ?
Be aware of platform dependent puzzles....
or did you know that?
public class GetViewFragment extends Fragment {
public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) {
super.onCreateView(inf, view, saved);
return inf.inflate(R.layout.fragment_get_view, view);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getView().setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getActivity(),
"My name is droid, android",
Toast.LENGTH_SHORT);}});
}
}
A View to (a) Kill
a) shows a Toast "my name is droid, android"
b) does nothing but showing a view
c) Exception in Inflater.inflate()
d) Exception in GetViewFragment.onCreate()
e) Exception in Fragment.onCreateView()
public class GetViewFragment extends Fragment {
public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) {
super.onCreateView(inf, view, saved);
return inf.inflate(R.layout.fragment_get_view, view);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getView().setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getActivity(),
"My name is droid, android",
Toast.LENGTH_SHORT);}});
}
}
A View to (a) Kill
a) shows a Toast "my name is droid, android"
b) does nothing but showing a view
c) Exception in Inflater.inflate()
d) Exception in GetViewFragment.onCreate()
e) Exception in Fragment.onCreateView()
public class GetViewFragment extends Fragment {
public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) {
super.onCreateView(inf, view, saved);
return inf.inflate(R.layout.fragment_get_view, view);
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getView().setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(getActivity(),
"My name is droid, android",
Toast.LENGTH_SHORT);}});
}
}
A View to (a) Kill
a) shows a Toast "my name is droid, android"
b) does nothing but showing a view
c) Exception in Inflater.inflate() (2nd winner)
d) Exception in GetViewFragment.onCreateView()
e) Exception in Fragment.onCreateView()
● Know about the life cycle of Activities,
Fragments, Services!
● Example: methods as
getView() or getActivity() can return null in
certain points of their life cycle!
public class ToastFragment extends Fragment {
...
public void onDestroy() {
super.onDestroy();
new ToastingAsyncTask().execute(null,null,null);
}
class ToastingAsyncTask extends AsyncTask<String, Integer, Long>{
protected void onPostExecute(Long result) {
if (getActivity() == null){
System.out.println(getString(R.string.no_activity));
}else{
System.out.println(getString(R.string.activity_leak));
}
}
protected Long doInBackground(String... params) {
try {Thread.sleep(500);} catch (InterruptedException e) {}
return null;}
...
Live and let die ...
what happens when I change the orientation of my Galaxy Nexus?
a) Sysout: no_activity
b) Sysout: activity_leak
c) Sysout: null
d) Exception in getActivity
e) Exception in getString
public class ToastFragment extends Fragment {
...
public void onDestroy() {
super.onDestroy();
new ToastingAsyncTask().execute(null,null,null);
}
class ToastingAsyncTask extends AsyncTask<String, Integer, Long>{
protected void onPostExecute(Long result) {
if (getActivity() == null){
System.out.println(getString(R.string.no_activity));
}else{
System.out.println(getString(R.string.activity_leak));
}
}
protected Long doInBackground(String... params) {
try {Thread.sleep(500);} catch (InterruptedException e) {}
return null;}
...
Live and let die ...
what happens when I change the orientation of my Galaxy Nexus?
a) Sysout: no_activity
b) Sysout: activity_leak
c) Sysout: null
d) Exception in getActivity
e) Exception in getString
LogCat shows:
java.lang.IllegalStateException: Fragment ToastFragment not attached to Activity
A Context holds your Resources!
Know about the life cycle of Activities, Fragments,
Services!!
Until Android 4 (possible 3.x) android.app.DownloadManager only supports http
connections:
if (scheme == null || !scheme.equals("http")) {
throw new IllegalArgumentException("Can only download HTTP URIs: " + uri);
}
Because of that the stock browser (and Chrome)
could not handle https downloads.
Be aware of platform dependent puzzles....
or did you know that?
public class MyListActivity extends ListActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFromServer(getApplicationContext());
}
void getFromServer(Context context) {
final ProgressDialog dlg = new ProgressDialog(context);
dlg.show();
new AsyncTask<Context, Void, ArrayAdapter>() {
protected ArrayAdapter doInBackground(Context... params) {
return new ArrayAdapter<String>(params[0],
android.R.layout.simple_list_item_1,
Arrays.asList(new String[] { "0", "0", "7" }));
}
protected void onPostExecute(ArrayAdapter result) {
setListAdapter(result);
dlg.dismiss();
}
}.execute(context);
}...
a) shows list with entries 0,0,7
b) shows list with entries 0,7
c) Exception in getFromServer
d) Exception in doInBackground
e) Exception in onPostExecute
Tomorrow never dies...
public class MyListActivity extends ListActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getFromServer(getApplicationContext());
}
void getFromServer(Context context) {
final ProgressDialog dlg = new ProgressDialog(context);
dlg.show();
new AsyncTask<Context, Void, ArrayAdapter>() {
protected ArrayAdapter doInBackground(Context... params) {
return new ArrayAdapter<String>(params[0],
android.R.layout.simple_list_item_1,
Arrays.asList(new String[] { "0", "0", "7" }));
}
protected void onPostExecute(ArrayAdapter result) {
setListAdapter(result);
dlg.dismiss();
}
}.execute(context);
}...
a) shows list with entries 0,0,7
b) shows list with entries 0,7
c) Exception in getFromServer
d) Exception in doInBackground
e) Exception in onPostExecute
Tomorrow never dies...
● Know your Context!
● Never use Application Context for UI!
Prefer Application Context for Non-UI!
● Be aware of Context Leaks!
public class ActionbarFragment extends Fragment {
...
public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) {
setHasOptionsMenu(true);
return inf.inflate(R.layout.fragment_get_view, null);
}
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(getActivity(), "from Fragment", Toast.LENGTH_SHORT).show();
return true;
}
}
public class ActionbarActivity extends Activity {
...
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(this, "from Activity", Toast.LENGTH_SHORT).show();
return true;
}
}
From Fragment with Love...
When clicking an item from actionbar:
a) shows toast: "from Activity"
b) shows toast: "from Fragment"
c) shows both toasts
d) shows toast depending on who the creator of the menu item
public class ActionbarFragment extends Fragment {
...
public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) {
setHasOptionsMenu(true);
return inf.inflate(R.layout.fragment_get_view, null);
}
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(getActivity(), "from Fragment", Toast.LENGTH_SHORT).show();
return true;
}
}
public class ActionbarActivity extends Activity {
...
public boolean onOptionsItemSelected(MenuItem item) {
Toast.makeText(this, "from Activity", Toast.LENGTH_SHORT).show();
return true;
}
}
From Fragment with Love...
When clicking an item from actionbar:
a) shows toast: "from Activity"
b) shows toast: "from Fragment"
c) shows both toasts
d) shows toast depending on who the creator of the menu item
"If you added the menu item from a fragment, then the
respective onOptionsItemSelected() method is called for that
fragment.
However the activity gets a chance to handle it first, so the
system calls onOptionsItemSelected() on the activity before
calling the fragment."
● RTFJD: read the fucking java doc:
On pre 2.3.6 Android's HttpUrlConnection is broken
as soon as the server supports more than Basic authentication ?
You'll get IndexOutOfBoundsException
Be aware of platform dependent puzzles....
or did you know that?
int realm = challenge.indexOf("realm="") + 7;
WWW-Authenticate: Basic realm="RealmName"
WWW-Authenticate: NTLM
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] longList = new String[1000000];
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra("droidcon", longList);
startActivityForResult(intent, 100);
}
protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
Log.e("droidcon", "result" + request + " " + result);
}
a) OutOfMemoryException in onCreate()
b) IllegalArgumentException in onCreate()
c) Nothing happens, direct invocation of onActivityResult (with cancel code)
d) Nothing happens, no invocation of onActivityResult
e) System-Intent-Picker for "sent" action will be shown normally
The world is not enough ...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] longList = new String[1000000];
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra("droidcon", longList);
startActivityForResult(intent, 100);
}
protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
Log.e("droidcon", "result" + request + " " + result);
}
a) OutOfMemoryException in onCreate()
b) IllegalArgumentException in onCreate()
c) Nothing happens, direct invocation of onActivityResult (with cancel code)
d) Nothing happens, no invocation of onActivityResult
e) System-Intent-Picker for "sent" action will be shown normally
The world is not enough ...
Logcat shows:
11-12 16:25:53.391: E/JavaBinder(6247): !!! FAILED BINDER TRANSACTION !!!
Some devices show TransactionTooLargeException:
The Binder transaction failed because it was too large.
During a remote procedure call, the arguments and the return value of the call are transferred as
Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too
large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be
thrown.
The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all
transactions in progress for the process. Consequently this exception can be thrown when there are
many transactions in progress even when most of the individual transactions are of moderate size.
Be aware of platform dependent puzzles....
or did you know that?
Starting with 2.3 HTTPUrlConnection handles and sets gzip as Content-Encoding.
This leads to errors if you get a response where there is no content but a Content-
Length (=0) or a Content-Encoding(=gzip).
getResponceCode leads to EOFException
public class AsyncTaskActivity extends Activity{
...
protected void onResume() {
super.onResume();
TextView testV = (TextView) findViewById(R.id.async_text_1);
new WritingAsyncTask(testV,2000).execute(" Bob");
new WritingAsyncTask(testV,100).execute("Alice");
new WritingAsyncTask(testV,500).execute("loves");
}
...
public class WritingAsyncTask extends AsyncTask<String, Void, String>{...
private final TextView view;//set in Constructor
private final int timeout;//set in Constructor
protected String doInBackground(String... params) {
try {Thread.sleep(timeout);} catch (InterruptedException e) {}
return params[0];
}
protected void onPostExecute(String result) {
view.setText(view.getText()+" "+result);}
Casino Royal
What is shown in the TextView when I start the Activity on my Galaxy Nexus:
a) Alice loves Bob
b) Bob Alice loves
c) Bob loves Alice
d) There is not enough information to decide
public class AsyncTaskActivity extends Activity{
...
protected void onResume() {
super.onResume();
TextView testV = (TextView) findViewById(R.id.async_text_1);
new WritingAsyncTask(testV,2000).execute(" Bob");
new WritingAsyncTask(testV,100).execute("Alice");
new WritingAsyncTask(testV,500).execute("loves");
}
...
public class WritingAsyncTask extends AsyncTask<String, Void, String>{...
private final TextView view;//set in Constructor
private final int timeout;//set in Constructor
protected String doInBackground(String... params) {
try {Thread.sleep(timeout);} catch (InterruptedException e) {}
return params[0];
}
protected void onPostExecute(String result) {
view.setText(view.getText()+" "+result);}
Casino Royal
What is shown in the TextView when I start the Activity on my Galaxy Nexus:
a) Alice loves Bob
b) Bob Alice loves
c) Bob loves Alice
d) There is not enough information to decide
public class AsyncTaskActivity extends Activity{
...
protected void onResume() {
super.onResume();
TextView testV = (TextView) findViewById(R.id.async_text_1);
new WritingAsyncTask(testV,2000).execute(" Bob");
new WritingAsyncTask(testV,100).execute("Alice");
new WritingAsyncTask(testV,500).execute("loves");
}
...
public class WritingAsyncTask extends AsyncTask<String, Void, String>{...
private final TextView view;//set in Constructor
private final int timeout;//set in Constructor
protected String doInBackground(String... params) {
try {Thread.sleep(timeout);} catch (InterruptedException e) {}
return params[0];
}
protected void onPostExecute(String result) {
view.setText(view.getText()+" "+result);}
Casino Royal
What is shown in the TextView when I start the Activity on my Galaxy Nexus:
a) Alice loves Bob
b) Bob Alice loves .... usually
c) Bob loves Alice
d) There is not enough information to decide
public class AsyncTaskActivity extends Activity{
...
protected void onResume() {
super.onResume();
TextView testV = (TextView) findViewById(R.id.async_text_1);
new WritingAsyncTask(testV,2000).execute(" Bob");
new WritingAsyncTask(testV,100).execute("Alice");
new WritingAsyncTask(testV,500).execute("loves");
}
...
public class WritingAsyncTask extends AsyncTask<String, Void, String>{...
private final TextView view;//set in Constructor
private final int timeout;//set in Constructor
protected String doInBackground(String... params) {
try {Thread.sleep(timeout);} catch (InterruptedException e) {}
return params[0];
}
protected void onPostExecute(String result) {
view.setText(view.getText()+" "+result);}
Casino Royal
What is shown in the TextView when I start the Activity on my Galaxy Nexus:
a) Alice loves Bob
b) Bob Alice loves
c) Bob loves Alice
d) There is not enough information to decide
You need to know the android:targetSdkVersion (and the Device Version of the Target Device)
android:targetSdkVersion < 13:
AsyncTask is executed parallel
android:targetSdkVersion >= 13 && Device OS >= 14:
AsyncTask is executed serial
from AsyncTask.Java:
...
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
...
You can enforce parallel execution in SDK >= 11
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,...);
public class ManyAsyncTasksActivity extends Activity{
...
protected void onResume() {
super.onResume();
TextView testV = (TextView) findViewById(R.id.async_text_1);
for (int i = 0; i < 200; i++) {
new WritingAsyncTask(testV,100).execute(i+",");
}
}
...
public class WritingAsyncTask extends AsyncTask<String, Void, String>{...
private final TextView view;//set in Constructor
private final int timeout;//set in Constructor
protected String doInBackground(String... params) {
try {Thread.sleep(timeout);} catch (InterruptedException e) {}
return params[0];
}
protected void onPostExecute(String result) {
view.setText(view.getText()+" "+result);}
Moonraker
What is shown when I start the Activity on my G. Nexus (target SDK is 11, A: 4.1.2) :
a) Numbers from 0 to 199 in random order
b) Application Error (App has stopped)
c) Numbers from 0 to 199 in serial order
d) Numbers from 0 to 127 in random order
e) Numbers from 0 to 137 in random order
public class ManyAsyncTasksActivity extends Activity{
...
protected void onResume() {
super.onResume();
TextView testV = (TextView) findViewById(R.id.async_text_1);
for (int i = 0; i < 200; i++) {
new WritingAsyncTask(testV,100).execute(i+",");
}
}
...
public class WritingAsyncTask extends AsyncTask<String, Void, String>{...
private final TextView view;//set in Constructor
private final int timeout;//set in Constructor
protected String doInBackground(String... params) {
try {Thread.sleep(timeout);} catch (InterruptedException e) {}
return params[0];
}
protected void onPostExecute(String result) {
view.setText(view.getText()+" "+result);}
Moonraker
What is shown when I start the Activity on my G. Nexus (target SDK is 11, A: 4.1.2) :
a) Numbers from 0 to 199 in random order
b) Application Error (App has stopped)
c) Numbers from 0 to 199 in serial order
d) Numbers from 0 to 127 in random order
e) Numbers from 0 to 137 in random order
LogCat shows:
java.lang.RuntimeException: Unable to resume activity {...}: java.util.concurrent.
RejectedExecutionException: Task android.os.AsyncTask ... rejected from java.util.concurrent.
ThreadPoolExecutor ... [Running, pool size = 128, active threads = 128, queued tasks = 10,
completed tasks = 0]
ThreadPoolExecutor currently only accepts a given number of Tasks, default value is a pool size of
128.
private static final int MAXIMUM_POOL_SIZE = 128;
...
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
Thank you!
dpreussler@ebay.com
johannes.orgis@team.cortado.com

Android Code Puzzles (DroidCon Amsterdam 2012)

  • 1.
  • 3.
    ● Danny Preussler: ●Lead Engineer Android ebay Kleinanzeigen, Germany ● Path to the green side: C++ -> Java SE -> JME -> BlackBerry -> Android ● My current Droids: Galaxy Nexus, Nexus 7, Logitech Revue, I'm watch ● My 1st Droid was a: Motorola Droid/Milestone ● Johannes Orgis: ● Software Development Manager Cortado Mobile Clients Cortado AG, Germany ● Path to the green side: Java EE -> Java SE -> BlackBerry -> Android ● My current Droids: Galaxy Nexus, Nexus 7 ● My 1st Droid was a: HTC Magic
  • 4.
    ● We showcode snippets that does something or maybe not ● Look out for hidden pitfalls ● All Puzzles are run with Android 4.1 Jelly Beans There should be no platform depended puzzles ● Test yourself! ● Raise your hands when we ask you! ● Guess if you're unsure! ● Learn something if you were wrong! ● Have fun! :D
  • 5.
    public class DroidConPuzzleextends Activity { @Override protected void onCreate(Bundle saved) { FragmentTransaction trans = getFragmentManager().beginTransaction(); trans.add(android.R.id.content, new Fragment() { @Override public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { View v = inf.inflate(R.layout.fragment_default, null); return null; } }); trans.commit(); super.onCreate(saved); } } For your eyes only... a) NullPointerException in Inflater.inflate() b) NullPointerException after onCreateView() c) llegalStateException in super.onCreate() d) llegalStateException in Transaction.commit() e) shows an empty screen
  • 6.
    public class DroidConPuzzleextends Activity { @Override protected void onCreate(Bundle saved) { FragmentTransaction trans = getFragmentManager().beginTransaction(); trans.add(android.R.id.content, new Fragment() { @Override public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { View v = inf.inflate(R.layout.fragment_default, null); return null; } }); trans.commit(); super.onCreate(saved); } } For your eyes only... a) NullPointerException in Inflater.inflate() b) NullPointerException after onCreateView() c) llegalStateException in super.onCreate() d) llegalStateException in Transaction.commit() e) shows an empty screen
  • 7.
    public class DroidConPuzzleextends Activity { @Override protected void onCreate(Bundle saved) { FragmentTransaction trans = getFragmentManager().beginTransaction(); trans.add(android.R.id.content, new Fragment() { @Override public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { View v = inf.inflate(R.layout.fragment_default, null); return null; } }); trans.commit(); super.onCreate(saved); } } For your eyes only... e) shows an empty screen BUT after rotation it will crash:
  • 8.
    ● Fragments needto be public: not possible as inner or anonymous class ● Fragments must have a public empty constructor ● RTFW: read the fucking warnings: ●
  • 9.
    <Button ... android:onClick="clicked"/> public class OnClickToastFragment extends Fragment {... public void clicked(View v) { Toast.makeText(getActivity(),getClass().getName(),Toast.LENGTH_SHORT).show(); } } public class OnClickToastFragmentActivity extends FragmentActivity{ public void clicked(View v) { Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show(); } } public class MyMainActivity extends Activity{ public void clicked(View v) { Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show(); } } Licence to click What happens when I click the Button? a) Toast : MyMainActivity b) Toast : OnClickToastFragmentActivity c) Toast : OnClickToastFragment d) Nothing e) IllegalStateException - Could not find a method ...
  • 10.
    <Button ... android:onClick="clicked"/> public class OnClickToastFragment extends Fragment {... public void clicked(View v) { Toast.makeText(getActivity(),getClass().getName(),Toast.LENGTH_SHORT).show(); } } public class OnClickToastFragmentActivity extends FragmentActivity{ public void clicked(View v) { Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show(); } } public class MyMainActivity extends Activity{ public void clicked(View v) { Toast.makeText(this,getClass().getName(),Toast.LENGTH_SHORT).show(); } } Licence to click What happens when I click the Button? a) Toast : MyMainActivity b) Toast : OnClickToastFragmentActivity c) Toast : OnClickToastFragment d) Nothing e) IllegalStateException - Could not find a method ...
  • 11.
    android:onClick is aneasy way to trigger "click" events in xml but remember: the calling Activity MUST implement the xxx(Viev v) method otherwise a IllegalStateException will be thrown at runtime be careful if you are using Fragments in multiple Activities
  • 12.
    Every 2.2. Devicehas the "unique" ANDROID_ID 9774d56d682e549c ? Be aware of platform dependent puzzles.... or did you know that?
  • 13.
    public class GetViewFragmentextends Fragment { public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { super.onCreateView(inf, view, saved); return inf.inflate(R.layout.fragment_get_view, view); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getView().setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Toast.makeText(getActivity(), "My name is droid, android", Toast.LENGTH_SHORT);}}); } } A View to (a) Kill a) shows a Toast "my name is droid, android" b) does nothing but showing a view c) Exception in Inflater.inflate() d) Exception in GetViewFragment.onCreate() e) Exception in Fragment.onCreateView()
  • 14.
    public class GetViewFragmentextends Fragment { public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { super.onCreateView(inf, view, saved); return inf.inflate(R.layout.fragment_get_view, view); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getView().setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Toast.makeText(getActivity(), "My name is droid, android", Toast.LENGTH_SHORT);}}); } } A View to (a) Kill a) shows a Toast "my name is droid, android" b) does nothing but showing a view c) Exception in Inflater.inflate() d) Exception in GetViewFragment.onCreate() e) Exception in Fragment.onCreateView()
  • 15.
    public class GetViewFragmentextends Fragment { public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { super.onCreateView(inf, view, saved); return inf.inflate(R.layout.fragment_get_view, view); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getView().setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Toast.makeText(getActivity(), "My name is droid, android", Toast.LENGTH_SHORT);}}); } } A View to (a) Kill a) shows a Toast "my name is droid, android" b) does nothing but showing a view c) Exception in Inflater.inflate() (2nd winner) d) Exception in GetViewFragment.onCreateView() e) Exception in Fragment.onCreateView()
  • 16.
    ● Know aboutthe life cycle of Activities, Fragments, Services! ● Example: methods as getView() or getActivity() can return null in certain points of their life cycle!
  • 17.
    public class ToastFragmentextends Fragment { ... public void onDestroy() { super.onDestroy(); new ToastingAsyncTask().execute(null,null,null); } class ToastingAsyncTask extends AsyncTask<String, Integer, Long>{ protected void onPostExecute(Long result) { if (getActivity() == null){ System.out.println(getString(R.string.no_activity)); }else{ System.out.println(getString(R.string.activity_leak)); } } protected Long doInBackground(String... params) { try {Thread.sleep(500);} catch (InterruptedException e) {} return null;} ... Live and let die ... what happens when I change the orientation of my Galaxy Nexus? a) Sysout: no_activity b) Sysout: activity_leak c) Sysout: null d) Exception in getActivity e) Exception in getString
  • 18.
    public class ToastFragmentextends Fragment { ... public void onDestroy() { super.onDestroy(); new ToastingAsyncTask().execute(null,null,null); } class ToastingAsyncTask extends AsyncTask<String, Integer, Long>{ protected void onPostExecute(Long result) { if (getActivity() == null){ System.out.println(getString(R.string.no_activity)); }else{ System.out.println(getString(R.string.activity_leak)); } } protected Long doInBackground(String... params) { try {Thread.sleep(500);} catch (InterruptedException e) {} return null;} ... Live and let die ... what happens when I change the orientation of my Galaxy Nexus? a) Sysout: no_activity b) Sysout: activity_leak c) Sysout: null d) Exception in getActivity e) Exception in getString
  • 19.
    LogCat shows: java.lang.IllegalStateException: FragmentToastFragment not attached to Activity A Context holds your Resources! Know about the life cycle of Activities, Fragments, Services!!
  • 20.
    Until Android 4(possible 3.x) android.app.DownloadManager only supports http connections: if (scheme == null || !scheme.equals("http")) { throw new IllegalArgumentException("Can only download HTTP URIs: " + uri); } Because of that the stock browser (and Chrome) could not handle https downloads. Be aware of platform dependent puzzles.... or did you know that?
  • 21.
    public class MyListActivityextends ListActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getFromServer(getApplicationContext()); } void getFromServer(Context context) { final ProgressDialog dlg = new ProgressDialog(context); dlg.show(); new AsyncTask<Context, Void, ArrayAdapter>() { protected ArrayAdapter doInBackground(Context... params) { return new ArrayAdapter<String>(params[0], android.R.layout.simple_list_item_1, Arrays.asList(new String[] { "0", "0", "7" })); } protected void onPostExecute(ArrayAdapter result) { setListAdapter(result); dlg.dismiss(); } }.execute(context); }... a) shows list with entries 0,0,7 b) shows list with entries 0,7 c) Exception in getFromServer d) Exception in doInBackground e) Exception in onPostExecute Tomorrow never dies...
  • 22.
    public class MyListActivityextends ListActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getFromServer(getApplicationContext()); } void getFromServer(Context context) { final ProgressDialog dlg = new ProgressDialog(context); dlg.show(); new AsyncTask<Context, Void, ArrayAdapter>() { protected ArrayAdapter doInBackground(Context... params) { return new ArrayAdapter<String>(params[0], android.R.layout.simple_list_item_1, Arrays.asList(new String[] { "0", "0", "7" })); } protected void onPostExecute(ArrayAdapter result) { setListAdapter(result); dlg.dismiss(); } }.execute(context); }... a) shows list with entries 0,0,7 b) shows list with entries 0,7 c) Exception in getFromServer d) Exception in doInBackground e) Exception in onPostExecute Tomorrow never dies...
  • 23.
    ● Know yourContext! ● Never use Application Context for UI! Prefer Application Context for Non-UI! ● Be aware of Context Leaks!
  • 24.
    public class ActionbarFragmentextends Fragment { ... public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { setHasOptionsMenu(true); return inf.inflate(R.layout.fragment_get_view, null); } public boolean onOptionsItemSelected(MenuItem item) { Toast.makeText(getActivity(), "from Fragment", Toast.LENGTH_SHORT).show(); return true; } } public class ActionbarActivity extends Activity { ... public boolean onOptionsItemSelected(MenuItem item) { Toast.makeText(this, "from Activity", Toast.LENGTH_SHORT).show(); return true; } } From Fragment with Love... When clicking an item from actionbar: a) shows toast: "from Activity" b) shows toast: "from Fragment" c) shows both toasts d) shows toast depending on who the creator of the menu item
  • 25.
    public class ActionbarFragmentextends Fragment { ... public View onCreateView(LayoutInflater inf, ViewGroup view, Bundle saved) { setHasOptionsMenu(true); return inf.inflate(R.layout.fragment_get_view, null); } public boolean onOptionsItemSelected(MenuItem item) { Toast.makeText(getActivity(), "from Fragment", Toast.LENGTH_SHORT).show(); return true; } } public class ActionbarActivity extends Activity { ... public boolean onOptionsItemSelected(MenuItem item) { Toast.makeText(this, "from Activity", Toast.LENGTH_SHORT).show(); return true; } } From Fragment with Love... When clicking an item from actionbar: a) shows toast: "from Activity" b) shows toast: "from Fragment" c) shows both toasts d) shows toast depending on who the creator of the menu item
  • 26.
    "If you addedthe menu item from a fragment, then the respective onOptionsItemSelected() method is called for that fragment. However the activity gets a chance to handle it first, so the system calls onOptionsItemSelected() on the activity before calling the fragment." ● RTFJD: read the fucking java doc:
  • 27.
    On pre 2.3.6Android's HttpUrlConnection is broken as soon as the server supports more than Basic authentication ? You'll get IndexOutOfBoundsException Be aware of platform dependent puzzles.... or did you know that? int realm = challenge.indexOf("realm="") + 7; WWW-Authenticate: Basic realm="RealmName" WWW-Authenticate: NTLM
  • 28.
    protected void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); String[] longList = new String[1000000]; Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra("droidcon", longList); startActivityForResult(intent, 100); } protected void onActivityResult(int request, int result, Intent data) { super.onActivityResult(request, result, data); Log.e("droidcon", "result" + request + " " + result); } a) OutOfMemoryException in onCreate() b) IllegalArgumentException in onCreate() c) Nothing happens, direct invocation of onActivityResult (with cancel code) d) Nothing happens, no invocation of onActivityResult e) System-Intent-Picker for "sent" action will be shown normally The world is not enough ...
  • 29.
    protected void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); String[] longList = new String[1000000]; Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra("droidcon", longList); startActivityForResult(intent, 100); } protected void onActivityResult(int request, int result, Intent data) { super.onActivityResult(request, result, data); Log.e("droidcon", "result" + request + " " + result); } a) OutOfMemoryException in onCreate() b) IllegalArgumentException in onCreate() c) Nothing happens, direct invocation of onActivityResult (with cancel code) d) Nothing happens, no invocation of onActivityResult e) System-Intent-Picker for "sent" action will be shown normally The world is not enough ...
  • 30.
    Logcat shows: 11-12 16:25:53.391:E/JavaBinder(6247): !!! FAILED BINDER TRANSACTION !!! Some devices show TransactionTooLargeException: The Binder transaction failed because it was too large. During a remote procedure call, the arguments and the return value of the call are transferred as Parcel objects stored in the Binder transaction buffer. If the arguments or the return value are too large to fit in the transaction buffer, then the call will fail and TransactionTooLargeException will be thrown. The Binder transaction buffer has a limited fixed size, currently 1Mb, which is shared by all transactions in progress for the process. Consequently this exception can be thrown when there are many transactions in progress even when most of the individual transactions are of moderate size.
  • 31.
    Be aware ofplatform dependent puzzles.... or did you know that? Starting with 2.3 HTTPUrlConnection handles and sets gzip as Content-Encoding. This leads to errors if you get a response where there is no content but a Content- Length (=0) or a Content-Encoding(=gzip). getResponceCode leads to EOFException
  • 32.
    public class AsyncTaskActivityextends Activity{ ... protected void onResume() { super.onResume(); TextView testV = (TextView) findViewById(R.id.async_text_1); new WritingAsyncTask(testV,2000).execute(" Bob"); new WritingAsyncTask(testV,100).execute("Alice"); new WritingAsyncTask(testV,500).execute("loves"); } ... public class WritingAsyncTask extends AsyncTask<String, Void, String>{... private final TextView view;//set in Constructor private final int timeout;//set in Constructor protected String doInBackground(String... params) { try {Thread.sleep(timeout);} catch (InterruptedException e) {} return params[0]; } protected void onPostExecute(String result) { view.setText(view.getText()+" "+result);} Casino Royal What is shown in the TextView when I start the Activity on my Galaxy Nexus: a) Alice loves Bob b) Bob Alice loves c) Bob loves Alice d) There is not enough information to decide
  • 33.
    public class AsyncTaskActivityextends Activity{ ... protected void onResume() { super.onResume(); TextView testV = (TextView) findViewById(R.id.async_text_1); new WritingAsyncTask(testV,2000).execute(" Bob"); new WritingAsyncTask(testV,100).execute("Alice"); new WritingAsyncTask(testV,500).execute("loves"); } ... public class WritingAsyncTask extends AsyncTask<String, Void, String>{... private final TextView view;//set in Constructor private final int timeout;//set in Constructor protected String doInBackground(String... params) { try {Thread.sleep(timeout);} catch (InterruptedException e) {} return params[0]; } protected void onPostExecute(String result) { view.setText(view.getText()+" "+result);} Casino Royal What is shown in the TextView when I start the Activity on my Galaxy Nexus: a) Alice loves Bob b) Bob Alice loves c) Bob loves Alice d) There is not enough information to decide
  • 34.
    public class AsyncTaskActivityextends Activity{ ... protected void onResume() { super.onResume(); TextView testV = (TextView) findViewById(R.id.async_text_1); new WritingAsyncTask(testV,2000).execute(" Bob"); new WritingAsyncTask(testV,100).execute("Alice"); new WritingAsyncTask(testV,500).execute("loves"); } ... public class WritingAsyncTask extends AsyncTask<String, Void, String>{... private final TextView view;//set in Constructor private final int timeout;//set in Constructor protected String doInBackground(String... params) { try {Thread.sleep(timeout);} catch (InterruptedException e) {} return params[0]; } protected void onPostExecute(String result) { view.setText(view.getText()+" "+result);} Casino Royal What is shown in the TextView when I start the Activity on my Galaxy Nexus: a) Alice loves Bob b) Bob Alice loves .... usually c) Bob loves Alice d) There is not enough information to decide
  • 35.
    public class AsyncTaskActivityextends Activity{ ... protected void onResume() { super.onResume(); TextView testV = (TextView) findViewById(R.id.async_text_1); new WritingAsyncTask(testV,2000).execute(" Bob"); new WritingAsyncTask(testV,100).execute("Alice"); new WritingAsyncTask(testV,500).execute("loves"); } ... public class WritingAsyncTask extends AsyncTask<String, Void, String>{... private final TextView view;//set in Constructor private final int timeout;//set in Constructor protected String doInBackground(String... params) { try {Thread.sleep(timeout);} catch (InterruptedException e) {} return params[0]; } protected void onPostExecute(String result) { view.setText(view.getText()+" "+result);} Casino Royal What is shown in the TextView when I start the Activity on my Galaxy Nexus: a) Alice loves Bob b) Bob Alice loves c) Bob loves Alice d) There is not enough information to decide
  • 36.
    You need toknow the android:targetSdkVersion (and the Device Version of the Target Device) android:targetSdkVersion < 13: AsyncTask is executed parallel android:targetSdkVersion >= 13 && Device OS >= 14: AsyncTask is executed serial from AsyncTask.Java: ... public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; ... You can enforce parallel execution in SDK >= 11 executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,...);
  • 37.
    public class ManyAsyncTasksActivityextends Activity{ ... protected void onResume() { super.onResume(); TextView testV = (TextView) findViewById(R.id.async_text_1); for (int i = 0; i < 200; i++) { new WritingAsyncTask(testV,100).execute(i+","); } } ... public class WritingAsyncTask extends AsyncTask<String, Void, String>{... private final TextView view;//set in Constructor private final int timeout;//set in Constructor protected String doInBackground(String... params) { try {Thread.sleep(timeout);} catch (InterruptedException e) {} return params[0]; } protected void onPostExecute(String result) { view.setText(view.getText()+" "+result);} Moonraker What is shown when I start the Activity on my G. Nexus (target SDK is 11, A: 4.1.2) : a) Numbers from 0 to 199 in random order b) Application Error (App has stopped) c) Numbers from 0 to 199 in serial order d) Numbers from 0 to 127 in random order e) Numbers from 0 to 137 in random order
  • 38.
    public class ManyAsyncTasksActivityextends Activity{ ... protected void onResume() { super.onResume(); TextView testV = (TextView) findViewById(R.id.async_text_1); for (int i = 0; i < 200; i++) { new WritingAsyncTask(testV,100).execute(i+","); } } ... public class WritingAsyncTask extends AsyncTask<String, Void, String>{... private final TextView view;//set in Constructor private final int timeout;//set in Constructor protected String doInBackground(String... params) { try {Thread.sleep(timeout);} catch (InterruptedException e) {} return params[0]; } protected void onPostExecute(String result) { view.setText(view.getText()+" "+result);} Moonraker What is shown when I start the Activity on my G. Nexus (target SDK is 11, A: 4.1.2) : a) Numbers from 0 to 199 in random order b) Application Error (App has stopped) c) Numbers from 0 to 199 in serial order d) Numbers from 0 to 127 in random order e) Numbers from 0 to 137 in random order
  • 39.
    LogCat shows: java.lang.RuntimeException: Unableto resume activity {...}: java.util.concurrent. RejectedExecutionException: Task android.os.AsyncTask ... rejected from java.util.concurrent. ThreadPoolExecutor ... [Running, pool size = 128, active threads = 128, queued tasks = 10, completed tasks = 0] ThreadPoolExecutor currently only accepts a given number of Tasks, default value is a pool size of 128. private static final int MAXIMUM_POOL_SIZE = 128; ... private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(10);
  • 40.