Presentation about how to build flexible (using fragments), smooth (using async tasks and intent services) and "data up to date" (using loaders) Android applications.
7. public class ContactsList extends ListFragment {
/** Key to find the data uri in a bundle. */
private static String ARG_DATA_URI = "ArgDataUri";
private Uri mDataUri;
public ContactsList() {
// Do NOT use constructors
}
public static ContactsList newInstance(Uri uri) {
Bundle args = new Bundle();
args.putParcelable(ARG_DATA_URI, uri);
ContactsList fragment = new ContactsList();
fragment.setArgments(args);
return fragment;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDataUri = getArguments().getParcelable(ARG_DATA_URI);
}
}
Fragments creation
9. public class ContactsList extends ListFragment
implements AdapterView.OnItemClickListener {
// Container Activity must implement this interface
public interface OnContactsActionListener {
void onViewContactAction(Uri contactUri);
}
private OnContactsActionListener mCallback;
@Override
public void onAttach(Activity activity) {
super.onAttach(context);
try {
mCallback = (OnContactsActionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnContactsActionListener");
}
}
}
Communication with activities
10. public class ContactsActivity extends FragmentActivity
implements OnContactsActionListener {
:::
@Override
public void onViewContactAction(Uri contactUri) {
ContactDetail contactDetailFragment = (ContactDetail)
getFragmentManager().findFragmentById(R.id.contact_detail);
if (contactDetailFragment != null) {
// If contact detail is available we are in two-pane layout
// Update contact detail’s data
contactDetailsFragment.loadData(contactUri);
} else {
// Otherwise we are in one-pane layout
// Start activity to view the contact
startActivity(new Intent(Intent.ACTION_VIEW, contactUri));
}
}
}
Communication with activities
13. public class BackupAccountDialog extends DialogFragment {
public interface OnAccountSelectedListener {
void onAccountSelected(Account account);
}
public OnAccountSelectedListener mCallback;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (getTargetFragment() instanceof OnAccountSelectedListener) {
mCallback = (OnAccountSelectedListener) getTargetFragment();
} else if (getParentFragment() instanceof OnAccountSelectedListener) {
mCallback = (OnAccountSelectedListener) getParentFragment();
} else {
if (activity instanceof OnAccountSelectedListener) {
mCallback = (OnAccountSelectedListener) activity;
} else {
throw new RuntimeExcpetion("What now?");
}
}
}
}
Communication with fragments
14. public class ContactsList extends Fragment
implements OnAccountSelectedListener {
:::
private void showSelectBackupAccount() {
BackupAccountDialog dialog = BackupAccountDialog.newInstance();
dialog.setTargetFragment(this, 0);
dialog.show(getFragmentManager(), "selectBackupAccountDialog");
}
@Override
public void onAccountSelected(Account account) {
// Do something when account is selected
}
}
Communication with fragments
15. How to be smooth?
Offload long-running operations
from Main UI thread.
17. Main Thread
• In charge of dispatching events (incl. drawing events)
to user interface widgets.
18. Main Thread
• In charge of dispatching events (incl. drawing events)
to user interface widgets.
• All components that run in the same process are
instantiated in the Main (UI) thread.
19. Main Thread
• In charge of dispatching events (incl. drawing events)
to user interface widgets.
• All components that run in the same process are
instantiated in the Main (UI) thread.
• Android UI toolkit (components from the android.widget
and android.view packages) is not thread-safe.
20. Main Thread Rules
Do not block the Main thread.
Do not access the Android toolkit
from outside the Main thread.
24. What is an Async Task?
• Designed to be helper class around Thread and Handler.
25. What is an Async Task?
• Designed to be helper class around Thread and Handler.
• Ideally to be used for short operations (a few seconds at
the most).
26. What is an Async Task?
• Designed to be helper class around Thread and Handler.
• Ideally to be used for short operations (a few seconds at
the most).
• Defined by 3 generic types: Params, Progress and
Result and 4 steps: onPreExecute, doInBackground,
onProgressUpdate and onPostExecute.
31. Why Service?
• A service can run in the background to perform work
even while the user is in a different application.
32. Why Service?
• A service can run in the background to perform work
even while the user is in a different application.
• A service can allow other components to bind to it, in
order to interact with it and perform interprocess
communication.
38. What is a Result Receiver?
• Generic interface for receiving a callback result from
someone.
39. public class ContactEditorActivity extends FragmentActivity {
:::
private void saveContact() {
Intent saveAction = ContactSaveService.createSaveContactIntent(
getActivity(), mOnSaveContactCallback, …);
startService(saveAction);
}
ResultReceiver mOnSaveContactCallback = new ResultReceiver(mHandler) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
// Do something when contact is saved.
// On a thread associated with given mHandler.
}
};
:::
}
Result Receiver – Client side
40. public class ContactSaveService extends IntentService {
private static final String EXTRA_CALLBACK = "extraCallback";
public static Intent createSaveContactIntent(Context context,
ResultReceiver resultReceiver, …) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.putExtra(EXTRA_CALLBACK, resultReceiver);
serviceIntent.putExtra(…, …);
return serviceIntent;
}
private void doSaveContact(Intent intent) {
:::
int resultCode = 0;
Bundle resultData = new Bundle(); // Result for the listener
ResultReceiver callback = intent.getParcelable(EXTRA_CALLBACK);
callback.send(resultCode, resultData);
}
}
Result Receiver – Service side
41. What is a Broadcast Receiver?
sendBroadcast() onReceive()
43. Local Broadcast Manager
• Helper to register for and send broadcasts of Intents to
local objects within your process.
44. Local Broadcast Manager
• Helper to register for and send broadcasts of Intents to
local objects within your process.
Private Secure
Efficient
45. Broadcast Receiver – Client side
public class ContactEditorActivity extends FragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
LocalBroadcastManager mLocalBroadcastManager =
LocalBroadcastManager.getInstance(this);
IntentFilter mContactSavedIntentFilter =
new IntentFilter(Constants.BROADCAST_CONTACT_SAVED);
mLocalBrodcastManager.registerReceiver(
mContactSavedReceiver, mContactSavedIntentFilter);
}
protected void onDestroy() {
mLocalBrodcastManager.unregisterReceiver(mContactSavedReceiver);
}
BroadcastReceiver mContactSavedReciver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Do something when contact is saved.
}
};
}
46. Broadcast Receiver – Service side
public class ContactSaveService extends IntentService {
public static Intent createSaveContactIntent(Context context, …) {
Intent serviceIntent = new Intent(context, ContactSaveService.class);
serviceIntent.putExtra(…, …);
return serviceIntent;
}
private void doSaveContact(Intent intent) {
:::
Intent localIntent = new Intent(Constants.BROADCAST_CONTACT_SAVED);
localIntent.putExtra(…, …);
// Broadcasts the Intent to receivers in this app.
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
}
}
47. How to be up to date?
Loaders are your friends when
performing asynchronous
loading of data.
51. Why Loaders?
• They are available to every Activity and Fragment.
• They provide asynchronous loading of data.
52. Why Loaders?
• They are available to every Activity and Fragment.
• They provide asynchronous loading of data.
• They monitor the source of their data and deliver new
results when the content changes.
53. Why Loaders?
• They are available to every Activity and Fragment.
• They provide asynchronous loading of data.
• They monitor the source of their data and deliver new
results when the content changes.
• They automatically reconnect to the last loader's cursor
when being recreated after a configuration change.
Thus, they don't need to re-query their data.
54. Using Cursor Loader
public class ContactsList extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
:::
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Initializes the loader. It will use the existing one or
// create and start a new one.
getLoaderManager().initLoader(ID, null /*bundle*/, this /*callbacks*/);
}
private void setDataUri(Uri dataUri) {
mDataUri = dataUri; // Assuming they are not equal
getLoaderManager().restartLoader(ID, null, this);
}
:::
55. Using Cursor Loader
:::
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), mDataUri, CONTACTS_PROJECITON,
null /*selection*/, null /*selArgs*/, Contacts.DISPLAY_NAME);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. The framework will close the old cursor.
mAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// Last cursor is about to be closed. We have to stop using it.
mAdapter.swapCursor(null);
}
}
56. • Volite Javu?
• Fokusirani ste na visok kvalitet
koda i optimizaciju performansi?
• Zainteresovani ste za prelazak
na Mobile razvoj?
bit.ly/Java2AndroidJava2Android