Presentation is loading. Please wait.

Presentation is loading. Please wait.

Android Application Model II

Similar presentations


Presentation on theme: "Android Application Model II"— Presentation transcript:

1 Android Application Model II
CSE 5236: Mobile Application Development Instructor: Adam C. Champion, Ph.D. Course Coordinator: Dr. Rajiv Ramnath Reading: Big Nerd Ranch Guide, Chaps. 3, 5 (Activities); Chap. 28 (Services); Chap. 14 (DB); Chap. 15 (Contacts)

2 Outline Activity Lifecycle Services Persistence Content Providers

3 Recap Android Framework Activities General UI: Specialized UIs:
Layouts, Handler methods Widgets, Custom UI classes, Handling within the activity Specialized UIs: Menus and the Action Bar Special Activities – Preferences UI for larger screens: Fragments

4 The Activity Lifecycle
Activities are managed by the Android runtime Activities have a “lifecycle” consisting of states From creation till death Standard (lifecycle) methods on the activity are invoked at each state change (can test by rotating device)

5 Activity States Created: Born to run Active: Working 9 to 5
Paused: I’m about to break Resumed: Back to work Stopped: Obscured by clouds, vulnerable

6 Activity Transitions Created ⟹ Active Active ⟺ Paused
Paused ⟹ Stopped ⟹ Active Stopped ⟹ Killed

7 Timing of Activity Lifecycle Methods

8 Lifecycle Methods onCreate(Bundle savedInstanceState): create views, (re) initialize state onStart(): Restore transient state or other one-time processing onResume(): Session-specific processing, restore transient state onPause(): Save persistent data, release resources, quickly! Last method guaranteed to be called. onStop(): Called optionally by runtime onDestroy(): If finish() is called, or object is being temporarily destroyed. Use isFinishing() to distinguish. Can test lifecu

9 Transient State Management Methods
onSaveInstanceState(...): Called before onPause(). Use to save transient state. onRestoreInstanceState(...): Called after onStart() and onResume(). Use to restore state.

10 Transient State Management: Java
// MapsActivity.java public class MapsActivity extends AppCompatActivity { private MapView mMapView; private String whereAmIString = null; . . protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mMapView.onSaveInstanceState(outState); if (whereAmIString != null) outState.putString(WHERE_AM_I_STRING, whereAmIString); } protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); whereAmIString = savedInstanceState.getString(WHERE_AM_I_STRING); if (whereAmIString != null) mEditLocation.setText(whereAmIString); } }

11 Interaction Across Activities
Activity 1: onPause() Activity 2: onCreate(), onStart(), onResume() Activity 1: onStop() – if it is obscured

12 Starting Activities: Intents and Intent Filters
Message posted to the Android runtime to launch an activity. Matched against IntentFilter of activity in AndroidManifest.xml file. Encourages activity reuse among applications Uniform mechanism for launching internal and external activities Allows loose coupling between caller and responder

13 Intent and Intent Filter Components
Target – fully qualified name (string), direct reference Action – standard or custom (string) Data (URI) Category of the target object

14 Intent Filters: Examples
<!-- AndroidManifest.xml --> <intent-filter> <!== for SplashScreen activity ==> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter> <!== for Login activity ==> <action android:name="com.wiley.fordummies.androidsdk.tictactoe.Login"> <category android:name="android.intent.category.DEFAULT" />

15 Intent Invocations: Examples: Java
Matching component name: startActivity(new Intent( "com.wiley.fordummies.androidsdk.tictactoe.Login")); Direct invocation: startActivity(new Intent(getActivity().getApplicationContext(), SettingsActivity.class)); Passing data: public void sendScoresVia () { Intent Intent = new Intent(android.content.Intent.ACTION_SEND); Intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Look at my AWESOME TicTacToe Score!"); Intent.setType("plain/text"); Intent.putExtra(android.content.Intent.EXTRA_TEXT, firstPlayerName + " score is " + scorePlayerOne + " and " secondPlayerName + " score is " + scorePlayerTwo); startActivity( Intent); }

16 Intent Invocations: Examples: Kotlin
Matching component name: startActivity(Intent( "com.wiley.fordummies.androidsdk.tictactoe.Login")) Direct invocation: startActivity(Intent(activity.applicationContext, SettingsActivity::class.java)) Passing data: fun sendScoresVia () { val Intent = Intent(Intent.ACTION_SEND) Intent.putExtra(Intent.EXTRA_SUBJECT, "Look at my AWESOME TicTacToe Score!") Intent.type = "plain/text" Intent.putExtra(Intent.EXTRA_TEXT, mFirstPlayerName + " score is " + mScorePlayerOne + " and " mSecondPlayerName + " score is " + mScorePlayerTwo) startActivity( Intent) }

17 Tasks and Activity Stacks
Task: a (conceptual) container for sets of “like-minded” activities Roughly corresponds to a Linux process Activities are stacked in tasks Activities can move across tasks Task is requested by calling intent or selected based on taskaffinity attribute in AndroidManifest.xml file

18 Outline Activity Lifecycle Services Persistence Content Providers

19 Services Activities for background, long-term processing
, music, synchronization Local: Accessible by a single application. Remote (aka bound): Accessible by all applications on the device See: services.html#CreatingBoundService Comparison with threads: For coarser-grained processing Lifecycle separated from launching activity More resources consumed More respected  by OS

20 Service Example: Java public class MediaPlaybackService extends Service { MediaPlayer public IBinder onBind(Intent intent) {...} public void onCreate() { player = MediaPlayer.create(this, R.raw.sampleaudio); player.setLooping(true); } public int onStartCommand(Intent intent, int flags, int startId) { Bundle extras = intent.getExtras(); String audioFileURIString = extras.getString("URIString"); Uri audioFileURI = Uri.parse(audioFileURIString); player.reset(); player.setDataSource(this.getApplicationContext(), audioFileURI); player.prepare(); player.start(); public void onDestroy() { player.stop(); }

21 Service Example: Kotlin
class MediaPlaybackService : Service() { override fun onBind(intent: Intent): IBinder? {. . .} override fun onCreate() { player = MediaPlayer.create(this, R.raw.sample_audio) player.apply { isLooping = true } } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) val extras = intent.extras if (extras != null) { val audioFileURIString = extras.getString("URIString") val audioFileURI = Uri.parse(audioFileURIString) player.reset() player.setDataSource(this.applicationContext, audioFileURI) player.prepare() player.start() } } override fun onDestroy() {player.stop() } }

22 Service Invocation Java Kotlin
. . . Intent musicIntent = new Intent( getApplicationContext(), MyPlaybackService.class); musicIntent.putExtra( "URIString", mAudioFileURI.toString()); getActivity(). startService(musicIntent); . . . val musicIntent = Intent( activity.applicationContext, MediaPlaybackService:: class.java) musicIntent.putExtra( "URIString", mAudioFileUri.toString()) activity.startService( musicIntent)

23 Outline Activity Lifecycle Services Persistence Content Providers

24 Saving Persistent Data – Files
Same as in any Java or Kotlin app Internal storage (Linux file system) Local to app Destroyed upon uninstall External storage (e.g., SD card) Stored to SD card or flash memory (device-dependent) Shared among applications E.g.: Java Kotlin File imageFile = new File(imageFilePath); val imageFile = File(imageFilePath)

25 Saving Persistent Data – SQLite
Sqlite.org: “Most widely deployed SQL database engine in the world”  Lightweight, transactional Helper class for database creation Methods for operating on SQL statements: Creating a statement Binding data Executing

26 SQLite Example: Account Model Class
Java Kotlin // Account.java public class Account { private String mName, mPassword; public Account(String name, String password) {mName = name; mPassword = password; } public String getName() { } public String getPassword() { ... public boolean equals(Object o) public int hashCode() { } // toString() } // Account.kt data class Account(val name: String, val password: String) { // We don't need anything here! } Kotlin data classes automatically generate getters, setters, equals(), hashCode(), and toString()

27 SQLite Example: Database Schema: Java
// AccountDbSchema.java public class AccountDbSchema { public static final class AccountsTable { public static final String NAME = "accounts"; public static final class Cols { public static final String NAME = "name"; public static final String PASSWORD = "password"; } } } // Also used with Kotlin

28 SQLite Example: Account CursorWrapper
Java Kotlin // AccountCursorWrapper.java public class AccountCursorWrapper extends CursorWrapper { public AccountCursorWrapper( Cursor cursor) { super(cursor); } public Account getAccount() { String name = getString( getColumnIndex( AccountsTable.Cols.NAME)); String password = getString( getColumnIndex( AccountsTable.Cols.PASSWORD)); Account account = new Account(name, password); return account; } } // AccountCursorWrapper.kt class AccountCursorWrapper(cursor: Cursor) : CursorWrapper(cursor) { val account: Account get() { val name = getString( getColumnIndex( AccountsTable.Cols.NAME)) val password = getString( getColumnIndex( AccountsTable.Cols.PASSWORD)) return Account(name, password) } }

29 SQLite Example: Account Singleton: Java
// AccountSingleton.java public class AccountSingleton { private static AccountSingleton sAccount; private AccountDbHelper mDbHelper; private SQLiteDatabase mDatabase; private static final String INSERT_STMT = "INSERT INTO " + AccountsTable.NAME + "(name, password) VALUES (?, ?)" ; private AccountSingleton(Context context) { mDbHelper = new AccountDbHelper( context.getApplicationContext()); mDatabase = mDbHelper .getWritableDatabase(); } private static ContentValues getContentValues( Account account) { ContentValues values = new ContentValues(); values.put(AccountsTable.Cols.NAME, account.getName()); values.put(AccountsTable.Cols.PASSWORD, account.getPassword()); return values; } // Continued . . . // Continued public void addAccount(Account account) { ContentValues contentValues = getContentValues(account); SQLiteStatement statement = mDatabase.compileStatement(INSERT_STMT); statement.bindString(1, contentValues.getAsString( AccountsTable.Cols.NAME)); statement.bindString(2, contentValues.getAsString( AccountsTable.Cols.PASSWORD)); statement.executeInsert(); } public void deleteAllAccounts() { mDatabase.delete(AccountsTable.NAME, null, null); } } // Also used with Kotlin

30 SQLite Example: Helper Class: Java
// AccountDbHelper.java public class AccountDbHelper extends SQLiteOpenHelper { private Context mContext; private static final String DATABASE_NAME = "TicTacToe.db"; private static final int DATABASE_VERSION = 1; public AccountDbHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL("CREATE TABLE " + AccountsTable.NAME + "(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + AccountsTable.Cols.NAME + " TEXT, " + AccountsTable.Cols.PASSWORD + " TEXT" + public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { Log.w("Example", "Example: upgrading DB; dropping/recreating tables."); sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + AccountsTable.NAME); onCreate(sqLiteDatabase); } }

31 SQLite Example: Helper Class: Kotlin
// AccountDbHelper.kt class AccountDbHelper(context: Context) : SQLiteOpenHelper( context, DATABASE_NAME, null, DATABASE_VERSION) { private lateinit var mContext: Context override fun onCreate(sqLiteDatabase: SQLiteDatabase) { sqLiteDatabase.execSQL("CREATE TABLE " + AccountsTable.NAME + "(" + "_id INTEGER PRIMARY KEY AUTOINCREMENT, " + AccountsTable.Cols.NAME + " TEXT, " + AccountsTable.Cols.PASSWORD + " TEXT" + ")")} override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) { Log.w("Example", ”Upgrading DB; dropping/recreating tables.") database.execSQL("DROP TABLE IF EXISTS " + AccountsTable.NAME) onCreate(database) } companion object { private val DATABASE_NAME = "TicTacToe.db" private val DATABASE_VERSION = 1 } }

32 SQLite Example: Database Operations: Java
// AccountFragment.java private void createAccount() { FragmentActivity activity = getActivity(); String username = mEtUsername.getText().toString(), String password = mEtPassword.getText().toString(); String confirm = mEtConfirm.getText().toString(); if (activity != null) { if ((password.equals(confirm)) && (!username.equals("")) && (!password.equals("")) && (!confirm.equals(""))) { AccountSingleton singleton = AccountSingleton.get( activity.getApplicationContext()); Account account = new Account(username, password); singleton.addAccount(account); Toast.makeText(activity.getApplicationContext(), "New record inserted", Toast.LENGTH_SHORT).show(); } else if ((username.equals("")) || (password.equals("")) || (confirm.equals(""))) { Toast.makeText(activity.getApplicationContext(), "Missing entry", Toast.LENGTH_SHORT).show(); } else if (!password.equals(confirm)) { FragmentManager manager = getFragmentManager(); AccountErrorDialogFragment fragment = new AccountErrorDialogFragment(); if (manager != null) { fragment.show(manager, "account_error"); } } else { /* Error handling */ } } }

33 SQLite Example: Database Operations: Kotlin
// AccountFragment.kt private fun createAccount() { val username = mEtUsername.text.toString() val password = mEtPassword.text.toString() val confirm = mEtConfirm.text.toString() if (password == confirm && username != "" && password != "" && confirm != "") { val singleton = AccountSingleton.get(activity?.applicationContext) val account = Account(username, password) singleton.addAccount(account) Toast.makeText(activity?.applicationContext, "New record inserted", Toast.LENGTH_SHORT).show() } else if (username == "" || password == "" || confirm == "") { Toast.makeText(activity?.applicationContext, "Missing entry", Toast.LENGTH_SHORT).show() } else if (password != confirm) { val manager = fragmentManager val fragment = AccountErrorDialogFragment() fragment.show(manager, "account_error") } else { /* Error handling */ } }

34 Outline Activity Lifecycle Services Persistence Content Providers

35 Sharing Content – Content Providers
Standard content providers: Contacts, dictionary, media Referred to by URI prefix: content:// Standard providers Permission must be requested in manifest: <uses-permission android:name= "android.permission.READ_CONTACTS"/> Android 6+: permission for “dangerous” actions must be requested at runtime ( training/permissions/requesting.html ) ListView of user’s contacts

36 Content Provider Example: Java (1)
// ContentsFragment.java public class ContactsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { private ListView mContactsListView; private static final String[] PROJECTION = { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY }; private final static String[] FROM_COLUMNS = { ContactsContract.Contacts.DISPLAY_NAME_PRIMARY }; private final static int[] TO_IDS = { R.id.contact_info }; . . public void onActivityCreated(. . .) { // First, call super.onActivityCreated() Activity activity = getActivity(); if (activity != null) { mContactsListView = activity.findViewById(R.id.contact_list_view); requestContacts(); } } private void requestContacts() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!hasReadContactPermission()) { requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, PERMISSION_REQUEST_READ_CONTACTS); } else { showContacts(); } } else { showContacts(); } }

37 Content Provider Example: Java (2)
// ContactsFragment.java, continued private boolean hasReadContactPermission() { Activity activity = getActivity(); return activity != null && activity.checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED; public void onRequestPermissionsResult(. . . ) { if (requestCode == PERMISSION_REQUEST_READ_CONTACTS) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { showContacts(); } else { /* Show error message */} } } private void showContacts() { // Gets a CursorAdapter mCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.list_item_contact, null, FROM_COLUMNS, TO_IDS, 0); // Sets the adapter for the ListView mContactsListView.setAdapter(mCursorAdapter); // Initializes the loader, which loads the contacts asynchronously getLoaderManager().initLoader(0, null, this); }

38 Content Provider Example: Java (3)
// ContactsFragment.java, continued . . public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new CursorLoader(getActivity(), ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null, ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC" ); public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Put the result Cursor in the adapter for the ListView mCursorAdapter.swapCursor(data); public void onLoaderReset(Loader<Cursor> loader) { mCursorAdapter.swapCursor(null); } } // End of Fragment

39 Content Provider Example: Kotlin (1)
// ContactsFragment.kt class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { private lateinit var mContactsListView: ListView companion object { private val PROJECTION = arrayOf(ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY) private val PERMISSION_REQUEST_READ_CONTACTS = 1 private val FROM_COLUMNS = arrayOf(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY) private val TO_IDS = intArrayOf(R.id.contact_info) } private lateinit var mCursorAdapter: SimpleCursorAdapter override fun onActivityCreated(savedInstanceState: Bundle?) {// First, call super... mContactsListView = activity!!.findViewById(R.id.contact_list_view) requestContacts()} private fun requestContacts() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!hasReadContactPermission()) { requestPermissions( arrayOf(Manifest.permission.READ_CONTACTS), PERMISSION_REQUEST_READ_CONTACTS) } else { showContacts() } } else { showContacts() } }

40 Content Provider Example: Kotlin (2)
// ContactsFragment.kt, continued private fun hasReadContactPermission(): Boolean { return activity?.checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED } override fun onRequestPermissionsResult( ) { if (requestCode == PERMISSION_REQUEST_READ_CONTACTS) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {showContacts()}} else { /* Show error message */ } } private fun showContacts() { // Gets a CursorAdapter mCursorAdapter = SimpleCursorAdapter(activity, R.layout.list_item_contact, null, FROM_COLUMNS, TO_IDS, 0) // Sets the adapter for the ListView mContactsListView.adapter = mCursorAdapter // Initializes the loader loaderManager.initLoader(0, null, this) }

41 Content Provider Example: Kotlin (3)
// ContactsFragment.kt, continued override fun onCreateLoader(id: Int, args: Bundle): Loader<Cursor> { return CursorLoader(activity, ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null, ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC") } override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { // Put the result Cursor in the adapter for the ListView mCursorAdapter.swapCursor(data) } override fun onLoaderReset(loader: Loader<Cursor>) { mCursorAdapter.swapCursor(null) } } // End of class definition

42 Even More?! Yes! More widgets, styles and themes Maps
, media, telephony Sensors

43 Summary Activities: “Single screen” of content that user sees
Can be paused, resumed by users Managing Activity lifecycle is crucial! Services: Long-running tasks Persistence: Files (internal, external storage) SQLite database Look at data/data-storage.html for info about Room ORM (easier to use than SQLiteOpenHelper) Content Providers: mechanism for sharing data


Download ppt "Android Application Model II"

Similar presentations


Ads by Google