NullPointerException when linking to Service that uses ContentProvider

Posted by Danny Chia on Stack Overflow See other posts from Stack Overflow or by Danny Chia
Published on 2012-03-22T23:19:55Z Indexed on 2012/03/22 23:29 UTC
Read the original article Hit count: 361

Filed under:
|

H.i everyone, this is my first post here!

Anyways, I'm trying to write a "todo list" application. It stores the data in a ContentProvider, which is accessed via a Service. However, my app crashes at launch. My code is below:

Manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.examples.todolist"
  android:versionCode="1"
  android:versionName="1.0">
  <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="True">
    <activity 
      android:name=".ToDoList"
      android:label="@string/app_name"
      android:theme="@style/ToDoTheme">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
     <service android:name="TodoService"/>
     <provider android:name="TodoProvider"
          android:authorities="com.examples.provider.todolist" />
  </application>
  <uses-sdk android:minSdkVersion="7" />
</manifest> 

ToDoList.java:

package com.examples.todolist;

import com.examples.todolist.TodoService.LocalBinder;

import java.util.ArrayList;
import java.util.Date;

import android.app.Activity;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.ContextMenu;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class ToDoList extends Activity {

  static final private int ADD_NEW_TODO = Menu.FIRST;
  static final private int REMOVE_TODO = Menu.FIRST + 1;

  private static final String TEXT_ENTRY_KEY = "TEXT_ENTRY_KEY";
  private static final String ADDING_ITEM_KEY = "ADDING_ITEM_KEY";
  private static final String SELECTED_INDEX_KEY = "SELECTED_INDEX_KEY";

  private boolean addingNew = false;
  private ArrayList<ToDoItem> todoItems;
  private ListView myListView;
  private EditText myEditText;
  private ToDoItemAdapter aa;
  int entries = 0;
  int notifs = 0;

  //ToDoDBAdapter toDoDBAdapter;
  Cursor toDoListCursor;


  TodoService mService;
  boolean mBound = false;

  /** Called when the activity is first created. */
  public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);


    myListView = (ListView)findViewById(R.id.myListView);
    myEditText = (EditText)findViewById(R.id.myEditText);

    todoItems = new ArrayList<ToDoItem>();
    int resID = R.layout.todolist_item;
    aa = new ToDoItemAdapter(this, resID, todoItems);
    myListView.setAdapter(aa);

    myEditText.setOnKeyListener(new OnKeyListener() {
      public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN)
          if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            ToDoItem newItem = new ToDoItem(myEditText.getText().toString(), 0);
            mService.insertTask(newItem);
            updateArray();
            myEditText.setText("");
            entries++;
            Toast.makeText(ToDoList.this, "Entry added", Toast.LENGTH_SHORT).show();
            aa.notifyDataSetChanged();
            cancelAdd();
            return true; 
          }
        return false;
      }
    });

    registerForContextMenu(myListView);
    restoreUIState();
    populateTodoList();
  }

  private void populateTodoList() {
    // Get all the todo list items from the database.
    toDoListCursor = mService. getAllToDoItemsCursor();
    startManagingCursor(toDoListCursor);

    // Update the array.
    updateArray();
    Toast.makeText(this, "Todo list retrieved", Toast.LENGTH_SHORT).show();

  }

  private void updateArray() {
      toDoListCursor.requery();

      todoItems.clear();

      if (toDoListCursor.moveToFirst())
        do { 
          String task = toDoListCursor.getString(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_TASK));
          long created = toDoListCursor.getLong(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_CREATION_DATE));
          int taskid = toDoListCursor.getInt(toDoListCursor.getColumnIndex(ToDoDBAdapter.KEY_ID));
          ToDoItem newItem = new ToDoItem(task, new Date(created), taskid);
          todoItems.add(0, newItem);
        } while(toDoListCursor.moveToNext());

      aa.notifyDataSetChanged();
    }

  private void restoreUIState() {
    // Get the activity preferences object.
    SharedPreferences settings = getPreferences(0);

    // Read the UI state values, specifying default values.
    String text = settings.getString(TEXT_ENTRY_KEY, "");
    Boolean adding = settings.getBoolean(ADDING_ITEM_KEY, false);

    // Restore the UI to the previous state.
    if (adding) {
      addNewItem();
      myEditText.setText(text);
    }
  }

  @Override
  public void onSaveInstanceState(Bundle outState) {
    outState.putInt(SELECTED_INDEX_KEY, myListView.getSelectedItemPosition());

    super.onSaveInstanceState(outState);
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    int pos = -1;

    if (savedInstanceState != null)
      if (savedInstanceState.containsKey(SELECTED_INDEX_KEY))
        pos = savedInstanceState.getInt(SELECTED_INDEX_KEY, -1);

    myListView.setSelection(pos);
  }

  @Override
  protected void onPause() {
    super.onPause();

    // Get the activity preferences object.
    SharedPreferences uiState = getPreferences(0);
    // Get the preferences editor.
    SharedPreferences.Editor editor = uiState.edit();

    // Add the UI state preference values.
    editor.putString(TEXT_ENTRY_KEY, myEditText.getText().toString());
    editor.putBoolean(ADDING_ITEM_KEY, addingNew);
    // Commit the preferences.
    editor.commit();
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);

    // Create and add new menu items.
    MenuItem itemAdd = menu.add(0, ADD_NEW_TODO, Menu.NONE,
                                R.string.add_new);
    MenuItem itemRem = menu.add(0, REMOVE_TODO, Menu.NONE,
                                R.string.remove);

    // Assign icons
    itemAdd.setIcon(R.drawable.add_new_item);
    itemRem.setIcon(R.drawable.remove_item);

    // Allocate shortcuts to each of them.
    itemAdd.setShortcut('0', 'a');
    itemRem.setShortcut('1', 'r');

    return true;
  }

  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    super.onPrepareOptionsMenu(menu);

    int idx = myListView.getSelectedItemPosition();

    String removeTitle = getString(addingNew ? 
                                   R.string.cancel : R.string.remove);

    MenuItem removeItem = menu.findItem(REMOVE_TODO);
    removeItem.setTitle(removeTitle);
    removeItem.setVisible(addingNew || idx > -1);

    return true;
  }

  @Override
  public void onCreateContextMenu(ContextMenu menu, 
                                  View v, 
                                  ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);

    menu.setHeaderTitle("Selected To Do Item");
    menu.add(0, REMOVE_TODO, Menu.NONE, R.string.remove);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    super.onOptionsItemSelected(item);

    int index = myListView.getSelectedItemPosition();

    switch (item.getItemId()) {
      case (REMOVE_TODO): {
        if (addingNew) {
          cancelAdd();
        } 
        else {
          removeItem(index);
        }
        return true;
      }
      case (ADD_NEW_TODO): { 
        addNewItem();
        return true; 
      }
    }

    return false;
  }

  @Override
  public boolean onContextItemSelected(MenuItem item) {  
    super.onContextItemSelected(item);
    switch (item.getItemId()) {
      case (REMOVE_TODO): {
        AdapterView.AdapterContextMenuInfo menuInfo;
        menuInfo =(AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
        int index = menuInfo.position;

        removeItem(index);
        return true;
      }
    }
    return false;
  }

  @Override
  public void onDestroy() {
    super.onDestroy();

  }

  private void cancelAdd() {
    addingNew = false;
    myEditText.setVisibility(View.GONE);
  }

  private void addNewItem() {
    addingNew = true;
    myEditText.setVisibility(View.VISIBLE);
    myEditText.requestFocus(); 
  }

  private void removeItem(int _index) {
    // Items are added to the listview in reverse order, so invert the index.
    //toDoDBAdapter.removeTask(todoItems.size()-_index);
     ToDoItem item = todoItems.get(_index);
     final long selectedId = item.getTaskId();
     mService.removeTask(selectedId);
     entries--;
    Toast.makeText(this, "Entry deleted", Toast.LENGTH_SHORT).show();
     updateArray();
  }

  @Override
  protected void onStart() {
      super.onStart();

      Intent intent = new Intent(this, TodoService.class);
      bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

  }

  @Override
  protected void onStop() {
      super.onStop();
      // Unbind from the service
      if (mBound) {
          unbindService(mConnection);
          mBound = false;
      }
  }

  private ServiceConnection mConnection = new ServiceConnection() {

      public void onServiceConnected(ComponentName className, IBinder service) {
          LocalBinder binder = (LocalBinder) service;
          mService = binder.getService();
          mBound = true;
      }

      public void onServiceDisconnected(ComponentName arg0) {
          mBound = false;
      }
  };

  public class TimedToast extends AsyncTask<Long, Integer, Integer> {

    @Override
    protected Integer doInBackground(Long... arg0) {
        if (notifs < 15) {
            try {
                Toast.makeText(ToDoList.this, entries + " entries left", Toast.LENGTH_SHORT).show();
                notifs++;
                Thread.sleep(20000);
            }
            catch (InterruptedException e) {
            }
        }
        return 0;
    }
  }

}

TodoService.java:

package com.examples.todolist;

import android.app.Service;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.os.Binder;
import android.os.IBinder;

public class TodoService extends Service {

    private final IBinder mBinder = new LocalBinder();

    @Override
    public IBinder onBind(Intent arg0) {
        return mBinder;
    }

    public class LocalBinder extends Binder {
        TodoService getService() {
            return TodoService.this;
        }
    }

    public void insertTask(ToDoItem _task) {
        ContentResolver cr = getContentResolver();
        ContentValues values = new ContentValues();    
        values.put(TodoProvider.KEY_CREATION_DATE, _task.getCreated().getTime());
        values.put(TodoProvider.KEY_TASK, _task.getTask());
        cr.insert(TodoProvider.CONTENT_URI, values);
    }

    public void updateTask(ToDoItem _task) {
        long tid = _task.getTaskId();
        ContentResolver cr = getContentResolver();
        ContentValues values = new ContentValues(); 
        values.put(TodoProvider.KEY_TASK, _task.getTask());
        cr.update(TodoProvider.CONTENT_URI, values, TodoProvider.KEY_ID + "=" + tid, null);
    }

    public void removeTask(long tid) {
        ContentResolver cr = getContentResolver();
        cr.delete(TodoProvider.CONTENT_URI, TodoProvider.KEY_ID + "=" + tid, null);
    }

    public Cursor getAllToDoItemsCursor() {
        ContentResolver cr = getContentResolver();
        return cr.query(TodoProvider.CONTENT_URI, null, null, null, null);
      }

}

TodoProvider.java:

package com.examples.todolist;

import android.content.*;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class TodoProvider extends ContentProvider {

  public static final Uri CONTENT_URI = Uri.parse("content://com.examples.provider.todolist/todo");

  @Override
  public boolean onCreate() {
    Context context = getContext();

    todoHelper dbHelper = new todoHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
    todoDB = dbHelper.getWritableDatabase();
    return (todoDB == null) ? false : true;
  }

  @Override
  public Cursor query(Uri uri, String[] projection, 
       String selection, String[] selectionArgs, String sort) {

    SQLiteQueryBuilder tb = new SQLiteQueryBuilder();

    tb.setTables(TODO_TABLE);

    // If this is a row query, limit the result set to the passed in row. 
    switch (uriMatcher.match(uri)) {
      case TASK_ID:
          tb.appendWhere(KEY_ID + "=" + uri.getPathSegments().get(1));
          break;
      default:
          break;
    }

    // If no sort order is specified sort by date / time
    String orderBy;
    if (TextUtils.isEmpty(sort)) {
      orderBy = KEY_ID;
    } else {
      orderBy = sort;
    }

    // Apply the query to the underlying database.
    Cursor c = tb.query(todoDB, projection, selection, selectionArgs, null, null, orderBy);

    // Register the contexts ContentResolver to be notified if
    // the cursor result set changes. 
    c.setNotificationUri(getContext().getContentResolver(), uri);

    // Return a cursor to the query result.
    return c;
  }

  @Override
  public Uri insert(Uri _uri, ContentValues _initialValues) {
    // Insert the new row, will return the row number if 
    // successful.
    long rowID = todoDB.insert(TODO_TABLE, "task", _initialValues);

    // Return a URI to the newly inserted row on success.
    if (rowID > 0) {
      Uri uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
      getContext().getContentResolver().notifyChange(uri, null);
      return uri;
    }
    throw new SQLException("Failed to insert row into " + _uri);
  }

  @Override
  public int delete(Uri uri, String where, String[] whereArgs) {
    int count;

    switch (uriMatcher.match(uri)) {
      case TASKS:
        count = todoDB.delete(TODO_TABLE, where, whereArgs);
        break;

      case TASK_ID:
        String segment = uri.getPathSegments().get(1);
        count = todoDB.delete(TODO_TABLE, KEY_ID + "=" + segment
            + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
        break;

      default: throw new IllegalArgumentException("Unsupported URI: " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);
    return count;
  }

  @Override
  public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
    int count;
    switch (uriMatcher.match(uri)) {
      case TASKS: count = todoDB.update(TODO_TABLE, values, 
                                               where, whereArgs);
                   break;

      case TASK_ID: String segment = uri.getPathSegments().get(1);
                     count = todoDB.update(TODO_TABLE, values, KEY_ID 
                             + "=" + segment + (!TextUtils.isEmpty(where) ? " AND (" 
                             + where + ')' : ""), whereArgs);
                     break;

      default: throw new IllegalArgumentException("Unknown URI " + uri);
    }

    getContext().getContentResolver().notifyChange(uri, null);
    return count;
  }

  @Override
  public String getType(Uri uri) {
    switch (uriMatcher.match(uri)) {
      case TASKS: return "vnd.android.cursor.dir/vnd.examples.task";
      case TASK_ID: return "vnd.android.cursor.item/vnd.examples.task";
      default: throw new IllegalArgumentException("Unsupported URI: " + uri);
    }
  }

  // Create the constants used to differentiate between the different URI 
  // requests.
  private static final int TASKS = 1;
  private static final int TASK_ID = 2;

  private static final UriMatcher uriMatcher;

  // Allocate the UriMatcher object, where a URI ending in 'tasks' will
  // correspond to a request for all tasks, and 'tasks' with a 
  // trailing '/[rowID]' will represent a single task row.
  static {
   uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
   uriMatcher.addURI("com.examples.provider.Todolist", "tasks", TASKS);
   uriMatcher.addURI("com.examples.provider.Todolist", "tasks/#", TASK_ID);
  }

  //The underlying database
  private SQLiteDatabase todoDB;

  private static final String TAG = "TodoProvider";
  private static final String DATABASE_NAME = "todolist.db";
  private static final int DATABASE_VERSION = 1;
  private static final String TODO_TABLE = "todolist";

  // Column Names
  public static final String KEY_ID = "_id";
  public static final String KEY_TASK = "task";
  public static final String KEY_CREATION_DATE = "date";

  public long insertTask(ToDoItem _task) {
        // Create a new row of values to insert.
        ContentValues newTaskValues = new ContentValues();
        // Assign values for each row.
        newTaskValues.put(KEY_TASK, _task.getTask());
        newTaskValues.put(KEY_CREATION_DATE, _task.getCreated().getTime());
        // Insert the row.
        return todoDB.insert(TODO_TABLE, null, newTaskValues);
      }

  public boolean updateTask(long _rowIndex, String _task) {
        ContentValues newValue = new ContentValues();
        newValue.put(KEY_TASK, _task);
        return todoDB.update(TODO_TABLE, newValue, KEY_ID + "=" + _rowIndex, null) > 0;
      }

  public boolean removeTask(long _rowIndex) {
        return todoDB.delete(TODO_TABLE, KEY_ID + "=" + _rowIndex, null) > 0;
      }

  // Helper class for opening, creating, and managing database version control
  private static class todoHelper extends SQLiteOpenHelper {
    private static final String DATABASE_CREATE =
      "create table " + TODO_TABLE + " (" 
      + KEY_ID + " integer primary key autoincrement, "
      + KEY_TASK + " TEXT, "
      + KEY_CREATION_DATE + " INTEGER);";

    public todoHelper(Context cn, String name, CursorFactory cf, int ver) {
      super(cn, name, cf, ver);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
      db.execSQL(DATABASE_CREATE);           
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                  + newVersion + ", which will destroy all old data");

      db.execSQL("DROP TABLE IF EXISTS " + TODO_TABLE);
      onCreate(db);
    }
  }
}

I've omitted the other files as I'm sure they are correct. When I run the program, LogCat shows that the NullPointerException occurs in populateTodoList(), at toDoListCursor = mService.getAllToDoItemsCursor(). mService is the Cursor object returned by TodoService. I've added the service to the Manifest file, but I still cannot find out why it's causing an exception.

Thanks in advance.

© Stack Overflow or respective owner

Related posts about android

Related posts about android-service