Skip to content

Instantly share code, notes, and snippets.

@IngmarBoddington
Last active October 16, 2017 15:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IngmarBoddington/c7cac2de22c3bef49e576adaa5c36834 to your computer and use it in GitHub Desktop.
Save IngmarBoddington/c7cac2de22c3bef49e576adaa5c36834 to your computer and use it in GitHub Desktop.
Notes from Android / Java Study for Learning Tree exam
IDE
======
Recommended -> Android Studio
Common (Legacy) -> Eclipse with Android Development Tools (ADT)
Requires Android SDK + Java JDK
Applications are ran in an emulator -> Android Virtual Device (AVD) by the AVD Manager
Allows multiple configurations to be created
Different API versions and screen sizes
Detailed configuration, such as memory size and GPS emulation, available
Android applications can be developed on almost any platform
Android
=====
Based on Linux Kernal, open source
Android Runtime
Set of core libraries supporting most of the Java language
The Android Runtime Virtual Machine (ART VM)
Java virtual machine highly optimized for minimal memory
Runs class files converted to Dalvik Executable (.dex) format
Compiled to native code Ahead Of Time (AOT)
Prior to Android 5, the VM was called Dalvik
Libraries
C/C++ libraries providing low-level support to applications
System library—core operating system support
Media libraries, WebKit, SQLite, etc.
Exposed to developers through the application framework
Version history -> https://en.wikipedia.org/wiki/Android_version_history
Launches a seperate Linux process for each application, seperate Java VM
Google recommend that all projects include
Support-v4
Appcompat-v7
To test version (for newer feature checks) - v4 and above:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB){
return true;
}
return false;
Application Stores
=====
Google Play -> No approval process, 30% cut
Others -> “Unknown sources” must be enabled on the device to use them
Screen Sizes
=====
Android uses a resource system to support multiple resolutions
Four screen sizes defined -> small, normal, large, and xlarge
Six screen densities defined -> ldpi 120dpi, mdpi 160dpi, hdpi 240dpi, xhdpi 320dpi, xxhdpi 480dpi, xxxhdpi 640dpi
Build
=====
Gradle (written in a Groovy-based Domain-Specific Language (DSL)) used as build tool generally
Supports incremental builds by intelligently determining which parts of the
build tree are up-to-date
build.gradle files define build including dependencies
Packaging / Manifest
=====
All components are packaged in an .apk file
Manifest details contents of package - AndroidManifest.xml
Components cannot be used unless in the manifest
Example Manifest:
<manifest>
<uses-permission />
<permission /> …
<uses-sdk />
<uses-configuration />
<uses-feature />
<supports-screens />…
<application>
<activity>...</activity>
<activity-alias>...</activity-alias>
<service>...</service>
<receiver>...</receiver>
<provider>... </provider>
<uses-library />
</application>
</manifest>
uses-permission A permission needed for the application to run: access to the camera, Internet, external storage, etc.
permission Allows restriction of access from other applications
uses-sdk Information about SDK versions; attributes specify minimum version needed to run, target SDK version, the maximum SDK version on which the application can run*
usesconfiguration Specific hardware configuration required; e.g., a physical keyboard*
uses-feature Hardware features required: Bluetooth, camera, GPS, etc.*
supports-screens Screen sizes supported—default since API level 4 is that all screen sizes are supported; use this, for instance, to state that small screens are not supported*
Google play uses the last four of the items above to determine whether to show an application to a user in the store
Manifest must specify permissions it uses
Which features of Android system it needs at runtime
android.Manifest.permission defines the base set of permission
Failure to specify a permission results in an AccessControlException
Unchecked: Can result in “silent failures”
Google Play warns user about the uses-permissions of the application they are installing
Example:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
activity, activity-alias, service, and receiver May have an intent-filter nested within them to allow receipt of Intents from other applications
Android 6.0 added fine-grained runtime permission control
User may grant/revoke permissions while an app is running!
If targetSdkVersions is 23 or higher
App must check permission prior to accessing the protected resource
If a permission is dangerous, then Android must check it
If permission is not granted, then Android will prompt
uses-permissionsmust still be declared in the manifest
Example:
private void doSomethingIfWeHavePermission(String permissionToCheck){
if (ActivityCompat.checkSelfPermission(this, permissionToCheck) == PackageManager.PERMISSION_GRANTED) {
// Do the work that requires a permission here
} else {
String[] perms = {permissionToCheck};
ActivityCompat.requestPermissions(this, perms, REQUEST_PERMISSIONS);
}
}
Android would then call back:
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Do the work originally requested
} else {
Toast.makeText(this, "Permission not granted“, Toast.LENGTH_LONG).show();
}
}
}
For release, the package must be
Zipaligned – Optimizes the structure of the package for memory efficiency
Signed – Digitally signed to provide an anti-tamper mechanism
Google has introduced a license verification scheme
– http://developer.android.com/guide/publishing/licensing.html
Performance
=====
Do not run long processes in th primary (user interface) thread
May cause Application Not Responding (ANR) errors
Use services, AsyncTasks or intents
Activities
=====
The user interacts with an Android application through activities
Each activity is a single screen
Displays or captures data through a view (which may be split into fragments)
Activities are managed by the Android application framework
Main activity launched in response to user request
Responsible for
Capturing user input
Supporting the activity life cycle
Interacting with underlying data and business logic
Extends android.app.Activity, although generally we extend AppCompatActivity for compatibility
onCreate override for launch of activity from intent - called by framework when application is ready
Call the overridden method
Set the view for the activity
Example:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
Can get the activity which launched the activity:
Intent getIntent()
Or retrieve the URI for a content provider / any other set data
Uri getData()
double getDoubleExtra(String name)
int getIntExtra(String name)
int[] getIntArrayExtra(String name)
Serializable and Parcelable extras must be cast to correct type
Type type = (Type) getIntent().getParcelableExtra("Name");
Subactivities
=====
Can be launched using:
startActivityForResult (Intent launchIntent, int requestCode)
And the callback implementation:
protected void onActivityResult(int requestCode, int resultCode, Intent resultIntent)
The requestCode can be used to correlate the subactivity result
Activity class provides constants to check the resultCode
The subactivity must use setResult with option resultIntent and the finish
setResult(Activity.RESULT_OK, resultIntent);
finish()
Views
=====
Defined in XML
Defined in res folder of project
ID maps to location R.<folder>.<folder> where R = res
Visual portion of an activity, what the user sees
Set using setContentView:
setContentView(int resourceId) - Using declared resource item
setContentView(View view) - Using view object
Views can be organised in collections using ViewGroups (which are Views)
View contains widgets, such as:
TextView - Used to display text; optionally allows text to be edited
EditText - A subclass of TextView used to create edit fields on forms
AutoCompleteTextView - A text edit control with an associated list of autocomplete entries; autocomplete entries are shown in a drop-down list
CheckBox - A button with two states—checked or unchecked
RadioButton - A two-state button that can be grouped using RadioGroup so that only one button in the group can be checked
Button - A button that has an associated event handler View.OnClickListener()
ImageButton - Similar to the Button but supports an image rendered on its surface
Widgets can either be defined directly in code or using XML resource files (which is recommended)
Widget reference -> https://developer.android.com/reference/android/widget/package-summary.html
If declared in XML than can fetch from R:
example = (Type) findViewById(R.id.<identifier>);
Android provides several different layouts (which are ViewGroups)
Frame Layout: Child Views displayed as a stack, fixed to upper left of frame
Example:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:src="@drawable/emo_im_yelling"
android:id="@+id/frm_img_yell" android:visibility="invisible" />
<Button android:text="Button" android:id="@+id/button1" … />
</FrameLayout>
Linear Layout: Row or Column of children
Space is allocated in proportion to child layout_weight value
If multiple with highest value: space equally shared
Children may be assigned a gravity value
top, bottom, left, right, center_vertical, center_horizontal...
Example:
<LinearLayout … android:orientation="vertical">
<TextView …
android:layout_weight="1"
android:layout_gravity="center_vertical" />
<TextView … />
</LinearLayout>
Table Layout
Each row is a <TableRow>, each View within a cell
Example:
<TableLayout … android:layout_width="match_parent"
android:layout_height="match_parent">
<TableRow android:id="@+id/tableRow1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView android:text="R1_C1"></TextView>
<TextView android:text="Row1_Column2" ></TextView>
<TextView android:text="R1_C3" ></TextView>
</TableRow>
</TableLayout>
Relative Layout
Child Views may be positioned relative to each other, or parent layout
layout_alignRight, layout_alignBelow, layout_alignParentRight, layout_alignParentBelow...
Example:
<RelativeLayout...>
<EditText android:layout_alignParentRight="true" android:text="EditText1"
android:id="@+id/eT1" />
<EditText android:layout_below="@id/eT1" android:text="EditText2"
android:id="@+id/eT2" android:layout_toLeftOf="@id/eT1" />
<EditText android:layout_below="@id/eT2" android:text="EditText3"
android:id="@+id/eT3" android:layout_toRightOf="@id/eT2" />
<EditText android:layout_alignParentLeft="true" android:text="EditText4"
android:id="@+id/eT4" />
</RelativeLayout>
++ Sliding Drawer, and Absolute
All Views may have height, width, and padding specifiers
layout_width, layout_height, paddingTop, paddingLeft, paddingRight, paddingBottom
match_parent - View should fill its parent (allowing space for any padding)
wrap_content - View should be as small as possible to wrap its content (allowing for padding)
Values may be specified as
px (pixels)
dp (density-independent pixels)
sp (scaled pixels based on preferred font size)
in (inches)
mm (millimeters)
Fragments
=====
Multi-pane user interfaces
A single activity may host multiple fragments
Has a view and some additional lifecycle methods
Multiple fragments can be active and can be optionally put on the backstack
Children of a ViewGroup
Five additional lifecycle methods:
onAttach The Fragment has been associated with an Activity
onCreateView The Fragment must create its View
onActivityCreated The parent Activity has completed its creation
onDestroyView The View associated with the Fragment is being destroyed
onDetach The Fragment is about to be disassociated from the Activity
A fragment must inflate it's own view (in the onCreateView method using inflater.inflate)
Can get / interact with parent activity
Activity parentActivity = getActivity()
Example declaration in Activity:
<LinearLayout android:orientation="horizontal" …>
<fragment class="com.ltree.expenses.ExampleListFragment" android:id="@+id/frag_example_list" android:layout_width=… />
...
To load a Fragment dynamically:
ExampleFragment frag = new ExampleFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.frag_example_details, frag);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
Can add to backstack (for back functionality):
ft.addToBackStack("name");
Data can be passed into a Fragment from an Activity using a Bundle:
Bundle args = new Bundle();
args.putLong("name", value);
frag.setArguments(args);
Which can then be fetched in the onCreate method of the activity:
example = getArguments().getLong("name");
Good practice to set Framelayout's for dynamic Fragments:
<LinearLayout …
<FrameLayout android:id="@+id/frag_example_list" …/>
<FrameLayout android:id="@+id/frag_example_details" …/>
</LinearLayout>
Can then set one Framelayout to have a "gone" visibility in Potrait and dynamically add missing fragment to the backstack instead
Resources / res / R
=====
The layout XML files are stored within the resource directory structure
Drawables - res/drawable - Graphics and bitmaps
Image files (.png, .jpg, .gif)
Nine-patch file—a .png with stretchable areas
Should have a version in each of:
res/drawable-xhdpi: extra-high–density images
res/drawable-hdpi: high-density images
res/drawable-ldpi: low-density images
res/drawable-mdpi: medium-density images
– Android autofits it to the available space
Layouts - res/layout - Layout files
Menus - res/menu - XML declarations of menus
Strings - res/values - XML declaration of strings
Styles - res/values - XML declaration of styles and themes
R is an auto generated class based on the res directory contents, created during build of .apk
Example (Strings, same naming for other resource types) - normally res/values/strings.xml):
<resources>
<string name="hello">Hello Course 577</string>
<string name="app_name">Samples</string>
</resources>
Can then be referenced in Views:
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="@string/hello"/>
In code: R.string.hello
Views are slightly different in ID structure (than the other resource types):
<EditText android:id="@+id/eT2" android:layout_below="@id/eT1" .../>
In code: findViewById(R.id.eT2)
Styles can be used instead of directives like android:textColor="#ffff4d6b" / android:typeface="monospace"
<style name="ButtonStyle" parent="@android:style/Widget.Button">
<item name="android:textColor">#ffff4d6b</item>
<item name="android:typeface">monospace</item>
</style>
Apply a style in the manifest:
<application android:theme="@style/ApplicationTheme" >
Can also use localisation in resource directory structure
res/<type>-<languageCode>-<regionCode>
Language code is defined by a two-letter ISO 639 code
Region code is two-letter ISO 3166-1-alpha-2 preceded by an r
Specificity matters
Other qualifiers see https://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources
Used generally for differ resolutions, orientation
Can use shortcuts to use to much repition, e.g:
<resources>
<item name="main" type="layout">@layout/narrow_layout</item>
</resources>
Intents
=====
Intents are sent to the Framework to request an activity
Explicit Intent: Specify a named class, can only be used within application
Implicit Intent: Request an action (resolved by the framework)
By the user, through interaction, by applications
Must be sent through the framework using startService() or startActivity()
Example:
Intent startSomethingIntent = new Intent(this, Something.class);
startService(startSomethingIntent);
Data can be attached to an intent:
setData(URI data) – Used to pass details about a content provider
putExtra(String name, xxx value)
Used to pass other data as name/value pairs
xxx may be a primitive, CharSequence, or arrays of these types
Object data may also be sent using the Parcelable or Serializable types
Serializable is part of the JDK
Serializable objects may be written to and read from a stream
Class be must be marked as implementing Serializable
No methods to implement
Parcelable classes may be converted to a Parcel
Intended to pass data between Android processes
Using the Android IPC mechanisms
Relatively complex to implement
Implicit Intent Example:
// Specify an Action in the constructor
Intent intent = new Intent("android.intent.action.VIEW");
// Add optional categories
intent.addCategory("lt.samples.showAll");
// Add the URI of the content provider
intent.setData(Uri.parse("content://com.ltree.weather/stations"));
startActivity(intent);
Intent Filters (For implicit intents)
=====
Activities, services, and broadcast receivers register IntentFilters
Specify Action, Category, and Data handled
Usually declared in the manifest
Registered automatically on installation
Implicit Intent resolution is handled by the PackageManager
Matches Action, Category, and Data
Of the Intent against the values in the IntentFilter
When the PackageManager resolves an Intent it compares the values in the Intent with available IntentFilters to select a target
Intent IntentFilter
Action A single action One or more. Intent action must match one. A filter with no actions matches any intent action.
Category Zero or more Zero or more. Every category in the Intent object must match a category in the filter. If the filter contains additional values, it will still match.
Data Zero or one. Specifies the URI of the data to be acted on Zero or more MIME types. Matches if one of the filter values matches the Intent content type. May refine the filter by specifying values for scheme, authority, and path (zero or more of each).
Example implementation:
<activity …>
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.learningtree.station" />
</intent-filter>
</activity>
#Specify a main activity (get launcher icon)
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Broadcast Reciever
=====
Recieves system intents
Example:
<receiver android:name="BootCompletedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
To catch event and start a service:
public class BootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent bootIntent) {
// Start a service here
Intent intent = new Intent(context, SimpleIntentService.class);
context.startService(intent);
}
}
Contexts
=====
Activities, services, and other components interact with the application framework via the Context object
Parent of both Activities and Services
Services
=====
Used for background / long running operations
Continue to run after associated activities are destroyed
Started in main thread so must start a thread for long running actions
Service -> IntentService -> MyService
IntentService can automatically launch a background thread
onCreate() override
Code for main thread
onHandleIntent() override
Code for background thread
When startService() is called for an IntentService, the system
1. Places the Intent on a queue (FIFO)
2. Starts the service if not already running
3. Invokes the service's onHandleIntent()for each Intent – In a background thread
4. Stops the service when all Intents have been processed
Example:
public class SyncService extends IntentService {
public SyncService() {
// Super-class constructor requires a name
// (only used for debugging)
super("SyncService");
}
@Override
protected void onHandleIntent(Intent intent) {
// Background processing goes in here
// It can't access the UI as it is in a thread
Content Providers
=====
Provide access to a data set to one or more applications
Exposed through a standard table like model
Each ContentProvider is identified by a unique URI
Standard providers (bookmarks, contacts, etc.) have standard URIs built into the system
ContactsContract.Thing.PROPERTY
Custom providers—such as the Expenses app—must define a URI
content://com.company.app/thing/property
URIs generall follow a REST like standard to identify resources
ContentProviders present data in tabular form
The first column must be called _ID, is unique numeric, Used to identify record—the end of the provider URI
Other columns can be of any type
ContentResolver locates and communicate with content providers
protected void onResume() {
super.onResume();
String[] projection = new String[] { Browser.BookmarkColumns._ID, Browser.BookmarkColumns.TITLE, Browser.BookmarkColumns.URL };
String[] displayFields = new String[] { Browser.BookmarkColumns.TITLE, Browser.BookmarkColumns.URL };
int[] displayViews = new int[] { R.id.text1, R.id.text2 };
Cursor cur = getContentResolver().query(
Browser.BOOKMARKS_URI,
projection, null, null, null);
setListAdapter(new SimpleCursorAdapter(this,
R.layout.two_item_list,
cur, displayFields, displayViews));
}
Example Insert:
ContentValues values = new ContentValues();
values.put("<name>", value);
getContentResolver().insert(CONTENT_URI, values);
Example Delete (could also use where in query):
Uri uri = ContentUris.withAppendedId(CONTENT_URI, id);
getContentResolver().delete(uri, null, null);
Example Update:
ContentValues values = new ContentValues();
values.put("<key>","<value>");
String where = "<name> = ?";
String[] selectionArgs = {"<arg>"};
getContentResolver().update(uri, values, where, selectionArgs);
A loader can be used to load content in the background:
public class BookmarksLoaderActivity extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor> {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mProjection = new String[] { Browser.BookmarkColumns._ID,…};
String[] displayFields = new String[] {…};
mAdapter = new SimpleCursorAdapter(this, R.layout.two_item_list,
null, displayFields, displayViews,0);
setListAdapter(mAdapter);
getLoaderManager().initLoader(0, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this, android.provider.Browser.BOOKMARKS_URI, mProjection, null, null, null );
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
}
To create own content provider:
public class MyContentProvider extends ContentProvider {
// Initialize the provider
public boolean onCreate(){…}
// Get the MIME type for the content
// Return one of
// vnd.android.cursor.item/vnd.<company>.<type> for single items
// vnd.android.cursor.dir/vnd.<company>.<type> for multiple items
// standard MIME type if this actually applies
public String getType(Uri uri) {…}
// Query the provider – results as a Cursor
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {…}
// Insert new data – supplied in ContentValues
public Uri insert(Uri uri, ContentValues values) {…}
// Update values specified by where clause in where, whereArgs
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {…}
// Delete – supplied a where clause in where, whereArgs
public int delete(Uri uri, String where, String[] whereArgs) {…}
}
In order to get an ID from the uri (# for int, * for string match):
#Add a pattern
private static final ID = 2;
sUriMatcher.addURI("<com.authority.identifier>", "<path>/#", ID);
#Match a pattern
sUriMatcher.match("content://<com.authority.identifier>/<path>/6"); //Returns 2
#Can then switch on this value for implementation of methods
#If matches ID patter then can extract using:
long Id = ContentUris.parseId(uri);
#Inserts must return the new uri and dont need _ID
Register a content provider in the manifest:
<manifest … package="lt.samples" … >
<application>
...
<provider android:authorities="com.<company>.<app>" android:name=".db.SamplesCP" android:exported="false">
</provider>
</application>
</manifest>
Storage
=====
Database (on or off the device)
Internal (on the device)
External (removable)
Broadcast Receivers
=====
Generally thin components designed to respond to a systemwide broadcast
Lifecycle
=====
When an activity is started it is put on the back stack, and the previous activity is stopped
Previous activity restarted on user pressing the back button generally, current activity is destroyed
Three states:
Resumed
Paused - May be killed by OS, obscured
Stopped - In background, may be killed by OS
Generally any data should be saved when a activity is removed from the foreground
Lifecycle methods (in order)
onCreate()
onStart()
onResume()
RUNNING STATE - In foreground
onPause() - can also go to onResume()
PAUSED - In background (partially obscured), can be cleaned up by OS (rare)
onStop() - can also go to onStart() via onRestart()
STOPPED STATE - In background (fully obscured), can be killed by OS
onDestroy()
DEAD - Killed
Android sees applications as nonresponsive if
They fail to respond to user input within 5 seconds
A BroadcastReceiver hasn’t finished executing within 10 seconds
Don't do long running work in the main thread (the UI thread)
The interface thread is not thread safe, updating from a background thread will fail
Creating a thread (main thread get priority, CPU time split between threads):
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.exec(new Runnable() {
@Override
public void run() {
// Do something in the background
}
});
Or to run in main thread:
view.postDelayed(new Runnable() {
public void run() {
// Stuff to do after 500mS delay
}
},500);
AsyncTask
=====
For background processing that needs to interact with the user interface
AsyncTask is started by calling execute() on an instance of the class
Operation of AsyncTask
1. doInBackground(Params...): The main method for the background thread
2. onProgressUpdate(Progress...): Runs on UI thread – In response to call to publishProgress()from the background thread
3. onPostExecute(Result): Runs on UI thread – Result is the return value from doInBackground()
An instance of AsyncTask can only be started (execute() called) once, construct a new one to run again
Example AsyncTask:
class UpdateServerTask extends AsyncTask<String, Integer, Double> {
// Background processing (runs in the background thread)
protected Double doInBackground(String... params) {
return updateServer(urls[0]);
}
// Runs on the UI thread (parameter is result of doInBackground)
protected void onPostExecute(Double result) {
mTextViewResult.setText("Done: Result=" + result
}
// Runs on UI thread in response to call to publishProgress()
protected void onProgressUpdate(Integer... progress) {
mProgressBar.setProgress(progress[0]);
}
private Double updateServer(String url){
while(stuffToDo){
result = doWork(url);
publishProgress(percentComplete);
}
return result;
}
}
Services
=====
Perform long-running background tasks
Even when an application’s activities are paused or destroyed
But poor service design can still lead to ANR errors
Can be bound to an activity to create a view
Run in the main thread
Subclass android.app.Service and create code to
Start and stop the service
Manage background threading
Subclass android.app.IntentService
Android manages service start/stop and creates a thread for each request
Logging
=====
Android has a logging tech similar to log4j
Held in memory
LogCat displayes logs during development
Log.v (verbose), Log.d (debug), Log.i (information), Log.w (warning), Log.e (error)
Define a tag in the class to use with log lines
private static final String TAG="IntentService"
Toasts
=====
Invoke a short lived pop-up message:
static Toast makeText(Context context, int resId, int duration)
static Toast makeText(Context context, CharSequence text, int duration)
Status Bar
=====
Invoked by activities and services, clicking on expanded notification can launch activity
Example:
int icon = R.drawable.emo_im_yelling;
CharSequence notiTickerText = "NOTIFICATION! – Ticker Text";
long notiTime = System.currentTimeMillis();
CharSequence notiTitle = "A NOTIFICATION!";
CharSequence notiContent ="I'm trying to tell you something!";
Intent notificationIntent = new Intent(context, NotificationActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
Notification.Builder notiBuilder = new Notification.Builder(context);
notiBuilder.setSmallIcon(icon).setTicker(notiTickerText).setWhen(notiTime)
.setAutoCancel(true).setContentText(notiContent).setTitleText(notiTitle).setContentIntent(pendingIntent);
Notification notification = notiBuilder.build();
String ns = Context.NOTIFICATION_SERVICE;
NotificationManager mNotificationManager = (NotificationManager) getSystemService(ns);
mNotificationManager.notify(SAMPLE_NOTIFICATION, notification);
Event Handling
=====
Common events:
Click - User clicks (touches widget or presses an “enter” key)
Long click - Same inputs as click, but held for one second
Focus change - User navigates onto or away from View item
Key - User presses a key while view has focus
Touch - Any touch event
Views have associated listener methods for the different events:
Click - View.OnClickListener
Long click - View.OnLongClickListener
Focus change - View.OnFocusChangeListener
Key - View.OnKeyListener
Touch - View.OnTouchListener
Example:
View but = findViewById(R.id.name);
but.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
Dialogs
=====
Pop-up window to which the user must respond
Implemented by DialogFragment
Example:
public void onClick(View v) {
DatePickerFragment newFragment = new DatePickerFragment();
mExpenseDate = Calendar.getInstance();
Bundle args = new Bundle();
args.putSerializable(DatePickerFragment.INPUT_DATE_MS, mExpenseDate);
newFragment.setArguments(args);
newFragment.show(getActivity().getSupportFragmentManager(), "datePicker");
}
public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
public static final String INPUT_DATE_MS = "Input Date in Millis";
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Calendar c = (Calendar)
getArguments().getSerializable(INPUT_DATE_MS);
return new DatePickerDialog(getActivity(), this, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
}
public void onDateSet(DatePicker view, int y, int m, int d) {
final Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, y);
c.set(Calendar.MONTH, m);
c.set(Calendar.DAY_OF_MONTH, d);
updateDate(c);
}
}
Menus
=====
Options Menu: When the menu button is pressed
Context Menus: Long click (like a right click)
Defined in res/menu
e.g. res/menu/menu.xml is identified by R.menu.menu
Example:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_add" android:title="@string/menu_str_add" />
<item android:id="@+id/menu_sync" android:title="@string/menu_str_sync" android:icon="@drawable/ic_menu_sync" />
</menu>
In activity, override:
public boolean onCreateOptionsMenu(Menu menu)
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
onCreateContextMenu() is only called if registerForContextMenu(View view) has previously been called
Example Inflation:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.name, menu);
return true;
}
Example Options menu handling:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
//...
return true;
case R.id.menu_add:
//...
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Action Bar
=====
Displays option menu items at top of screen
Options menu items may be displayed as actions
<menu …>
<item … android:icon="@drawable/ic_menu_add" android:showAsAction="ifRoom|withText" />
Action bar is configured in the manifest
Automatically enabled if targetSdkVersion is >= 11
May be disabled by setting the theme to a non-holographic theme
Adaptors and AdaptorViews
=====
Android provides many AdapterView subclasses
Each provides a different way of visually organizing items
Examples:
ListView - Displays items in a vertically scrolling list, uses ListAdapter
GridView - A two-dimensional scrolling grid view, uses ListAdapter
Spinner - Expanding list view; displays single selected item until activated, uses Adapter
Gallery - Horizontally scrolling list, typically of images, uses Adapter
The items are Views returned from the Adapter
Example (Using a list and ArrayAdapter):
#Main Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.stations_list);
#Layout Res File
<LinearLayout …
<ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listView" android:layout_weight="1" />
</LinearLayout>
#Layout For Items Res File (Coult use R.layout defaults instead):
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text1" android:layout_width="match_parent" android:layout_height="match_parent" />
#List Activity
ArrayList<String> theArrayList = getListData();
ArrayAdapter<String> theListAdapter = new ArrayAdapter<String>(this, R.layout.list_item, theArrayList);
#Main Activity
ListView mList;
mList = (ListView) findViewById(R.id.listView);
mList.setAdapter(stationListAdapter);
Example (Using TextViews and SimpleCursorAdapter):
#R.layout.two_item_list
<LinearLayout …>
<TextView android:id="@+id/text1" …/>
<TextView android:id="@+id/text2" …/>
</LinearLayout>
#ShowDatabaseInListActivity.java
protected void onResume() {
String[] projection = new String[] { "<columnName>", "<columnName>", "<columnName>" };
Cursor cursor = qb.query(mDb, projection, null, null, null, null, null);
String[] fromFields = new String[] { "<columnName>", "<columnName>" };
int[] toViews = new int[] { R.id.text1, R.id.text2 };
SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(this,
R.layout.two_item_list,
cursor,
fromFields, toViews);
setListAdapter(cursorAdapter);
}
JUnit
=====
Android uses extentions of JUnit to allow for context / lifecycle based unit testing
ActivityTestCase, ProviderTestCase, ServiceTestCase, etc
Otherwise the same as in Java, test classes extend AndroidTestCase
How to create on list item click listener:
public void onItemClick(AdapterView<?> parent, View, int position, long id)
parent - The AdapterView where the click happened
view - The view that was clicked within the ListView
position - The position of the view in the list
id - The row ID of the item that was clicked
Use l.getItemAtPosition(position); to get item clicked
Example:
public class TheActivity extends Activity implements AdapterView.OnItemClickListener
protected void onCreate(Bundle savedInstanceState) {
mList = (ListView) findViewById(R.id.listView);
mList.setOnItemClickListener(this);
}
public void onItemClick(AdapterView<?> theList,
View view, int position, long id) {
String station = (String)theList.getItemAtPosition(position);
}
...
Changes to the Adapter data cascade generally, can be forced with notifyDataSetChanged()
Form Widgets (Views)
=====
Fetch with ID
<Type> name = (<Type>) findViewById(R.id.<id>);
Set content:
name.setText(<CharSequence>);
Fetch content:
CharSequence name = name.getText();
Content Providers
=====
All types of storage can be accessed using a content provider
Storage - Shared Preferences
=====
For simple application preferences
Restricted to primitives and strings
Deleted when application is removed
Saved into memory private to the application /data/data/<packageName>
Access:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
<Type> name = prefs.get<Type>("<identifer>", false); #Where type is one of boolean, int, float, long, String
Edit:
SharedPreferences.Editor editor = prefs.edit();
editor.put<Type>("<identifer>", <value>); #Where type is one of boolean, int, float, long, String
commit();
Storage - Internal
=====
Android supports the standard Java API for file I/O with some additions specific to Android’s storage mechanisms
Read:
FileInputStream openFileInput (String name)
Write:
FileOutputStream openFileOutput (String name, int mode)
Storage - External
=====
Accessed much like internal, but must check for state:
String state = Environment.getExternalStorageState(); #Want Environment.MEDIA_MOUNTED or Environment.MEDIA_MOUNTED_READ_ONLY
Can have private or shared storage on external
Private locations:
File getExternalFilesDir(String type) #From Context
Shared locations:
File getExternalStoragePublicDirectory(String type) #From Environment
Use File.mkdirs() to create directories
Storage - SQLite
=====
Is self-contained, Is serverless, Requires zero configuration, Is transactional, Implements most of SQL92, Is written in C/C++
Private to the application, unless shared with content providers
android.database.sqlite.SQLiteDatabase
Example class with onCreate implementation (called when DB does not exist):
class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_CREATE = "<SQL>";
public void onCreate(SQLiteDatabase db) {
db.execSQL(DB_CREATE); //Does not return a result
}
Methods for insert, update, delete, query - use ContentValues to add key / value pairs
nullableColumnName in some methods requires name of a column which can contain NULL value - this protects against a SQLite limitiation (https://stackoverflow.com/questions/2662927/android-sqlite-nullcolumnhack-parameter-in-insert-replace-methods)
Example Insert:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("<columnName>", "<value>");
long rowId = db.insert("<Tablename>", "<nullableColumnName>", values);
Example Update:
String where = "<columnName> = ?";
String[] selectionArgs = {"<values>"};
int count = db.update("<tableName>", values, where, selectionArgs);
Example Select:
SQLiteDatabase db = dbHelper.getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables("<table>");
String where = "<columnName> = ?";
String[] selectionArgs = {Long.toString(id)};
Cursor c = qb.query(
db,
new String[] {"<columnToReturnInOrder>", "<columnToReturnInOrder>"},
where,
selectionArgs,
"<groupBy>",
"<having>",
"<sort>"
);
while (c.moveToNext()) {
Log.i(TAG, "<columnNameOne>=" + c.get<Type>(0));
Log.i(TAG, "<columnNameOne>=" + c.get<Type>(1));
}
Networking
=====
Android provides good support for networked applications
java.net.*: basic Java networking support
android.net.*: additional network functionality for the Android platform
NetworkInfo: support for querying the network status
Wi-Fi or mobile network connection
Is the device roaming?
Etc.
Additional support for SSL in the mobile environment
URL sanitizers
Etc.
Can use sockets, basic http or web service clients
For maximum efficiency
Use the java.net.HttpURLConnection class for HTTP communication
To keep the application responsive
Connections must be performed in a background thread
Use AsyncTask or implement your own threading
Network connection made on the main thread will cause exceptions in API 11+
Simple HTTP example:
private String fetchHTML(URL serviceUrl) throws Exception {
String result = "Nothing received! ";
HttpURLConnection urlConnection = (HttpURLConnection)serviceUrl.openConnection();
try {
int responseCode = urlConnection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
result = readStringFromStream(urlConnection.getInputStream());
} else {
result = readStringFromStream(urlConnection.getErrorStream());
}
} catch (Exception e) {
Log.e(TAG, "Connection failed " + e.getMessage());
} finally {
urlConnection.disconnect();
}
return result;
}
Use getOutputStream to write a post request (as Bytes)
To build from stream:
private String readStringFromStream(InputStream in) {
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in));) {
String nextLine = "";
while ((nextLine = reader.readLine()) != null) {
sb.append(nextLine);
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
To create JSON to send to a web service, use JSONArray for arrays:
JSONObject jObj = new JSONObject();
jObj.put("name", value);
jObj.put("name", value);
jObj.put("name", value);
String json = jObj.toString();
To get data from JSON response example:
JSONTokener tokenizer = new JSONTokener(jsonString);
JSONObject wrapper = (JSONObject)tokenizer.nextValue();
JSONArray things = wrapper.getJSONArray("thing");
for (int i = 0; i < things.length(); i++) {
Log.i(TAG, "Description = " + things.getJSONObject(i).getString("name"));
}
Drag and Drop
=====
Android drag-and-drop has four stages in its life cycle
Start: The drag has been started
Continuing: Events created as potential targets are dragged over
Dropped: Notification that the item has been dropped
Ended: The drag has stopped—either dropped or released
Example Listener:
protected class TrashDragEventListener implements OnDragListener {
public boolean onDrag(View v, DragEvent event) {
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED: // Drag has started
case DragEvent.ACTION_DRAG_ENTERED: // Drag is over this view
case DragEvent.ACTION_DRAG_EXITED: // Drag has left this view
case DragEvent.ACTION_DRAG_ENDED: // Drag finished
case DragEvent.ACTION_DROP: // Dropped in this view
return false; // true to accept the event
}
}
Can access passed data:
ClipData data = event.getClipData();
ClipData.Item uriClipItem = data.getItemAt(0);
Set Listener with:
view.setOnDragListener(new View.OnDragListener()…
To start the drag:
public boolean onItemLongClick(AdapterView<?> av, View v, int pos, long id) {
...
Uri exampleUri = ContentUris.withAppendedId(intent.getData(), id);
ClipData data = ClipData.newUri(getActivity().getContentResolver(), "Drag data", exampleUri);
v.startDrag(data, new View.DragShadowBuilder(v), null, 0);
return true;
}
Geolocation
=====
Android provides several mechanisms for determining location
Global Positioning System
Cell-based—position based on the cell to which you are connected
Wi-Fi—position based on Google’s database of Wi-Fi names
Example to get last known location (Using GPS):
LocationManager locMan = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
Location lastKnownLocation = locMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (null != lastKnownLocation) {
Log.i(TAG,"Location. Lat: " + lastKnownLocation.getLatitude() + "lon" + lastKnownLocation.getLongitude());
}
To get update events, register a LocationListener:
locMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, new LocationListener() {
public void onLocationChanged(Location location) {
storePosition(location);
}
...
});
Google Maps can be integrated easily using Android Studio (which automatically insert permissions into the manifest and map fragment)
Keys are required for dev and live for google maps
There are methods to add markers and navigate to given locations
Example:
private void setUpMap() {
Location location = (Location) getIntent().getParcelableExtra("FIX");
LatLng pos = new LatLng(location.getLatitude(),
location.getLongitude());
CameraPosition cameraPosition = new CameraPosition.Builder().target(pos).zoom(9).build();
CameraUpdate camUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition);
mMap.moveCamera(camUpdate);
mMap.animateCamera(camUpdate);
MarkerOptions marker = new MarkerOptions().position(pos).title("Location").draggable(false);
mMap.addMarker(marker);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment