Date post: | 15-Jan-2015 |
Category: |
Technology |
Upload: | aleksandar-ilic |
View: | 772 times |
Download: | 2 times |
Communication Between
Android Application
Components
Aleksandar Ilić
March 20, 2014
@aleksandar_ilic
linkedin.com/in/ailic
How to be flexible?
Encapsulate atomic portions
of application’s user interface
or behavior.
Fragments
What is a Fragment?
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
Fragments creation
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
Communication with activities
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
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
Communication with fragments
Communication with fragments
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
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
How to be smooth?
Offload long-running operations
from Main UI thread.
Threads
Main Thread
• In charge of dispatching events (incl. drawing events)
to user interface widgets.
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.
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.
Main Thread Rules
Do not block the Main thread.
Do not access the Android toolkit
from outside the Main thread.
Accessing Main Thread
• Activity.runOnUiThread(Runnable)
• View.post(Runnable)
• View.postDelayed(Runnable, long)
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
Handlers
What is an Async Task?
• Designed to be helper class around Thread and Handler.
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).
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.
Async Task – Handling configuration changes
Async Task – Handling configuration changes
Services
What is not a Service?
Why Service?
• A service can run in the background to perform work
even while the user is in a different application.
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.
Intent Service
public class ContactEditorActivity extends FragmentActivity { ::: private void saveContact() { Intent saveAction = ContactSaveService.createSaveContactIntent(…); startService(saveAction); } ::: }
IntentService usage
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
What about callbacks?
What about callbacks?
Result Receiver
Broadcast Receiver
What is a Result Receiver?
• Generic interface for receiving a callback result from
someone.
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
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
What is a Broadcast Receiver?
sendBroadcast() onReceive()
Broadcast Receiver - Lifecycles
Local Broadcast Manager
• Helper to register for and send broadcasts of Intents to
local objects within your process.
Local Broadcast Manager
• Helper to register for and send broadcasts of Intents to
local objects within your process.
Private Secure
Efficient
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. } }; }
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); } }
How to be up to date?
Loaders are your friends when
performing asynchronous
loading of data.
Loaders
What are Loaders?
API
LoaderManager LoaderManager.LoaderCallbacks
Loader AsyncTaskLoader CursorLoader
Why Loaders?
• They are available to every Activity and Fragment.
Why Loaders?
• They are available to every Activity and Fragment.
• They provide asynchronous loading of data.
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.
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.
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); } :::
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); } }
• Volite Javu?
• Fokusirani ste na visok kvalitet
koda i optimizaciju performansi?
• Zainteresovani ste za prelazak
na Mobile razvoj?
bit.ly/Java2Android Java2Android