Search code examples
javaandroidnullpointerexceptionandroid-contentproviderandroid-context

ContentProvider mHelper stays null


in my code I have a my custom ContentProvider class that can be seen here:

public class MaiMobileProvider extends ContentProvider {


    protected MaiMobileDbHelper mHelper;
    protected UriMatcher mMatcher = buildUriMatcher();
    //region code return on UriMatcher
    public static final int GNR_CODE = 1;
    public static final int PSP_CODE = 2;
    public static final int SERVICES_CODE = 3;
    public static final int HIGHLIGHT_CODE = 4;

    //endregion


    static UriMatcher buildUriMatcher() {
        // 1) The code passed into the constructor represents the code to return for the root
        // URI.  It's common to use NO_MATCH as the code for this case. Add the constructor below.
        final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = MaiMobileContract.CONTENT_AUTHORITY;

        // 2) Use the addURI function to match each of the types.  Use the constants from
        // WeatherContract to help define the types to the UriMatcher.
        matcher.addURI(authority, MaiMobileContract.PATH_GNR, GNR_CODE);
        matcher.addURI(authority, MaiMobileContract.PATH_PSP, PSP_CODE);
        matcher.addURI(authority, MaiMobileContract.PATH_SERVICES, SERVICES_CODE);
        matcher.addURI(authority, MaiMobileContract.PATH_SERVICES, HIGHLIGHT_CODE);

        // 3) Return the new matcher!
        return matcher;
    }

    //region Selections
    //services.isHighlighted = 1
    private static final String sServicesIsHighlightedSelection =
            MaiMobileContract.ServicesEntry.TABLE_NAME +
                    "." + MaiMobileContract.ServicesEntry.COLUMN_IS_HIGHLIGHTED + " = 1 ";


    //endregion

    //region custom cursors


    protected Cursor getHighlightedServices(Uri uri, String[] projection, String sortOrder) {
        boolean isHighlighted = MaiMobileContract.ServicesEntry.isServiceHighlighted(uri);
        String selection = null;

        if (isHighlighted)
            selection = sServicesIsHighlightedSelection;


        return mHelper.getReadableDatabase().query(
                MaiMobileContract.ServicesEntry.TABLE_NAME,
                projection,
                selection,
                null,
                null,
                null,
                sortOrder);
    }


    //endregion

    @Override
    public boolean onCreate() {
        mHelper = new MaiMobileDbHelper(getContext());
        return (mHelper == null) ? false : true;
    }


    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Cursor mCursor;

        switch (mMatcher.match(uri)) {
            case GNR_CODE:
                mCursor = mHelper.getReadableDatabase().query(
                        MaiMobileContract.GnrEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder);
                break;
            case PSP_CODE:
                mCursor = mHelper.getReadableDatabase().query(
                        MaiMobileContract.PspEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder);
                break;
            case SERVICES_CODE:
                mCursor = mHelper.getReadableDatabase().query(
                        MaiMobileContract.ServicesEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder);
                break;
            case HIGHLIGHT_CODE:
                mCursor = getHighlightedServices(uri, projection, sortOrder);
                break;
            default:
                throw new UnsupportedOperationException("Unknow uri: " + uri);
        }
        mCursor.setNotificationUri(getContext().getContentResolver(), uri);
        return mCursor;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        final int match = mMatcher.match(uri);

        switch (match) {

            case GNR_CODE:
                return MaiMobileContract.GnrEntry.CONTENT_TYPE;
            case PSP_CODE:
                return MaiMobileContract.PspEntry.CONTENT_TYPE;
            case SERVICES_CODE:
                return MaiMobileContract.ServicesEntry.CONTENT_TYPE;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        final SQLiteDatabase db = mHelper.getWritableDatabase();
        final int match = mMatcher.match(uri);
        Uri returnedUri;
        switch (match) {
            case GNR_CODE: {
                long id = db.insert(MaiMobileContract.GnrEntry.TABLE_NAME, null, values);
                if (id > 0)
                    returnedUri = MaiMobileContract.GnrEntry.buildGnrUri(id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            case PSP_CODE: {
                long id = db.insert(MaiMobileContract.PspEntry.TABLE_NAME, null, values);
                if (id > 0)
                    returnedUri = MaiMobileContract.PspEntry.buildPspUri(id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            case SERVICES_CODE: {
                long id = db.insert(MaiMobileContract.ServicesEntry.TABLE_NAME, null, values);
                if (id > 0)
                    returnedUri = MaiMobileContract.ServicesEntry.buildServicesUri(id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            case HIGHLIGHT_CODE: {
                long id = db.insert(MaiMobileContract.ServicesEntry.TABLE_NAME, null, values);
                if (id > 0)
                    returnedUri = MaiMobileContract.ServicesEntry.buildServicesUri(id);
                else
                    throw new android.database.SQLException("Failed to insert row into " + uri);
                break;
            }
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return returnedUri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {

        final SQLiteDatabase db = mHelper.getWritableDatabase();


        // Student: Use the uriMatcher to match the WEATHER and LOCATION URI's we are going to
        final int match = mMatcher.match(uri);
        int rowsDeleted;
        // handle.  If it doesn't match these, throw an UnsupportedOperationException.
        if (null == selection)
            selection = "1";
        switch (match) {
            case GNR_CODE:
                rowsDeleted = db.delete(MaiMobileContract.GnrEntry.TABLE_NAME, selection,
                        selectionArgs);
                break;
            case PSP_CODE:
                rowsDeleted = db.delete(MaiMobileContract.PspEntry.TABLE_NAME, selection,
                        selectionArgs);
                break;
            case SERVICES_CODE:
                rowsDeleted = db.delete(MaiMobileContract.ServicesEntry.TABLE_NAME, selection,
                        selectionArgs);
                break;
            case HIGHLIGHT_CODE:
                rowsDeleted = db.delete(MaiMobileContract.ServicesEntry.TABLE_NAME, selection,
                        selectionArgs);
                break;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        if (rowsDeleted != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return rowsDeleted;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

        final SQLiteDatabase db = mHelper.getWritableDatabase();
        final int match = mMatcher.match(uri);
        int updatedRows;

        switch (match) {
            case GNR_CODE:
                updatedRows = db.update(MaiMobileContract.GnrEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            case PSP_CODE:
                updatedRows = db.update(MaiMobileContract.PspEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            case SERVICES_CODE:
                updatedRows = db.update(MaiMobileContract.ServicesEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            case HIGHLIGHT_CODE:
                updatedRows = db.update(MaiMobileContract.ServicesEntry.TABLE_NAME, values, selection,
                        selectionArgs);
                break;
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
        if (updatedRows != 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }

        return updatedRows;
    }


    @Override
    @TargetApi(11)
    public void shutdown() {
        mHelper.close();
        super.shutdown();
    }
}

This provider is used on requesting a method that I use to make my map markers and eventually add them to the ClusterManager, which can be seen here:

public class BaseMapFragment extends SupportMapFragment  {


    //region Parameters
    private final String LOG_TAG = "BaseMapFragment";
    public static int layout;
    protected FetchDataInterface mApiGson;
    protected GoogleMap gMap;
    protected SecurityMapFragment mapFragment;
    protected MaiMobileProvider mProvider;

    protected List<MaiClusterItem> pspItems = new ArrayList<>();
    protected List<MaiClusterItem> gnrItems = new ArrayList<>();
    protected ClusterManager<MaiClusterItem> mClusterManager;
    //endregion


    //region Methods

    protected void addMapMarkers(MaiMobileProvider provider, String markerGroup) {
        switch (markerGroup) {//between a switch and a if statement, I adopted the switch for future implementations
            case "psp":{
                Cursor cursor = provider.query(MaiMobileContract.PspEntry.CONTENT_URI,null,null,null,MaiMobileContract.PspEntry.COLUMN_DISTANCE +" ASC");
                cursor.moveToFirst();
                Log.v("Cursor psp: ", cursor.toString());
                Log.v("Cursor psp ", "has: " + cursor.getCount());

                while(cursor.moveToNext()){
                //cyclic add items to each MaiClusterItem List
                    LatLng coco = new LatLng(
                            cursor.getDouble(ColumnIndexes.PspIndexes.COLUMN_COORD_LAT),
                            cursor.getDouble(ColumnIndexes.PspIndexes.COLUMN_COORD_LONG));
                    pspItems.add(new MaiClusterItem(coco,
                            BitmapDescriptorFactory.fromResource(R.mipmap.psp),
                            cursor.getString(ColumnIndexes.PspIndexes.COLUMN_NAME),
                            cursor.getString(ColumnIndexes.PspIndexes.COLUMN_DESCRIPTION)));
                    Log.v("pspItems ", "has: " + pspItems.size());
                }
                cursor.close();
                break;
            }

            case "gnr": {
                Cursor cursor = provider.query(MaiMobileContract.GnrEntry.CONTENT_URI, null, null, null, MaiMobileContract.GnrEntry.COLUMN_DISTANCE + " ASC");
                cursor.moveToFirst();
                Log.v("Cursor gnr: ", cursor.toString());
                while(cursor.moveToNext()){
                    LatLng coco = new LatLng(
                            cursor.getDouble(ColumnIndexes.GnrIndexes.COLUMN_COORD_LAT),
                            cursor.getDouble(ColumnIndexes.GnrIndexes.COLUMN_COORD_LONG));
                    gnrItems.add(new MaiClusterItem(
                            coco,
                            BitmapDescriptorFactory.fromResource(R.mipmap.gnr_green),
                            cursor.getString(ColumnIndexes.GnrIndexes.COLUMN_NAME),
                            ""));
                    Log.v("gnrItems ", "has: " + gnrItems.size());
                }
                cursor.close();
                break;
            }
            default:
                throw new UnsupportedOperationException("Unknown error adding markers: " + getContext() + " at " + LOG_TAG);
        }
    }
    //region PspData Call

    protected void makePspMarkers() {
        addMapMarkers(mProvider, "psp");
        mClusterManager.addItems(pspItems);
        mClusterManager.setRenderer(new MaiClusterItem.ClusterItemRenderer(getActivity(), gMap, mClusterManager));

    }
    //endregion


    //region GnrData Call
    protected void makeGnrMarkers() {
        addMapMarkers(mProvider, "gnr");
        mClusterManager.addItems(gnrItems);
        mClusterManager.setRenderer(new MaiClusterItem.ClusterItemRenderer(getActivity(), gMap, mClusterManager));

    }
    //endregion


    //endregion
}

and this BaseMapFragment is extended on SecurityMapFragment class:

public class SecurityMapFragment extends BaseMapFragment implements
        OnMapReadyCallback {


    //region Properties
    public CameraPosition userCameraPosition;
    //endregion


    //region Methods


    //region MapReady
    @Override
    public void onMapReady(GoogleMap googleMap) {
        gMap = googleMap;


        //region map design
        gMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
        gMap.getUiSettings().setZoomControlsEnabled(false);
        gMap.setMyLocationEnabled(true);
        gMap.getUiSettings().setMyLocationButtonEnabled(false);
        //endregion


        //region camera+clustering
        userCameraPosition =
                new CameraPosition.Builder()
                        .target(MainActivity.userCoords)
                        .zoom(9f)
                        .bearing(-10f)
                        .build();

        gMap.moveCamera(CameraUpdateFactory.newCameraPosition(userCameraPosition));//set camera on user position (static for now)
        mClusterManager = new ClusterManager<>(getContext(), gMap);
        gMap.setOnCameraChangeListener(mClusterManager);//cluster call
        //endregion
    }
    //endregion


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mProvider = new MaiMobileProvider();

        //generate map
        mapFragment = (SecurityMapFragment) this.getFragmentManager().findFragmentById(R.id.security_map_view);
        getMapAsync(this);

        //interface api instance for Gson
        mApiGson = RetrofitUtils.createGsonRetrofitInterface();
        makePspGnrMarkers();//this will be called on created and also on filter selection


    }


    //region Add markers methods
    private void makePspGnrMarkers() {
        //Todo mClusterManager.clearItems();
        makePspMarkers();
        makeGnrMarkers();
    }

    //endregion


    //endregion
}

The MainActivity calls the SecurityMapFragment, then on the fragment it is called the methods to use the data from the Database that was created on my service. The problem comes now, for some reason on doing mHelper.getReadableDatabase().query(...); it gives me a NullPointerException on that method call, this happens on the MaiMobileProvider on the .query() method, which is the method that BaseMapFragment uses to fetch Database data. I know that the mHelper is null, what I don't know is why. The inicialization of mHelper is made on the onCreate of the Provider.

NOTE: yes my provider is being declared on the manifest inside the <application>

<provider
    android:name=".data.database.MaiMobileProvider"
    android:authorities="pt.gov.mai.mobile.android"
    android:exported="false" />

Thank you all in advance for reading and for your insight.


Solution

  • Just like Selvin said I should use the ContentResolver.query() because the application is already in charge of instantiating the Provider that is in the manifest. So as part of "what has changed":

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mResolver = getContext().getContentResolver();//From a Provider to a ContentResolver
        //generate map
        mapFragment = (SecurityMapFragment) this.getFragmentManager().findFragmentById(R.id.security_map_view);
        getMapAsync(this);
    
        //interface api instance for Gson
        mApiGson = RetrofitUtils.createGsonRetrofitInterface();
        makePspGnrMarkers();//this will be called on created and also on filter selection
    
    
    }
    

    And then after we get our ContentResolver from our context we can use it to query our provider, the same way it was done. Also have to refer on Selvin that commented my question and pointed that a Resolver should be used. And also Android documentation on ContentProvider and ContentResolver.