Thursday, June 20, 2013

Android: Solution for NetworkOnMainThreadException


NetworkOnMainThreadException
Occurs in Runtime Exception
The exception that is thrown when an application attempts to perform a networking operation on its main thread. (NetworkOnMainThreadException)

android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork
StrictMode : StrictMode is most commonly used to catch accidental disk or network access on the application's main thread. (StrictMode.ThreadPolicy.Builder)
From above statement, we can conclude there is some network operation is performed in Main Therad.
Exception:
android.os.NetworkOnMainThreadException
android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1117)
libcore.io.BlockGuardOs.connect(BlockGuardOs.java:84)
libcore.io.IoBridge.connectErrno(IoBridge.java:127)
libcore.io.IoBridge.connect(IoBridge.java:112)
java.net.PlainSocketImpl.connect(PlainSocketImpl.java:192)
java.net.PlainSocketImpl.connect(PlainSocketImpl.java:459)
java.net.Socket.connect(Socket.java:842)
org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:119)
org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:144)
org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
.
.
.
android.view.View.performClick(View.java:4204)
android.view.View$PerformClick.run(View.java:17355)
android.os.Handler.handleCallback(Handler.java:725)
android.os.Handler.dispatchMessage(Handler.java:92)
android.os.Looper.loop(Looper.java:137)
android.app.ActivityThread.main(ActivityThread.java:5041)
java.lang.reflect.Method.invokeNative(Native Method)
java.lang.reflect.Method.invoke(Method.java:511)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
dalvik.system.NativeStart.main(Native Method)

Solutions:
Remove Network operation from main thread and use background operations like Services, Async Task
Or
StrictMode implemented from api level 9, so your min android sdk support should be 9

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

Tuesday, June 11, 2013

Android: Unit Test for Custom Content Provider using Roboelectric



Robolectric provides a simple approach to automate unit test case for android by using standard JUnit tests. It runs on any JVM, without needing the emulator or a device.
Advantages of Roboelectric:
No Mocking Frameworks Required

Run Tests Outside of the Emulator

Test Annotations

@RunWith

To run your test with Robolectric, you need to tell JUnit using the @RunWith annotation on your test class:
@RunWith (RobolectricTestRunner.class) // required for Robolectric!
For more details: http://pivotal.github.io/robolectric/user-guide.html
With Robolectric, we can write a small unit test for custom content provide like this.

@RunWith(RobolectricTestRunner.class)
public class BookContentProviderTest {

 private Activity activity;
 private BookProvider bookProvider;
 private ContentResolver contentResolver;

 public static final String AUTHORITY = "com.test.provider.Book";

 public static final String BOOK_TABLE_NAME = "book";

 /**
  * The columns we are interested in from the database
  */
 public static final String[] BOOK_PROJECTION = new String[] {
   BookColumns._ID, // 0
   BookColumns.BOOK_NAME, // 1
 };

 public static final Uri BOOK_URI = Uri.parse("content://" + AUTHORITY + "/"
   + BOOK_TABLE_NAME);

 private String[] bookList = new String[] { "angels and demons", "Book1",
   "Book2", "Book3" };

 @Before
 public void setUp() throws Exception {
  bookProvider = new BookProvider();
  activity = new Activity();
  contentResolver = activity.getContentResolver();

  bookProvider.onCreate();
  ShadowContentResolver.registerProvider(AUTHORITY, bookProvider);
 }

 @Test
 public void testContentProvider() {

  insertBook(new Book("53bfc0b980b0", "angels and demons"));
  insertBook(new Book("53bfc0b980b3", "Book1"));
  insertBook(new Book("53bfc0b980b1", "Book2"));
  insertBook(new Book("53bfc0b980b2", "Book3"));

  Cursor cursor = contentResolver.query(BOOK_URI, BOOK_PROJECTION, null,
    null, BookColumns.DEFAULT_SORT_ORDER);

  cursor.moveToFirst();

  Assert.assertTrue(cursor.getCount() == 4);

  if (cursor.getCount() != 0) {
   do {
    Assert.assertEquals(bookList[cursor.getPosition()],
      cursor.getString(cursor
        .getColumnIndex(BookColumns.BOOK_NAME)));
   } while (cursor.moveToNext());
  }

 }

 private void insertBook(Book book) {
  contentResolver.insert(BOOK_URI, getContentValue(book));
 }

 private ContentValues getContentValue(Book book) {
  ContentValues values;
  values = new ContentValues();
  values.put(BookColumns._ID, book.getBookId());
  values.put(BookColumns.BOOK_NAME, book.getName());
  return values;
 }
}