sábado, 10 de setembro de 2011

Android Best Practices: StrictMode

Android Best Practices: StrictMode:

A recent update to the Android platform added a new class called StrictMode (android.os.StrictMode). This class can be used to enable and enforce various policies that can be checked for and reported upon. These policies generally include best-practice type coding issues, such as monitoring for actions done on the main thread that shouldn’t be and other naughty or lazy coding practices.


StrictMode has various policies. Each policy has various rules. Each policy also has various methods of showing when a rule is violated. We’ll define these first and then give a quick example of how they are used.


StrictMode Policies, Rules, and Penalties


Currently, there are two categories of policies available for use. One is the thread policy and the other is the VM (virtual machine, not to be confused with virtual memory) policy. The thread policy can monitor for:



  • Disk Reads

  • Disk Writes

  • Network access

  • Custom Slow Code


The first three items on this list are relatively self explanatory in how they are triggered. The fourth is triggered simply by a call you can make to the class. You’d do this from your own code that is known to be slow. The detection of policy violations happens when the calls are made on the main thread. For example, you might trigger the “slow code” violation each and every time your application downloads and parses a large amount of data.


The VM policy can monitor for the following issues:



  • Leaked Activity objects

  • Leaked SQLite objects

  • Leaked Closable objects


While the first two items are self explanatory, the third is a little less so. The leaked closable objects checker monitors for objects that should be closed, via a call to close() or the likes, before they are finalized.


Each policy also has a variety of different means for letting you know when a rule has been violated. Violations can be written to LogCat (useful for those slow code examples), stored in the DropBox (android.os.DropBox) service, or crash the application. In addition, thread policy violations can flash the screen background or show a dialog. All of these methods can be used to help you zero in and eradicate these application flaws.


Step 1: Enabling StrictMode


To enable and configure StrictMode in your application, you’ll want to use the StrictMode methods of setThreadPolicy() and setVmPolicy() as early in your application lifecycle as possible. When it comes to the Thread policy, which Thread it’s launched on matters, too (it watches for errors only on that thread, usually the main thread). A good place to set policies is at the entry points to your application and activities. For instance, in a simple application, you may need to just put the code in the onCreate() method of the launch Activity class.


The following code enables all rules on both current policies. A dialog is displayed whenever a Thread policy rule is violated.


StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.penaltyDialog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
.penaltyLog()
.build());

You should not leave this code enabled in a production application. It is designed for pre-production use only. Instead, a flag can be used to conditionally turn on StrictMode or off.


Step 2: Running With StrictMode


An application in StrictMode behaves no differently than normal. Simply run through your application and test as you normally would. Performing an exhaustive test script that thoroughly exercises your application and touches the entire codebase with StrictMode enabled will likely provide you with the most useful results.


Here is an example of the LogCat output when we run a version of the TutList application with StrictMode enabled (all policies, all rules):


09-04 16:15:34.592: DEBUG/StrictMode(15883): StrictMode policy violation; ~duration=319 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1041)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteStatement.acquireAndLock(SQLiteStatement.java:219)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:83)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1829)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1780)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at com.mamlambo.tutorial.tutlist.data.TutListProvider.update(TutListProvider.java:188)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.content.ContentProvider$Transport.update(ContentProvider.java:233)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.content.ContentResolver.update(ContentResolver.java:847)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at com.mamlambo.tutorial.tutlist.data.TutListProvider.markItemRead(TutListProvider.java:229)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at com.mamlambo.tutorial.tutlist.TutListFragment.onListItemClick(TutListFragment.java:99)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.support.v4.app.ListFragment$2.onItemClick(ListFragment.java:53)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.widget.AdapterView.performItemClick(AdapterView.java:282)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.widget.AbsListView.performItemClick(AbsListView.java:1037)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.widget.AbsListView$PerformClick.run(AbsListView.java:2449)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.widget.AbsListView$1.run(AbsListView.java:3073)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.os.Handler.handleCallback(Handler.java:587)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.os.Handler.dispatchMessage(Handler.java:92)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.os.Looper.loop(Looper.java:132)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at android.app.ActivityThread.main(ActivityThread.java:4123)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at java.lang.reflect.Method.invokeNative(Native Method)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at java.lang.reflect.Method.invoke(Method.java:491)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
09-04 16:15:34.592: DEBUG/StrictMode(15883): at dalvik.system.NativeStart.main(Native Method)

You can see this for yourself by adding the StrictMode code to Version Code 13 of TutList. Simply select a different item from the list.


What does this log tell us? The act of simply flagging a message as read should have been done off the main thread. Instead, it’s eating up a third of a second! Not only is that surprisingly slow, the effect on the UI is noticeable.


And here is what the warning dialog looks like:



Screen showing TutList with StrictMode violation

Step 3: Ignoring Some Policy Violations


Most warnings generated by StrictMode should be followed up on, but not all of the warnings generated mean that something is wrong with your code. There are plenty of situations where, for instance, you know a quick read from disk on the main thread won’t hinder the application noticeably. Or maybe you have some other debugging code that violates the rules that won’t be enabled in a production build.


The first way to ignore the policy violations is to document them for yourself or your team and list them as known violations. The second way is to explicitly add code to stop checking for a particular rule violation just before the offending code is executed and then re-enable detection for that rule after the offending code has completed. For instance:


StrictMode.ThreadPolicy old = StrictMode.getThreadPolicy();
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(old)
.permitDiskWrites()
.build());
doCorrectStuffThatWritesToDisk();
StrictMode.setThreadPolicy(old);

This code saves off the current policy, creates a similar policy that ignores the disk writes violation, runs the code that does some disk writes, and then restores the original policy.


Conclusion


StrictMode is a useful class in the arsenal of diagnostic tools available to Android developers. Through it, developers can find and fix subtle performance issues, object leaks, and other hard-to-find runtime issues. Are you using StrictMode? Are there other policies you’d like to see added to this feature of the Android SDK?


About the Authors


Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development, Second Edition and Sams Teach Yourself Android Application Development in 24 Hours, Second Edition. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to androidwirelessdev+mt@gmail.com, via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.

Need More Help Writing Android Apps? Check out our Latest Books and Resources!


Buy Android Wireless Application Development, 2nd Edition Buy Sam's Teach Yourself Android Application Development in 24 Hours, Second Edition Mamlambo code at Code Canyon

Nenhum comentário:

Minha lista de blogs