I have data inside a table. when I set adapter from it, the app crashes, but if I load the info from server then everything is ok. I get NullPointerException on setting the listview's adapter. even though, I do exactly same things as when table is empty.
// check if database is empty
cursor = sh.getReadableDatabase().rawQuery(
"SELECT * FROM " + PetopenTable.TABLE_NAME, null);
if (cursor.getCount() < 1) {
//cursor is empty
tableHasData = false;
} else {
tableHasData = true;
}
if (tableHasData) {
// we have data in database, show it, then get info
// from server and reload the UI
Log.d("Cursor", "PetOpenFragment; cursor is not empty");
prepareListFromCursor(cursor);
// initialize the list
lview3 = (ListView) getActivity().findViewById(R.id.listView1);
adapter = new ListViewCustomAdapter(getActivity(), itemList);
// here adapter is not null
if (adapter != null) {
Log.d("Adapter in Petopen", "is not null");
lview3.setAdapter(adapter); // line 94
} else {
Log.d("ADapter in petopen", "is null");
}
// do not show the loading message as we can fill it instantly from database
// now get data from server to update
new getPetopenUsers().execute((Void) null);
} else {
// show loading and load from server, database is empty
Log.d("Cursor", "PetOpenFragment; cursor is empty");
new getPetopenUsers().execute((Void) null);
mDialog.setMessage("Loading...");
mDialog.setCancelable(false);
mDialog.show();
}
...
// inside prepareListFromCursor I initialize the itemlist
itemList = new ArrayList<Object>();
...
//when i get data from server, i initialize same as when there is data in table
lview3 = (ListView) getActivity().findViewById(R.id.listView1);
adapter = new ListViewCustomAdapter(getActivity(), itemList);
lview3.setAdapter(adapter);
Logcat error:
ATAL EXCEPTION: main
java.lang.NullPointerException
at com.petcial.petopen.fragments.petOpenFragment.onCreateView(petOpenFragment.java:94)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1500)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
at android.os.Handler.handleCallback(Handler.java)
at android.os.Handler.dispatchMessage(Handler.java)
at android.os.Looper.loop(Looper.java)
at android.app.ActivityThread.main(ActivityThread.java)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:126)
at dalvik.system.NativeStart.main(Native Method)
Update
Here is how i fill the itemList, i call this method after i parse the info from table
private void AddObjectToList(String image, String name, String status,
String distance, String location, String[] petImage,
String[] petName) {
// TODO Auto-generated method stub
bean = new ItemBean();
bean.setProfileImage(image);
bean.setName(name);
bean.setStatus(status);
bean.setDistance(distance);
bean.setLocation(location);
bean.setPetImage(petImage);
bean.setPetName(petName);
itemList.add(bean);
}
Finally I tried putting the whole thing which loads data from database inside an AsyncTask, and it worked. In case someone faced same problem here is how you have to do it:
Create an AsyncTask, which is called from onCreate or onViewCreated(as in my case, I fragment)
In the doInBackground method I did not have anything, instead in the onPostExecute method, i call all the methods that processes all the data from cursor and initializes the list and adapter.
If someone has a better solution on how to make this inside an AsyncTask, please answer to this post.
Related
I am making an android app and have to save JSON to an SQLite database.
The app already has a function to get JSON and display this in a listview and a function to save data to a database. Now I want to combine those, but I am a little lost.
public void get_data(String data) {
try {
JSONArray data_array=new JSONArray(data);
for (int i = 0 ; i < data_array.length() ; i++)
{
JSONObject obj=new JSONObject(data_array.get(i).toString());
Courses add=new Courses();
add.name = obj.getString("name");
add.ects = obj.getString("ects");
add.grade = obj.getString("grade");
add.period = obj.getString("period");
courses.add(add);
}
adapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
This loops through the JSON so I think this is where is should save to the database.
public boolean insertCourse(String course, int ects, int period, int grade) {
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COURSE_COLUMN_COURSE, course);
contentValues.put(COURSE_COLUMN_ECTS, ects);
contentValues.put(COURSE_COLUMN_PERIOD, period);
contentValues.put(COURSE_COLUMN_GRADE, grade);
db.insert(COURSE_TABLE_NAME, null, contentValues);
return true;
}
This is in a DBHelper.class should be able to use this I think.
I was hoping to reuse the code which i used to save input fields to the database but no luck so far.
else {
if(dbHelper.insertCourse(courseEditText.getText().toString(),
Integer.parseInt(ectsEditText.getText().toString()),
Integer.parseInt(periodEditText.getText().toString()),
Integer.parseInt(gradeEditText.getText().toString()))) {
Toast.makeText(getApplicationContext(), "Course Inserted", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(getApplicationContext(), "Could not Insert course", Toast.LENGTH_SHORT).show();
}
Does anyone have a suggestion how to integrate both (if possible at all).
Thnx in advance
EDIT:
the logcat crash report:
04-09 17:45:37.204 12244-12244/com.stefspakman.progress2 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.stefspakman.progress2, PID: 12244
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean com.stefspakman.progress2.ProgressDBHelper.insertCourse(java.lang.String, int, int, int)' on a null object reference
at com.stefspakman.progress2.gradingActivity.get_data(gradingActivity.java:60)
at com.stefspakman.progress2.Download_data$1.handleMessage(Download_data.java:58)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5422)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
You need to instantiate your DBHelper class before using the insertCourse Method .
DBHelper helper = new DBHelper(this);
helper.insertCourse(Course course);
Also , its better if you use your Model class object as paramater for database queries .
public boolean insertCourse(Courses courses) {
SQLiteDatabase db = getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(COURSE_COLUMN_COURSE, courses.getCourse());
contentValues.put(COURSE_COLUMN_ECTS, courses.getEcts());
contentValues.put(COURSE_COLUMN_PERIOD, courses.getPeriod());
contentValues.put(COURSE_COLUMN_GRADE, courses.getGrade());
db.insert(COURSE_TABLE_NAME, null, contentValues);
return true;
}
In this way you can save your data from JSON as
db.insert(courses);
The exception says that your ProgressDBHelper is null, please make sure that you are creating an instance of ProgressDBHelper before calling its methods. Creating an instance is just calling ProgressDBHelper dbHelper = new ProgressDBHelper() and you have to call it before calling dbHelper.insertCourse(...)
So, I'm debugging my project in Android Studio, it's just a tutorial I'm doing, and I have a Handler I'm trying to use.
I can step over all the code in the run part. Then when I get out of run it steps into Handler.java and android studio has all these errors marked in in the Handler.java file and the program crashes.
I'm pretty sure Handler is part of the jdk and when added Handler to my activity it imported the file automatically. I've invalidated caches and cleaned the project. I've ran into this problem with a number of tutorials but never found an answer on how to solve.
In the other cases I uninstalled and reinstalled android studio but that didn't help me out.
This is the code that eventually steps into Handler.java but that is not the problem. The problem is that Android Studio says there are errors throughout Handler.java. I'm using Android Studio 2.0 but I've run into this with other versions too with other jdk java files.
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Cursor c = sqLite.rawQuery("SELECT CITY_NAME FROM USER_PREF", null);
Log.d("arindam", "c count"+ c.getCount());
if (c.getCount() == 0){
sqLite.execSQL("INSERT INTO USER_PREF (CITY_NAME, VOICE_ON, NOTIF)" +
" VALUES('NONE', 'Y', 'Y')");
}
c.close();
Cursor d = sqLite.rawQuery("SELECT CITY_NAME FROM USER_PREF", null);
Log.d("arindam", "d count" + d.getCount());
if (d.moveToFirst()){
Log.d("arindam", "d NONE" + d.getString(0));
if (d.getString(0).equals("NONE")){
Intent intent = new Intent(StartScreen.this, CityScreen.class);
startActivity(intent);
}
else {
//Intent intent = new Intent(StartScreen.this, HomeScreen.this);
//startActivity(intent);
}
d.close();
finish();
}
}
},1000);
This is the logcat.
11-24 22:05:47.270 1740-1740/com.example.andrewspiteri.basket E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.andrewspiteri.basket, PID: 1740
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.andrewspiteri.basket/com.example.andrewspiteri.basket.CityScreen}: java.lang.RuntimeException: native typeface cannot be made
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.RuntimeException: native typeface cannot be made
at android.graphics.Typeface.<init>(Typeface.java:175)
at android.graphics.Typeface.createFromAsset(Typeface.java:149)
at com.example.andrewspiteri.basket.CityScreen.onCreate(CityScreen.java:26)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2148)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
This is CityScreen.java, the program doesn't even get there.
public class CityScreen extends ActionBarActivity {
SQLiteDatabase sqLite;
Spinner city_spinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_city_screen);
Typeface type = Typeface.createFromAsset(getAssets(),"fonts/books.TTF");
//section is to hide the action bar.
ActionBar actionBar = getActionBar();
actionBar.hide();
//Ideally SQL should be handled in a separate helper,
//but for ease of understanding to start
//off, I have kept the code here.
sqLite = this.openOrCreateDatabase("basketbuddy",MODE_PRIVATE, null);
Cursor c = sqLite.rawQuery("SELECT CITY_NAME FROM CITY_LIST",null);
//ideally at least 1 city should be there in city_name
//As I have already synced this with the serve in
//StartScreen.java
if (c.getCount() == 0){
Toast.makeText(getApplicationContext(), "Oh ho..." +
"Some unexpected problem. Please restart the application",
Toast.LENGTH_LONG);
}
TextView city_selection = (TextView) findViewById(R.id.SelectCityText);
city_selection.setTypeface(type);
//Defining the array that will hold the City Names
String[] city_name_db = new String[(c.getCount()+1)];
//By default, the first entry for city list is "Choose City"
//We will understand who this is necessary later.
city_name_db[0] = "Choose City";
//Moving the city names from sqlite to an array city_name_db
if (c.moveToFirst()){
int count = 1;
do {
city_name_db[count] = c.getString(0);
count++;
}
while (c.moveToNext());{
}
//creating an ArrayAdapter for the spinner and then
//associating the ArrayAdapter to the spinner
ArrayAdapter<String> aa = new ArrayAdapter<String>
(getApplicationContext(),R.layout.spinner_item,city_name_db);
city_spinner = (Spinner) findViewById(R.id.spinner1);
city_spinner.setAdapter(aa);
//There is an inherent problem with Spinners. Lets
//assume that there are 3 cities Delhi, Gurgaon, Noida.
//The moment I populate these 3 cities to the spinner,
//by default Delhi will get selected as this is the first
//entry. OnItemSelectedListener will get triggered
//immediately with Delhi as selection and the code will
//proceed. Net net, even the default first value is
//taken as an ItemSelected trigger. The way to bypass
//this is to add a default value 'Choose City' in the
// ArrayAdapter list. Then inside the onItemSelected method,
//ignore if 'Choose City' has been selected.
//SetOnItemSelectedListener listens for any change in item
//if found then it will call onItemSelectedListener listens
//listens for any change in item selected, if found
//then it will call onItemSelected method.
city_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView
parent, View view,
int position, long id) {
if (parent.getItemAtPosition(position).equals("Choose City")){
//do nothing
}
else {
//save selected city as a default city
//for shopping. This city selection is saved in DB
//We may even decide to send to send this data to server,
// however in this example, we are not doing so.
String city_name = city_spinner.getSelectedItem().toString();
Cursor c = sqLite.rawQuery("SELECT CITY_NAME FROM USER_PREF",
null);
if (c.getCount() == 0){sqLite.execSQL("insert into USER_PREF"+"" +
"(CITY_NAME, VOICE_ON) VALUES ('" + city_name +
"', 'Y', 'Y')");
}
if (c.moveToFirst()){
sqLite.execSQL("update USER_PREF set CITY_NAME = '" +
city_name + "'");
}
//Intent intent = new Intent(CityScreen.this, HomeScreen.class);
//startActivity(intent);
sqLite.close();
finish();
}
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
}
I get this error :
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131034188, class android.widget.ListView) with Adapter(class .MainActivity$ListAdapter)]
This is what I do , I run a AsyncTask and get json data , then , onPostExecute, I call ListAdapter that makes the data to listview .
#Override
protected Void doInBackground(Void... arg0) {
Spots_tab1_json sh = new Spots_tab1_json();
String jsonStr = sh.makeServiceCall(url + page, Spots_tab1_json.GET);
if (jsonStr != null) {
JSONObject jsonObj = new JSONObject(jsonStr);
contacts = jsonObj.getJSONArray(TAG_CONTACTS);
for (int i = 0; i < contacts.length(); i++) {
JSONObject c = contacts.getJSONObject(i);
String onvan = new String(c.getString("onvan").getBytes("ISO-8859-1"), "UTF-8");
String id = new String(c.getString("id").getBytes("ISO-8859-1"), "UTF-8");
String dates = new String(c.getString("dates").getBytes("ISO-8859-1"), "UTF-8");
String price = new String(c.getString("gheymat").getBytes("ISO-8859-1"), "UTF-8");
HashMap<String, String> contact = new HashMap<String, String>();
contact.put("onvan", onvan);
contact.put("img", new String(c.getString("img").getBytes("ISO-8859-1"), "UTF-8"));
contactList.add(contact);
}
}
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (!isCancelled() && goterr == false) {
final ListAdapter ladap=new ListAdapter(MainActivity.this, contactList);
lv.setAdapter(ladap);
runOnUiThread(new Runnable() {
public void run() {
ladap.notifyDataSetChanged();
}});
}
}
and after that, my listView is built. I've tested it on several different devices , non of them had any problem but some users told me and I logged and see this error.
What should I do to solve it ? what is wrong ?
thanks you
Apologies for the belated reply, but it appears as suspected in my earlier comment: you're modifying contactList from two different threads.
ListView caches the element count and whenever it has to layout its children, it'll check this count againt the current number of items in the bound adapter. If the count has changed and the ListView wasn't notified about this, an error is thrown:
else if (mItemCount != mAdapter.getCount()) {
throw new IllegalStateException("The content of the adapter has changed but "
+ "ListView did not receive a notification. Make sure the content of "
+ "your adapter is not modified from a background thread, but only from "
+ "the UI thread. Make sure your adapter calls notifyDataSetChanged() "
+ "when its content changes. [in ListView(" + getId() + ", " + getClass()
+ ") with Adapter(" + mAdapter.getClass() + ")]");
}
Source.
In other words: that's exactly the error you're getting. In your scenario, the count difference is being caused by modifying the backing dataset from more than a single thread, leading to a synchronization issue: the background thread may modify the dataset, get suspended, and the ui thread may then call layoutChildren() without having been notified about the dataset changes.
Now, onto the solution. The easiest one is to make sure you're not modifying the list that is bound to the ListView's adapter from different threads. That is, either make a copy that you can then freely modify, or allocate a new list.
So, do something like this, i.e. in the onPreExecute() method of the AsyncTask.
New list:
List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>();
A (shallow) copy:
List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(contactList);
Then, in doInBackground(), add the data to mNewContactList. Finally, in onPostExecute(), you can:
Create a new adapter using mNewContactList and set it to the ListView.
Or (if you didn't make a copy of the original list) add the contents of mNewContactList to the already existing contactList and call notifyDatasetChanged() on the ListView.
data update and your adapter calls notifyDataSetChanged() must in the same code block.
for example:
Correct case:
runOnUiThread(new Runnable() {
#Override
public void run() {
mSelectedImages.add(image);
mAdapter.notifyDataSetChanged();
}
});
Wrong case:
mSelectedImages.add(image);
runOnUiThread(new Runnable() {
#Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
i have a listview with data from database using an sqlite.
when first time (onCreate()) i show the data, there`s no problems, the data all shown.
the problem is, when i want to filter my list view using datepicker and button, the listview not change the data.
ive use listview.invalidateViews(), notifyDataSetChanged()... but still not solved yet. maybe theres some mistake with my code... hope you guys can give me some solution.
thx...
here`s my code
Getting Data From DataBase
public ArrayList<BonRokokModel> getBonRokokList(String userGUID, String transDate, String searchText){
dbPath = "/data/data/app.chameleon.mobile/databases/";
dbName = "SalesTrans.sqlite";
bonRokok = SQLiteDatabase.openDatabase(dbPath + dbName, null,SQLiteDatabase.OPEN_READWRITE);
String strQuery;
strQuery = "SELECT BonRokokID, TransDate, Gudang, Status FROM tblSATBonRokok WHERE UserGUID = ? AND TransDate = ?";
// if(!searchText.equals("")){
// strQuery += "AND (BonRokokID LIKE %'"+ searchText +"'% ) ORDER BY TransDate DESC, BonRokokID Desc";
// }
ArrayList<BonRokokModel> listRokok = new ArrayList<BonRokokModel>();
Cursor mCursor = bonRokok.rawQuery(strQuery, new String[]{userGUID, transDate});
if(mCursor.moveToFirst()){
do {
BonRokokModel dataRokok = new BonRokokModel();
dataRokok.setBonRokokID(mCursor.getString(mCursor.getColumnIndex("BonRokokID")));
dataRokok.setTransDate(mCursor.getString(mCursor.getColumnIndex("TransDate")));
dataRokok.setGudang(mCursor.getString(mCursor.getColumnIndex("Gudang")));
dataRokok.setStatus(mCursor.getString(mCursor.getColumnIndex("Status")));
listRokok.add(dataRokok);
} while (mCursor.moveToNext());
}
bonRokok.close();
return listRokok;
}
My Code to Get Data
public void settingTabItem() {
listView = (ListView) findViewById(R.id.bonRokokMain_lvMain);
listviewMain = new ArrayList<BonRokokModel>();
listviewMain = bonRokokDAO.getBonRokokList(Main_Login.userGUID,utilities.convertDateToDateDBString(edt1.getText().toString()),"");
BonRokokListAdapter adapter = new BonRokokListAdapter(this, listviewMain);
listView.setAdapter(adapter);
}
thank you
Try like this ..
ListArrayAdapter.clear(); // Clear your adapter
ListArrayAdapter.addAll(newItems); // Add new items to it
ListArrayAdapter.notifyDataSetChanged(); // Notify List view for new items in list
OR
You can update list view like this also ..
Firstly clear your list view ..
Then after fetching new data .... create complete list view again.
Use the logical of method settingTabItem() on refresh method. Don't forget to clear the listview. Sorry for the bad English.
I am trying to get saved values in a list. I am creating anotepad and I want when anybody open notepad every saved list display on homepage in a list.
I have successfully saved the value in a database but when I am trying to get a value in a list it is giving full string value like this "com.todo.task.activity#4106a690" in every single row.
I think problem is in my database getlist() method please check:
public List<TaskDetailsActivity> GetAddTaskLists() {
List<TaskDetailsActivity> TaskLists = new ArrayList<TaskDetailsActivity>();
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_TASKLISTS;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
TaskDetailsActivity tasks = new TaskDetailsActivity();
tasks.settaskLists_ID(cursor.getString(0));
tasks.settasklists_Title(cursor.getString(1));
// Adding Doc to list
TaskLists.add(tasks);
} while (cursor.moveToNext());
}
// return Doc list
return TaskLists;
}
Here I am calling database method like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView list_tasklistname = (ListView)findViewById(R.id.list_tasklistname);
TaskManager_Database db = new TaskManager_Database(getApplicationContext());
list = db.GetAddTaskLists();
ArrayAdapter<TaskDetailsActivity> adapter = new ArrayAdapter<TaskDetailsActivity>(getApplicationContext(),
android.R.layout.simple_list_item_1, list);
list_tasklistname.setAdapter(adapter);
adapter.notifyDataSetChanged();
Please let me know what is the error. Thanks
You are getting object representation of objects been added to ListView...
In your case you should return data in cursor.getString(1) for adapter input param i.e. String array...... i.e list must be a string array or ArrayList... if you know what I mean..
check out this sample for ref
Your TaskDetailsActivity class should define a toString() method that returns whatever you want to display for that row.