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.
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.