Java Svet - Communication Between Android App Components

1,465 views
1,376 views

Published on

Presentation about how to build flexible (using fragments), smooth (using async tasks and intent services) and "data up to date" (using loaders) Android applications.

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,465
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
30
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Java Svet - Communication Between Android App Components

  1. 1. Communication Between Android Application Components Aleksandar Ilić March 20, 2014 @aleksandar_ilic linkedin.com/in/ailic
  2. 2. How to be flexible? Encapsulate atomic portions of application’s user interface or behavior.
  3. 3. Fragments
  4. 4. What is a Fragment?
  5. 5. res/layout/contacts_activity.xml <FrameLayout> <fragment android:name="rs.pstech.android.ContactsList“ android:id="@+id/contacts_list" android:layout_width="match_parent" android:layout_height="match_parent“ /> </FrameLayout> res/layout-sw800dp/contacts_activity.xml <LinearLayout> <fragment android:name="rs.pstech.android.ContactsList“ android:id="@+id/contacts_list" android:layout_weight="0.30" android:layout_width="0dp" android:layout_height="match_parent“ /> <fragment android:name="rs.pstech.android.ContactDetail“ android:id="@+id/contact_details" android:layout_weight="0.70" android:layout_width="0dp" android:layout_height="match_parent“ /> </LinearLayout> Fragments creation
  6. 6. Fragments creation
  7. 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
  8. 8. Communication with activities
  9. 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. 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
  11. 11. Communication with fragments
  12. 12. Communication with fragments
  13. 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. 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. 15. How to be smooth? Offload long-running operations from Main UI thread.
  16. 16. Threads
  17. 17. Main Thread • In charge of dispatching events (incl. drawing events) to user interface widgets.
  18. 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. 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. 20. Main Thread Rules Do not block the Main thread. Do not access the Android toolkit from outside the Main thread.
  21. 21. Accessing Main Thread • Activity.runOnUiThread(Runnable) • View.post(Runnable) • View.postDelayed(Runnable, long)
  22. 22. public void onClick(View v) { @Override new Thread(new Runnable() { public void run() { final Bitmap bitmap = downloadImage("http://pstech.rs/logo.png"); mImageView.post(new Runnable() { @Override public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); } Worker Thread - Example
  23. 23. Handlers
  24. 24. What is an Async Task? • Designed to be helper class around Thread and Handler.
  25. 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. 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.
  27. 27. Async Task – Handling configuration changes
  28. 28. Async Task – Handling configuration changes
  29. 29. Services
  30. 30. What is not a Service?
  31. 31. Why Service? • A service can run in the background to perform work even while the user is in a different application.
  32. 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.
  33. 33. Intent Service
  34. 34. public class ContactEditorActivity extends FragmentActivity { ::: private void saveContact() { Intent saveAction = ContactSaveService.createSaveContactIntent(…); startService(saveAction); } ::: } IntentService usage
  35. 35. public class ContactSaveService extends IntentService { private static final String ACTION_SAVE_CONTACT = "saveContact"; private static final String ACTION_DELETE_CONTACT = "deleteContact"; @Override protected void onHandleIntent(Intent intent) { String action = intent.getAction(); if (ACTION_SAVE_CONTACT.equals(action)) { doSaveContact(intent); } else if (ACTION_DELETE_CONTACT.equals(action) { doDeleteContact(intent); } } public static Intent createSaveContactIntent(Context context, …) { Intent serviceIntent = new Intent(context, ContactSaveService.class); serviceIntent.putExtra(…, …); return serviceIntent; } ::: } IntentService creation
  36. 36. What about callbacks?
  37. 37. What about callbacks? Result Receiver Broadcast Receiver
  38. 38. What is a Result Receiver? • Generic interface for receiving a callback result from someone.
  39. 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. 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. 41. What is a Broadcast Receiver? sendBroadcast() onReceive()
  42. 42. Broadcast Receiver - Lifecycles
  43. 43. Local Broadcast Manager • Helper to register for and send broadcasts of Intents to local objects within your process.
  44. 44. Local Broadcast Manager • Helper to register for and send broadcasts of Intents to local objects within your process. Private Secure Efficient
  45. 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. 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. 47. How to be up to date? Loaders are your friends when performing asynchronous loading of data.
  48. 48. Loaders
  49. 49. What are Loaders? API LoaderManager LoaderManager.LoaderCallbacks Loader AsyncTaskLoader CursorLoader
  50. 50. Why Loaders? • They are available to every Activity and Fragment.
  51. 51. Why Loaders? • They are available to every Activity and Fragment. • They provide asynchronous loading of data.
  52. 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. 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. 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. 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. 56. • Volite Javu? • Fokusirani ste na visok kvalitet koda i optimizaciju performansi? • Zainteresovani ste za prelazak na Mobile razvoj? bit.ly/Java2AndroidJava2Android
  57. 57. Thank You! @aleksandar_ilic linkedin.com/in/ailic ailic89@gmail.com

×