I have the following code in an AsyncTask that retrieves a list of notifications from a DB.
protected void onPostExecute(List<NotificationItem> result) {
super.onPostExecute(result);
if (dialog.isShowing())
dialog.dismiss();
if (adpt != null) {
if (result != null && result.size() > 0)
adpt.setItemList(result);
else if (adpt.getItemList() != null)
adpt.clear();
adpt.notifyDataSetChanged();
}
srl.setRefreshing(false);
}
As you can see, I check the result, the adapter (adpt) and its items (adpt.getItemList()) for being null, before making the relevant action such as .clear(). I want to clear the list when the result size is 0 meaning nothing should be shown to the user (before, it did). When the debug is on the .clear() line, all three objects are not null but still I get NullPointerException.
When debugging inside clear(), I got inside ArrayAdapter.java:
public void clear() {
synchronized (mLock) {
if (mOriginalValues != null) {
mOriginalValues.clear();
} else {
mObjects.clear();
}
}
if (mNotifyOnChange) notifyDataSetChanged();
}
And both mOriginalValues and mObjects are null, hence the error. Not sure what am I doing wrong, can you see the problem?
Update: I instantiate the adapter like this: notificationsAdapter = new NotificationsAdapter(null); ofcourse I do this as first step when I still don't have any items.
The error is:
java.lang.NullPointerException: Attempt to invoke interface method
'void java.util.List.clear()' on a null object reference at
android.widget.ArrayAdapter.clear(ArrayAdapter.java:258) at
com.example.example.fragments.NotificationsList$NotificationsRetriever.onPostExecute(NotificationsList.java:184)
Related
I am querying POJO which is NOT being Observed / Non-Live data from an IntentService that was started in a PreferenceFragment. However a second my application crashes and log displays:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:204)
at android.arch.persistence.room.RoomDatabase.query(RoomDatabase.java:232)
at vault.dao.xxxDao_Impl.getAllNonLivePojoItems(xxxDao_Impl.java:231)
I want to know why is my program throwing this exception. as per https://stackoverflow.com/a/23935791/8623507
my database query[s] are inside an IntentService that runs In its own thread so i should be in the green. here is my code:
Inside IntentService
--------------------
// ERROR OCCURS HERE
List<POJO> pojoList = localRepo.getAllNonLivePojoItems(); // <= ERROR POINTS HERE
if (pojoList != null && pojoList.size() > 0) {
for (Pojo pojo : pojoList ){
// Do Long Running Task Here ....
}
Also I instantiate The Objects Being Used and call the above methods from those Objects Throughout the IntentService in OnHandleIntent like so:
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
LocalRepo localRepo = new LocalRepo(this.getApplication());
PojoHelper pojoHelper = new PojoHelper(this, localRepo);
if (LOGOUT.equals(action) && type != null) {
Log.d(TAG, "onHandleIntent: LOGOUT");
pojoHelper.logoutPojo();
}
else if(DELETE.equals(action) && type != null){
Log.d(TAG, "onHandleIntent: DELETE_POJO");
pojoHelper.deletePojo(true);
}
}
}
I assume you get callback from AsyncTask onPostExecute() method which runs on UI thread. It is prohibited to use database or network calls inside UI thread because it can block UI.
Execute your code where you access database inside new thread.
Example:
Executors.newSingleThreadExecutor().execute(()->{
//TODO access Database
});
One thing i failed to mention was that the method was being executed within an async's response callback method
PojoWarehouse.processPojoItems(new AsyncPojoCallback() {
#Override
public void done(Exception e) {
if (e == null) {
// ERROR OCCURS HERE
List<POJO> pojoList = localRepo.getAllNonLivePojoItems(); // <= ERROR POINTS HERE
if (pojoList != null && pojoList.size() > 0) {
for (Pojo pojo : pojoList ){
// Do Long Running Task Here ....
}
} else
Log.d(TAG, "done: Error Logging Out: " + e.getLocalizedMessage());
}
});
I cannot explain on a technical level why this fixed the issue, however suggestions are welcomed.
I am having a WTF moment today.
Consider the following code snippet in an Android fragment -:
Adapter adapter = new Adapter(getActivity(), points_array_list);
listView.setAdapter(adapter);
//When returning from adapter a particular UI object(ie. a TextView) becomes //null
for(Point p : points_array_list) {
if (p.getTextView() == null) {
Log.wtf("WTF","Why is it NULL ?");
}
}
In the adapter the code is as follows -:
TextView view = (TextView) view.findViewById(R.id.point_textview);
point.setView(view);
Both the adapter and fragment share the same ArrayList<Point> reference.
Now the reason I am doing this is because I have to update the TextView on an event.
Can anyone tell me why does it become null after it returns from the adapter call ?
Also please note I have added the following check to make sure it is not null there -:
if(point.getCoordView() != null) {
Log.wtf("WTF", "Its not null");
}
And the above sanity check works as expected but the object still becomes null when I return from the adapter.
The results in adapter need to a few milliseconds for initializing. You can use this
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
for(Point p : points_array_list) {
if (p.getTextView() == null) {
Log.wtf("WTF","Why is it NULL ?");
}
}
}
}, 500);
I am currently getting a java.lang.IllegalStateException: Fragment HomeFragment{b862cf1} not attached to Activity error when the following async method updates my home page.
new AsyncTask<HomeCardHolder, Void, List<HomeCardModel>>() {
HomeCardHolder mHomeCardHolder;
#Override
protected List<HomeCardModel> doInBackground(HomeCardHolder... params) {
if (params != null && params.length > 0) {
mHomeCardHolder = params[0];
}
return getPunchCardDeals();
}
#Override
protected void onPostExecute(List<HomeCardModel> punchCard) {
if (isActivityAlive() && !ListUtils.isEmpty(punchCard) && mHomeCardHolder != null) {
mCardAdapter.refreshDataForCards(mHomeCardHolder.getSectionName(), punchCard);
}
}
}.execute(homeCardHolder);
The thing is getPunchCardDeals() retrieves the list of deals but also updates UI elements. I've read that UI elements cannot be updated on a background thread, so what is the proper structure here?
Thanks,
Otterman
edit:
I am checking if the fragment is added.
boolean isActivityAlive() {
return null != getActivity() && isAdded() && !getActivity().isFinishing();
}
You should ensure fragment is added with the activity when you are updating view. For this you should use fragment.isAdded().
So replacing
if (isActivityAlive() && !ListUtils.isEmpty(punchCard) && mHomeCardHolder != null)
with
if (isAdded()&&isActivityAlive() && !ListUtils.isEmpty(punchCard) && mHomeCardHolder != null)
may help you.
So I've used the same method in Fragment to other Fragments but only the first Fragment which I got it working saved its state and items inside the Adapter.
Here's the code in onViewCreated checking whether the Adapter is null and if not, use the same old adapter :
if(getArguments() != null){
Parcelable savedRecyclerLayoutState = getArguments().getParcelable(utilities.BUNDLE_RECYCLER_LAYOUT);
if (getArguments().getString("finishPost") != null && getArguments().getString("finishPost").equals("true")){
refreshView();
lobiProgressBar.setVisibility(View.VISIBLE);
} else {
if(savedRecyclerLayoutState == null){
lobiAdapter = new LobiAdapter(this.getActivity());
lobiAdapter.setDisplay(width, height);
recyclerView.setAdapter(lobiAdapter);
refreshView();
} else {
if (lobiAdapter != null && lobiAdapter.getItemCount() > 0) {
manager.onRestoreInstanceState(savedRecyclerLayoutState);
if(lobiAdapter == null){
lobiAdapter = new LobiAdapter(this.getActivity());
lobiAdapter.setDisplay(width, height);
}
recyclerView.setAdapter(lobiAdapter);
lobiProgressBar.setVisibility(View.GONE);
} else /*if (lobiAdapter != null && lobiAdapter.getItemCount() == 0)*/{
refreshView();
lobiProgressBar.setVisibility(View.VISIBLE);
}
}
}
} else {
refreshView();
lobiProgressBar.setVisibility(View.VISIBLE);
}
And here's my onPause() method in the same Fragment which saves RecyclerView's state :
#Override
public void onPause() {
super.onPause();
getArguments().putParcelable(utilities.BUNDLE_RECYCLER_LAYOUT, recyclerView.getLayoutManager().onSaveInstanceState());
if(getActivity().getSupportFragmentManager().getBackStackEntryCount() > 1){
((NewsfeedActivity)getActivity()).hideToolbarBottom();
}
}
Weird thing is, it saves the adapter's values, but I don't know when or how. When I debug it, lobiAdapter shows that it has items in it, whereas, other Fragments which uses same method of saving state has no item.
Am I actually doing it wrong or I am unaware of something which I've done to save the state and items?
While I have been unable to duplicate this bug, I still get a trickle of crash reports with this occurring. I thought adding a null check for my list adapter would fix it, but its still occurring. What am I missing?
Full stacktrace:
http://pastebin.com/Q6GwDU7Q
public void onLoaderReset(Loader<Cursor> loader) {
final int id = loader.getId();
switch (id) {
case LOADER_ID1:
if (mAdapter != null)
mAdapter.changeCursor(null); //Line 512 where stacktrace references
break;
case LOADER_ID2:
//Other code here
break;
default:
throw new InvalidParameterException("id=" + id);
}
}
mAdapter is initialized in onActivityCreated, but I realize while typing this I do not ever release it, maybe I should perform that in onDetach? mAdapter is attached to a ListView set up by a ListFragment. And I set the adapters cursor to null to clear the list I have. So yes, what am I overlooking?
Well after searching through the Android source, I see now that like GreyBearedGeek stated, I should allow the loader to handle the Cursor destruction.
As you can see in CursorLoader it will handle closing the old cursor if passing a new one:
/* Runs on the UI thread */
#Override
public void deliverResult(Cursor cursor) {
if (isReset()) {
// An async query came in while the loader is stopped
if (cursor != null) {
cursor.close();
}
return;
}
Cursor oldCursor = mCursor;
mCursor = cursor;
if (isStarted()) {
super.deliverResult(cursor);
}
if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
oldCursor.close();
}
}
As well as closing the cursor upon a reset.
#Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mCursor != null && !mCursor.isClosed()) {
mCursor.close();
}
mCursor = null;
}
So I will use swapCursor in the future as to not interfere with CursorLoaders handling, even though I've yet to see how this causes my NPE in the stacktrace above.