theedge 2010: android advanced techniques
DESCRIPTION
TheEdge 2010:The Android platform has matured rapidly during the last two years.We're going to bring you up to speed in the latest cutting edge Android development techniques including: New UI Patterns, supporting multiple screen resolutions, REST clients, Push applications and optimizing your applications.TRANSCRIPT
Advanced Android TechniquesBy: Gilad Garon
2
» This presentation is based onTheEdge 2010 Android Application: Download it from the Market Download the source code:
http://code.google.com/p/theedge2010/for all the examples in this presentation
Before we begin!
TheEdge 2010 Android App
3
» UI Design Patterns» I/O Access» Optimizations
Agenda
UI Design Patterns
5
» Activity» Layouts» Intent
A little reminder about:
UI Design Patterns
6
» Dashboard» Action Bar» Search Bar» Quick Actions» Flingable List Item
“Applications are ahead of the framework”
UI Design Patterns
7
DashboardYou know what they say about First Impressions...
UI Design Patterns
Dashboard
8
» Introduce your application» Reveal its functionality» Update the user» Just a layout, nothing fancy
Dashboard
UI Design Patterns
9
Dashboard Components
UI Design Patterns
Action Bar
Updates Panel
Functionality Panel
10
Implementation
UI Design Patterns
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@color/background">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="45dip" android:background="@color/action_bar_background"> <!-- Action Bar Code --> </LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1"> <!-- Functionality Panel --> </LinearLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/now_playing" android:layout_width="fill_parent" android:layout_height="90dip" android:orientation="horizontal"> <!-- Update Panel --> </LinearLayout></LinearLayout>
11
Action Bar
UI Design Patterns
Action Bar
12
» Improved Title Bar» Persistent across the application» Includes common actions» Navigation Control
Action Bar
UI Design Patterns
13
Implementation
UI Design Patterns
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="45dip" android:background="@color/action_bar_background"> <ImageButton style="@style/action_bar_button" android:src="@drawable/home_btn_default" android:onClick="onHomeClick"/> <ImageView style="@style/action_bar_separator"/> <TextView style="@style/action_bar_text" android:text="Sessions"/> <ImageView style="@style/action_bar_separator"/> <ImageButton android:id="@+id/search_button" style="@style/action_bar_button" android:src="@drawable/search_button" android:onClick="onSearchClick"/></LinearLayout>
14
Search Bar
UI Design Patterns
Search Bar
15
» Implement search in your app» Persistent across the application» Can be used with the Action Bar» Multiple search modes» A clearer way to filter results
Search Bar
UI Design Patterns
16
» Declare your application as searchable» Declare an activity that handles the search» Handle the search itself
Implementation
UI Design Patterns
17
» Declare in /res/xml/searchable.xml
Searchable Configuration
UI Design Patterns
<?xml version="1.0" encoding="utf-8"?><searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/app_name" android:hint="Hint" ></searchable>
18
» Declare as Searchable in the AndroidManifest.xml:Searchable Activity
UI Design Patterns
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.alphacsp.theedge" android:versionCode="4" android:versionName="1.03" android:installLocation="preferExternal"> <application android:label="TheEdge 2010" android:icon="@drawable/ic_launcher"> <activity android:name=".ui.activities.SearchActivity" android:label="Search" android:theme="@style/Theme.TheEdge"> <intent-filter> <action android:name="android.intent.action.SEARCH"/> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/> </activity> <activity android:name=".ui.activities.AboutActivity" android:label="About“ android:theme="@style/Theme.TheEdge"/> <meta-data android:name="android.app.default_searchable" android:value=".ui.activities.SearchActivity"/> </manifest>
19
» Handle the Query:Searchable Activity
UI Design Patterns
public class SearchActivity extends TabActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.search_activity); Intent intent = getIntent(); String query = intent.getStringExtra(SearchManager.QUERY); final TabHost host = getTabHost(); Intent speakersIntent = new Intent(SearchActivity.this, SpeakersActivity.class); speakersIntent.putExtra(SearchManager.QUERY, query); host.addTab(host.newTabSpec("Speakers").setIndicator(buildIndicator("Speakers")).setContent(speakersIntent)); Intent sessionsIntent = new Intent(SearchActivity.this, SessionsActivity.class); sessionsIntent.putExtra(SearchManager.QUERY, query); host.addTab(host.newTabSpec("Sessions").setIndicator(buildIndicator("Sessions")).setContent(sessionsIntent)); }
20
Quick ActionsQuick context menu
UI Design Patterns
21
» Contextual actions driven popup menu» Natural flow to the screen context» Simple and effective» Wow effect
Quick Actions
UI Design Patterns
22
» Not in the SDK» Custom Views» Implementation taken from: Lorenz’s Blog
http://www.londatiga.net/it/how-to-create-quickaction-dialog-in-android/
Implementation
UI Design Patterns
23
Flingable List ItemFlingable context menu
UI Design Patterns
24
» Contextual actions driven flinged menu» Natural flow to the screen context» Wow effect
Flingable List Item
UI Design Patterns
25
» Major players: ViewFlipper Layout GestureDetector ListAdaptor ListActivity
Implementation
UI Design Patterns
26
» Vertical scrollable view that allows multiple rows views (List Item)
» Uses ListAdapter to populate each List Item» Recycles Views (So don’t cache them!)» Supports different view types
A few words about ListView
UI Design Patterns
27
» ListView should not hold N Views.» Use ConvertView in getView method:
ListView Recycler
UI Design Patterns
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.speakers_list, parent, false); } //Get your components from the view final TextView speakerName = (TextView) convertView.findViewById(R.id.speaker_name); //Get your item Speaker speaker = getItem(position); return convertView; }
28
ListView Recycler in action
UI Design Patterns
29
ViewFlipper
UI Design Patterns
<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/flipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<include android:id="@+id/first" layout="@layout/sessions_list"/>
<include android:id="@+id/second" layout="@layout/sessions_list_flinged"/>
</ViewFlipper>
» Allows transition between layouts» Supports animations
30
GestureDetector
UI Design Patterns
GestureDetector gestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { } @Override public boolean onDown(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { } });
» Detects various gestures and events using the supplied MotionEvents
31
ViewFlipp
er
• Handles layouts switches• Handles animations
ListAdapt
or
• Controls ViewFlipper• Handles gestures detection for the List Item• Handle Buttons presses from View Flipper
ListActivit
y
• Handles scrolling detection• Handles buttons presses
Implementation
UI Design Patterns
Read the Source Code…
32
I/O Access
33
» File System» Database» Network
Types of I/O:
I/O Access
34
» Reading is fast» Writing is slow » Disk space affects performance» Make sure you support External Storage
(Both in installation and in caches/data)
Flash I/O
I/O Access
35
» Virtual Table that allows Full Text Search» Produces results significantly faster than
LIKE» Comprehensive syntax
SQLite FTS
I/O Access
36
» Similar to creating a regular table:Creating FTS table
I/O Access
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "TheEdge";
public static final String SPEAKERS_TABLE = "speakers";
@Language("SQLite")
private static final String CREATE_SPEAKERS_TABLE = "CREATE VIRTUAL TABLE " + SPEAKERS_TABLE + " USING fts3 (speaker_name TEXT , bio TEXT, company TEXT, image_uri TEXT)";
public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
Log.i(this.getClass().getSimpleName(), "Creating DB");
sqLiteDatabase.execSQL(CREATE_SPEAKERS_TABLE);
}
37
» Use the MATCH keyword:Using:
I/O Access
public List<Session> searchSessions(String query) { final List<Session> sessionList = new ArrayList<Session>(); final SQLiteDatabase readableDatabase = getReadableDatabase(); final Cursor cursor = readableDatabase.rawQuery("select * from " + SESSIONS_TABLE + " where " + SESSIONS_TABLE + " match '" + query +
"'", null); if (cursor.moveToFirst()) { do { final Session session = new Session(); session.setTopic(cursor.getString(cursor.getColumnIndex("topic"))); session.setPresenter(cursor.getString(cursor.getColumnIndex("presenter"))); session.setSessionAbstract(cursor.getString(cursor.getColumnIndex("abstract"))); sessionList.add(session); } while (cursor.moveToNext()); } if (!cursor.isClosed()) { cursor.close(); } return sessionList;}
38
» Use a Service» Use Apache HTTPClient not URLConnection» Don’t do it on the UI thread» Parsing can be costly, design your data
structure carefully
Network
I/O Access
39
» Availability:Always check connectivity with ConnectivityManager.
» Performance:Don’t run on the UI Thread, use AsyncTask or Service.
» Bandwidth:Use as little as you can.
» Battery drain:Only use the network when you have to.
When using network, remember the following:
I/O Access
40
I/O Access
Activity.onCreate UI Thread
Fetch data from Database / Files
UI Thread
Populate Activity with persistent data
New Thread
Check network for updates (MD5
hash / last modified header)
New Thread
Fetch new data from network and
persist itUI
ThreadUpdate UI Thread with updated data.
Activity with separate thread for Network
41
Optimization
42
» Allows you to execute a task without having to manage thread pools.» Defined by three generic types: Parameters, Progress and Result.» Has a lifecycle:
onPreExecute – Called before running the task. doInBackground – Actual work onProgressUpdate – Callback to update the UI on task progress onPostExecute – Called after task has ended, receives the
response object» Very smart and easy.
AsyncTask
Optimization
43
» In order to implment lazy loading, we’ll need to do the following:1. Load the ListView an you would normally do.2. Create a task to fetch the data, use
AsyncTask or a Service.3. Change the data on the UI thread.4. Use notifyDataSetChanged() to let ListView
to update itself.
Lazy Loading your ListView
Optimization
44
Example
Optimization
public class SpeakersActivity extends ListActivity implements ServiceListener { private void fetchImages() { for (Speaker speaker : speakers) { SpeakerImageFetcher speakerImageFetcher = new SpeakerImageFetcher(); speakerImageFetcher.execute(speaker); } } private class SpeakerImageFetcher extends AsyncTask<Speaker, Void, Void> { @Override protected Void doInBackground(Speaker... speakers) { final Speaker speaker = speakers[0]; final Bitmap bitmap = dataAccessor.fetchImage(speaker.getImageUrl()); speaker.setSpeakerImage(bitmap); return null; } @Override protected void onPostExecute(Void aVoid) { speakersAdapter.notifyDataSetChanged(); } }}
45
» Runs in the background» Is not killed when the application ends» Running a service is easy» Getting callbacks is a bit awkward» Declare it in your AndroidManifest.xml
Services
Optimization
46
Services oriented application:
Optimization
Activity
Service Helper
Service
Data Accessor
SQLite HttpClient
47
Service Example:
Optimization
public class NetworkService extends IntentService { @Override protected void onHandleIntent(Intent intent) { final DataAccessor dataAccessor = DataAccessor.getSingleton(this); final ResultReceiver receiver = intent.getParcelableExtra(STATUS_LISTENER); final int requestAction = intent.getIntExtra(REFRESH_ACTION, -1); boolean result = false; if (requestAction == REFRESH_SPEAKERS) { result = dataAccessor.syncAllSpeakers(); } else if (requestAction == REFRESH_SESSIONS) { result = dataAccessor.syncAllSessions(); } else if (requestAction == REFRESH_SCHEDULE) { result = dataAccessor.syncAllEvents(); } else if (requestAction == REFRESH_ALL) { dataAccessor.syncAllSpeakers(); dataAccessor.syncAllSessions(); dataAccessor.syncAllEvents(); } if (result) { receiver.send(STATUS_REFRESHED, Bundle.EMPTY); } receiver.send(STATUS_NOT_REFRESHED, Bundle.EMPTY); }}
48
» ResultReceiver: Implements Parcelable, so can be passed
in the Intent object Good for “Here and now” results per
intent
Getting a Callback from a Service:
Optimization
49
» Accepts the Activity as the Listener using a custom interface
ResultReceiver Example
Optimization
public class NetworkServiceHelper extends ResultReceiver {
private final ServiceListener listener;
public NetworkServiceHelper(ServiceListener listener) { super(new Handler()); this.listener = listener; }
@Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (listener != null) { listener.onReceiveResult(resultCode, resultData); } }}
50
Optimization
public class ScheduleActivity extends Activity implements ServiceListener { private NetworkServiceHelper networkServiceHelper;
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.schedule_activity); dataAccessor = DataAccessor.getSingleton(this); initCalendar(); networkServiceHelper = new NetworkServiceHelper(this); final Intent serviceIntent = new Intent(Intent.ACTION_SYNC, null, this, NetworkService.class); serviceIntent.putExtra(NetworkService.STATUS_LISTENER, networkServiceHelper); serviceIntent.putExtra(NetworkService.REFRESH_ACTION, NetworkService.REFRESH_SCHEDULE); startService(serviceIntent); }
@Override public void onReceiveResult(int resultCode, Bundle resultData) { if (resultCode == NetworkService.STATUS_REFRESHED) { calendarLayout.removeAllViews(); initCalendar(); } }}
Using the service:
51
Reference
52
Reference
» http://developer.android.com/guide/index.html» http://www.londatiga.net/it/how-to-create-quickaction-dialog-i
n-android/
» http://code.google.com/p/theedge2010/» http://android-developers.blogspot.com/?hl=en» http://www.google.com/events/io/2010/sessions.html#Android» http://www.sqlite.org/fts3.html» http://www.sqlite.org/cvstrac/wiki/wiki?p=FtsUsage
53
Thank You!We appreciate your feedback