Async Task Updating UI - android

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.

Related

How To Query Room Database Within An IntentService Without Throwing Exception

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.

Android - Saving RecyclerView content works in one Fragment but not the other

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?

ArrayAdapter.clear() invokes on a null object reference

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)

Using popBackStack() in Android does not update android-listview with Firebase data

At the beginning of the chat app user see a list off groups (listview group) available and the user have the possibility to create a new group or click on some off the available groups and then start to write messages (listview messages). The functions CreateNewMessage and CreateNewGroup pushes information to firebase correctly
Above scenarios works finne problems arise when user navigates backwards (popBackStack()) from listview with messages to GroupFragment, here should user be presented a list off available groups but the listview is empty. The ReadGroupData() function is not reading the already created groups from firebase and inserts them in the group listview. How to make this happen?
GroupFragment:
public void ReadGroupData() {
Firebase firebaserootRef = new Firebase("https://000.firebaseio.com");
firebaserootRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String s) {
if (snapshot.getValue() != null) {
Group newGroup = new Group((String)snapshot.child("name").getValue(),
(String) snapshot.child("id").getValue());
if(!groupKeyValues.contains(newGroup.GetId())) {
groupKeyValues.add(newGroup.GetId());
AddToLstViewGroup(newGroup);
System.out.println("Read group data from firebase and
inserted in listView");
}
}
}
});
}
public void AddToLstViewGroup(Group newGroup) {
groupNameList.add(newGroup);
if(groupAdapter == null) {
groupAdapter = new GroupAdapter(getActivity(), groupNameList);
}
if (lstViewGroup == null) {
lstViewGroup = (ListView) getView().
findViewById(R.id.listView_group);
}
lstViewGroup.setOnItemClickListener(onItemClickListener);
lstViewGroup.setOnItemLongClickListener(onItemLongClickListener);
groupAdapter.notifyDataSetChanged();
lstViewGroup.setAdapter(groupAdapter);
}
ChatFragment:
public void ReadChatMessages(Firebase firebaseRootRef) {
firebaseRootRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String s) {
if (snapshot.child(GetGroupId()).child("messages").
getChildren() != null) {
for (DataSnapshot c :
snapshot.child(GetGroupId()).child("messages").getChildren()) {
String key = c.getKey();
Message newMessage = new Message();
newMessage.SetFrom((String) c.child("from").getValue());
newMessage.SetMsg((String)
c.child("message").getValue());
newMessage.SetTime((String) c.child("time").getValue());
newMessage.SetId((String) c.child("id").getValue());
if ((!msgKeyValues.contains(key)) ||
newMessage.GetFrom() != "") {
msgKeyValues.add(key);
AddToLstViewChat(newMessage);
//Automatic scrolls to last line in listView.
lstViewChat.setSelection(chatAdapter.getCount() -1);
}
}
}
}
public void AddToLstViewChat(Message newMessage) {
chatMsgList.add(newMessage);
if (chatAdapter == null) {
chatAdapter = new ChatAdapter(getActivity(), chatMsgList);
}
if(IsMsgFromMe(newMessage)) {
lstViewChat = (ListView)
getView().findViewById(R.id.listView_chat_message_me);
} else {
lstViewChat =
(ListView)getView().findViewById(R.id.listView_chat_message_others);
}
chatAdapter.notifyDataSetChanged();
lstViewChat.setAdapter(chatAdapter);
}
ChatActivity:
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
finish();
}
}
For all the code click on the link: "http://pastebin.com/97nR68Rm"
SOLUTION!
Kato thank you for you patience and help. I have now found a solution for the problem. I'm calling ReadGroupData() and ReadChatMessages() at the end (before return) in my onCreateView methods. As Kato pointed out onCreate() is not getting called on popBackStack()
In my AddToLStViewGroup the if statement for lstViewGroup is deleted so now it always sets the listView otherwise it will throw an exception for not finding the correct view, To clarifying:
Deleted this line:
if (lstViewGroup == null) {
lstViewGroup = (ListView)getView().findViewById(R.id.listView_group);
}
And replaced with:
ListView lstViewGroup=(ListView)getView().findViewById(R.id.listView_group);
Kato thank you for you patience and help. I have now found a solution for the problem. I'm calling ReadGroupData() and ReadChatMessages() at the end (before return) in my onCreateView methods. As Kato pointed out onCreate() is not getting called on popBackStack()
In my AddToLStViewGroup the if statement for listViewGroup is deleted so now it always sets the listView otherwise it will throw an exception for not finding the correct view.
To clarify:
I deleted this line:
if (lstViewGroup == null) {
lstViewGroup = (ListView)getView().findViewById(R.id.listView_group);
}
And replaced it with:
ListView lstViewGroup =(ListView)getView().findViewById(R.id.listView_group);
(The original asker posted the answer as part of the question. I'm copying it here as a matter of housekeeping.)

Fragments android orientation loss of fragment

I'm having a struggle with fragment loss on orientation change.
At first my fragments seem to be responding well to orientation changes and nothing is wrong.
But the exception is when I navigate back to a previously made fragment. Since I'm checking that in my fragmenthandler activity.
I chose to use a big if statement instead of switch cases, since that was more within the grasp of my knowledge. The handling of the if statements works correctly, have been debugging it with log statements and nothing seems to be wrong there. Below is the code for the handling of fragments, pre-used or create a new one.
public void fragmentHandle(Fragment fl, Fragment fr, String tl, String tr) {
Fragment cFragLeft = manager.findFragmentById(R.id.fragment_left);
Fragment cFragRight = manager.findFragmentById(R.id.fragment_right);
Fragment checkLeft = manager.findFragmentByTag(tl);
Fragment checkRight = manager.findFragmentByTag(tr);
FragmentTransaction ft = manager.beginTransaction();
if (cFragLeft != null && cFragRight != null && checkLeft != null
&& checkRight != null) {
ft.detach(cFragRight).detach(cFragLeft).attach(fr).attach(fl)
.addToBackStack(null).commit();
} else if (cFragLeft != null && cFragRight != null && checkLeft == null
&& checkRight == null) {
ft.detach(cFragLeft).detach(cFragRight)
.add(R.id.fragment_left, fl, tl)
.add(R.id.fragment_right, fr, tr).addToBackStack(null)
.commit();
} else if (cFragLeft != null && cFragRight != null && checkLeft == null
&& checkRight != null) {
ft.detach(cFragLeft).detach(cFragRight).attach(fr)
.add(R.id.fragment_left, fl, tl).addToBackStack(null)
.commit();
} else if (cFragLeft != null && cFragRight != null
&& checkRight == null && checkLeft != null) {
ft.detach(cFragLeft).detach(cFragRight).attach(fl)
.add(R.id.fragment_right, fr, tr).addToBackStack(null)
.commit();
} else {
ft.add(R.id.fragment_left, fl, tl).add(R.id.fragment_right, fr, tr)
.commit();
}
}
When I change orientation, and then navigating back to a previously saved fragment (first if statement) both of my left and right fragments show blank screens. This happens only when case 1 is called, it doesn't seem to remember the fragments that were there, even though the fragmentmanager found them by tag.
I've also been checking my onCreate, and it's shown below.
protected void onCreate(Bundle savedInstanceState) {
// super.onCreate
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_handler);
if (savedInstanceState == null) {
fragmentHandle(nFragLeft, nFragRight, "menuMain", "main");
}
}
Also including a button statement just for completion's sake. Haven't been using onClickListeners and not really sure whether I should prefer this over xml onClick or not.
public void sendDestination(View v) {
nFragLeft = menudestFrag;
nFragRight = destFrag;
String tagLeft = "menuDestination";
String tagRight = "destination";
fragmentHandle(nFragLeft, nFragRight, tagLeft, tagRight);
}
Is there any way to work around this? Only happens after changing orientation.
This happens because your activity is reacreated when orientation is changed.
you can solve this problem adding android:configChanges="orientation|screenSize" in manifest(prevent to recreate activity when orientation was changed):
<activity
android:name="com.example.MyActivity"
android:configChanges="orientation|screenSize" >
</activity>

Categories

Resources