1

Normally, in the activity, I use SQLiteOpenHelper like this

DatabaseHandler mDbHelper = new DatabaseHandler(getBaseContext());

Now, I want to use SQLite in service. However, when I use the above code in the method of the Service class, it causes NullPointerException error.

This is my Service class

    public class MyService extends Service{     
        public void LoadDB(){
            DatabaseHandler mDbHelper = new DatabaseHandler(getBaseContext());
            ArrayList <MyPosition> positionlist = mDbHelper.getAllPositions();   **//error here**   
        }
        private final IBinder mBinder = new LocalBinder();
        public class LocalBinder extends Binder {
            MyService getService() {
                // Return this instance of LocalService so clients can call public methods
                return MyService.this;
            }
        }

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

This is the code that I call Service in MainActivity class

        public class MainActivity extends Activity {
            MyService mService;
            boolean mBound = false;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            activity  = this;       

            mService = new MyService();
            doBindService();
            if (mBound){
                mService.LoadDB();
            }
         }

      private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName className, IBinder service) {
                  LocalBinder binder = (LocalBinder) service;
                  mService = binder.getService();
                  mBound = true;
            }

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

            void doBindService() {
                bindService(new Intent(MainActivity.this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
                mBound = true;
            }

            void doUnbindService() {
                if (mBound) {
                    // Detach our existing connection.
                    unbindService(mConnection);
                    mBound = false;
                }
            }
        }
}

My DatabaseHandler class

package com.example.example10;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class DatabaseHandler extends SQLiteOpenHelper{
    // All Static variables
    // Database Version
    private static final int DATABASE_VERSION = 1;

    // Database Name
    private static final String DATABASE_NAME = "ListPosition";

    // Position table name
    private static final String TABLE_POSITIONS = "positions";

    // Position Table Columns names
    public static final String KEY_ROWID = "_id";
    private static final String KEY_ADDRESS = "address";
    private static final String KEY_LONG = "longPos";
    private static final String KEY_LAT = "latPos";
    private static final String KEY_LASTVISITED = "lastVisited";
    private static final String KEY_IMAGE = "image";

    public DatabaseHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // Creating Tables
    @Override
    public void onCreate(SQLiteDatabase db) {
        String CREATE_POSITIONS_TABLE = "CREATE TABLE " + TABLE_POSITIONS + "(" + KEY_ROWID 
                + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KEY_ADDRESS + " TEXT," 
                + KEY_LONG + " REAL, " + KEY_LAT + " REAL, " + KEY_LASTVISITED + " TEXT, " 
                + KEY_IMAGE + " BLOB" +")";
        db.execSQL(CREATE_POSITIONS_TABLE);
    }

    // Upgrading database
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // Drop older table if existed
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_POSITIONS);

        // Create tables again
        onCreate(db);
    }

    /**
     * All CRUD(Create, Read, Update, Delete) Operations
     */

    // Adding new position
    void addPosition(MyPosition position) {
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(KEY_ADDRESS, position.GetAddress()); 
        values.put(KEY_LONG, position.GetLongPos()); 
        values.put(KEY_LAT, position.GetLatPos());
        values.put(KEY_LASTVISITED, position.GetLastVisited()); 
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        position.GetImage().compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] imageInByte = stream.toByteArray();
        values.put(KEY_IMAGE, imageInByte); 

        // Inserting Row
        db.insert(TABLE_POSITIONS, null, values);
        db.close(); // Closing database connection
    }

    // Getting All Positions
    public ArrayList <MyPosition> getAllPositions() {
        ArrayList <MyPosition> position_list = new ArrayList <MyPosition>();
        // Select All Query
        String selectQuery = "SELECT * FROM " + TABLE_POSITIONS;

        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor = db.rawQuery(selectQuery, null);

        // looping through all rows and adding to list
        if (cursor.moveToFirst()) {
            do {
                MyPosition position = new MyPosition();
                position.SetAddress(cursor.getString(1));
                position.SetLongPos(cursor.getFloat(2));
                position.SetLatPos(cursor.getFloat(3));
                position.SetLastVisited(cursor.getString(4));
                byte[] imagebyte = cursor.getBlob(5);
                Bitmap image = BitmapFactory.decodeByteArray(imagebyte, 0, imagebyte.length);
                position.SetImage(image);

                // Adding position to list
                position_list.add(position);
            } while (cursor.moveToNext());
        }

        // return position list
        return position_list;
    }

    // Updating single position
    public int updatePosition(MyPosition position, int index) {
        SQLiteDatabase db = this.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put(KEY_ADDRESS, position.GetAddress()); 
        values.put(KEY_LONG, position.GetLongPos()); 
        values.put(KEY_LAT, position.GetLatPos());
        values.put(KEY_LASTVISITED, position.GetLastVisited()); 
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        position.GetImage().compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] imageInByte = stream.toByteArray();
        values.put(KEY_IMAGE, imageInByte); 

        // updating row
        return db.update(TABLE_POSITIONS, values, KEY_ROWID + "=" + (index + 1), null);
    }

    // Deleting single position
    public void deletePosition(MyPosition position, int index) {
        SQLiteDatabase db = this.getWritableDatabase();
        db.delete(TABLE_POSITIONS, KEY_ROWID + "=" + (index + 1), null);
        db.close();
    }


    // Getting position Count
    public int getPositionCount() {
        String countQuery = "SELECT * FROM " + TABLE_POSITIONS;
        SQLiteDatabase db = this.getReadableDatabase();
        Cursor cursor = db.rawQuery(countQuery, null);
        int count = cursor.getCount();
        cursor.close();

        // return count
        return count;
    }

    // Getting single position
    MyPosition getPosition(int index) {
        SQLiteDatabase db = this.getReadableDatabase(); 
        Cursor cursor = db.query(TABLE_POSITIONS, new String[] { KEY_ADDRESS, KEY_LONG, KEY_LAT,  KEY_LASTVISITED, KEY_IMAGE}, 
                KEY_ROWID + "=?", new String[] { String.valueOf(index) }, null, null, null, null);
        if (cursor != null)
            cursor.moveToFirst();

        Bitmap image = BitmapFactory.decodeByteArray(cursor.getBlob(5), 0, cursor.getBlob(5).length);
        MyPosition position = new MyPosition(cursor.getString(1), cursor.getDouble(2), cursor.getDouble(3), 
                                cursor.getString(4), image);
        // return contact
        return position;
    }

}

Do you have any suggestion for me? Thanks all.

lolyoshi
  • 1,536
  • 3
  • 24
  • 42

3 Answers3

4

You shouldn't be using getBaseContext() for your context (in your activities, or in this service).

Service extends (is a) Context so you can fix your code like this:

public class MyService extends Service{     
  public void LoadDB(){
    DatabaseHandler mDbHelper = new DatabaseHandler(this);  
  }...

Here is a good place to start learning about Context

Edit:

You shouldn't be using the Service() constructor. You should read the Service javadoc

To start a service from an Activity you create an intent, and then call startService() passing it in.

Intent i = new Intent(this, YourService.class);
startService(i);
Community
  • 1
  • 1
FoamyGuy
  • 46,603
  • 18
  • 125
  • 156
  • I tried with this but it still causes the NullPointerException E/AndroidRuntime(21752): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.example10/com.example.example10.MainActivity}: java.lang.NullPointerException – lolyoshi Aug 30 '13 at 02:27
  • You shouldn't be using the Service() constructor see my edit. – FoamyGuy Aug 30 '13 at 02:35
  • Still NullPointerException :( – lolyoshi Aug 30 '13 at 02:38
  • I posted my DatabaseHandler class above. The NullPointerException happened when I call the method in this class – lolyoshi Aug 30 '13 at 02:45
  • Instead of `this` use `getApplicationContext()` using `this` can introduce memory leaks. Also unless all you are doing in reading, you're going to introduce database locking issues in your app, you should change your `SQLLiteHelper`to be a singleton. – Ali Aug 30 '13 at 02:55
  • YOu can make the read write process as serialized procedures by opening the db table only necessary for write / read Or you can try to make it as a intent Service – Jeff Bootsholz Aug 30 '13 at 03:00
  • @Ali The code with SQLite is ok if I call it in the Activity class, but when I put it in the Service class, it caused error. I tried getApplicationContext() but it still causes the nullpointerexception – lolyoshi Aug 30 '13 at 03:04
  • @RajuGujarati Can you explain for me more? – lolyoshi Aug 30 '13 at 03:05
0

You can't call serive.loadDB from your activity... not unless it's a static call and even then it's wrong. Services in android run at a driver level and applications cannot interact with them directly. The only way to interact with them is through AIDL(there is one other way actually but I forget what it is).

If you need your activity to tell your service when you load the DB use AIDL.

Regards,

Ali
  • 12,354
  • 9
  • 54
  • 83
  • So you meant I have to use AIDL to load data in service? Besides, when I created the AIDL file, it has this error interface IAdditionService { void LoadDB(); void DeletePosition(MyPosition position, int index); **unknown type MyPosition } How can I solve it? – lolyoshi Aug 30 '13 at 03:36
  • No, the purpose of AIDL is to send messages to a service. You will send a message to your service to load the data when you want it to load the data. However if you want to load the data when the service starts or is created, then that is different then just do it in your services `onCreate()` or `onStart()` method and make sure to call `onStart()` from `onStartCommand()` if you use `onStart()`. Remember to launch your service from the activity with `startService(new Intent(this, MyService.class));`. Remember that services run in the main thread. – Ali Aug 30 '13 at 05:04
  • okay I think I figured out what you are trying to do, you want to define and consume a local service. There is an example for it [here](http://www.vogella.com/articles/AndroidServices/article.html#exercise_bindlocalservice) have you tried using this code? – Ali Aug 30 '13 at 05:15
  • I'm trying to do this. I have an application which stores the data in SQLite. If I use only SQLite, it's ok, but when I try to load/update/delete data by calling a service (then import to database). I'm stuck. Do you have any suggestion for me? – lolyoshi Aug 30 '13 at 07:57
  • Well... Try doing what I mentioned earlier. Change the SQLiteHelper to a singleton and don't call close on the db. Just get a writable db from the singleton and use it for all your operations. – Ali Aug 30 '13 at 11:32
  • @Ali `"...and applications cannot interact with them directly. The only way to interact with them is through AIDL`" this is not accurate. You can use [ServiceConnection](https://developer.android.com/reference/android/content/ServiceConnection.html) and Binders to call methods on your service from an Activity – FoamyGuy Aug 30 '13 at 13:35
  • @FoamyGuy yeah I didn't realize he was trying to use a local service. I've personally never had to! I did point him to a working example of a LocalService. – Ali Sep 01 '13 at 08:28
  • @Ali so which way is the simplest in my case? In addition, I'm a girl (she not he) :) – lolyoshi Sep 02 '13 at 03:40
  • @FoamyGuy I'm trying to use Binders as you mentioned – lolyoshi Sep 02 '13 at 03:41
0

After reading the answers to this question I thought this wouldn't be possible to do but I managed to call methods from my SQliteOpenHelper class (DatabaseHelper) without the app crashing when I added the onCreate() method.

public class YourService  extends Service {

    private DatabaseHelper dbHeper;
    private User user;
    private String userName;

    @Override
    public void onCreate() {
        dbHeper = new DatabaseHelper(getApplicationContext());
        user = dbHeper.getUser(1);
        userName = user.getUserName();
        System.out.println("User Name: " + userToken);
    }

}

I hope this helps!

Mark O'Sullivan
  • 10,138
  • 6
  • 39
  • 60