Have a good day. I'm learning Android development so i'm creating a simple CRUD application, right now i have my main activity that has the listview (MarcaActivity) with the next code:
package com.example.crudapp.activity;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import com.example.crudapp.MarcaCursorAdapter;
import com.example.crudapp.R;
import com.example.crudapp.cp.MarcaCP;
public class MarcaActivity extends Activity implements
LoaderManager.LoaderCallbacks<Cursor> {
MarcaCursorAdapter crsAdap;
ListView lstMarcas;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_marca);
lstMarcas = (ListView)findViewById(R.id.lstMarcas);
lstMarcas.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
Intent i = new Intent(MarcaActivity.this, MarcaDetailActivity.class);
Uri todoUri = Uri.parse(MarcaCP.CONTENT_URI + "/" + arg3);
i.putExtra("DET", todoUri);
i.putExtra("ACTION", "EXISTING");
startActivity(i);
}
});
getLoaderManager().initLoader(0, null, this);
fillData();
}
protected void onRestoreInstanceState(Bundle savedInstanceState) {
getLoaderManager().restartLoader(0, null, this);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.insertMnu:
Intent i = new Intent(this, MarcaDetailActivity.class);
i.putExtra("ACTION", "NEW");
startActivityForResult(i, 0);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public boolean onContextItemSelected(MenuItem item) {
/*switch (item.) {
case DELETE_ID:
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
.getMenuInfo();
Uri uri = Uri.parse(MarcaCP.CONTENT_URI + "/"
+ info.id);
getContentResolver().delete(uri, null, null);
fillData();
return true;
}*/
return super.onContextItemSelected(item);
}
private void fillData() {
crsAdap = new MarcaCursorAdapter(this, R.layout.template_marcas, null ,
new String[]{MarcaCP.Marca.NOMBRE, MarcaCP.Marca.RANKING}, new int[]{R.id.nomMarca, R.id.rankMarca});
lstMarcas.setAdapter(crsAdap);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.marca, menu);
return true;
}
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
return new CursorLoader(this, MarcaCP.CONTENT_URI,
new String[]{MarcaCP.Marca.ID, MarcaCP.Marca.NOMBRE, MarcaCP.Marca.CODIGO, MarcaCP.Marca.RANKING}, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor arg1) {
crsAdap.swapCursor(arg1);
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
crsAdap.swapCursor(null);
} }
Then, i have the activity that manages the detail view where i can update or add items, called MarcaDetailActivity:
package com.example.crudapp.activity;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.example.crudapp.MarcaCursorAdapter;
import com.example.crudapp.R;
import com.example.crudapp.cp.MarcaCP;
public class MarcaDetailActivity extends Activity {
MarcaCursorAdapter crsAdap;
private Integer idReg;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detalle_marca);
Bundle extras = getIntent().getExtras();
if(extras != null && extras.get("ACTION").equals("EXISTING")) //Invocamos fillData
fillData((Uri)extras.get("DET"));
else
cleanForm();
}
public void cleanForm() {
((Button)findViewById(R.id.todo_edit_button)).setVisibility(View.INVISIBLE);
((Button)findViewById(R.id.todo_insert_button)).setVisibility(View.VISIBLE);
}
public void fillData(Uri uri) {
String[] projection = { MarcaCP.Marca.CODIGO,
MarcaCP.Marca.CLASIFICACION, MarcaCP.Marca.NOMBRE, MarcaCP.Marca.RANKING};
//builder.
Cursor cur = getContentResolver().query(uri, projection, null, null, null);
if(cur != null) {
cur.moveToFirst();
((EditText)findViewById(R.id.detNombre)).setText(cur.getString(cur.getColumnIndex(MarcaCP.Marca.NOMBRE)));
((EditText)findViewById(R.id.detCodigo)).setText(cur.getString(cur.getColumnIndex(MarcaCP.Marca.CODIGO)));
setIdReg(cur.getInt(cur.getColumnIndex(MarcaCP.Marca.ID)));
((EditText)findViewById(R.id.todo_edit_button)).setVisibility(View.VISIBLE);
((EditText)findViewById(R.id.todo_insert_button)).setVisibility(View.INVISIBLE);
String clasif = cur.getString(cur.getColumnIndex(MarcaCP.Marca.CLASIFICACION));
int rank = cur.getInt(cur.getColumnIndex(MarcaCP.Marca.RANKING));
Spinner spRnk = ((Spinner)findViewById(R.id.detRanking));
Spinner spClasf = ((Spinner)findViewById(R.id.detClasificacion));
//Buscamos setear los spinners en sus valores
for(short ps = 0; ps < spRnk.getCount(); ps++) {
String tmpRnk = (String)spRnk.getItemAtPosition(ps);
if(tmpRnk.equalsIgnoreCase("" + rank))
spRnk.setSelection(ps);
}
//Buscamos setear los spinners en sus valores
for(short ps = 0; ps < spClasf.getCount(); ps++) {
String tmpClas = (String)spClasf.getItemAtPosition(ps);
if(tmpClas.equalsIgnoreCase(clasif))
spClasf.setSelection(ps);
}
cur.close();
}
}
private boolean validate() {
boolean res = true;
return res;
}
public void guardar(View view) {
if(validate()) {
ContentValues cv = new ContentValues();
cv.put(MarcaCP.Marca.CLASIFICACION, (String)((Spinner)findViewById(R.id.detClasificacion)).getSelectedItem() );
cv.put(MarcaCP.Marca.CODIGO, ((TextView)findViewById(R.id.detCodigo)).getText().toString() );
cv.put(MarcaCP.Marca.NOMBRE, ((TextView)findViewById(R.id.detNombre)).getText().toString() );
cv.put(MarcaCP.Marca.RANKING, Integer.parseInt("" + ((Spinner)findViewById(R.id.detRanking)).getSelectedItem() ));
getContentResolver().insert(MarcaCP.CONTENT_URI, cv);
}
Toast.makeText(MarcaDetailActivity.this, "Registro registrado",
Toast.LENGTH_SHORT).show();
//Refrescamos el grid y cerramos la actividad
//((MarcaActivity)this.getCallingActivity()).refrescarLista()
//getLoaderManager().restartLoader(0, null, MarcaActivity.class);
this.finish();
}
public void regresar() {
Intent i = new Intent(MarcaDetailActivity.this, MarcaActivity.class);
i.putExtra("REFRESH", true);
startActivity(i);
}
public void actualizar(View view) {
if(validate()) {
ContentValues cv = new ContentValues();
cv.put(MarcaCP.Marca.CLASIFICACION, (String)((Spinner)findViewById(R.id.detClasificacion)).getSelectedItem() );
cv.put(MarcaCP.Marca.CODIGO, (String)((TextView)findViewById(R.id.detCodigo)).getText() );
cv.put(MarcaCP.Marca.NOMBRE, (String)((TextView)findViewById(R.id.detNombre)).getText() );
cv.put(MarcaCP.Marca.ID, getIdReg() );
cv.put(MarcaCP.Marca.RANKING, Integer.parseInt("" + ((Spinner)findViewById(R.id.detRanking)).getSelectedItem() ));
getContentResolver().update(MarcaCP.CONTENT_URI, cv, null, null);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.marca, menu);
return true;
}
public Integer getIdReg() {
return idReg;
}
public void setIdReg(Integer idReg) {
this.idReg = idReg;
} }
As you can see in the method guardar of MarcaDetailActivity, first i insert the item using my custom content provider, then i use Toast to show a confirm message, and finally finishing the activity, but when i do that, the listview isn't refreshed via the cursorloader. I read here (stackoverflow) and in other pages that i have to call
getLoaderManager().restartLoader(LIST_ID, null, this);
But in all examples that i found all the stuff (inserting data in the database and refreshing the list) is in the same activity, but in this case i have an activity with the list and the second activity that inserts the item, so i think that i have to call restartLoader from my detail activity, but i don't know how to do that or if that's the right way to do this. I was thinking about saving a reference to the list activity in extras when i create the intent to call the detail activity and have a public method that calls restartLoader but i don't know if that is the best approach. What's the best way to refresh the cursor loader from my detail activity?
And there's my content provider, MarcaCP:
package com.example.crudapp.cp;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import com.example.crudapp.util.ConnHandler;
public class MarcaCP extends ContentProvider {
private static final int TODOS = 10;
private static final int TODO_ID = 20;
public static final Uri CONTENT_URI = Uri.parse("content://provider.marcacp/marcas");
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI("provider.marcacp", "marcas", TODOS);
sURIMatcher.addURI("provider.marcacp", "marcas" + "/#", TODO_ID);
}
public static class Marca {
public static final String TABLA = "MARCA";
public static final String ID = "_id";
public static final String CODIGO = "codigo";
public static final String NOMBRE = "nombre";
public static final String CLASIFICACION = "clasificacion";
public static final String RANKING = "ranking";
}
private ConnHandler conn;
SQLiteDatabase dbObj;
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int uriType = sURIMatcher.match(uri);
int rowsDeleted = 0;
switch (uriType) {
case TODOS:
rowsDeleted = dbObj.delete(Marca.TABLA, selection, selectionArgs);
break;
case TODO_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsDeleted = dbObj.delete(Marca.TABLA,
Marca.ID + " = " + id,
null);
} else {
rowsDeleted = dbObj.delete(Marca.TABLA,
Marca.ID + " =" + id
+ " and " + selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsDeleted;
}
#Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
int uriType = sURIMatcher.match(uri);
long id = 0;
switch (uriType) {
case TODOS:
id = dbObj.insert(Marca.TABLA, null, values);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse("marca/" + id);
}
#Override
public boolean onCreate() {
conn = new ConnHandler(getContext());
dbObj = conn.getDb();
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// Uisng SQLiteQueryBuilder instead of query() method
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
checkColumns(projection);
queryBuilder.setTables(Marca.TABLA);
int uriType = sURIMatcher.match(uri);
switch (uriType) {
case TODOS:
break;
case TODO_ID:
// Digamos para ver el detalle de un registro
queryBuilder.appendWhere(Marca.ID + " = "
+ uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URIx: " + uri);
}
Cursor cur = queryBuilder.query(dbObj, projection, selection,
selectionArgs, null, null, sortOrder);
return cur;
}
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int uriType = sURIMatcher.match(uri);
int rowsUpdated = 0;
switch (uriType) {
case TODOS:
rowsUpdated = dbObj.update(Marca.TABLA,
values,
selection,
selectionArgs);
break;
case TODO_ID:
String id = uri.getLastPathSegment();
if (TextUtils.isEmpty(selection)) {
rowsUpdated = dbObj.update(Marca.TABLA,
values,
Marca.ID + " =" + id,
null);
} else {
rowsUpdated = dbObj.update(Marca.TABLA,
values,
Marca.ID + " =" + id
+ " and "
+ selection,
selectionArgs);
}
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return rowsUpdated;
}
private void checkColumns(String[] projection) {
/*String[] available = { TodoTable.COLUMN_CATEGORY,
TodoTable.COLUMN_SUMMARY, TodoTable.COLUMN_DESCRIPTION,
TodoTable.COLUMN_ID };
if (projection != null) {
HashSet<String> requestedColumns = new HashSet<String>(Arrays.asList(projection));
HashSet<String> availableColumns = new HashSet<String>(Arrays.asList(available));
// Check if all columns which are requested are available
if (!availableColumns.containsAll(requestedColumns)) {
throw new IllegalArgumentException("Unknown columns in projection");
}
}*/
} }
It's already registered in the AndroidManifest.xml:
<provider android:name=".cp.MarcaCP" android:authorities="provider.marcacp" />
Regards.
Try getContentResolver().notifyChange(MarcaCP.CONTENT_URI, null, false) from the second activity before finishing it.
It's a bit late to answer this.
since in your ContentProvider, you have
getContext().getContentResolver().notifyChange
which is great and correct!
so there is no need to called
getContentResolver().notifyChange(MarcaCP.CONTENT_URI, null, false) from the second activity before finishing it.
the problem is that instead of using getLoaderManager().initLoader
which is deprecated and not life-cycled aware.
try use
LoaderManager.getInstance(this).initLoader
this: is a class that maintains its own android.arch.lifecycle.Lifecycle and android.arch.lifecycle.ViewModelStore. For instance, androidx.fragment.app.FragmentActivity or androidx.fragment.app.Fragment.
so that when you pop back to the first activity and OnResume() called, there is no need to restart the loader again.
P.s. also a great reminder. Do not close any cursor related to the onLoadFinished(#NonNull Loader<Cursor> loader, Cursor cursor)
if doing so will also cause the loader won't update itself after data changed in DB.
Related
I am calling the insert function in WordListActivity.java from MainActivity.java has the below code by sending the data that needs to be inserted in database.
getContentResolver().insert gives error.
I have also provided the provider permissions in AndroidManifest.xml
MainActivity.java
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.cardview.widget.CardView;
import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import android.Manifest;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.example.personaldictionary.adapter.DictionaryAdapter;
import com.example.personaldictionary.database.DictionaryContract;
import com.example.personaldictionary.database.DictionaryDbHelper;
import com.example.personaldictionary.utils.NetworkUtils;
import com.google.android.material.navigation.NavigationView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
public class MainActivity extends AppCompatActivity {
private static final String LOG_TAG = "MainActivity";
private SQLiteDatabase mDatabase;
private CardView mCardView;
private TextView mWordText;
private TextView mWordDef;
private TextView mWordEtylo;
private DictionaryDbHelper mDbHelper;
WordListActivity wordListActivity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mToggle = new ActionBarDrawerToggle(this,mDrawerLayout, R.string.open, R.string.close);
mDrawerLayout.addDrawerListener(mToggle);
mToggle.syncState();
wordListActivity = new WordListActivity();
mCardView = (CardView) findViewById(R.id.word_card);
mCardView.setVisibility(View.GONE);
mWordText = (TextView) findViewById(R.id.word_text);
mWordDef = (TextView) findViewById(R.id.word_definition);
mWordEtylo = (TextView) findViewById(R.id.word_etymologies);
mDbHelper = new DictionaryDbHelper(this);
mDatabase = mDbHelper.getWritableDatabase();
mCardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Log.d(LOG_TAG," insertWord called: ");
wordListActivity.insertWord(mWordText.getText().toString(),mWordDef.getText().toString(),mWordEtylo.getText().toString());
return true;
}
});
}
}
WordListActivity.java
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.personaldictionary.adapter.DictionaryAdapter;
import com.example.personaldictionary.database.DictionaryContract;
import com.example.personaldictionary.database.DictionaryDbHelper;
public class WordListActivity extends AppCompatActivity {
private static final String LOG_TAG = "WordListActivity";
DictionaryAdapter mAdapter;
DictionaryDbHelper mDbHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_word_list);
RecyclerView word_rv = (RecyclerView) findViewById(R.id.word_rv);
word_rv.setLayoutManager(new LinearLayoutManager(this));
View emptyView = findViewById(R.id.empty_view);
emptyView.setVisibility(View.GONE);
mDbHelper = new DictionaryDbHelper(this);
mAdapter = new DictionaryAdapter(this,getAllItems());
word_rv.setAdapter(mAdapter);
}
public void insertWord(String word,String word_def,String word_etylo){
Uri newUri = null;
Log.d(LOG_TAG,"insertWord() word: "+word+" word_def: "+word_def+" word_et: "+word_etylo);
ContentValues values = new ContentValues();
values.put(DictionaryContract.DictionaryEntry.COLUMN_WORD,word);
values.put(DictionaryContract.DictionaryEntry.COLUMN_WORD_DEFINITION,word_def);
values.put(DictionaryContract.DictionaryEntry.COLUMN_WORD_ETYMOLOGIES,word_etylo);
Log.d(LOG_TAG,"DictionaryContract.DictionaryEntry.CONTENT_URI: "+DictionaryContract.DictionaryEntry.CONTENT_URI);
Log.d(LOG_TAG,"values: "+values);
newUri = getContentResolver().insert(DictionaryContract.DictionaryEntry.CONTENT_URI, values);
mAdapter.swapCursor(getAllItems());
if (newUri == null) {
Toast.makeText(WordListActivity.this, "Word insert error ", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(WordListActivity.this, "Word insert Success", Toast.LENGTH_SHORT).show();
}
}
private Cursor getAllItems(){
Log.d(LOG_TAG,"getAllItems inside");
String[] projection = {DictionaryContract.DictionaryEntry._ID,
DictionaryContract.DictionaryEntry.COLUMN_WORD,
DictionaryContract.DictionaryEntry.COLUMN_WORD_DEFINITION,
DictionaryContract.DictionaryEntry.COLUMN_WORD_ETYMOLOGIES};
return getContentResolver().query(DictionaryContract.DictionaryEntry.CONTENT_URI,projection,null,null,null);
}
}
DictionaryProvider.java
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import com.example.personaldictionary.database.DictionaryContract.DictionaryEntry;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class DictionaryProvider extends ContentProvider {
private static final String LOG_TAG = "DictionaryProvider";
public DictionaryDbHelper mDbHelper;
/** URI matcher code for the content URI for the dictionary table */
private static final int WORDS = 100;
/** URI matcher code for the content URI for a single word in the dictionary table */
private static final int WORD_ID = 101;
#Override
public boolean onCreate() {
Log.d(LOG_TAG,"onCreate called");
mDbHelper = new DictionaryDbHelper(getContext());
return true;
}
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static{
Log.d(LOG_TAG,"sUriMatcher called");
sUriMatcher.addURI(DictionaryEntry.CONTENT_AUTHORITY, DictionaryEntry.PATH_WORDS, WORDS);
sUriMatcher.addURI(DictionaryEntry.CONTENT_AUTHORITY, DictionaryEntry.PATH_WORDS+"/#",WORD_ID);
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection, #Nullable String[] selectionArgs, #Nullable String sortOrder) {
Log.d(LOG_TAG,"query called");
SQLiteDatabase database = mDbHelper.getReadableDatabase();
Cursor cursor;
int match = sUriMatcher.match(uri);
Log.d(LOG_TAG,"query match: "+match);
switch(match){
case WORDS:
Log.d(LOG_TAG,"match WORDS");
cursor = database.query(DictionaryEntry.TABLE_NAME,projection,selection,selectionArgs,null,null,null,null);
break;
case WORD_ID:
Log.d(LOG_TAG,"match WORD_ID");
selection = DictionaryEntry._ID+"=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
cursor = database.query(DictionaryEntry.TABLE_NAME,projection,selection,selectionArgs,null,null,sortOrder,null);
break;
default:
throw new IllegalArgumentException("Cannot query unknown Uri "+uri);
}
cursor.setNotificationUri(getContext().getContentResolver(),uri);
return cursor;
}
#Nullable
#Override
public String getType(#NonNull Uri uri) {
Log.d(LOG_TAG,"getType");
final int match = sUriMatcher.match(uri);
Log.d(LOG_TAG,"getType match: "+match);
switch (match) {
case WORDS:
Log.d(LOG_TAG,"return ProductEntry.CONTENT_LIST_TYPE");
return DictionaryEntry.CONTENT_LIST_TYPE;
case WORD_ID:
Log.d(LOG_TAG,"return ProductEntry.CONTENT_ITEM_TYPE");
return DictionaryEntry.CONTENT_ITEM_TYPE;
default:
throw new IllegalStateException("Unknown URI " + uri + " with match " + match);
}
}
#Nullable
#Override
public Uri insert(#NonNull Uri uri, #Nullable ContentValues values) {
Log.d(LOG_TAG,"insert uri "+ uri +" values: "+values);
final int match = sUriMatcher.match(uri);
Log.d(LOG_TAG,"insert match: "+match);
switch(match){
case WORDS:
Log.d(LOG_TAG,"insertDictionary");
return insertDictionary(uri, values);
default:
throw new IllegalArgumentException("Insertion is not supported for "+uri);
}
}
#Override
public int delete(#NonNull Uri uri, #Nullable String selection, #Nullable String[] selectionArgs) {
Log.d(LOG_TAG,"delete uri:"+ uri+" selection: "+selection+"selectionArgs: "+selectionArgs);
// Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
// Track the number of rows that were deleted
int rowsDeleted;
final int match = sUriMatcher.match(uri);
Log.d(LOG_TAG,"delete match:"+ match);
switch (match) {
case WORDS:
Log.d(LOG_TAG,"delete WORDS");
// Delete all rows that match the selection and selection args
rowsDeleted = database.delete(DictionaryEntry.TABLE_NAME, selection, selectionArgs);
break;
case WORD_ID:
Log.d(LOG_TAG,"delete WORD_ID");
// Delete a single row given by the ID in the URI
selection = DictionaryEntry._ID + "=?";
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
rowsDeleted = database.delete(DictionaryEntry.TABLE_NAME, selection, selectionArgs);
break;
default:
throw new IllegalArgumentException("Deletion is not supported for " + uri);
}
// If 1 or more rows were deleted, then notify all listeners that the data at the
// given URI has changed
if (rowsDeleted != 0) {
Log.d(LOG_TAG,"delete rowsDeleted != 0");
getContext().getContentResolver().notifyChange(uri, null);
}
// Return the number of rows deleted
return rowsDeleted;
}
#Override
public int update(#NonNull Uri uri, #Nullable ContentValues values, #Nullable String selection, #Nullable String[] selectionArgs) {
Log.d(LOG_TAG,"update uri: "+uri+" values: "+ values+" selection: "+ selection+" selectionArgs: "+selectionArgs);
final int match = sUriMatcher.match(uri);
Log.d(LOG_TAG,"update match: "+match);
switch (match){
case WORDS:
Log.d(LOG_TAG,"update WORDS");
return updateDictionary(uri, values, selection, selectionArgs);
case WORD_ID:
Log.d(LOG_TAG,"update WORD_ID");
selection = DictionaryEntry._ID+"=?";
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri))};
return updateDictionary(uri, values, selection, selectionArgs);
default:
throw new IllegalArgumentException("Update is not supported for "+uri);
}
}
private Uri insertDictionary(Uri uri, ContentValues values){
Log.d(LOG_TAG,"insertDictionary uri: "+uri+" values: "+values);
String word = values.getAsString(DictionaryEntry.COLUMN_WORD);
Log.d(LOG_TAG,"insertDictionary word: "+word);
if (word == null) {
throw new IllegalArgumentException("Dictionary requires a word");
}
String word_def_1 = values.getAsString(DictionaryEntry.COLUMN_WORD_DEFINITION);
Log.d(LOG_TAG,"insertDictionary word_def_1: "+word_def_1);
if (word_def_1 == null) {
throw new IllegalArgumentException("Dictionary requires a word definition");
}
String etymologies = values.getAsString(DictionaryEntry.COLUMN_WORD_ETYMOLOGIES);
Log.d(LOG_TAG,"insertDictionary etymologies: "+etymologies);
SQLiteDatabase db = mDbHelper.getWritableDatabase();
long id = db.insert(DictionaryEntry.TABLE_NAME, null, values);
Log.d(LOG_TAG,"insertDictionary() id "+id);
if(id==-1){
Log.e(LOG_TAG,"Failed to insert row for "+uri);
return null;
}
getContext().getContentResolver().notifyChange(uri,null);
Log.d(LOG_TAG,"return ContentUris.withAppendedId(uri,id): "+ContentUris.withAppendedId(uri,id));
return ContentUris.withAppendedId(uri,id);
}
private int updateDictionary(Uri uri, ContentValues values, String selection, String[] selectionArgs){
if (values.containsKey(DictionaryEntry.COLUMN_WORD)) {
String word = values.getAsString(DictionaryEntry.COLUMN_WORD);
if (word == null) {
throw new IllegalArgumentException("Dictionary requires a word");
}
}
if (values.containsKey(DictionaryEntry.COLUMN_WORD_DEFINITION)) {
// Check that the price is greater than or equal to 0 Rs
String word_def_1 = values.getAsString(DictionaryEntry.COLUMN_WORD_DEFINITION);
if (word_def_1 == null) {
throw new IllegalArgumentException("Dictionary requires a word definition");
}
}
// If there are no values to update, then don't try to update the database
if (values.size() == 0) {
return 0;
}
SQLiteDatabase database = mDbHelper.getWritableDatabase();
int rowsUpdated = database.update(DictionaryEntry.TABLE_NAME, values, selection, selectionArgs);
// If 1 or more rows were updated, then notify all listeners that the data at the
// given URI has changed
Log.d(LOG_TAG,"updateDictionary rowsUpdated: "+rowsUpdated);
if (rowsUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsUpdated;
}
}
Error Logs:
2020-06-14 18:55:15.948 24271-24271/com.example.personaldictionary E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.personaldictionary, PID: 24271
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.ContentResolver android.content.Context.getContentResolver()' on a null object reference
at android.content.ContextWrapper.getContentResolver(ContextWrapper.java:101)
at com.example.personaldictionary.WordListActivity.insertWord(WordListActivity.java:68)
at com.example.personaldictionary.MainActivity$2.onLongClick(MainActivity.java:110)
at android.view.View.performLongClickInternal(View.java:6685)
at android.view.View.performLongClick(View.java:6643)
at android.view.View.performLongClick(View.java:6661)
at android.view.View$CheckForLongPress.run(View.java:25862)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6692)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Why do I get this error?
So, I've been used Realm for a while. For now, I have a task to share the login data with my other apps.
Since the login data is stored using Realm. I choose to use Content Provider.
I found an example: https://speakerdeck.com/androhi/realm-with-contentprovider
Unfortunately, I was unable to make it work. This is my Content Provider in app A
static final String[] sColumns = new String[]{
"LoginResultData"
};
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection,
#Nullable String[] selectionArgs, #Nullable String sortOrder) {
Realm mRealm = Realm.getDefaultInstance();
RealmQuery<LoginResultData> query = mRealm.where(LoginResultData.class);
LoginResultData result = query.findFirst();
String json = new Gson().toJson(result);
MatrixCursor matrixCursor = new MatrixCursor(sColumns);
Object[] rowData = new Object[]{json};
matrixCursor.addRow(rowData);
return matrixCursor;
}
App B (which need to get the login data) got hang when I
getContentResolver.query(uri, null, null, null, null);
I don't know why but it worked well when I use SQlite. So I'm assuming that Realm doesn't work well with Content Provider smh. Is that true?
If not, please show me a sample to using Content Provider with Realm.
Thanks!
Content Provider works well with RealmDB.
All you need to do is to override the CRUD methods inside the ContentProvider. Please take a look at this content provider class below. 3 things to note:
RealmDB is initialized in the onCreate() method of the
ContentProvider (not the app activity)
You override the CRUD methods(Query, Insert, Delete, Update) in a manner appropriate for RealmDB. Check below samples.
This is all you need to do. In the rest of the code, you will be using native components like recyclerview, adapter, loaders, services. Everywhere you need a query, you will call it with getContentResolver.query(uri, null, null, null, null);
TaskProvider.java
package com.example.rgher.realmtodo.data;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.text.format.DateUtils;
import android.util.Log;
import io.realm.DynamicRealm;
import io.realm.Realm;
import io.realm.RealmConfiguration;
import io.realm.RealmMigration;
import io.realm.RealmResults;
import io.realm.RealmSchema;
import com.example.rgher.realmtodo.data.DatabaseContract.TaskColumns;
public class TaskProvider extends ContentProvider {
private static final String TAG = TaskProvider.class.getSimpleName();
private static final int CLEANUP_JOB_ID = 43;
private static final int TASKS = 100;
private static final int TASKS_WITH_ID = 101;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
// content://com.example.rgher.realmtodo/tasks
sUriMatcher.addURI(DatabaseContract.CONTENT_AUTHORITY,
DatabaseContract.TABLE_TASKS,
TASKS);
// content://com.example.rgher.realmtodo/tasks/id
sUriMatcher.addURI(DatabaseContract.CONTENT_AUTHORITY,
DatabaseContract.TABLE_TASKS + "/#",
TASKS_WITH_ID);
}
#Override
public boolean onCreate() {
//Innitializing RealmDB
Realm.init(getContext());
RealmConfiguration config = new RealmConfiguration.Builder()
.schemaVersion(1)
.migration(new MyRealmMigration())
.build();
Realm.setDefaultConfiguration(config);
manageCleanupJob();
return true;
}
#Nullable
#Override
public String getType(Uri uri) {
return null; /* Not used */
}
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
int match = sUriMatcher.match(uri);
//Get Realm Instance
Realm realm = Realm.getDefaultInstance();
MatrixCursor myCursor = new MatrixCursor( new String[]{TaskColumns._ID, TaskColumns.DESCRIPTION
, TaskColumns.IS_COMPLETE, TaskColumns.IS_PRIORITY
, TaskColumns.DUE_DATE
});
try {
switch (match) {
//Expected "query all" Uri: content://com.example.rgher.realmtodo/tasks
case TASKS:
RealmResults<RealmTask> tasksRealmResults = realm.where(RealmTask.class).findAll();
for (RealmTask myTask : tasksRealmResults) {
Object[] rowData = new Object[]{myTask.getTask_id(), myTask.getDescription(), myTask.getIs_complete()
, myTask.getIs_priority(), myTask.getDue_date()};
myCursor.addRow(rowData);
Log.v("RealmDB", myTask.toString());
}
break;
//Expected "query one" Uri: content://com.example.rgher.realmtodo/tasks/{id}
case TASKS_WITH_ID:
Integer id = Integer.parseInt(uri.getPathSegments().get(1));
RealmTask myTask = realm.where(RealmTask.class).equalTo("task_id", id).findFirst();
myCursor.addRow(new Object[]{myTask.getTask_id(), myTask.getDescription(), myTask.getIs_complete(), myTask.getIs_priority(), myTask.getDue_date()});
Log.v("RealmDB", myTask.toString());
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
myCursor.setNotificationUri(getContext().getContentResolver(), uri);
} finally {
realm.close();
}
return myCursor;
}
#Nullable
#Override
public Uri insert(Uri uri, final ContentValues contentValues) {
//COMPLETE: Expected Uri: content://com.example.rgher.realmtodo/tasks
//final SQLiteDatabase taskDb = mDbHelper.getReadableDatabase();
int match = sUriMatcher.match(uri);
Uri returnUri;
//Get Realm Instance
Realm realm = Realm.getDefaultInstance();
try {
switch (match) {
case TASKS:
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
Number currId = realm.where(RealmTask.class).max(TaskColumns._ID);
Integer nextId = (currId == null) ? 1 : currId.intValue() + 1;
RealmTask myNewTask = realm.createObject(RealmTask.class, nextId);
myNewTask.setDescription(contentValues.get(TaskColumns.DESCRIPTION).toString());
myNewTask.setIs_complete((Integer) contentValues.get(TaskColumns.IS_COMPLETE));
myNewTask.setIs_priority((Integer) contentValues.get(TaskColumns.IS_PRIORITY));
myNewTask.setDue_date((Long) contentValues.get(TaskColumns.DUE_DATE));
}
});
returnUri = ContentUris.withAppendedId(DatabaseContract.CONTENT_URI, '1');
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
}finally {
realm.close();
}
return returnUri;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//Expected Uri: content://com.example.rgher.realmtodo/tasks/{id}
Realm realm = Realm.getDefaultInstance();
int match = sUriMatcher.match(uri);
int nrUpdated = 0;
try {
switch (match) {
case TASKS_WITH_ID:
Integer id = Integer.parseInt(uri.getPathSegments().get(1));
RealmTask myTask = realm.where(RealmTask.class).equalTo("task_id", id).findFirst();
realm.beginTransaction();
myTask.setIs_complete(Integer.parseInt(values.get(TaskColumns.IS_COMPLETE).toString()));
if (values.get(TaskColumns.DUE_DATE) != null) {
myTask.setDue_date(Long.valueOf(values.get(TaskColumns.DUE_DATE).toString()));
}
nrUpdated++;
realm.commitTransaction();
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
} finally {
realm.close();
}
if (nrUpdated != 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return nrUpdated;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
Realm realm = Realm.getDefaultInstance();
try {
switch (sUriMatcher.match(uri)) {
case TASKS:
selection = (selection == null) ? "1" : selection;
RealmResults<RealmTask> tasksRealmResults = realm.where(RealmTask.class).equalTo(selection, Integer.parseInt(selectionArgs[0])).findAll();
realm.beginTransaction();
tasksRealmResults.deleteAllFromRealm();
count++;
realm.commitTransaction();
break;
case TASKS_WITH_ID:
Integer id = Integer.parseInt(String.valueOf(ContentUris.parseId(uri)));
RealmTask myTask = realm.where(RealmTask.class).equalTo("task_id", id).findFirst();
realm.beginTransaction();
myTask.deleteFromRealm();
count++;
realm.commitTransaction();
break;
default:
throw new IllegalArgumentException("Illegal delete URI");
}
} finally {
realm.close();
}
if (count > 0) {
//Notify observers of the change
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
}
// Example of REALM migration
class MyRealmMigration implements RealmMigration {
#Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
if (oldVersion != 0) {
schema.create(DatabaseContract.TABLE_TASKS)
.addField(DatabaseContract.TaskColumns._ID, Integer.class)
.addField(DatabaseContract.TaskColumns.DESCRIPTION, String.class)
.addField(DatabaseContract.TaskColumns.IS_COMPLETE, Integer.class)
.addField(DatabaseContract.TaskColumns.IS_PRIORITY, Integer.class);
oldVersion++;
}
}
}
You can find the full working app here
https://github.com/rgherta/RealmTodo
Good luck
Anyone knows how to use cursorLoader with DBFlow ? I seen this issue but this is not added to DBFlow.
Thanks.
You can find official docs here or you can implement it the way i have
DBFlow ver used 3
//I have edited my answer & provided easier way for content provider part below
add this to manifest inside application
<provider
android:authorities="com.hashx19.pristinekashmir.mycontentprovider"
android:exported="false"
android:name=".MyContentProvider"/>
Create java file named MyContentProvider & copy below code in it
& replace AUTHORITY ,ENDPOINT, AppDatabase(Your database name) ,TableClassName as per you project.
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.CursorIndexOutOfBoundsException;
import android.net.Uri;
import com.hashx19.pristinekashmir.MySQLiteHelper;
import com.raizlabs.android.dbflow.annotation.ConflictAction;
import com.raizlabs.android.dbflow.config.FlowManager;
import com.raizlabs.android.dbflow.structure.ModelAdapter;
import java.util.Arrays;
import java.util.HashSet;
/**
* Created by Filu on 8/25/2016.
*/
public class MyContentProvider extends ContentProvider {
public static final String AUTHORITY = "com.hashx19.pristinekashmir.mycontentprovider";
private static final String ENDPOOINT = "feeds";
// #ContentUri(path = ENDPOOINT, type = ContentUri.ContentType.VND_MULTIPLE + ENDPOOINT)
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ "/" + ENDPOOINT);
private static final int feeds_CONTENT_URI = 0;
private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
static {
MATCHER.addURI(AUTHORITY, ENDPOOINT, feeds_CONTENT_URI);
}
;
#Override
public final String getType(Uri uri) {
String type = null;
switch(MATCHER.match(uri)) {
case feeds_CONTENT_URI: {
type = "vnd.android.cursor.dir/" +ENDPOINT;
break;
}
default: {
throw new IllegalArgumentException("Unknown URI" + uri);
}
}
return type;
}
#Override
public boolean onCreate() {
return false;
}
#Override
public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
android.database.Cursor cursor = null;
switch(MATCHER.match(uri)) {
case feeds_CONTENT_URI: {
cursor = FlowManager.getDatabase("AppDatabase").getWritableDatabase().query("TableClassName", projection, selection, selectionArgs, null, null, sortOrder);
break;
}
}
if (cursor != null) {
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
return cursor;
}
#Override
public final Uri insert(Uri uri, ContentValues values) {
switch(MATCHER.match(uri)) {
case feeds_CONTENT_URI: {
ModelAdapter adapter = FlowManager.getModelAdapter(FlowManager.getTableClassForName("AppDatabase", "TableClassName"));
final long id = FlowManager.getDatabase("AppDatabase").getWritableDatabase().insertWithOnConflict("TableClassName", null, values, ConflictAction.getSQLiteDatabaseAlgorithmInt(adapter.getInsertOnConflictAction()));
getContext().getContentResolver().notifyChange(uri, null);
return ContentUris.withAppendedId(uri, id);
}
default: {
throw new IllegalStateException("Unknown Uri" + uri);
}
}
}
#Override
public final int delete(Uri uri, String selection, String[] selectionArgs) {
switch(MATCHER.match(uri)) {
case feeds_CONTENT_URI: {
long count = FlowManager.getDatabase("AppDatabase").getWritableDatabase().delete("TableClassName", selection, selectionArgs);
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return (int) count;
}
default: {
throw new IllegalArgumentException("Unknown URI" + uri);
}
}
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
switch(MATCHER.match(uri)) {
case feeds_CONTENT_URI: {
ModelAdapter adapter = FlowManager.getModelAdapter(FlowManager.getTableClassForName("AppDatabase", "TableClassName"));
long count = FlowManager.getDatabase("AppDatabase").getWritableDatabase().updateWithOnConflict("TableClassName", values, selection, selectionArgs, ConflictAction.getSQLiteDatabaseAlgorithmInt(adapter.getUpdateOnConflictAction()));
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
return (int) count;
}
default: {
throw new IllegalStateException("Unknown Uri" + uri);
}
}
}
}
then when overriding Loader methods do something like this
getLoaderManager().initLoader(1, null, this);
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String selection, sortOrder;
String[] selectionArgs, projection;
selection = ...;
selectionArgs = ...;
sortOrder = ...;
projection= new String[]{"id","date", "link","title","content","excerpt","author",};
CursorLoader cursorLoader = new CursorLoader(getContext(),MyContentProvider.CONTENT_URI, projection,null,null,null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
TableClass post = new TableClass();
while (!cursor.isAfterLast()) {
try{
post.setId(cursor.getInt(cursor.getColumnIndex("id")));
}catch (NullPointerException e){
e.printStackTrace();
}catch (CursorIndexOutOfBoundsException c){
c.printStackTrace();
}
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
}
editted
Figured out easier way to implement content provider .
add this to your manifest / or modify this way if you already have added Provider code .
modify your AppDatabase Class as
#ContentProvider(authority = AppDatabase.AUTHORITY,
database = AppDatabase.class,
baseContentUri = AppDatabase.BASE_CONTENT_URI)
#Database(name = AppDatabase.NAME, version = AppDatabase.VERSION)
public class AppDatabase {
public static final String NAME = "AppDatabase"; // we will add the .db extension
public static final int VERSION = 2;
public static final String AUTHORITY = "com.hashx19.pristinekashmir.dbflowcontentprovider";
public static final String BASE_CONTENT_URI = "content://"; }
modify each table you want to use as provider as
#TableEndpoint(name = PostData.ENDPOINT, contentProvider = AppDatabase.class)
#Table(database = AppDatabase.class ,allFields = true ,name = PostData.ENDPOINT)
public class PostData extends BaseModel {
public static final String ENDPOINT = "PostData";
#ContentUri(path = ENDPOINT, type = ContentUri.ContentType.VND_MULTIPLE + ENDPOINT)
public static final Uri CONTENT_URI = Uri.parse(AppDatabase.BASE_CONTENT_URI + AppDatabase.AUTHORITY
+ "/" + ENDPOINT);
#PrimaryKey
public int id;
public String image;
}
For using Content provider as in Cursor Loader use TableName.CONTENT_URI as in this case
CursorLoader cursorLoader = new CursorLoader(getContext(),PostData.CONTENT_URI,projection,null,null,null);
I'm trying to populate a ListView in my activity using a ContentProvider that I created, but when the onLoadFinished() method is called, it receives a null Cursor. (The CursorLoader is not null though).
I'm trying to debug the ContentProvider, but the debugger doesn't stop on the query() method (it stops when the URI doesn't match, when I fix that, it doesn't stop!!!). So without debug I'm having a hard time to find out why my cursor returns as null on the onLoadFinished() method.
Here is the code for the activity:
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.Menu;
import android.widget.ListView;
public class PicturesListActivity extends FragmentActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
SimpleCursorAdapter myAdapter;
String[] projection = {"_id", "name"};
String[] from = {"name"};
String orderBy = "year DESC";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pictures_list);
final ListView listview = (ListView) findViewById(R.id.lv_pictures_list);
// The TextView in simple_list_item_1
int[] toViews = {android.R.id.text1};
// Prepare the loader. Either re-connect with an existing one, or start a new one.
getSupportLoaderManager().initLoader(0, null, this);
myAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, from, toViews, 0);
listview.setAdapter(myAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.pictures_list, menu);
return true;
}
#Override
// Called when a new Loader needs to be created (initLoader)
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = Uri.parse(DatabaseContentProvider.AUTHORITY + DatabaseContentProvider.TABLE_PATH_PICTURE);
CursorLoader cl = new CursorLoader(this, uri, projection, null, null, orderBy);
return cl;
}
#Override
// Called automatically when a previously created loader has finished loading
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
myAdapter.swapCursor(data);
}
#Override
// Called when a previously created loader is reset, making the data unavailable
public void onLoaderReset(Loader<Cursor> loader) {
myAdapter.swapCursor(null);
}
}
And here is the ContentProvider that I created:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
public class DatabaseContentProvider extends ContentProvider {
//database
private DatabaseAssetHelper dbhelper;
//stuff
public static final String AUTHORITY = "com.frlnrl.myapp.DatabaseContentProvider";
public static final String TABLE_PATH_PICTURE = DatabaseContract.Pictures.TABLE_PICTURE;
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
//UriMatcher
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, TABLE_PATH_PICTURE , 37);
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int numberOfLinesDeleted = 0;
return numberOfLinesDeleted;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean onCreate() {
dbhelper = new DatabaseAssetHelper(getContext());
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
SQLiteDatabase db = dbhelper.getReadableDatabase();
int uriReceived = sURIMatcher.match(uri);
switch (uriReceived) {
case 37:
if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
queryBuilder.setTables(DatabaseContract.Pictures.TABLE_PICTURE);
break;
default:
throw new IllegalArgumentException("Unknown oh eu aqui URI");
}
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
int numberOfLineUpdated = 0;
return numberOfLineUpdated;
}
}
Does anyone got any idea why is returns a empty cursor?
First you should fix the CONTENT_URI for your path.
Change
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
to
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + TABLE_PATH_PICTURE);
You weren't including the path on your Uri.
Then when creating your cursor loader you can use the CONTENT_URI instead of trying to parse the Uri each time (Which you were doing wrong resulting in your cursor loader trying to query a Uri that didn't exist and returning a null cursor )
#Override
// Called when a new Loader needs to be created (initLoader)
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = DatabaseContentProvider.CONTENT_URI;
CursorLoader cl = new CursorLoader(this, uri, projection, null, null, orderBy);
return cl;
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have an adapter that extends simplecursoradapter. For some reason I can't seem to see, my getView is not even being called. I have a breakpoint inside getView and it never gets there and the list just shows up empty. Can anyone take a look thru and see what I've done wrong?
package com.example.ok1;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.app.ListActivity;
public class MainActivity extends Activity {
// Button btnCalendar;
//*******************************************8
String[] names = {"Иван", "Марья", "Петр", "Антон", "Даша", "Борис",
"Костя", "Игорь", "Анна", "Денис", "Андрей"};
//Button buttonAddTask;
final String Tag="States";
final String Ten = "Ten";
TextView txtDataTaskToday;
String id_for_listtsk_today;
ListView lvMain_today;
String[] arr_date;
SharedPreferences sPref;
static Cursor c;
private ListView listView = null;
//public static String id_for_listtsk_today;
// static SQLiteDatabase db;
MySqlCursorAdapter adapter = null;
//***********************************************8
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// btnCalendar = (Button) findViewById(R.id.btnActTwo);
// btnCalendar.setOnClickListener(this);
//*********************************************
// переменные для query
String[] columns = null;
String selection = null;
String[] selectionArgs = null;
String groupBy = null;
String having = null;
String orderBy = null;
//*********работа с БД****************
// создаем объект для данных
// txtDataTaskToday = (TextView) findViewById(R.id.txtDataTaskToday);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String id_for_listtsk_today = sdf.format(new Date());
//final String b = id_for_listtsk_today;
// txtDataTaskToday.setText(id_for_listtsk_today.toString());
Log.d(Tag, "id_for_listtsk_today ="+id_for_listtsk_today );
ContentValues cv = new ContentValues();
DBHelper dbHelper = new DBHelper(this);
final SQLiteDatabase db = dbHelper.getWritableDatabase();
columns = new String[] {"name"};
selection = "data_id = ?";
selectionArgs = new String[] {id_for_listtsk_today};
//c = db.query("mytable", columns, selection, selectionArgs, null, null, null);
try {
c=dbHelper.getCursor();
} catch (SQLException sqle) {
Log.d(Tag, "неудача");
throw sqle;
}
String[] arr_date = logCursor(c);
//*********работа с БД****************
lvMain_today = (ListView) findViewById(R.id.list);
// lvMain_today.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
//this.listView=getl
//listView = MainActivity.this.getlgetListView();
lvMain_today.setItemsCanFocus(false);
lvMain_today.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
//ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_multiple_choice, arr_date);// R.layout.item, my_list_item
startManagingCursor(c);
int[] listFields = new int[] { R.id.txtTitle };
String[] dbColumns = new String[] { DBHelper.COLUMN_NAME };
Log.d(Tag, "трассировка" );
MainActivity.this.adapter = new MySqlCursorAdapter(
this, R.layout.my_list_item,
c, dbColumns, listFields,
dbHelper);
//
lvMain_today.setAdapter(MainActivity.this.adapter);
// setListAdapter(MainActivity.this.adapter);
names = arr_date;
c.close();
db.close();
dbHelper.close();
lvMain_today.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
SparseBooleanArray chosen = ((ListView) parent).getCheckedItemPositions();
for (int i = 0; i < chosen.size(); i++) {
int key = chosen.keyAt(i);
if (chosen.get(key))
Log.d(Tag, "выделены ====="+names[key]);
Log.d(Tag, "itemClick: position = " + position + ", id = "
+ id);}
//****************nen пробная фигня**************
// String[] columns = null;
// String selection = null;
// String[] selectionArgs = null;
// String groupBy = null;
// String having = null;
// String orderBy = null;
// columns = new String[] {"name"};
// selection = "data_id = ?";
// selectionArgs = new String[] {id_for_listtsk_today};//id_for_listtsk_today
// Cursor c = db.query("mytable", columns, selection, selectionArgs, null, null, null);
// String[] arr = logCursor(c);
//**************************************************
// String s=test();
}
});
// lvMain_today.setOnItemSelectedListener(new OnItemSelectedListener() {
// public void onItemSelected(AdapterView<?> parent, View view,
// int position, long id) {
// Log.d(Tag, "Было выделение позиции меню!!!!position = " + position + ", id = "
// + id);
// }
//
// public void onNothingSelected(AdapterView<?> parent) {
// Log.d(Tag, "itemSelect: nothing");
// }
// });
}
private String[] logCursor(Cursor c) {
// TODO Auto-generated method stub
final String Tag="States";
String[] arr_date = new String[c.getCount()];//String[] arr_date = new String[] {};
Log.d(Tag,"мы в курсоре");
if (c!=null) {
if (c.moveToFirst()) {
// Log.d(Tag,"мы в курсоре1");
String str;
int i=-1;
do {
// Log.d(Tag,"мы в курсоре2");
str="";
i=i+1;
for (String cn: c.getColumnNames()) {
str = str.concat(c.getString(c.getColumnIndex(cn)));
}
Log.d(Tag, "++++"+str);
arr_date[i]=String.valueOf(str);
} while (c.moveToNext());
}
}
return arr_date;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
menu.add(0, 1, 0, "календарь");
menu.add(0, 2, 0, "Убрать выполненные");
menu.add(0, 3, 3, "Уйти");
// menu.add(1, 4, 1, "copy");
// menu.add(1, 5, 2, "paste");
// menu.add(1, 6, 4, "exit");
return super.onCreateOptionsMenu(menu);
// getMenuInflater().inflate(R.menu.main, menu);
//return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
StringBuilder sb = new StringBuilder();
// Выведем в TextView информацию о нажатом пункте меню
// txtDataTaskToday.setText("Item Menu");
// txtDataTaskToday.setText(item.getGroupId());
//// txtDataTaskToday.setText("\r\n itemId: " + String.valueOf(item.getItemId()));
// txtDataTaskToday.setText("\r\n order: " + String.valueOf(item.getOrder()));
// txtDataTaskToday.setText("\r\n title: " + item.getTitle());
switch (item.getItemId()) {
case 1:
Intent intent = new Intent(this, ToDoCalendarActivity.class);
startActivity(intent);
break;
case 2:
SparseBooleanArray sbArray = lvMain_today.getCheckedItemPositions();
for (int i = 0; i < sbArray.size(); i++) {
int key = sbArray.keyAt(i);
if (sbArray.get(key))
Log.d(Tag, "выделены "+names[key]);
sPref = getPreferences(MODE_PRIVATE);
Editor ed = sPref.edit();
ed.putString(Ten, "1");
ed.commit();
Log.d(Tag, "ставим константу для скрытия");
}
break;
case 3:
sPref = getPreferences(MODE_PRIVATE);
String savedText = sPref.getString(Ten, "");
Log.d(Tag, "ten= "+ savedText);
finish();
break;
}
return super.onOptionsItemSelected(item);
}
// #Override
// public void onClick(View v) {
// // TODO Auto-generated method stub
// switch (v.getId()) {
// case R.id.btnActTwo:
//
// Intent intent = new Intent(this, ToDoCalendarActivity.class);
// startActivity(intent);
// break;
// }
// }
}
MySqlCursorAdapter
package com.example.ok1;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
public class MySqlCursorAdapter extends SimpleCursorAdapter implements OnClickListener {
final String Tag="States";
private Context context;
private DBHelper dbHelper;
private Cursor currentCursor;
public MySqlCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, DBHelper dbHelper) {
super(context, layout, c, from, to);
Log.d(Tag, "трассировка1" );
this.currentCursor = c;
this.context = context;
this.dbHelper = dbHelper;
Log.d(Tag, "MySqlCursorAdapter()");
Integer b = c.getCount();
Log.d(Tag, "b="+b);
}
public View getView(int pos, View inView, ViewGroup parent) {
Log.d(Tag, "getView() + posss=" + pos);
View v = inView;
if (v == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.my_list_item, null);
}
this.currentCursor.moveToPosition(pos);
CheckBox cBox = (CheckBox) v.findViewById(R.id.bcheck);
cBox.setTag(Integer.parseInt(this.currentCursor
.getString(this.currentCursor
.getColumnIndex(DBHelper.COLUMN_ID))));
Log.d(Tag, "tag="+cBox.getTag().toString());
if (this.currentCursor.getString(this.currentCursor
.getColumnIndex(DBHelper.COLUMN_STATUS)) != null
&& Integer.parseInt(this.currentCursor
.getString(this.currentCursor
.getColumnIndex(DBHelper.COLUMN_STATUS))) != 0) {
cBox.setChecked(true);
} else {
cBox.setChecked(false);
}
cBox.setOnClickListener(this);
TextView txtTitle = (TextView) v.findViewById(R.id.txtTitle);
txtTitle.setText(this.currentCursor.getString(this.currentCursor
.getColumnIndex(DBHelper.COLUMN_NAME)));
return (v);
}
public void ClearSelections() {
Log.d(Tag, "ClearSelections()");
//this.dbHelper.clearSelections();
//this.currentCursor.requery();
}
#Override
public void onClick(View v) {
Log.d(Tag, "onClick");
CheckBox cBox = (CheckBox) v;
Integer _id = (Integer) cBox.getTag();
Log.d(Tag, "Integer _id="+_id.toString());
ContentValues values = new ContentValues();
values.put(" selected", cBox.isChecked() ? 1 : 0);
//this.dbHelper.dbSqlite.update(SqlHelper.TABLE_NAME, values, "_id=?",
// new String[] { Integer.toString(_id) });
}
}
DBHelper
package com.example.ok1;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.util.Log;
public class DBHelper extends SQLiteOpenHelper {
final String Tag="States";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_DATA = "data_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_STATUS = "status";
public static final String TABLE_NAME = "mytable";
public SQLiteDatabase dbSqlite;
public DBHelper(Context context) {
// конструктор суперкласса
super(context, "myDB", null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
Log.d(Tag, "--- onCreate database ---");
// создаем таблицу с полями
db.execSQL("create table mytable ("
+ "_id integer primary key autoincrement,"
+ "data_id text,"
+ "name text,"
+ "task text,"
+ "status integer"
+ ");");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
public Cursor getCursor() {
Log.d(Tag, "getCursor() получили курсор с базы");
String[] columns = null;
String selection = null;
String[] selectionArgs = null;
String groupBy = null;
String having = null;
String orderBy = null;
// SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// queryBuilder.setTables(TABLE_NAME);
// String[] asColumnsToReturn = new String[] { COLUMN_ID, COLUMN_NAME,
// COLUMN_DATA, COLUMN_STATUS };
// Cursor mCursor = queryBuilder.query(dbSqlite, asColumnsToReturn, null,
// null, null, null, "title ASC");
// Log.d(Tag, "getCursor() получили курсор с базы конец");
final SQLiteDatabase db = this.getWritableDatabase();
columns = new String[] { COLUMN_ID, COLUMN_DATA, COLUMN_NAME, COLUMN_STATUS };
// selection = "data_id = ?";
// selectionArgs = new String[] {id_for_listtsk_today};
Cursor c = db.query("mytable", columns, null, null, null, null, null);
return c;
}
}
ActivityMain.xml
<!--?xml version="1.0" encoding="utf-8"?-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#81BEF7" android:scrollbars="vertical">
<TableLayout android:id="#+id/TableLayout01" android:layout_width="fill_parent" android:layout_height="fill_parent" >
<TableRow>
<ListView
android:id="#+id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>
</TableRow>
</TableLayout>
</LinearLayout>
my_list_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<CheckBox
android:id="#+id/bcheck"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:focusable="false">
</CheckBox>
<TextView
android:id="#+id/txtTitle"
android:layout_width="138dp"
android:layout_height="?android:attr/listPreferredItemHeight"
android:focusable="false"
android:gravity="left|center_vertical"
android:text="Test" >
</TextView>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="right|center_vertical"
android:orientation="horizontal" >
<ImageButton
android:id="#+id/ImageButton01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.50"
android:clickable="true" >
</ImageButton>
</LinearLayout>
</LinearLayout>
I removed
c.close();
db.close();
and now its working fine.