Hi I am developing an android app where I am trying to load sms from built in SMS app. This loading is too slow and it almost takes 10- 15 seconds to load all sms. I am laoding SMS in
doInBackground as below
private class MyBackgroundTask extends AsyncTask<Context, Integer, Boolean>
{
#Override
protected void onPreExecute()
{
pDialog = new ProgressDialog(Myapp.this);
pDialog.setMessage("Loading ...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
#Override
protected Boolean doInBackground(Context... params)
{
//fetching values from built-in message
getSMS();
return true;
}
#Override
public void onPostExecute(Boolean success)
{
pDialog.dismiss();
runOnUiThread(new Runnable()
{
public void run()
{
adapter = new SmsAdapter(EaseSms.this, listOfmessages);
smslist.setAdapter(adapter);
}
});
}
}
and the getSMS() method is as below
public void getSMS()
{
Uri uriSMSURI = Uri.parse("content://mms-sms/conversations?simple=true");
Cursor cursor = getContentResolver().query(uriSMSURI, new String[] {"*"}, null, null, "date desc");
while (cursor.moveToNext())
{
snippet = cursor.getString(cursor.getColumnIndexOrThrow("snippet"));
recipient_ids = cursor.getString(cursor.getColumnIndexOrThrow("recipient_ids"));
Cursor c = getContentResolver().query(Uri.parse("content://mms-sms/canonical-addresses"), null, "_id = " + recipient_ids, null, null);
c.moveToFirst();
r_address = c.getString(c.getColumnIndexOrThrow("address"));
Uri Nameuri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(r_address));
Cursor cs= getContentResolver().query(Nameuri, new String[]{PhoneLookup.DISPLAY_NAME,PhoneLookup._ID},PhoneLookup.NUMBER+"='"+r_address+"'",null,null);
if(cs.getCount()>0)
{
while (cs.moveToNext())
{
contactName = cs.getString(cs.getColumnIndex(PhoneLookup.DISPLAY_NAME));
contactID = cs.getString(cs.getColumnIndex(PhoneLookup._ID));
}
}
else
{
contactID = "01234567890";
contactName = r_address;
}
cs.close();
c.close();
addtomap();
}
cursor.close();}
}
Is it possible to load some 10 SMS onPreExecute and continue loading other in doInBackground ? I tried something similar. But it dint work . Can anybody guide me to resolve this slow loading.
Please Help.
Thanks!
Try using Cursor Adapter, instead of trying to read all the sms first and then displaying them. Cursor adapter loads data as required or you can say on demand, this will solve your issue.
http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html
To Elaborate it further
public class SimpleCursorAdapter extends CursorAdapter {
private LayoutInflater mLayoutInflater;
private Context mContext;
/**This is your constructor, Just pass it the cursor which you got from querying
* SMS database
*
* #param context
* #param c
*/
public SimpleCursorAdapter(Context context, Cursor c) {
super(context, c);
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
/**
* Write here code for binding you view to data form cursor
*
* Just like any other adapter, fetch all the columns details you want
* and set those values properly
*/
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
/**
* Just inflate your child view layout and return it to system
*/
View v = mLayoutInflater.inflate(R.layout.<YOUR_VIEW_NAME>, parent, false);
return v;
}
}
Thats it, once you done with above mentioned things, from your activity just get an cursor
from sms db, create an instance of this adapter and just like any set it to your list view.
Related
I have created a ListView using CursorAdapter . Now I am Trying to update the ListView and Refresh the value to the ListView .
But I am not able to figure out . How to work with Loader or changeCursor() to refresh my ListView
Below is My code of setting the CursorAdapter :
//SucessFully done here
SQLDataSore datastore = new SQLDataSore(PrintContent.this);
Cursor cursor = datastore.getJSONData();
final CursorDemo cursorDemo = new CursorDemo(PrintContent.this, cursor);
list_View.setAdapter(cursorDemo);
My Button onClick I am updating the Value into the Database
//SucessFully Done
btn_check.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View view ) {
String editTextValue = edit_check.getText().toString();
if (editTextValue!=null) {
SQLDataSore sqlDataSore = new SQLDataSore(PrintContent.this);
Cursor cursor_update = sqlDataSore.updateData(editTextValue);
//Here How Should I update my ListView ...?
}
}
My UpdateData Method:
public Cursor updateData(String editContent){
SQLiteDatabase updateContent = getReadableDatabase();
Cursor cursor_update = updateContent.rawQuery( "update " +TABLE_NAME + " set content = '"+ editContent
+"' "+" where _id = 357", null);
return cursor_update;
}
CursorDemo Class
public class CursorDemo extends CursorAdapter{
public CursorDemo(Context context, Cursor c) {
super(context, c , false);
// TODO Auto-generated constructor stub
}
#Override
public void changeCursor(Cursor cursor) {
// TODO Auto-generated method stub
super.changeCursor(cursor);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// TODO Auto-generated method stub
TextView txt_content = (TextView) view.findViewById(R.id.txt_content);
TextView txt_likes_count = (TextView) view.findViewById(R.id.txt_likescount);
TextView txt_name = (TextView) view.findViewById(R.id.txt_name);
TextView txt_display_name = (TextView) view.findViewById(R.id.txt_display_name);
txt_content.setText(cursor.getString(cursor.getColumnIndex("content")));
}
#Override
public View newView(Context context , Cursor cursor, ViewGroup viewGroup) {
// TODO Auto-generated method stub
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.message_row_view, viewGroup ,false);
return view;
}
}
Any Help is Appreciated...
});
If CursorDemo extends CursorAdapter, then you have to use adapter.swapCursor(cursor_update);
That should swap the old cursor out for the new one and reload the data. With swapCursor, the old cursor is not closed.
In your CursorDemo you have to owerwrite changeCursor() method and reset the Cursor if you have indexer you have to set it's cursor too.
#Override
public void changeCursor(Cursor cursor) {
mIndexer.setCursor(cursor);
super.changeCursor(cursor);
}
public void changeCursor (Cursor cursor)
Added in API level 1 Change the underlying cursor to a new cursor. If
there is an existing cursor it will be closed.
Parameters cursor The new cursor to be used
Also try for below method if it's apt for your requirement.
Set a FilterQueryProviderand pass your key to that filter.
final Cursor oldCursor = adapter.getCursor();
adapter.setFilterQueryProvider(myQueryProvider);
adapter.getFilter().filter(editTextValue, new FilterListener() {
public void onFilterComplete(int count) {
// assuming your activity manages the Cursor
// (which is a recommended way)
stopManagingCursor(oldCursor);
final Cursor newCursor = adapter.getCursor();
startManagingCursor(newCursor);
// safely close the oldCursor
if (oldCursor != null && !oldCursor.isClosed()) {
oldCursor.close();
}
}
});
private FilterQueryProvider myQueryProvider = new FilterQueryProvider() {
public Cursor runQuery(CharSequence searchKey) {
// assuming you have your custom DBHelper instance
// ready to execute the DB request
return sqlDataSore.updateData(searchKey);;
}
};
PS : The Cursor must include a column named _id or this class will not work see this.
btn_check.setOnClickListener( new OnClickListener() {
#Override
public void onClick(View view ) {
String editTextValue = edit_check.getText().toString();
if (editTextValue!=null) {
SQLDataSore sqlDataSore = new SQLDataSore(PrintContent.this);
Cursor cursor_update = sqlDataSore.updateData(editTextValue);
cursorDemo.swapCursor(cursor_update);
//or cursorDemo=new CursorDemo(this,cursor_update);
list_View.setAdapter(cursorDemo);
}
}
Put this on the activity where declare the ListView . Just create a new adapter and put it in new cursor then recreate it or swap the cursor. make sure your listview or adapter not constant.
you could call
adapter.notifyDataSetChanged();
".java.lang.IllegalArgumentException: column '_id' does not exist ...althouh I have _id field in to my db table" means that the value of "cursor" in your code is wrong.
Check the code of getting the value of "cursor" please. A cursor must have a column named '_id'。
This is my suggestion
If you want to adapt/replace new cursor value to your list view , you should remove the old cursor from adapter and add new cursor value to the adapter.And finally adapt this adapter to listview using listview.setadapter(CursorAdapter) as follows.
liveTagListCursor = ctx.getContentResolver().query(
LiveTagProvider.TAG_LIST_URI, null, null, null, null);
tagCursorAdapter = new LiveTagListCursorAdapter(getActivity(),
liveTagListCursor);
tagCursorAdapter.swapCursor(liveTagListCursor);
listview.setAdapter(tagCursorAdapter);
The way my gridview activity currently stands in that on my onCreate method I kick of a async task that searches for all the videos on my phone and then stores the id and filepath in a SQLite database using a do while loop. I then call a cursor adapter that uses a background thread to use the file path to create thumbnails and display them in the gridview. However, I'm running into the problem that when I first start up the activity nothing displays in the gridview. However, when I open it again everything will display. So my problem is that when the activity first starts the cursor has nothing in it so nothing can be displayed.
My question is how would I go about updating the cursor adapter as the new data is being entered in the background thread? How could I trigger the cursor adapter to use the data that was just entered in the background thread? My code is posted below (sorry its a little sloppy).
OnCreate
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preview);
GridView gridview = (GridView) this.findViewById(R.id.gridview);
cursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, mediaColumns, null, null, null);
columnindexid = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
columnindexdata = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
entry = new GridviewData(this);
entry.open();
DataEntry putitin = new DataEntry(entry);
putitin.execute();
//the cursor used in the cursor adapter
Cursor curs = entry.adapterCursor();
videoidindex = entry.Indexfinder(curs);
videopathindex = entry.Indexfinder2(curs);
config = new ImageLoaderConfiguration.Builder(this)
.imageDownloader(new BaseImageDownloader(this))
.build();
ImageLoader.getInstance().init(config);
Log.i(TAG, "Before set adapter");
gridview.setAdapter(new VideoAdapter(this, curs, flags));
}
Asynctask that puts data into database
private class DataEntry extends AsyncTask<Void, Integer, GridviewData>{
Cursor cursor;
GridviewData dataentry;
DataEntry(GridviewData gridviewdata){
this.dataentry = gridviewdata;
this.cursor = getContentResolver().query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
mediaColumns, null, null, null);
columnindexid = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
columnindexdata = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
}
#Override
protected GridviewData doInBackground(Void... params) {
cursor.moveToFirst();
do {
String videoid = cursor.getString(columnindexid);
String videopath = cursor.getString(columnindexdata);
int result = dataentry.findVideoID(videoid);
if (result == 1){
//this is where data is put into the database
dataentry.addVideoinfo(videoid, videopath);
}
if (result == 0){
Log.i(TAG, "Cursor wasn't processed, no getcount");
}
if(result == 2){
Log.i(TAG, "The data is already there");
}
} while (cursor.moveToNext());
Log.i(TAG, "After dowhile loop");
cursor.close();
return dataentry;
}
}
Cursor adapter
class VideoAdapter extends CursorAdapter {
Context context;
public VideoAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
this.context = context;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
String fileid = cursor.getString(videoidindex);
String filepath = cursor.getString(videopathindex);
BitmapDownloader bitdl = new BitmapDownloader(fileid, filepath, holder.imageview);
bitdl.execute();
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View eachgrid = inflater.inflate(R.layout.eachgrid, parent, false);
ViewHolder holder = new ViewHolder();
holder.imageview = (ImageView) eachgrid
.findViewById(R.id.Imageview1);
eachgrid.setTag(holder);
return eachgrid;
}
Call notifyDataSetChanged on your adapter after your background thread is done.
Notifies the attached observers that the underlying data has been
changed and any View reflecting the data set should refresh itself.
Well, I have read almost 50 links related to this question, but my code still not working.
I have a Custom adapter which extends SimpleCursorAdapter class, and I use that adapter to fill the ListView on onCreate method
private void populateListView()
{
String[] from = new String[] { SchemaHelper.TASK_DESCRIPTION, SchemaHelper.TASK_CREATED_ON, SchemaHelper.TASK_ID };
int[] to = new int[] {R.id.lv_row_description, R.id.lv_row_created_on};
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter = new TasksAdapter(this, R.layout.tasks_listview_row, tasksCursor, from, to);
setListAdapter(tasksAdapter);
}
The App is a simple task manager, I want to update the ListView contents when the user submits a new task without calling setListAdapter() again.
I have tried notifyDataSetChanged (running on ui thread), invalidate, requery(deprecated)... almost everything.
I'm doing something wrong?
EDIT:
This is the method where I add a new task to the database
private void addTask(String description)
{
String message = "";
schemaHelper.open();
if(schemaHelper.isAlreadyInDatabase(description))
{
message = getString(R.string.task_already_exists);
}
else
{
message = getString(R.string.task_succesfully_added);
schemaHelper.insertTask(description);
populateListView();
newTask.setText("");
}
schemaHelper.close();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
ADAPTER CLASS:
private class TasksAdapter extends SimpleCursorAdapter
{
private LayoutInflater layoutInflater;
private Cursor cursor;
public TasksAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
{
super(context, layout, c, from, to);
cursor = c;
cursor.moveToFirst();
layoutInflater = LayoutInflater.from(context);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
if(cursor.getPosition() < 0)
{
cursor.moveToFirst();
}
else
{
cursor.moveToPosition(position); // Here throws the error
}
View row = layoutInflater.inflate(R.layout.tasks_listview_row, null);
TextView description = (TextView) row.findViewById(R.id.lv_row_description);
TextView createdOn = (TextView) row.findViewById(R.id.lv_row_created_on);
description.setText(cursor.getString(cursor.getColumnIndexOrThrow(SchemaHelper.TASK_DESCRIPTION)));
createdOn.setText(getString(R.string.added_on) + " " + TaskHelper.formatDateWithSuffix(cursor.getString(cursor.getColumnIndexOrThrow(SchemaHelper.TASK_CREATED_ON))));
return row;
}
}
i dont know much of the taskCursor and taskAdapter but i used ArrayAdapter i guess, well have a look in my code and take your own conclusions.
//LISTVIEW database CONTATO
ListView user = (ListView) findViewById(R.id.lvShowContatos);
//String = simple value ||| String[] = multiple values/columns
String[] campos = new String[] {"nome", "telefone"};
list = new ArrayList<String>();
Cursor c = db.query( "contatos", campos, null, null, null, null, "nome" + " ASC ");
c.moveToFirst();
String lista = "";
if(c.getCount() > 0) {
while(true) {
list.add(c.getString(c.getColumnIndex("nome")).toString());
if(!c.moveToNext()) break;
}
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
user.setAdapter(adapter);
If you don't want to use requery() you can simply pass a new Cursor with the same query:
tasksCursor.close();
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter.changeCursor(tasksCursor);
I assume that when you call addTask() you have already called populateListView() once. Try changing addTask() to this:
private void addTask(String description)
{
String message = "";
schemaHelper.open();
if(schemaHelper.isAlreadyInDatabase(description))
{
message = getString(R.string.task_already_exists);
}
else
{
message = getString(R.string.task_succesfully_added);
schemaHelper.insertTask(description);
// Remove call to populateListView(), just update the Cursor
tasksCursor.close();
tasksCursor = schemaHelper.getTasks();
startManagingCursor(tasksCursor);
tasksAdapter.changeCursor(tasksCursor);
newTask.setText("");
}
schemaHelper.close();
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
}
If this "doesn't work", please be more specific. Is it throwing an error, if so what kind?
You are doing a little too much work in your adapter. Please watch Android's Romain Guy at Google Talks discuss adapters and getView(). However since you only want to pass one special string to your createdOn TextView, let's do something very different and override setViewText():
Try this:
public class TasksAdapter extends SimpleCursorAdapter {
String prefix;
public TasksAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
super(context, layout, cursor, from, to);
// This is constant so set it once and consider adding the space to the end of the String in strings.xml
prefix = getString(R.string.added_on) + " ";
}
#Override
public void setViewText(TextView v, String text) {
if(v.getId() == R.id.lv_row_created_on)
v.setText(prefix + TaskHelper.formatDateWithSuffix(text));
else
super.setViewText(v, text);
}
}
The rest of the data is taken care of with SimpleCursorAdapter's existing methods.
I am using SimpleCursorAdapter and database table to populate a List. The list gets populated and I am able to click on list items to open the desired item(This starts a new activity). The problem is when I press the back key, I got Following error.
IllegalStateException: database already closed.
My code is as follows:
public class populatingLectures extends ListActivity{
private static String[] FROM = {SUBJECT, TOPIC, LECTURENUMBER, DATE };
private static int[] TO = {R.id.subject, R.id.topic,
R.id.lecturenumber, R.id.date };
private static String[] data = { SUBJECT, TOPIC, LECTURENUMBER, _DATA };
private static String ORDER_BY = DATE + " DESC";
private SoftCopyDatabase lectures;
String gotId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoftCopyDatabase lectures = new SoftCopyDatabase(this);
try {
Cursor cursor = getLectures();
showLectures(cursor);
} finally {
lectures.close();
}
}
public void onStart() {
super.onStart();
lectures = new SoftCopyDatabase(this);
try {
Cursor cursor = getLectures();
showLectures(cursor);
} finally {
lectures.close();
}
}
private Cursor getLectures() {
SQLiteDatabase db = lectures.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME,null, "subject=?", new String[] {OpenClick.subjectName}, null, null,
ORDER_BY);
startManagingCursor(cursor);
return cursor;
}
private void showLectures(Cursor cursor) {
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.item_lectures, cursor, FROM, TO);
setListAdapter(adapter);
}
private Cursor getFileName(String ID) {
SQLiteDatabase db = lectures.getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, data, "_ID=?",
new String[] { ID }, null, null, null);
startManagingCursor(cursor);
return cursor;
}
#Override
protected void onListItemClick(ListView listView, View view, int position,
long id) {
super.onListItemClick(listView, view, position, id);
//...CODE TO START NEW ACTIVITY
}
}
}
Kindly tell me what mistake am I doing. Because I am not closing the database explicitly any where.
Regards,
Waneya Iqbal.
finally
{
lectures.close();
}
I think this line gives the exception, so put it in onDestroy().
You're closing the database as soon as you populate the list. You should move the
lectures.close()
line from onStart() to onDestroy(), which will make sure your database gets closed when the activity is complete, rather than as soon as the list is populated.
hi am using cursor adapter for my listview, my problems is when i put my application in background ,on retuning my screen is empty, in onresume i have open the database and creating cursor still i have the problem, how can i solve my problem ,pls help me .
searchCursor= dbReaderContact.rawQuery(query, null);
startManagingCursor(searchCursor);
String[] from=new String[] {ALDbAdapter.TITLE,DbAdapter.CATID,DbAdapter.LTID,DbAdapter.RK,DbAdapter.SUBTITLE};
int [] to=new int[] {R.layout.ctllist_item};
catSearchAdapter=new CategorySearchAdapter(context, R.layout.ctllist_item, searchCursor, from, to);
//////////////Adapter calss
public class CategorySearchAdapter extends SimpleCursorAdapter implements Filterable{
private Context context;
private int layout;
private ALDbAdapter dbadapter;
/**
* #param context
* #param layout
* #param c
* #param from
* #param to
*/
public CategorySearchAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.layout=layout;
this.context=context;
dbadapter=new ALDbAdapter(context);
try {
dbadapter.openAngiesListDb();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (getFilterQueryProvider() != null) { return getFilterQueryProvider().runQuery(constraint); }
StringBuilder buffer = null;
String[] args = null;
if (constraint != null) {
buffer = new StringBuilder();
buffer.append("UPPER(");
buffer.append("name");
buffer.append(") GLOB ?");
args = new String[] { constraint.toString().toUpperCase() + "*" };
}
Cursor c=null;
// c=dbadapter.getPartialNamesSearch(constraint.toString());
return c;
}
#Override
public void bindView(View v, Context context, Cursor c) {
TextView subTitle=(TextView)v.findViewById(R.id.ctlName);
TextView title=(TextView)v.findViewById(R.id.cltAssocationName);
ImageView imgIcon=(ImageView)v.findViewById(R.id.ctlLogo);
int subTitInd=c.getColumnIndex(ALDbAdapter.SUBTITLE);
int titInd=c.getColumnIndex(ALDbAdapter.TITLE);
int ltId=c.getColumnIndex(ALDbAdapter.LTID);
int ltype=c.getInt(ltId);
if(!c.getString(subTitInd).equalsIgnoreCase("")){
subTitle.setText(c.getString(subTitInd));
title.setText(c.getString(titInd));
}else{
subTitle.setText(c.getString(titInd));
}
if(ltype==1){
imgIcon.setImageResource(R.drawable.logo1);
}else if(ltype==2){
imgIcon.setImageResource(R.drawable.logo2);
}else{
imgIcon.setImageResource(R.drawable.logo3);
}
}
}
//On post i closed the cursor
///On Resume
searchCursor= dbReaderContact.rawQuery(query, null);
startManagingCursor(searchCursor);
You are talking about ListView and using SimpleCursorAdapter. Don't you think creating your own ArrayAdaptor isn't more efficient : you create a class of your own to hold the data you want from your DB request (iterate over your Cursor), then you populate an ArrayList of that class.
Then you give this ArrayList to a class which extens ArrayAdaptor. You should store the ArrayList in the ArrayAdaptor and override contructor and the public View getView(int position, View convertView, ViewGroup parent) method to create each view of your ListView.
You can google custom ListView (first result : http://www.softwarepassion.com/android-series-custom-listview-items-and-adapters/ ).