Upload
alfredo-morresi
View
3.053
Download
2
Embed Size (px)
DESCRIPTION
During the session, I'll explore lot of useful tricks I use during my everyday life as Android developer. Testing, background tasks, smartphone and tablet optimization, avoid memory leaks, UI view management, good external libraries to take into account, mobile design patterns and some best practices I found.
Citation preview
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 1
Who I am
Born in the past millenium and still an happy software developer
Mobile addictedAndroid enthusiastic
Free software fanProud rider
… and Tiramisu' lover
rainbowbreeze
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 2
Overview
Online learnig resources
Paintless rotation with threads
Easy IoC/DI framework
Testing, from unit to automation
Honeycomb and backward compatibility
Libraries and frameworks
Q&A session
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 3
Chocolate Interaction model
Don't be scare: interact, collaborate, share your fun!
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 4
Useful online material – Patterns
Designing and Implementing Android UIshttp://www.google.com/events/io/2011/sessions/designing-and-implementing-android-uis-
for-phones-and-tablets.html http://code.google.com/p/iosched/
Advanced Topics for Expert Android App Devshttp://www.google.com/events/io/2011/sessions/android-protips-advanced-topics-for-expert-
android-app-developers.html
Developing Android REST client applicationshttp://www.google.com/events/io/2010/sessions/developing-RESTful-android-apps.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 5
Useful online material - ListView
The world of ListViewhttp://www.google.com/events/io/2010/sessions/world-of-listview-android.html
Lazy-loading of remote thumbnailshttp://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
Drag and Drop capable ListView https://github.com/commonsguy/cwac-touchlist
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 6
Background thread, progress bar and rotation
Your application have to be smooth, don't use UI thread for long operation (> 200ms)
Yuo cannot update UI Thread from external threads
Use Activity.runOnUiThread(Runnable), View.post(Runnable), View.postDelayed(Runnable,
long), Handler to bypass the problem
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 7
Background thread, progress bar and rotation
public void onClick(View v) { //background thread new Thread(new Runnable() { public void run() { final Bitmap b = loadImageFromNetwork(); //update ui thread mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(b); } }); } }).start();}
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 8
Background thread, progress bar and rotation
The simplest way
AsyncTaskCan access to UI Thread from separate thread
http://android-developers.blogspot.com/2009/05/painless-threading.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 9
Background thread, progress bar and rotation
protected void onCreate(Bundle savedInstanceState) { ... mLblResult = (TextView) findViewById(R.id.lblResult); LongTask longTask = new LongTask(); longTask.execute(); …}
private class LongTask extends AsyncTask<Void, Void, String> ... protected void onPostExecute(String result) { mLblResult.setText(result); }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 10
Background thread, progress bar and rotation
But if the screen is rotated while the backgroud thread is still in execution?
UI not updated, dialogs disappear, NullPointerException, memory leaks...
OMG!
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 11
Background thread, progress bar and rotation
The clean way
- Define an Handler inside the Activity that process thread tasks (update/result)
- Create background thread passing the handler- Call the handler from the thread when a progress must be
published or the thread finish- Update activity reference inside thread using OnPause()
and OnResume()- Pay attention to OnStart and other Activity lifecycle events
http://code.google.com/p/rainbowlibs/source/browse/android/trunk/rainbowlibs/src/it/rainbowbreeze/libs/logic/RainbowBaseBackgroundThread.java
http://code.google.com/p/rainbowlibs/source/browse/android/trunk/rainbowlibs/src/it/rainbowbreeze/libs/ui/RainbowSplashScreenActivity.java
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 12
Background thread, progress bar and rotation
public class WebcamActivity extends Activity() {
private Handler mActivityHandler = new Handler() {
public void handleMessage(Message msg) { Log.debug("Message from external thread" + msg.what); switch (msg.what) { case RotationThread.OPERATION_STARTS: //create progress dialog + other UI operations break; case RotationThread.OPERATION_COMPLETED:
//remove the dialog + additional logic break; case RotationThread.OPERATION_ERROR: //remove the dialog + additional error logic
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 13
Background thread, progress bar and rotation
public class WebcamActivity extends Activity() {
private RotatingThread mRotatingThread;
private void showWebcam() { //show a progress dialog showDialog(DIALOG_PREPARE_FOR_FULLSCREEN);
mRotatingThread = new RotatingThread( mActivityHandler,
webcamToShowId); mRotatingThread.start(); }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 14
Background thread, progress bar and rotation
public class RotatingThread() extends Thread { private WeakReference<Handler> mCallerHandler;
public RotatingThread(Handler handler, int webcamId) { registerCallerHandler(handler); … }
public void registerCallerHandler(Handler newHandler { mCallerHandler = new WeakReference<Handler>(newHandler); }
public void unregisterCallerHandler() { mCallerHandler = null; }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 15
Background thread, progress bar and rotation
public class RotatingThread() extends Thread { ...
public void run() { callHandlerAndRetry(OPERATION_STARTS);
//your long operation here
callHandlerAndRetry(OPERATION_COMPLETED); }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 16
Background thread, progress bar and rotation
protected void callHandlerAndRetry(int messageCode) { for (int retries = 0; retries < TOTAL_RETRIES; retries++) {
if (null != mCallerHandler && null != mCallerHandler.get()) { Message message = mCallerHandler.get().obtainMessage(messageCode); message.arg1 = arg1; mCallerHandler.get().sendMessage(message); break; }
try { //what some times, maybe next time activity is ready Thread.sleep(INTERVAL_BETWEEN_RETRIES); } catch (InterruptedException ignoreExcepition) {} }}
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 17
Background thread, progress bar and rotation
public class WebcamActivity extends Activity() {
@Override protected void onPause() { mRotatingThread.unregisterCallerHandler(); super.onPause(); }
@Override protected void onResume() { mRotatingThread.registerCallerHandler(mActivityHandler); super.onResume(); }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 18
Background thread, progress bar and rotation
public class WebcamActivity extends Activity() {
@Override public Object onRetainNonConfigurationInstance() { return mRotatingThread; }
@Override protected void onStart() { super.onStart(); mRotatingThread = (RotatinThread)getLastNonConfigurationInstance(); //nothing saved, first run of the activity if (null == mRotatingThread) showWebcam(); }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 19
Background thread, progress bar and rotation
The short (but no totally complete) way
Use a Fragment and setRetainInstance(true)http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/
app/FragmentRetainInstance.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 20
Lazy loading singleton
Singleton PatternEnsure that only one instance of a class is created and provide a
global point of access to the object.
Create your own singleton or extend android.app.Application and modify its onCreate()
method.Base class for those who need to maintain global application state. You can provide your own implementation by specifying its name in
your AndroidManifest.xml’s <application> tag, which will cause that class to be instantiated for you when the process for your
application/package is created.
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 21
Lazy loading singleton
public class App extends Application { public static int myGlobalStaticValue; public int myGlobalValue;
@Override public void onCreate() { super.onCreate(); myGlobalStaticValue = 10; myGlobalValue = 20; }
<application android:name="it.rainbowbreeze.singleton.App">
int value = App.myGlobalStaticValue;int value = ((App)context.getApplication()).myGlobalValue;
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 22
Lazy loading singleton
DrawbacksSingleton is a pattern or an anti-pattern?
http://stackoverflow.com/questions/3826905/singletons-vs-application-context-in-android
OS may kill your application process, including the Application subclass instance. As a result the state is lost. When you later return to the application, then the OS will restore its activity
stack and Application subclass instance, but its fields will be null.
If you really need singleton, at least use lazy-loading singleton and avoid static fields to store
application lifecycle data
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 23
Lazy loading singleton
public class MySingleton() { private MySingleton mInstance;
public synchronized MySingleton getInstance(Context c) { if (null == mInstance) { mInstance = new MySingleton(context); } return mInstance; }
private MySingleton(Context appContext) { //initializes the object //reload SharedPreferences data }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 24
Inversion of Control / Dependency Injection
Dependency Injection PatternThe object does not need to know in advance about how the
other part of the system works. Instead, the programmer provides (injects) the relevant system component in advance
along with a contract that it will behave in a certain way.http://en.wikipedia.org/wiki/Dependency_injection
Reduce the coupling among software components: better code testability, useful in libraries, clean
code.
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 25
Inversion of Control / Dependency Injectionpublic class ItemDao implements ItemDao {
public ItemDao(Context appContext) { //performs initialization }}
public DataManager implements IDataManager { INetManager mNetManager; IItemDao mItemDao;
public DataManager(INetManager netMngr, IItemDao itemDao) { mNetManager = netMngr; mItemDao = itemDao; }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 26
Inversion of Control / Dependency Injection
DrawbacksComplex and less readable code
Chain of dependenciesMore code to write
SolutionsIoc/DI frameworks manage all the boring things!
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 27
Inversion of Control / Dependency Injection
public class AppEnv() { public static AppEnv i(Context appContext) { … mInstance = new AppEnv(appContext); return mInstance; }
private AppEnv(Context appContext) { mItemDao = new ItemDao(appContext); mDataManager = new DataManager(mNetMngr, mItemDao); }
ItemsDao mItemDao; public ItemsDao getItemDao(){ return mItemDao; }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 28
Inversion of Control / Dependency Injection
DrawbacksCannot inject mock for testiong purposes
SolutionsSeparate the object factory from the singleton
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 29
Inversion of Control / Dependency Injection
public class ObjFactory {
public ItemDao createDao(Context appContext) { return new ItemDao(appContext); }
public DataManager createDataManager( INetManager netMngr, ItemDao itemDao) { return new DataManager(netManager, itemDao); }
}
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 30
Inversion of Control / Dependency Injection
public class AppEnv() { private static ObjFactory mObjFactory = new ObjFactory();
public static AppEnv i(Context c) { … mInstance = new AppEnv(c, mObjFactory); return mInstance; }
public static AppEnv i(Context c, ObjFactory customFactory) { … mInstance = new AppEnv(c, customFactory); return mInstance; }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 31
Inversion of Control / Dependency Injection
public class AppEnv() { …
private AppEnv(Context appContext, ObjFactory objFactory) { mDao = objFactory.createDao(appContext); //... other initialization mDataManager = objFactory.createDataManager( mNetMngr, mDao); }
private ItemsDao mDao; public ItemsDao getItemDao(){ return mDao; }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 32
Inversion of Control / Dependency Injection
--- inside your activity / component ---AppEnv appEnv = AppEnv.i(getContext());ItemDao dao = appEnv.getItemDao();
--- inside your test class ---MockObjFactory mockFactory = new MockObjFactory() { @Overrides public ItemDao createDao(Context appContext) { return new MockItemDao(appContext); }};AppEnv appEnv = AppEnv.i(getContext(), mockFactory);ItemDao dao = appEnv.getItemDao();
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 33
Inversion of Control / Dependency Injection
RoboGuiceLike Google Guice, but for Android
http://code.google.com/p/roboguice/http://stackoverflow.com/questions/5067681/guice-performance-on-android
Other solutionshttp://stackoverflow.com/questions/1029696/the-hunt-for-the-j2me-friendly-ioc-container-is-on
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 34
Testing - basic
Software testing = reduce risks of software implementation
Lot of support Android sidehttp://developer.android.com/guide/topics/testing/testing_android.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 35
Testing - first steps
Create new “Android Test Project”Create a class that extends TestCase
Code first test
Red, Green, Refactor!
http://developer.android.com/resources/tutorials/testing/helloandroid_test.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 36
Testing - first steps
public class MyFirstTest extends TextCase() { Private MyStringConverter mConverter;
public void setUp() { mConverter = new MyStringConverter(); }
public testUpperCaseConversion() { String result = mConverter.toUppercase(“ota2011”); assertEquals(“Wrong value”, “OTA2011”, result); }}
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 37
Testing with special Android Mock
AndroidTestCaseaccess to a context
http://developer.android.com/reference/android/test/AndroidTestCase.html
ApplicationTestCasetest app lifecycle, inject mock context
http://developer.android.com/reference/android/test/ApplicationTestCase.html
Activity Testing APItest activity lifecycle, inject mocks, send key or touch events, etc
http://developer.android.com/resources/tutorials/testing/activity_test.html
http://developer.android.com/guide/topics/testing/activity_testing.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 38
Testing with special Android Mock
public class SpinnerActivityTest extends ActivityInstrumentationTestCase2<SpinnerActivity> {
protected void setUp() throws Exception { … mActivity = getActivity(); mSpinner = (Spinner) mActivity.findViewById(R.id.Spinner01); … } public void testSpinnerUI() { this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); mPos = mSpinner.getSelectedItemPosition(); mSelection = (String)mSpinner.getItemAtPosition(mPos); assertEquals(resultText, mSelection);
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 39
Testing with special Android Mock
ProviderTestCase2test contentProvicer with isolated context
http://developer.android.com/guide/topics/testing/contentprovider_testing.html
ServiceTestCasetest the service lifecycle, not the logic (detached)http://developer.android.com/guide/topics/testing/service_testing.html
ViewAsserts, MoreAssertsextended assertsextended asserts
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 40
Test automation with monkey
adb shell monkey -p your.package.name -v 500sends sequences of random events
http://developer.android.com/guide/developing/tools/monkey.html
monkeyrunner A Python program that runs an application, sends keystrokes to it,
takes screenshots of its user interface, and stores themhttp://developer.android.com/guide/developing/tools/monkeyrunner_concepts.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 41
Testing with external tools
RobotiumAutomatic black-box test cases for Android applications, with
gesture and actionshttp://code.google.com/p/robotium/
RobolectricFast and easy TDD, runs tests in normal JavaVM with mock
Android classes, no need to deploy on devicehttp://pivotal.github.com/robolectric/
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 42
Honeycomb and Backward Compatibility
Nowadays Android has two separate branches, but Ice Cream Sandwich will merge them
Avoid different versions of same app and prefer one adaptive Use alternative resources
AndroidManifest.xml filters for sdk, screen, hwPut some dirty if in code
http://code.google.com/p/iosched/
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 43
Honeycomb and Backward Compatibility
Resources
res/layout/main_activity.xml # For phonesres/layout-xlarge/main_activity.xml # Large screenres/layout-sw600dp/main_activity.xml # For 7” tabletsres/layout-sw720dp/main_activity.xml # For 10” tablets… and much more!
http://android-developers.blogspot.com/2011/07/new-tools-for-managing-screen-sizes.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 44
Honeycomb and Backward Compatibility
<!-- Tablet-only application --><manifest ... > <supports-screens android:smallScreens="false" android:normalScreens="false" android:largeScreens="false" android:xlargeScreens="true" android:requiresSmallestWidthDp="600" /> <application ... > ... </application></manifest>
http://android-developers.blogspot.com/2011/09/preparing-for-handsets.html http://android-developers.blogspot.com/2011/07/new-mode-for-apps-on-large-
screens.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 45
Testing with special Android Mock
Dirty if
private static boolean fragmentsSupported = false;
private static void checkFragmentsSupported()throws NoClassDefFoundError { fragmentsSupported = android.app.Fragment.class != null;}
static { try { checkFragmentsSupported(); } catch (NoClassDefFoundError e) { fragmentsSupported = false; }
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 46
Testing with special Android Mock@Overridepublic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
Intent intent = null; if (!fragmentsSupported) intent = new Intent(this, MainNonFragmentActivity.class); else intent = new Intent(this, MainFragmentActivity.class);
startActivity(intent); finish(); }}
http://blog.radioactiveyak.com/2011/02/strategies-for-honeycomb-and-backwards.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 47
Honeycomb and Backward Compatibility
Android Compatibility PackageBackport some 3.x APIs to 1.6 and 2.x
http://developer.android.com/sdk/compatibility-library.html
Support for Fragment, Loader, ViewPager
Native code on 3.x, library code on 2.x
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 48
Honeycomb and Backward Compatibility
Fragment
http://android-developers.blogspot.com/2011/03/fragments-for-all.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 49
Honeycomb and Backward Compatibility
setRetainInstance(true)activity is rotated but fragment is not destroyed and
recreated, lifecycle events are called.Finally!
Bug: http://code.google.com/p/android/issues/detail?id=17423Bug: http://stackoverflow.com/questions/6250580/fragment-already-added-
illegalstateexception
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 50
Honeycomb Backward Compatibility
ActionBarSherlockLibrary that uses native code for 3.x or its own code for 2.x
http://actionbarsherlock.com/
GreenDroidBrand new actionbar implementation
http://android.cyrilmottier.com/?p=274
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 51
Useful libraries and resources
Android UI patterns blogshttp://www.androiduipatterns.com http://www.androidpatterns.com
Android-ui-utilsAsset Studio, Pencil stencils, icon templates
http://code.google.com/p/android-ui-utils/
Additional Eclipse plugins for Androidsorce code, api level analysishttp://code.google.com/p/adt-addons/
Maven Android pluginhttp://code.google.com/p/maven-android-plugin/
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 52
Useful libraries and resources
Kernel sourceto learn, to get inspired and to solve bugs!
http://grepcode.com/snapshot/repository.grepcode.com/java/ext/com.google.android/android/2.3.4_r1/
Clean code in AndroidIoC, Binding, Annotation etc
http://blog.springsource.com/2011/08/26/clean-code-with-android
Crash reporthttp://androidblogger.blogspot.com/2010/03/crash-reporter-for-android-slight.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 53
Useful libraries and resources
Use Apache HTTP library instead of Java.NetHttps bugs, bufferization problems, etchttp://code.google.com/p/android/issues/detail?id=3164
http://groups.google.com/group/android-developers/msg/4ddd2e502f195e3a http://stackoverflow.com/questions/2105364/
Be part of communitiesmailing list, StackOveflow, ...
http://developer.android.com/resources/community-groups.html
Android Survival GuideAlfredo Morresi (http://www.rainbowbreeze.it) Slide 54
Conclusion
Q & A(example: “Isn't it lunch time yet?”)