java.lang.OutOfMemoryError with ArrayList.addAll() - java

I have a list of threads which I have paginated to use an endless scroll the issue I'm having (well my users) is an OutOfMemoryError: Failed to allocate a [x] byte allocation with [y] free bytes and [z] until OOM. the x, y and z attribute is different per user but the cause of the bug is always in the same place and it's when I refresh the posts. I'm completely out of my depth here as I have no idea how to optimise my code or make it so this doesn't happen. As it's the biggest crash on my app at the moment. I've posted my PostFragment below please see the refreshPosts(ArrayList<Posts> newObjects) method as this is where the crash is happening.
public class PostFragment extends Fragment implements View.OnClickListener {
private View mRootView;
private GridLayoutManager mLayoutManager;
private ThreadItem mThreads;
private PostItem mPost;
private PostAdapter mAdapter;
private PostResponse mData;
private EmoticonResponse mEmoticon;
private PostFeedDataFactory mDataFactory;
private EmoticonFeedDataFactory mEmoticonDataFactory;
private static PostFragment mCurrentFragment;
private int REQUEST_CODE;
//Flip
private boolean isFlipped = false;
private Animation flipAnimation;
#BindView(R.id.postsRecyclerView)
RecyclerView mRecyclerView;
#BindView(R.id.toolbarForPosts)
Toolbar mToolbar;
#BindView(R.id.threadText)
TextView mThreadText;
#BindView(R.id.flipText)
TextView mFlipTextView;
#BindView(R.id.shareText)
TextView mShareTextView;
#BindView(R.id.replyText)
TextView mReplyTextView;
#BindView(R.id.scrimColorView)
View mBackgroundView;
#BindView(R.id.fabMenu)
FloatingActionButton mFabMenu;
#BindView(R.id.flipFab)
FloatingActionButton mFlipFab;
#BindView(R.id.shareFab)
FloatingActionButton mShareFab;
#BindView(R.id.replyFab)
FloatingActionButton mReplyFab;
//Collapsing Toolbar
#BindView(R.id.postParentAppBarLayout)
AppBarLayout postAppBarLayout;
#BindView(R.id.postCollapseToolbar)
CollapsingToolbarLayout postCollapseToolbarLayout;
#BindView(R.id.mainImageContainer)
ViewGroup mainContainer;
//Back to top
#BindView(R.id.backToTopButton)
Button mBackToTop;
public static boolean isFromReply;
//FAB
private boolean mIsFabOpen = false;
private Animation fab_open, fab_close, rotate_forward, rotate_backward;
//Pagination
private int mCurrentPage = 1;
private ArrayList<Posts> postList = new ArrayList<>();
private boolean mIsLoading = false;
private boolean mIsLastPage = false;
public static PostFragment newInstance(#NonNull ThreadItem threadItem) {
Bundle args = new Bundle();
args.putParcelable("ThreadItem", Parcels.wrap(threadItem));
mCurrentFragment = new PostFragment();
mCurrentFragment.setArguments(args);
isFromReply = false;
return mCurrentFragment;
}
public static PostFragment newPostInstance(#NonNull PostItem postItem) {
Bundle args = new Bundle();
args.putParcelable("PostItemFromCompose", Parcels.wrap(postItem));
mCurrentFragment = new PostFragment();
mCurrentFragment.setArguments(args);
isFromReply = true;
return mCurrentFragment;
}
public PostFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_post, container, false);
if (savedInstanceState == null) {
ButterKnife.bind(this, mRootView);
initUI();
}
return mRootView;
}
private void initUI() {
//UI Setup
mLayoutManager = new GridLayoutManager(getActivity(), 1);
mRecyclerView.setLayoutManager(mLayoutManager);
mDataFactory = new PostFeedDataFactory(getActivity());
mEmoticonDataFactory = new EmoticonFeedDataFactory(getActivity());
TextView textThreadTopic = (TextView) mRootView.findViewById(R.id.threadTopic);
TextView textNumPosts = (TextView) mRootView.findViewById(R.id.numPosts);
//FAB onClick Set-Up
mFabMenu.setOnClickListener(this);
mShareFab.setOnClickListener(this);
mReplyFab.setOnClickListener(this);
mFlipFab.setOnClickListener(this);
//FAB Animation Set up
fab_open = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.fab_open);
fab_close = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.fab_close);
rotate_forward = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.rotate_forward);
rotate_backward = AnimationUtils.loadAnimation(getActivity().getApplicationContext(),
R.anim.rotate_backward);
//Toolbar
((AppCompatActivity) getActivity()).setSupportActionBar(mToolbar);
((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayShowTitleEnabled(false);
mToolbar.setNavigationIcon(R.drawable.ic_back_white);
mToolbar.invalidate();
mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getActivity().finish();
}
});
//Load Parcel
Intent intent = getActivity().getIntent();
mThreads = Parcels.unwrap(getArguments().getParcelable("ThreadItem"));
mPost = Parcels.unwrap(getArguments().getParcelable("PostItemFromCompose"));
if (mThreads != null) {
if (mThreads.getName() != null) {
mThreadText.setText(mThreads.getName());
}
if (mThreads.getTopic_name() != null) {
textThreadTopic.setText(mThreads.getTopic_name());
}
if (mThreads.getNum_posts() != null) {
int numPosts = Integer.parseInt(mThreads.getNum_posts());
if (numPosts > 1000) {
textNumPosts.setText("1K");
} else {
textNumPosts.setText(mThreads.getNum_posts());
}
}
}
postAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = false;
int scrollRange = -1;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {
postCollapseToolbarLayout.setTitle("Threads");
mainContainer.setVisibility(View.INVISIBLE);
isShow = true;
} else if (isShow) {
postCollapseToolbarLayout.setTitle("");
isShow = false;
mainContainer.setVisibility(View.VISIBLE);
}
}
});
flipAnimation =
AnimationUtils.loadAnimation(getActivity().getApplicationContext(), R.anim.flip);
loadData(true, 1);
}
private void loadData(final boolean firstLoad, int readDirection) {
if (isFromReply) {
if (mPost.getThread_id() != null) {
mDataFactory.getPostFeed(mPost.getThread_id(), readDirection, mCurrentPage,
new PostFeedDataFactory.PostFeedDataFactoryCallback() {
#Override
public void onPostDataReceived(PostResponse response) {
mData = response;
if (mData.getItems() != null) {
for (int i = 0; i < mData.getItems().size(); i++) {
Posts singlePost = response.getItems().get(i);
postList.add(singlePost);
}
if (firstLoad) {
mIsLoading = false;
mData.getItems().clear();
mData.getItems().addAll(postList);
mEmoticonDataFactory.getEmoticonFeed(
new EmoticonFeedDataFactory.EmoticonFeedDataFactoryCallback() {
#Override
public void onEmoticonDataReceived(EmoticonResponse response) {
mEmoticon = response;
populateUIWithData();
}
#Override
public void onEmoticonDataFailed(Exception exception) {
}
});
} else {
mIsLoading = false;
refreshPosts(postList);
}
if (mData.getItems().size() > 0) {
if (Integer.valueOf(mData.getTotalPosts()) >= response.getItems().size()) {
mCurrentPage++;
} else {
mIsLastPage = true;
}
}
}
}
#Override
public void onPostDataFailed(Exception exception) {
customToast("Error: " + exception.toString());
}
});
}
} else {
if (mThreads.getId() != null)
mDataFactory.getPostFeed(mThreads.getId(), readDirection, mCurrentPage,
new PostFeedDataFactory.PostFeedDataFactoryCallback() {
#Override
public void onPostDataReceived(PostResponse response) {
mData = response;
if (mData.getItems() != null) {
for (int i = 0; i < mData.getItems().size(); i++) {
Posts singlePost = response.getItems().get(i);
postList.add(singlePost);
}
if (firstLoad) {
mIsLoading = false;
mData.getItems().clear();
mData.getItems().addAll(postList);
mEmoticonDataFactory.getEmoticonFeed(
new EmoticonFeedDataFactory.EmoticonFeedDataFactoryCallback() {
#Override
public void onEmoticonDataReceived(EmoticonResponse response) {
mEmoticon = response;
populateUIWithData();
}
#Override
public void onEmoticonDataFailed(Exception exception) {
}
});
} else {
mIsLoading = false;
refreshPosts(postList);
}
if (mData.getItems().size() > 0) {
if (Integer.valueOf(mData.getTotalPosts()) >= response.getItems().size()) {
mCurrentPage++;
} else {
mIsLastPage = true;
}
}
}
}
#Override
public void onPostDataFailed(Exception exception) {
customToast("Error: " + exception.toString());
}
});
}
}
private void populateUIWithData() {
ImageButton moreOptionsButton = (ImageButton) mRootView.findViewById(R.id.moreOptions);
moreOptionsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PopupMenu popupMenu = new PopupMenu(v.getContext(), v);
popupMenu.inflate(R.menu.thread_options);
popupMenu.getMenu();
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.watch:
WatchedThreadsRequestData watchedThreadsRequestData = new WatchedThreadsRequestData(getActivity());
watchedThreadsRequestData.setWatchedThread(mThreads.getId(), new WatchedThreadsRequestData.WatchedThreadsFeedback() {
#Override
public void onWatchedRequestReceived(ThreadResponse response) {
customToast("Thread watched");
}
#Override
public void onWatchedRequestFailed(Exception exception) {
customToast("Thread wasn't watched: " + exception.toString());
}
});
return true;
case R.id.shareThread:
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.putExtra(Intent.EXTRA_TEXT, mThreads.getName() + " - " + Constants.LIVE_URL +
"talk/" + mThreads.getTopic_url() + '/' + mThreads.getThread_url());
sharingIntent.setType("text/plain");
getActivity().startActivity(Intent.createChooser(sharingIntent, "Share via"));
return true;
case R.id.hideThread:
customToast("Hide: coming soon");
return true;
default:
customToast("Somethings Wrong");
return true;
}
}
});
setForceShowIcon(popupMenu);
popupMenu.show();
}
});
if (mAdapter == null) {
mAdapter = new PostAdapter(getActivity(), mData, mEmoticon);
mRecyclerView.setAdapter(mAdapter);
} else {
mAdapter.setData(mData.getItems());
mAdapter.notifyDataSetChanged();
}
mRecyclerView.addOnScrollListener(paginationListener);
}
public static void setForceShowIcon(PopupMenu popupMenu) {
try {
Field[] fields = popupMenu.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popupMenu);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private RecyclerView.OnScrollListener paginationListener = new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
boolean hasEnded = newState == SCROLL_STATE_IDLE;
if (hasEnded) {
mFabMenu.show();
mFabMenu.setClickable(true);
} else {
if (mIsFabOpen)
closeMenu();
mFabMenu.hide();
mFabMenu.setClickable(false);
}
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = mLayoutManager.getChildCount();
int totalItemCount = mLayoutManager.getItemCount();
int firstVisibleItemPosition = mLayoutManager.findFirstVisibleItemPosition();
if (!mIsLoading && !mIsLastPage) {
if ((visibleItemCount + firstVisibleItemPosition) >= totalItemCount) {
loadMoreItems();
}
}
//Back to top
if (mLayoutManager.findLastVisibleItemPosition() == totalItemCount - 1) {
mBackToTop.setVisibility(View.VISIBLE);
mBackToTop.setClickable(true);
mBackToTop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mLayoutManager.scrollToPositionWithOffset(0,0);
}
});
} else {
mBackToTop.setVisibility(View.GONE);
mBackToTop.setClickable(false);
}
}
};
private void loadMoreItems() {
if (!isFlipped) {
mIsLoading = true;
loadData(false, 1);
} else {
mIsLoading = true;
loadData(false, -1);
}
}
private void refreshPosts(ArrayList<Posts> newObjects) {
postList.addAll(newObjects);
populateUIWithData();
}
#Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.fabMenu:
animateFAB();
break;
case R.id.shareFab:
share();
break;
case R.id.replyFab:
reply();
break;
case R.id.flipFab:
flip();
break;
}
}
public void animateFAB() {
if (mIsFabOpen) {
closeMenu();
} else {
mFabMenu.startAnimation(rotate_forward);
mReplyFab.startAnimation(fab_open);
mShareFab.startAnimation(fab_open);
mFlipFab.startAnimation(fab_open);
mReplyFab.setClickable(true);
mShareFab.setClickable(true);
mFlipFab.setClickable(true);
mFlipTextView.setVisibility(View.VISIBLE);
mShareTextView.setVisibility(View.VISIBLE);
mReplyTextView.setVisibility(View.VISIBLE);
mBackgroundView.setVisibility(View.VISIBLE);
mIsFabOpen = true;
}
}
private void closeMenu() {
mFabMenu.startAnimation(rotate_backward);
mReplyFab.startAnimation(fab_close);
mShareFab.startAnimation(fab_close);
mFlipFab.startAnimation(fab_close);
mReplyFab.setClickable(false);
mShareFab.setClickable(false);
mFlipFab.setClickable(false);
mFlipTextView.setVisibility(View.INVISIBLE);
mShareTextView.setVisibility(View.INVISIBLE);
mReplyTextView.setVisibility(View.INVISIBLE);
mBackgroundView.setVisibility(View.INVISIBLE);
mIsFabOpen = false;
}
private void reply() {
PreferenceConnector.writeString(getActivity().getApplicationContext(), "threadID", mThreads.getId());
PreferenceConnector.writeString(getActivity().getApplicationContext(), "threadTitle", mThreads.getName());
if (PreferenceConnector.readString(getActivity(), "authToken") == null ||
PreferenceConnector.readString(getActivity(), "authToken").equalsIgnoreCase("skip")) {
final AlertDialog.Builder loginDialog = new AlertDialog.Builder(getActivity());
loginDialog.setTitle("Please log in");
loginDialog.setMessage("You need to be logged in to reply");
loginDialog.setPositiveButton("Log in", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getActivity().getApplicationContext(), LoginActivity.class);
startActivity(intent);
}
});
loginDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
loginDialog.show();
} else {
closeMenu();
Intent intent = new Intent(getActivity().getApplicationContext(), NewPostActivity.class);
intent.putExtra("Threads", Parcels.wrap(mThreads));
getActivity().finish();
startActivityForResult(intent, REQUEST_CODE);
}
}
private void share() {
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.putExtra(Intent.EXTRA_TEXT, mThreads.getName() + " - " + Constants.LIVE_URL +
"talk/" + mThreads.getTopic_url() + '/' + mThreads.getThread_url());
sharingIntent.setType("text/plain");
startActivity(Intent.createChooser(sharingIntent, "Share via"));
}
private void flip() {
if (!isFlipped) {
mAdapter.clearAll();
isFlipped = true;
mRecyclerView.startAnimation(flipAnimation);
loadData(false, -1);
closeMenu();
} else {
mAdapter.clearAll();
isFlipped = false;
mRecyclerView.startAnimation(flipAnimation);
loadData(true, 1);
closeMenu();
}
}
private void customToast(String toastMessage) {
LayoutInflater inflater = getActivity().getLayoutInflater();
View layout = inflater.inflate(R.layout.custom_toast,
(ViewGroup) getActivity().findViewById(R.id.toastContainer));
TextView customToastText = (TextView) layout.findViewById(R.id.customToastText);
customToastText.setText(toastMessage);
Toast toast = new Toast(getActivity().getApplicationContext());
toast.setGravity(Gravity.BOTTOM, 0, 25);
toast.setDuration(Toast.LENGTH_LONG);
toast.setView(layout);
toast.show();
}
#Override
public void onResume() {
super.onResume();
if (mData != null && mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
if (mIsFabOpen) {
closeMenu();
} else {
getActivity().finish();
}
return true;
}
return false;
}
});
}
public void updateView() {
mAdapter.notifyDataSetChanged();
}
}
Thanks in advance once again.

Your problem basically boils down to this:
private void refreshPosts(ArrayList<Posts> newObjects) {
postList.addAll(newObjects);
populateUIWithData();
}
The list can only get bigger, never smaller. If the server has lots and lots of posts, then OutOfMemory is pretty much inevitable.
One approach to solving this problem is to use an LRU (Least Recently Used) cache. There is a utility class you can use: android.util.LruCache.
An LRU Cache is essentially a Map. Items are stored with a key, like an ID. With an LRU cache, you put new items in, but once a pre-determined limit is reached, old items start getting pushed out to make room for new items.
This will save memory, but make a lot more management code for you.
Your adapter, instead of having a list of posts, will just have a list of the post IDs. This should be much easier on memory.
As the user scrolls and you collect more posts, you add the post ID to the list, and map the post into the LRU cache using the post ID.
When you bind to the list item view, you look up the post using the post's ID in the LRU cache.
If it's there, great. That's called a cache hit. Bind the post to the
list item view.
If not, then you have a cache miss. You have some work to do.
Start a server request to retrieve the post by ID. I see your current code just retrieves blocks of posts, so you'll need some new server code here.
When the request completes, put the post in the LRU cache and let the adapter know your item has changed using adapter.notifyItemChanged(). Unless the user has scrolled beyond it, the RecyclerView should try to bind with the list item view again. This time, you should get a cache hit.
This is the basic idea. I'd write some code, but I still have a lot of questions since I can't see your model classes, data factories, and adapter class.
Once you have it working, you have to tune the limit on the cache so that it's low enough not to overrun memory, but high enough that your hit/miss ratio isn't close to zero.
BTW, I noticed that you are making the mistake of creating a new adapter and handing it to the RecyclerView each time you get a block of posts. You should create your adapter once, keep a reference to it and update it. Have a method that adds a block of posts then calls notifyDataSetChanged().
Another idea for conserving memory is to use text compression. If the problem is more a result of a large average size of the post rather than a large number of posts, you might explore this idea in addition to the LRU cache.
The concept is that you could take posts over a certain size, write them into a buffer using a ZipOutputStream then save the buffer in memory. When it's time to display the post, you read the buffer with a ZipInputStream to uncompress the text. Here the issue is performance as the compression/decompression is pretty CPU-intensive. But if the problem is really long posts, this approach might be something to consider.
An even better approach: Only save the first part of the post as an "overview" display in the list. When the user clicks on the list item, retrieve the entire post from the server and display that in another page.

Related

my app is crashing when there is no internet and i want my markers or my map to refresh each second

i made a googlemap app where my markers are displyed.
the problem is when there no internet, the app is crashing. The code under onResume does not solve the problem.
also i want to refresh the map or markers each second on the map.
if you have any ideas please i am here to learn from all of you.
here is my MapActivity code :
public class MapActivity extends AppCompatActivity implements OnMapReadyCallback
{
private static final String TAG = "MapActivity";
#Bind(R.id.back)
View back;
#Bind(R.id.zoom_in)
View zoom_in;
#Bind(R.id.zoom_out)
View zoom_out;
#Bind(R.id.updatetimer)
TextView updatetimer;
#Bind(R.id.autozoom)
ImageView autozoom;
#Bind(R.id.showtails)
ImageView showtails;
#Bind(R.id.geofences)
ImageView showGeofences;
#Bind(R.id.map_layer)
ImageView map_layer_icon;
private GoogleMap map;
#Bind(R.id.content_layout)
View content_layout;
#Bind(R.id.loading_layout)
View loading_layout;
#Bind(R.id.nodata_layout)
View nodata_layout;
private Timer timer;
private int autoZoomedTimes = 0;// dėl bugo osmdroid library, zoom'inam du kartus ir paskui po refresh'o nebe, nes greičiausiai user'is bus pakeitęs zoom'ą
private HashMap<Integer, Marker> deviceIdMarkers;
private HashMap<String, Device> markerIdDevices;
private HashMap<Integer, Polyline> deviceIdPolyline;
private HashMap<Integer, LatLng> deviceIdLastLatLng;
// private HashMap<Integer, Marker> deviceIdSmallMarkerInfo;
private long lastRefreshTime;
boolean isAutoZoomEnabled = true;
boolean isShowTitlesEnabled;
boolean isShowTailsEnabled = true;
boolean isShowGeofencesEnabled = true;
private String stopTime;
private AsyncTask downloadingAsync;
private boolean isRefreshLoced = false;
ApiInterface.GetGeofencesResult geofencesResult;
ArrayList<PolygonWithName> polygonsWithDetails = new ArrayList<>();
float previousZoomLevel = 0;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
ButterKnife.bind(this);
deviceIdMarkers = new HashMap<>();
markerIdDevices = new HashMap<>();
deviceIdPolyline = new HashMap<>();
deviceIdLastLatLng = new HashMap<>();
// deviceIdSmallMarkerInfo = new HashMap<>();
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
protected void onResume()
{
super.onResume();
timer = new Timer();
timer.schedule(new TimerTask()
{
#Override
public void run()
{
runOnUiThread(new Runnable()
{
#Override
public void run()
{
float timeleft = 10 - Math.round(System.currentTimeMillis() - lastRefreshTime) / 1000f;
if (timeleft < 0)
timeleft = 0;
updatetimer.setText(String.format("%.0f", timeleft));
if (System.currentTimeMillis() - lastRefreshTime >= 10 * 1000)
if (map != null)
refresh();
}
});
}
}, 0, 1000);
}
#Override
protected void onPause()
{
super.onPause();
try
{
timer.cancel();
timer.purge();
downloadingAsync.cancel(true);
} catch (Exception e)
{
e.printStackTrace();
}
}
private void refresh()
{
if (isRefreshLoced)
return;
isRefreshLoced = true;
lastRefreshTime = System.currentTimeMillis();
final String api_key = (String) DataSaver.getInstance(this).load("api_key");
API.getApiInterface(this).getDevices(api_key, getResources().getString(R.string.lang), new Callback<ArrayList<ApiInterface.GetDevicesItem>>()
{
#Override
public void success(final ArrayList<ApiInterface.GetDevicesItem> getDevicesItems, Response response)
{
Log.d(TAG, "success: loaded devices array");
final ArrayList<Device> allDevices = new ArrayList<>();
if (getDevicesItems != null)
for (ApiInterface.GetDevicesItem item : getDevicesItems)
allDevices.addAll(item.items);
API.getApiInterface(MapActivity.this).getFieldsDataForEditing(api_key, getResources().getString(R.string.lang), 1, new Callback<ApiInterface.GetFieldsDataForEditingResult>()
{
#Override
public void success(final ApiInterface.GetFieldsDataForEditingResult getFieldsDataForEditingResult, Response response)
{
Log.d(TAG, "success: loaded icons");
downloadingAsync = new AsyncTask<Void, Void, Void>()
{
ArrayList<MarkerOptions> markers;
ArrayList<Integer> deviceIds;
#Override
protected Void doInBackground(Void... params)
{
// add markers
int dp100 = Utils.dpToPx(MapActivity.this, 50);
markers = new ArrayList<>();
deviceIds = new ArrayList<>();
if (getFieldsDataForEditingResult == null || getFieldsDataForEditingResult.device_icons == null)
return null;
for (Device item : allDevices)
{
if (isCancelled())
break;
if (item.device_data.active == 1)
{
// ieškom ikonos masyve
DeviceIcon mapIcon = null;
for (DeviceIcon icon : getFieldsDataForEditingResult.device_icons)
if (item.device_data.icon_id == icon.id)
mapIcon = icon;
String server_base = (String) DataSaver.getInstance(MapActivity.this).load("server_base");
try
{
Log.d("MapActivity", "DOWNLOADING BITMAP: " + server_base + mapIcon.path);
Bitmap bmp = BitmapFactory.decodeStream(new URL(server_base + mapIcon.path).openConnection().getInputStream());
int srcWidth = bmp.getWidth();
int srcHeight = bmp.getHeight();
int maxWidth = Utils.dpToPx(MapActivity.this, mapIcon.width);
int maxHeight = Utils.dpToPx(MapActivity.this, mapIcon.height);
float ratio = Math.min((float) maxWidth / (float) srcWidth, (float) maxHeight / (float) srcHeight);
int dstWidth = (int) (srcWidth * ratio);
int dstHeight = (int) (srcHeight * ratio);
bmp = bmp.createScaledBitmap(bmp, dp100, dp100, true);
// marker
MarkerOptions m = new MarkerOptions();
m.position(new LatLng(item.lat, item.lng));
// marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
// marker.setIcon(new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true)));
m.icon(BitmapDescriptorFactory.fromBitmap(Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight, true)));
// info window
// MapMarkerInfoWindow infoWindow = new MapMarkerInfoWindow(MapActivity.this, item, R.layout.layout_map_infowindow, map);
// marker.setInfoWindow(infoWindow);
markers.add(m);
deviceIds.add(item.id);
} catch (OutOfMemoryError outOfMemoryError)
{
Toast.makeText(MapActivity.this, "Out of memory! Too many devices are selected to be displayed", Toast.LENGTH_LONG).show();
} catch (Exception e)
{
e.printStackTrace();
}
}
}
return null;
}
#Override
protected void onPostExecute(Void aVoid)
{
ArrayList<GeoPoint> points = new ArrayList<>();
if (autoZoomedTimes < 1)
{
new Handler().postDelayed(new Runnable()
{
#Override
public void run()
{
runOnUiThread(new Runnable()
{
#Override
public void run()
{
if (markers.size() > 1)
{
try
{
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (MarkerOptions item : markers)
builder.include(item.getPosition());
LatLngBounds bounds = builder.build();
// int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, Utils.dpToPx(MapActivity.this, 50));
map.animateCamera(cu);
} catch (Exception e)
{
}
} else if (markers.size() > 0)
{
map.moveCamera(CameraUpdateFactory.newLatLngZoom(markers.get(0).getPosition(), 15));
}
autoZoomedTimes++;
}
});
}
}, 50);
} else if (isAutoZoomEnabled)
{
if (markers.size() > 1)
{
try
{
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (MarkerOptions item : markers)
builder.include(item.getPosition());
LatLngBounds bounds = builder.build();
// int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, Utils.dpToPx(MapActivity.this, 50));
map.animateCamera(cu);
} catch (Exception e)
{
}
} else if (markers.size() > 0)
{
map.moveCamera(CameraUpdateFactory.newLatLngZoom(markers.get(0).getPosition(), 15));
}
autoZoomedTimes++;
}
Log.d(TAG, "onPostExecute: icons downloaded and added to map, total markers: " + markers.size());
loading_layout.setVisibility(View.GONE);
if (markers.size() != 0)
content_layout.setVisibility(View.VISIBLE);
else
nodata_layout.setVisibility(View.VISIBLE);
for (int i = 0; i < markers.size(); i++)
{
MarkerOptions options = markers.get(i);
int deviceId = deviceIds.get(i);
Marker m;
Polyline polyline;
if (deviceIdMarkers.containsKey(deviceId))
{
Log.d("aa", "moving to" + options.getPosition());
deviceIdMarkers.get(deviceId).setPosition(new LatLng(options.getPosition().latitude, options.getPosition().longitude));
m = deviceIdMarkers.get(deviceId);
polyline = deviceIdPolyline.get(deviceId);
} else
{
Log.d("aa", "putting new");
m = map.addMarker(options);
deviceIdMarkers.put(deviceId, m);
polyline = map.addPolyline(new PolylineOptions());
deviceIdPolyline.put(deviceId, polyline);
}
Device thatonedevice = null;
for (Device device : allDevices)
if (device.id == deviceId)
thatonedevice = device;
markerIdDevices.put(m.getId(), thatonedevice);
// update marker rotation based on driving direction
if (thatonedevice != null && deviceIdLastLatLng.containsKey(deviceId))
{
double dirLat = thatonedevice.lat - deviceIdLastLatLng.get(deviceId).latitude;
double dirLng = thatonedevice.lng - deviceIdLastLatLng.get(deviceId).longitude;
m.setRotation((float) Math.toDegrees(Math.atan2(dirLng, dirLat)));
}
deviceIdLastLatLng.put(deviceId, new LatLng(thatonedevice.lat, thatonedevice.lng));
List<LatLng> polylinePoints = new ArrayList<>();
for (TailItem item : thatonedevice.tail)
polylinePoints.add(new LatLng(Double.valueOf(item.lat), Double.valueOf(item.lng)));
polyline.setPoints(polylinePoints);
polyline.setWidth(Utils.dpToPx(MapActivity.this, 2));
polyline.setColor(Color.parseColor(thatonedevice.device_data.tail_color));
}
// else
map.setInfoWindowAdapter(new GoogleMap.InfoWindowAdapter()
{
#Override
public View getInfoWindow(Marker marker)
{
return null;
}
#Override
public View getInfoContents(final Marker marker)
{
synchronized (this)
{
}
final Device device = markerIdDevices.get(marker.getId());
if (device != null)
{
View view = getLayoutInflater().inflate(R.layout.layout_map_infowindow, null);
view.bringToFront();
view.findViewById(R.id.close).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
marker.hideInfoWindow();
}
});
TextView device_name = (TextView) view.findViewById(R.id.device_name);
device_name.setText(device.name);
TextView altitude = (TextView) view.findViewById(R.id.altitude);
altitude.setText(String.valueOf(device.altitude) + " " + device.unit_of_altitude);
TextView time = (TextView) view.findViewById(R.id.time);
time.setText(device.time);
TextView stopTimeView = (TextView) view.findViewById(R.id.stopTime);
stopTimeView.setText(stopTime);
TextView speed = (TextView) view.findViewById(R.id.speed);
speed.setText(device.speed + " " + device.distance_unit_hour);
TextView address = (TextView) view.findViewById(R.id.address);
address.setText(device.address);
final ArrayList<Sensor> showableSensors = new ArrayList<>();
for (Sensor item : device.sensors)
if (item.show_in_popup > 0)
showableSensors.add(item);
ListView sensors_list = (ListView) view.findViewById(R.id.sensors_list);
sensors_list.setAdapter(new AwesomeAdapter<Sensor>(MapActivity.this)
{
#Override
public int getCount()
{
return showableSensors.size();
}
#NonNull
#Override
public View getView(int position, View convertView, #NonNull ViewGroup parent)
{
if (convertView == null)
convertView = getLayoutInflater().inflate(R.layout.adapter_map_sensorslist, null);
Sensor item = showableSensors.get(position);
TextView name = (TextView) convertView.findViewById(R.id.name);
name.setText(item.name);
TextView value = (TextView) convertView.findViewById(R.id.value);
value.setText(item.value);
return convertView;
}
});
List<Address> addresses;
try
{
addresses = new Geocoder(MapActivity.this).getFromLocation(device.lat, device.lng, 1);
if (addresses.size() > 0)
address.setText(addresses.get(0).getAddressLine(0));
} catch (IOException e)
{
e.printStackTrace();
}
return view;
}
return null;
}
});
map.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener()
{
#Override
public boolean onMarkerClick(final Marker marker)
{
int px = Utils.dpToPx(MapActivity.this, 300);
map.setPadding(0, px, 0, 0);
stopTime = "...";
final Device device = markerIdDevices.get(marker.getId());
if (device != null)
{
API.getApiInterface(MapActivity.this).deviceStopTime((String) DataSaver.getInstance(MapActivity.this).load("api_key"), "en", device.id, new Callback<ApiInterface.DeviceStopTimeResult>()
{
#Override
public void success(ApiInterface.DeviceStopTimeResult result, Response response)
{
stopTime = result.time;
marker.showInfoWindow();
}
#Override
public void failure(RetrofitError retrofitError)
{
Toast.makeText(MapActivity.this, R.string.errorHappened, Toast.LENGTH_SHORT).show();
}
});
}
return false;
}
});
map.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener()
{
#Override
public void onInfoWindowClick(Marker marker)
{
marker.hideInfoWindow();
}
});
map.setOnInfoWindowCloseListener(new GoogleMap.OnInfoWindowCloseListener()
{
#Override
public void onInfoWindowClose(Marker marker)
{
map.setPadding(0, 0, 0, 0);
}
});
// updateSmallMarkerData(allDevices);
isRefreshLoced = false;
}
}.execute();
}
#Override
public void failure(RetrofitError retrofitError)
{
Toast.makeText(MapActivity.this, R.string.errorHappened, Toast.LENGTH_SHORT).show();
isRefreshLoced = false;
}
});
}
#Override
public void failure(RetrofitError retrofitError)
{
Toast.makeText(MapActivity.this, R.string.errorHappened, Toast.LENGTH_SHORT).show();
isRefreshLoced = false;
}
});
}
#Override
public void onMapReady(GoogleMap googleMap)
{
map = googleMap;
refresh();
}
this MapActivity is slow to load, could you teach me a way to make it goes faster?
Best regard :)
waiting for your propositions.
PS: i have removed some functions to make the code look short but i kept the most important in is case.
For the crashing issue
create a class
public class NetWorkChecker {
static NetworkInfo wifi, mobile;
public static Boolean check(Context c) {
ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
try {
wifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
mobile = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
} catch (Exception e) {
e.printStackTrace();
}
if (wifi != null && wifi.isConnected() && wifi.isAvailable()) {
return true;
} else if (mobile != null && mobile.isAvailable() && mobile.isConnected()) {
return true;
} else {
//Toast.makeText(c, "No Network Connection", Toast.LENGTH_SHORT).show();
// ((Activity) c).finish();
displayMobileDataSettingsDialog(c,"No Network Connection","No Network Connection");
return false;
}
}
public static AlertDialog displayMobileDataSettingsDialog(final Context context, String title, String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(title);
builder.setMessage(message);
builder.setCancelable(false);
builder.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
context.startActivity(intent);
}
});
builder.show();
return builder.create();
}
}
to check if the devise have an active internet connection
call
if (!NetWorkChecker.check(this)){
////do your refres
}

Android tv app keep Crashing with memory exception error

I am creating an android tv app in the app I am sliding infinity loop fragments (Image and Video URLs ). I use glide for image loading and exoplayer2 for video loading it's working perfectly in my emulator and other 2-3 devices but when I run it in my actual android tv it keeps crashing after some time with an error memory exception. I tested it In emulator using profiler how much memory it using but it is only using min 100 Mb not bigger than that I even check for a leak using leak cannery no leak was found I can't find any solution can someone please help me
Fragment:
`public class BannerFragment extends Fragment {
/*TODO TAGs*/
private String TAG = BannerFragment.class.getSimpleName();
/*TODO View Binding*/
private BannerFragmentBinding vb;
private PromotionalBanner promotionalBanner;
private int position;
private ArrayList<PromotionalBanner> promotionalBannerArrayList;
private CountDownTimer timer;
SimpleExoPlayer simpleExoPlayer;
private final String VIDEO_BUFFER_TIMER = "10000";
/*TODO Glide Listener*/
private RequestListener requestListener = new RequestListener<Drawable>() {
#Override
public boolean onLoadFailed(#Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
if (vb != null) {
ShowToast(" Can't load image ");
vb.avi.setVisibility(View.GONE);
slideNextItem();
}
return false;
}
#Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
if (vb != null) {
vb.avi.setVisibility(View.GONE);
}
return false;
}
};
/*TODO PlayerListener*/
private Player.Listener playerListener = new Player.Listener() {
#Override
public void onIsPlayingChanged(boolean isPlaying) {
Log.e(TAG, "onIsPlayingChanged: " + isPlaying);
}
#Override
public void onPlaybackStateChanged(int playbackState) {
if (playbackState == Player.STATE_ENDED) {
slideNextItem();
} else if (playbackState == Player.STATE_READY) {
vb.avi.setVisibility(View.GONE);
if (timer != null) {
timer.cancel();
timer = null;
}
Log.e("TimerCheck", "onPlaybackStateChanged: endTimer "+timer );
} else if (playbackState == Player.STATE_BUFFERING) {
vb.avi.setVisibility(View.VISIBLE);
setSlideTimer(VIDEO_BUFFER_TIMER);
Log.e("TimerCheck", "onPlaybackStateChanged: startTimer "+timer );
} else if (playbackState == Player.STATE_IDLE) {
slideNextItem();
}
}
#Override
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
}
};
/*TODO get Fragment Instant*/
public static Fragment getInstantFragment(PromotionalBanner promotionalBanner,
int index,
ArrayList<PromotionalBanner> promotionalBannerArrayList) {
BannerFragment fragment = new BannerFragment();
Bundle bundle = new Bundle();
bundle.putParcelable("promotionalBanner", promotionalBanner);
bundle.putInt("position", index);
bundle.putParcelableArrayList("list", promotionalBannerArrayList);
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.promotionalBanner = getArguments().getParcelable("promotionalBanner");
this.position = getArguments().getInt("position");
this.promotionalBannerArrayList = getArguments().getParcelableArrayList("list");
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
vb = BannerFragmentBinding.inflate(getLayoutInflater());
return vb.getRoot();
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
#Override
public void onResume() {
super.onResume();
if (!isImageFile(promotionalBanner.getMedia())) {
showVideoViewContainer();
setVideo(promotionalBanner);
} else {
hideVideoViewContainer();
loadImage(promotionalBanner.getMedia(), promotionalBanner.getDuration());
/*TODo set image Timer*/
setSlideTimer(promotionalBanner.getDuration());
}
}
/*TODO setVideo*/
private void setVideo(PromotionalBanner promotionalBanner) {
Uri uri = Uri.parse(promotionalBanner.getMedia());
Log.e("dataUriTest", "setVideo: " + uri);
simplePlayer(uri);
}
/*TODO Simple Player*/
private void simplePlayer(Uri uri) {
simpleExoPlayer = new SimpleExoPlayer.Builder(getContext()).build();
simpleExoPlayer.addMediaItem(MediaItem.fromUri(uri));
setSlideTimer(VIDEO_BUFFER_TIMER);
Log.e("TimerCheck", "onPlaybackStateChanged:insideSimplePlayer startTimer "+timer );
vb.avi.setVisibility(View.VISIBLE);
vb.videoView.hideController();
vb.videoView.setPlayer(simpleExoPlayer);
simpleExoPlayer.prepare();
simpleExoPlayer.setPlayWhenReady(true);
simpleExoPlayer.addListener(playerListener);
}
/*TODO Go To next From vide Slide*/
private void slideNextItem() {
if(simpleExoPlayer != null){
simpleExoPlayer.release();
simpleExoPlayer = null;
}
int nextposition = position + 1;
goToNextItem(nextposition);
}
#Override
public void onPause() {
super.onPause();
Log.e("LogBannerFragment", "onPause: ");
if (timer != null) {
timer.cancel();
}
if (simpleExoPlayer != null) {
simpleExoPlayer.release();
simpleExoPlayer = null;
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e("Destory", "onDestroy: " );
if (playerListener != null) {
playerListener = null;
}
if (requestListener != null) {
requestListener = null;
}
if (vb != null) {
vb = null;
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
}
/*TODO load Image*/
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void loadImage(String media, String duration) {
vb.avi.setVisibility(View.VISIBLE);
Glide.with(getContext()).load(convert_http_https(media))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.error(getContext().getDrawable(R.drawable.banner))
.addListener(requestListener)
.into(vb.imageView);
}
/*TODO convert url http to https in android studio*/
private String convert_http_https(String url) {
String original;
if (url.contains("https")) {
original = url;
} else {
original = url.replace("http", "https");
}
return original;
}
/*TODO set Image timer*/
private void setSlideTimer(String duration) {
timer = new CountDownTimer(Integer.parseInt(duration), 1000) {
#Override
public void onTick(long millisUntilFinished) {
}
#Override
public void onFinish() {
int nextPosition = position + 1;
// int nextPosition = DispolyActivity.vb.viewPager.getCurrentItem()+1;
goToNextItem(nextPosition);
}
};
timer.start();
}
/*TODO Next Position in arrayList*/
private void goToNextItem(int nextPosition) {
HandlerCompat.createAsync(Looper.getMainLooper())
.post(() ->
DispolyActivity.vb.viewPager.setCurrentItem(nextPosition)
);
}
/*TODO is image file*/
public static boolean isImageFile(String path) {
String mimeType = URLConnection.guessContentTypeFromName(path);
return mimeType != null && mimeType.startsWith("image");
}
/*TODO Video view Container*/
private void showVideoViewContainer() {
Log.e("datashow", "showVideoViewContainer: "+vb);
vb.videoContainer.setVisibility(View.VISIBLE);
vb.imageView.setVisibility(View.GONE);
}
/*TODO hide video view Container*/
private void hideVideoViewContainer() {
vb.videoContainer.setVisibility(View.GONE);
vb.imageView.setVisibility(View.VISIBLE);
}
/*TODO show Toast*/
private void ShowToast(String msg) {
Toast.makeText(getContext(), msg, Toast.LENGTH_LONG).show();
}
}
`
My Adapter for slide fragment :
public class SliderAdapter extends FragmentStatePagerAdapter {
private String TAG = SliderAdapter.class.getSimpleName();
private ArrayList<PromotionalBanner> promotionalBannerArrayList;
public SliderAdapter(#NonNull FragmentManager fm,ArrayList<PromotionalBanner> promotionalBannerArrayList) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.promotionalBannerArrayList = promotionalBannerArrayList;
}
#NonNull
#Override
public Fragment getItem(int position) {
PromotionalBanner promotionalBanner = DispolyActivity.promotionalBannerArrayList.get(position);
Log.e("positiondata", "dataPost: "+position );
return BannerFragment.getInstantFragment(promotionalBanner,
position,DispolyActivity.promotionalBannerArrayList);
}
#Override
public int getCount() {
return DispolyActivity.promotionalBannerArrayList.size();
}
#Override
public int getItemPosition(#NonNull Object object) {
return POSITION_UNCHANGED;
}
#Override
public void destroyItem(#NonNull ViewGroup container,
int position, #NonNull Object object) {
super.destroyItem(container, position, object);
Log.e("itemDestroy", "destroyItem: "+object );
}
/*TODO update Adapter Data*/
public void updateAdapter_Data(ArrayList<PromotionalBanner> promotionalBannersList){
this.promotionalBannerArrayList = promotionalBannersList;
// notifyDataSetChanged();
}
}
When working with the player, you need to carefully monitor the life cycle.
simpleExoPlayer.release();
simpleExoPlayer = null;
Try to use this not only in onPause(), try it also in onDestroy and onDestroyView.
If the problem goes away, then go through which life cycle methods are called and leave the release call in the right places
You have an issue not with memory, most likely you have an issue related to the codec.
Each device has its own limited number of codecs.
Some devices have more, some have less.
Therefore, on some devices, you can run the video more times, especially on emulators.

implementing Admob Native Advanced with RecyclerView but could not get the exact View Types

I already knew how the RecyclerView with different types of view works but this time I'm trying to add the Native Advance Admob ads to my RecyclerView. I followed theseYoutube Tutorials but there was an error printed to my logcat after the app crushed.
Logcat
java.lang.ClassCastException: com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity
at mgb.com.sdalyricsplus.newAdapters.DisplayItemAdapter.onBindViewHolder(DisplayItemAdapter.java:98)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
I reviewed the tutorial many times hoping that I've missed something that causes the error but it seems that I followed the tutorial correctly.
Here are my Codes
DisplayItemActivity
public class DisplayItemActivity extends AppCompatActivity{
public static final int NUMBER_OF_AD = 5;
AdLoader adLoader;
FastScrollRecyclerView recyclerView;
Global global;
RoomViewModel model;
List<Object> recyclerViewItems = new ArrayList<>();
List<UnifiedNativeAd> nativeAds = new ArrayList<>();
DisplayItemAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_songs);
MobileAds.initialize(this,"ca-app-pub-2493911630710964~1147957926");
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
global = (Global)getApplication();
model = ViewModelProviders.of(this).get(RoomViewModel.class);
adapter = new DisplayItemAdapter(getLayoutInflater());
recyclerView.setAdapter(adapter);
recyclerViewItems.addAll(model.selectAll());
loadNativeAds();
}
private void loadNativeAds() {
AdLoader.Builder builder = new AdLoader.Builder(this, getResources().getString(R.string.native_advance));
adLoader = builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
nativeAds.add(unifiedNativeAd);
if (!adLoader.isLoading()) {
insertAdToList();
}
}
}).withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int i) {
super.onAdFailedToLoad(i);
if (!adLoader.isLoading()) {
insertAdToList();
}
}
}).build();
adLoader.loadAds(new AdRequest.Builder().build(), NUMBER_OF_AD);
}
private void insertAdToList() {
int offset = recyclerViewItems.size() / (nativeAds.size() + 1);
int index = 0;
for (UnifiedNativeAd ad : nativeAds) {
recyclerViewItems.add(index,ad);
index = index + offset;
}
adapter.setList(recyclerViewItems);
}
}
And my Adapter
DisplayItemAdapter
public class DisplayItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int MENU_ITEM_VIEW_TYPE = 0;
private final int UNIFIED_NATIVE_AD_VIEW_TYPE = 1;
private List<Object> recyclerViewItems = new ArrayList<>();
private Global global;
private String searchTxt = "";
private final String newline = System.getProperty("line.separator");
private ClickSongItemListener clickSongItemListener;
private ChangeFavoriteListener changeFavoriteListener;
private LayoutInflater layoutInflater;
public DisplayItemAdapter(LayoutInflater layoutInflater) {
this.layoutInflater = layoutInflater;
}
public void setList(List<Object> list) {
this.recyclerViewItems = list;
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
global = (Global) ((Activity)parent.getContext()).getApplication();
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE:
View adView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_ad_view,parent,false);
return new UnifiedNativeAdViewHolder(adView);
case MENU_ITEM_VIEW_TYPE :
default:
View songitem = LayoutInflater.from(parent.getContext()).inflate(R.layout.gospel_song_item,parent,false);
return new SongItemViewHolder(songitem);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE :
UnifiedNativeAdViewHolder nativeAdViewHolder = (UnifiedNativeAdViewHolder)holder;
UnifiedNativeAd unifiedNativeAd = (UnifiedNativeAd) recyclerViewItems.get(position);
// poulateNativeAdView(unifiedNativeAd,((UnifiedNativeAdViewHolder)holder).getAdView());
FrameLayout frameLayout =
nativeAdViewHolder.view.findViewById(R.id.ad_frame_placement);
UnifiedNativeAdView adView = (UnifiedNativeAdView) layoutInflater
.inflate(R.layout.ad_unified, null);
populateUnifiedNativeAdView(unifiedNativeAd, adView);
frameLayout.removeAllViews();
frameLayout.addView(adView);
break;
case MENU_ITEM_VIEW_TYPE :
default:
SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position));
}
}
private void populateUnifiedNativeAdView(UnifiedNativeAd nativeAd, UnifiedNativeAdView adView) {
// Set the media view.
adView.setMediaView((MediaView) adView.findViewById(R.id.ad_media));
// Set other ad assets.
adView.setHeadlineView(adView.findViewById(R.id.ad_headline));
adView.setBodyView(adView.findViewById(R.id.ad_body));
adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action));
adView.setIconView(adView.findViewById(R.id.ad_app_icon));
adView.setPriceView(adView.findViewById(R.id.ad_price));
adView.setStarRatingView(adView.findViewById(R.id.ad_stars));
adView.setStoreView(adView.findViewById(R.id.ad_store));
adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser));
// The headline and mediaContent are guaranteed to be in every UnifiedNativeAd.
((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
adView.getMediaView().setMediaContent(nativeAd.getMediaContent());
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
if (nativeAd.getBody() == null) {
adView.getBodyView().setVisibility(View.INVISIBLE);
} else {
adView.getBodyView().setVisibility(View.VISIBLE);
((TextView) adView.getBodyView()).setText(nativeAd.getBody());
}
if (nativeAd.getCallToAction() == null) {
adView.getCallToActionView().setVisibility(View.INVISIBLE);
} else {
adView.getCallToActionView().setVisibility(View.VISIBLE);
((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
}
if (nativeAd.getIcon() == null) {
adView.getIconView().setVisibility(View.GONE);
} else {
((ImageView) adView.getIconView()).setImageDrawable(
nativeAd.getIcon().getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (nativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
} else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView) adView.getPriceView()).setText(nativeAd.getPrice());
}
if (nativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
} else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView) adView.getStoreView()).setText(nativeAd.getStore());
}
if (nativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
} else {
((RatingBar) adView.getStarRatingView())
.setRating(nativeAd.getStarRating().floatValue());
adView.getStarRatingView().setVisibility(View.VISIBLE);
}
if (nativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
} else {
((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());
adView.getAdvertiserView().setVisibility(View.VISIBLE);
}
// This method tells the Google Mobile Ads SDK that you have finished populating your
// native ad view with this native ad.
adView.setNativeAd(nativeAd);
// Get the video controller for the ad. One will always be provided, even if the ad doesn't
// have a video asset.
VideoController vc = nativeAd.getVideoController();
// Updates the UI to say whether or not this ad has a video asset.
if (vc.hasVideoContent()) {
// videoStatus.setText(String.format(Locale.getDefault(),
// "Video status: Ad contains a %.2f:1 video asset.",
// vc.getAspectRatio()));
// Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
// VideoController will call methods on this object when events occur in the video
// lifecycle.
vc.setVideoLifecycleCallbacks(new VideoController.VideoLifecycleCallbacks() {
#Override
public void onVideoEnd() {
// Publishers should allow native ads to complete video playback before
// refreshing or replacing them with another ad in the same UI location.
super.onVideoEnd();
}
});
} else {
}
}
private void setSongViews(SongItemViewHolder viewHolder, SongsEntity note) {
Context context = viewHolder.itemView.getContext();
if (note.getMedia_extension().equals("audio")) {
Glide.with(context)
.load(R.drawable.music_icon)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}else if (note.getGenre().toLowerCase().contains("karaoke")) {
File file = new File(context.getExternalFilesDir(null)+"/.file"+note.getId());
if (file.exists()) {
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
.load(file)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}else {
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
.load(note.getMedia_url())
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}
}else {
Glide.with(context)
.load(R.drawable.lyrics_icon)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}
viewHolder.title.setText(global.capitalize(note.getTitle()));
viewHolder.artist.setText(global.capitalize(note.getArtist()));
viewHolder.category.setText(note.getGenre());
viewHolder.favorite.setOnCheckedChangeListener(null);
viewHolder.favorite.setChecked(note.getFavorites());
viewHolder.views.setText(note.getFavorite_counter() <2 ? note.getFavorite_counter()+" heart" : note.getFavorite_counter()+" hearts");
String MY_ID = "JUntYdabhUh5XtMhfCIXXwNbsdW2";
if (!note.getUploader_id().equals(MY_ID))
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.mipmap.sda_logo).diskCacheStrategy(DiskCacheStrategy.ALL))
.load(note.getUploader_photo_url())
.into(viewHolder.user_logo);
else
Glide.with(context)
.load(R.mipmap.sda_logo)
.into(viewHolder.user_logo);
String lyrics = note.getLyrics().toLowerCase();
String searchFilter = searchTxt.toLowerCase();
if (searchTxt.isEmpty()) {
viewHolder.phrase_end.setText(note.getLyrics());
viewHolder.phrase.setText("");
} else
if (lyrics.contains(searchFilter) && (lyrics.indexOf(searchFilter)) + searchTxt.length() <= lyrics.length()) {
viewHolder.phrase.setText(searchTxt);
String filter = note.getLyrics().substring(lyrics.indexOf(searchFilter) + searchFilter.length());
assert newline != null;
viewHolder.phrase_end.setText(filter.replaceAll(newline, " "));
}else {
viewHolder.phrase_end.setText(note.getLyrics());
viewHolder.phrase.setText("");
}
viewHolder.favorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (changeFavoriteListener != null) {
changeFavoriteListener.onChange(buttonView.getContext(),note,isChecked);
}
}
});
}
private void poulateNativeAdView(UnifiedNativeAd unifiedNativeAd, UnifiedNativeAdView adView) {
((TextView)adView.getHeadlineView()).setText(unifiedNativeAd.getHeadline());
((TextView)adView.getBodyView()).setText(unifiedNativeAd.getBody());
((TextView)adView.getCallToActionView()).setText(unifiedNativeAd.getCallToAction());
NativeAd.Image icon = unifiedNativeAd.getIcon();
if (icon == null) {
adView.getIconView().setVisibility(View.INVISIBLE);
}else {
((ImageView)adView.getIconView()).setImageDrawable(icon.getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (unifiedNativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
}else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView)adView.getPriceView()).setText(unifiedNativeAd.getPrice());
}
if (unifiedNativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
}else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView)adView.getStoreView()).setText(unifiedNativeAd.getStore());
}
if (unifiedNativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
}else {
adView.getStarRatingView().setVisibility(View.VISIBLE);
((RatingBar)adView.getStarRatingView()).setRating(unifiedNativeAd.getStarRating().floatValue());
}
if (unifiedNativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
}else {
adView.getAdvertiserView().setVisibility(View.VISIBLE);
((TextView)adView.getAdvertiserView()).setText(unifiedNativeAd.getAdvertiser());
}
adView.setNativeAd(unifiedNativeAd);
}
#Override
public int getItemCount() {
return recyclerViewItems.size();
}
public class SongItemViewHolder extends RecyclerView.ViewHolder {
TextView title, phrase,phrase_end;
TextView artist;
TextView category;
ToggleButton favorite;
LinearLayout layoutWrapper;
LinearLayout phrase_layout;
TextView views;
CircleImageView user_logo;
ImageView iv_thumbnail;
public SongItemViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.text_view_title);
iv_thumbnail = itemView.findViewById(R.id.iv_thumbnail);
artist = itemView.findViewById(R.id.text_view_artist);
phrase = itemView.findViewById(R.id.phrase);
phrase_end = itemView.findViewById(R.id.phrase_end);
category = itemView.findViewById(R.id.text_view_category);
favorite = itemView.findViewById(R.id.toggleButton_favorite);
layoutWrapper = itemView.findViewById(R.id.layout_wrapper);
phrase_layout = itemView.findViewById(R.id.phrase_layout);
views = itemView.findViewById(R.id.tv_view_status);
user_logo = itemView.findViewById(R.id.user_logo);
}
}
private class UnifiedNativeAdViewHolder extends RecyclerView.ViewHolder {
private View view;
public UnifiedNativeAdViewHolder(View view) {
super(view);
this.view = view;
}
}
}
the logcat says that com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity and the error was pointed to the viewbindholder
case MENU_ITEM_VIEW_TYPE :
default:
SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position)); // this line
my suspect is the viewtype. Maybe the view type was not correctly assigned.
I tried this code also but the error still there.
#Override
public int getItemViewType(int position) {
if (position % DisplayItemActivity.NUMBER_OF_AD == 0) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else {
return MENU_ITEM_VIEW_TYPE;
}
}
can someone help me to find the cause of the error?
You need to merge your SongItem and UnifiedNativeAd to become 1 single data source.
Make a new object class name SongAdsData to merge your Song object and Ads from Admob into 1 single object like below:
public class SongAdsData {
public int getType() {
return type;
}
public UnifiedNativeAd getAds() {
return ads;
}
public Post getSong() {
return Song;
}
public int type; // 1 is ads and 2 is songs
public UnifiedNativeAd ads;
public Song song; // here is ur Song object as usual
}
In your adapter, modify it as below:
public class SongAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int UNIFIED_ADS_VIEW = 1;
private static final int SONG_ITEM_VIEW = 2;
private List<SongAdsData> songAdsDataList;
//here your onBindViewHolder,here your refer to item of `songAdsDataList`
// here your onCreateViewHolder
public void setSongAdsDataList(List<SongAdsData> songAdsDataForRecyclerView) {
this.songAdsDataList = songAdsDataForRecyclerView;
notifyDataSetChanged();
}
// Here is the function that insert the ads to RecyclerView
public void insertAdToRecyclerView(List<UnifiedNativeAds> nativeAds) {
int offset = songAdsDataList.size() / (nativeAds.size() + 1);
int index = 0;
for (UnifiedNativeAd ad : nativeAds) {
SongAdsData adsData = new SongAdsData();
adsData.song = null;
adsData.ads = ad;
adsData.type = 1; //1 for ads,2 for song
songAdsDataList.add(index,adsData);
index = index + offset;
}
notifyDataSetChanged();
}
#Override
public int getItemViewType (int position) {
if(songAdsDataList.get(position).getType() == 1){ // So here you compare the type of the object it the position
return UNIFIED_ADS;
}else{
return SONG_ITEM_VIEW;
}
}
#Override
public int getItemCount() {
if(songAdsDataList != null){
return songAdsDataList.size();
}else{
return 0;
}
}
}
So finally in your DisplayItemActivity
I not sure how you get your SongItem(your data from server or somewhere else),but the idea is transform your SongItem into SongAdsData that we created in the step one,
Example like this:
private void displaySongFromYourServer(List<Songs> songs) {
List<SongAdsData> songAdsDataList = new ArrayList<>();
for(Songs song : songs){
SongAdsData data = new SongAdsData();
data.ads = null;
data.song = song;
data.type = 2;
songAdsDataList.add(data);
}
songAdapter.setSongAdsDataList(songAdsDataList);
}
So by now,your Song item will become SongAdsData. At the same time, your UnifiedNativeAd object also need to transform become SongAdsData, therefore we need to
move all thing inside insertAdToList into SongAdapter(See the insertAdToRecyclerView in Song adapter above),so the it can refer to the same List of the recyclerView.
Therefore in your insertAdToList of DisplayItemActivity should become like this:
private void insertAdToList() {
songAdapter.insertAdsToRecyclerView(nativeAds); //called to the function inside songAdapter.
}
Hope you get the idea.
You need to override getItemViewType(int position) to check the instance of your Object in order to return the right view type:
#Override
public int getItemViewType(int position) {
if (this.recyclerViewItems.get(position) instanceof UnifiedNativeAd) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else {
return MENU_ITEM_VIEW_TYPE;
}
}

Check if a property of a custom object ArrayList is an empty string or not?

I want to hide a WebView object (txtCode) if the code property of a custom object Arraylist (arrQues) contains nothing.
if (arrQues.get(count).code.isEmpty())
txtCode.setVisibility(View.GONE);
Its an ArrayList of custom objects fetched from a database table which is shown below
And if the code property does contains code then I have dynamically added rules to layout as shown below:
if (!(arrQues.get(count).code.isEmpty())) {
submit_params.removeRule(RelativeLayout.BELOW);
submit_params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
submit_params.bottomMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 15));
main_params.addRule(RelativeLayout.ABOVE, submitContainer.getId());
mainContainer.setLayoutParams(main_params);
submitContainer.setLayoutParams(submit_params);
}
The issue is when I load the second question and so on... the layout gets messed up and the current question number does not shows as 2 even if its 2 as shown in below:
Both of these issues only arises whenever I use...
arrQues.get(count).code.isEmpty() in the code
I have also tried using "" instead of isEmpty() and even null, but the result was same.
Also what I have noticed is only those questions are loaded from database which have something in the code column.
Below is the complete code for Java file
public class QuestionsFragment extends Fragment implements View.OnClickListener {
TextView txtTimer, txtStatus;
LinearLayout boxA, boxB, boxC, boxD, mainContainer;
RelativeLayout submitContainer;
RelativeLayout.LayoutParams submit_params;
RelativeLayout.LayoutParams main_params;
ScrollView scrollView;
Button btnSubmit;
DBHelper dbHelper;
SharedPreferences sharedPreferences;
TextView txtQues;
WebView txtCode;
TextView txtOptA, txtOptB, txtOptC, txtOptD;
String ans;
ArrayList<QuestionModal> arrQues = new ArrayList<>();
ArrayList<String> arrAnswers = new ArrayList<>();
CountDownTimer countDownTimer;
boolean timerSwitch;
int selectedVal, id;
int curr_quesNo = 0;
int count = 0;
int right = 0;
int non_attempted = 0;
public QuestionsFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_questions, container, false);
txtQues = view.findViewById(R.id.txtQues);
txtOptA = view.findViewById(R.id.txtOptionA);
txtOptB = view.findViewById(R.id.txtOptionB);
txtOptC = view.findViewById(R.id.txtOptionC);
txtOptD = view.findViewById(R.id.txtOptionD);
txtCode = view.findViewById(R.id.txtCode);
txtStatus = view.findViewById(R.id.txtStatus);
boxA = view.findViewById(R.id.boxA);
boxB = view.findViewById(R.id.boxB);
boxC = view.findViewById(R.id.boxC);
boxD = view.findViewById(R.id.boxD);
scrollView = view.findViewById(R.id.scrollView);
btnSubmit = view.findViewById(R.id.btnSubmit);
submitContainer = view.findViewById(R.id.submitContainer);
mainContainer = view.findViewById(R.id.mainContainer);
submit_params = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
main_params = new RelativeLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
sharedPreferences = getActivity().getSharedPreferences("PrefFile", MODE_PRIVATE);
timerSwitch = sharedPreferences.getBoolean("timer_switch", true);
selectedVal = sharedPreferences.getInt("selectedVal", 10);
dbHelper = DBHelper.getDB(getActivity(), sharedPreferences.getString("db_name", null));
if (!dbHelper.checkDB()) {
dbHelper.createDB(getActivity());
}
dbHelper.openDB();
String levelKey = sharedPreferences.getString("level_key", null);
arrQues = dbHelper.getQues(levelKey, selectedVal);
loadQues(timerSwitch);
txtTimer = view.findViewById(R.id.txtTimer);
switch (sharedPreferences.getString("db_name", null)) {
case "Android":
((MainActivity) getActivity()).setFragTitle("Android Quiz");
// topicLogo.setImageResource(R.drawable.ic_nature_people_black_24dp);
break;
case "Java":
((MainActivity) getActivity()).setFragTitle("Java Quiz");
// topicLogo.setImageResource(R.drawable.ic_nature_people_black_24dp);
break;
case "C":
((MainActivity) getActivity()).setFragTitle("C Quiz");
((MainActivity) getActivity()).setFragLogo(R.drawable.ic_home_black_24dp);
break;
case "C++":
((MainActivity) getActivity()).setFragTitle("C++ Quiz");
break;
case "Python":
((MainActivity) getActivity()).setFragTitle("Python Quiz");
break;
case "Kotlin":
((MainActivity) getActivity()).setFragTitle("Kotlin Quiz");
break;
}
btnSubmit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (timerSwitch)
countDownTimer.cancel();
if (id == 0) {
non_attempted++;
arrAnswers.add("NotAttempted");
Toast.makeText(getActivity(), "Not Attempted!", Toast.LENGTH_SHORT).show();
}
switch (id) {
case R.id.boxA:
arrAnswers.add("A");
break;
case R.id.boxB:
arrAnswers.add("B");
break;
case R.id.boxC:
arrAnswers.add("C");
break;
case R.id.boxD:
arrAnswers.add("D");
break;
}
if ((id == R.id.boxA && ans.equals("A"))
|| (id == R.id.boxB && ans.equals("B"))
|| (id == R.id.boxC && ans.equals("C"))
|| (id == R.id.boxD && ans.equals("D"))) {
right++;
count++;
Toast.makeText(getActivity(), "RIGHT!", Toast.LENGTH_SHORT).show();
if (count < arrQues.size()) {
loadQues(timerSwitch);
} else {
sendResult();
}
} else {
count++;
if (count < arrQues.size()) {
loadQues(timerSwitch);
} else {
sendResult();
}
}
}
});
return view;
}
public void setBtnDefault() {
boxA.setBackgroundColor(getResources().getColor(android.R.color.transparent));
boxB.setBackgroundColor(getResources().getColor(android.R.color.transparent));
boxC.setBackgroundColor(getResources().getColor(android.R.color.transparent));
boxD.setBackgroundColor(getResources().getColor(android.R.color.transparent));
}
public void sendResult() {
int attempted = selectedVal - non_attempted;
Gson gson = new Gson();
String jsonAnswers = gson.toJson(arrAnswers);
String jsonQues = gson.toJson(arrQues);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putInt("right_key", right);
editor.putInt("wrong_key", attempted - right);
editor.putInt("total_key", selectedVal);
editor.putInt("attempted_key", attempted);
editor.putString("arr_answers", jsonAnswers);
editor.putString("arr_ques", jsonQues);
editor.commit();
((MainActivity) getActivity()).AddFrag(new ResultFragment(), 1);
}
public void LoadTimer() {
countDownTimer = new CountDownTimer(60000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
txtTimer.setText("0:" + millisUntilFinished / 1000);
}
#SuppressLint("SetTextI18n")
#Override
public void onFinish() {
txtTimer.setText("Time Over");
}
};
}
#SuppressLint("NewApi")
public void loadQues(boolean timer_switch) {
try {
id = 0;
setBtnDefault();
if (timer_switch) {
LoadTimer();
countDownTimer.start();
}
curr_quesNo++;
txtStatus.setText(curr_quesNo + "/" + selectedVal);
txtOptC.setVisibility(View.VISIBLE);
txtOptD.setVisibility(View.VISIBLE);
txtCode.setVisibility(View.VISIBLE);
main_params.removeRule(RelativeLayout.ABOVE);
submit_params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
submit_params.addRule(RelativeLayout.BELOW, mainContainer.getId());
submit_params.topMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 70));
mainContainer.setLayoutParams(main_params);
submitContainer.setLayoutParams(submit_params);
txtQues.setText(arrQues.get(count).ques);
txtOptA.setText(arrQues.get(count).optionA);
txtOptB.setText(arrQues.get(count).optionB);
txtOptC.setText(arrQues.get(count).optionC);
txtOptD.setText(arrQues.get(count).optionD);
txtCode.loadDataWithBaseURL(null, arrQues.get(count).code, "text/html", null, null);
if (txtOptC.getText().toString().isEmpty())
txtOptC.setVisibility(View.GONE);
if (txtOptD.getText().toString().isEmpty())
txtOptD.setVisibility(View.GONE);
if (arrQues.get(count).code.isEmpty())
txtCode.setVisibility(View.GONE);
if (!(arrQues.get(count).code.isEmpty())) {
submit_params.removeRule(RelativeLayout.BELOW);
submit_params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
submit_params.bottomMargin = (int) convertPxToDp(getContext(), convertDpToPx(getContext(), 15));
main_params.addRule(RelativeLayout.ABOVE, submitContainer.getId());
mainContainer.setLayoutParams(main_params);
submitContainer.setLayoutParams(submit_params);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
scrollView.arrowScroll(View.FOCUS_DOWN);
}
}, 1000);
}
ans = arrQues.get(count).answer;
boxA.setOnClickListener(this);
boxB.setOnClickListener(this);
boxC.setOnClickListener(this);
boxD.setOnClickListener(this);
} catch (Exception e) {
((MainActivity) getActivity()).AddFrag(new QuestionsFragment(), 1);
}
}
#Override
public void onClick(View v) {
setBtnDefault();
id = v.getId();
v.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
}
public float convertDpToPx(Context context, float dp) {
return dp * context.getResources().getDisplayMetrics().density;
}
public float convertPxToDp(Context context, float px) {
return px / context.getResources().getDisplayMetrics().density;
}
}
I solved it, all issues were happening because arrQues.get(count).code was fetching null values from the database(The column "Code" had null values). As soon as I replaced null values with empty strings "" isEmpty() worked perfectly. I guess isEmpty() doesn't work with null values and is only intended for empty strings.

duplicate actions in android application when going to home screen

Intro:
My client require some components across the application like navigationDrawer, toolbar. So I have a MainActivity having two toolbar(top/bottom) and two navigation drawer(left/right). All other screens consist of fragments that I change in MainActivity. I have a little problem that is some of the action are being duplicated.
Cases:
In some fragments when user perform an action i.e.
1: User press "Add To Cart" button and press home button that is in toolbar Bottom, the code of "Add To Cart" button run twice (once clicking button, once going to home screen) so my item is being added to cart twice.
2: In another fragment I have "apply coupon" checkbox when user check that checkbox an AlertDialog appear and when user go to home screen the AlertDialog again appear in the home screen.
I'm simply recreating the main activity on home button press with recreate();
I check the code but can't understand why those actions duplicate. If someone faced the same or a bit similar problem please guide me how to tackle that. Any help would be appreciated.
If I need to show code tell me which part?
Edit:
inside onCreateView of fragment Cart Detail
useCoupon.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
createAndShowCustomAlertDialog();
}
}
});
Sequence of this code is Main Activity(Main Fragment)=>Product Frag=>Cart Detail Frag.
Edit 2: MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
this.utils = new Utils(this);
utils.changeLanguage("en");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
this.context = this;
setupToolbar(this);
utils.switchFragment(new MainFrag());
setOnClickListener();
initRightMenuData();
initLeftMenuData();
listAdapter = new ExpandableListAdapter(headerListLeft, hashMapLeft,
false, loggedInIconList);
listViewExpLeft.setAdapter(listAdapter);
if (isLoggedIn()) {
listAdapterRight = new ExpandableListAdapterRight(headerListRight, hashMapRight,
loggedInIconList);
} else {
listAdapterRight = new ExpandableListAdapterRight(headerListRight, hashMapRight,
NotLoggedInIconList);
}
listViewExpRight.setAdapter(listAdapterRight);
enableSingleSelection();
setExpandableListViewClickListener();
setExpandableListViewChildClickListener();
}
private void setOnClickListener() {
myAccountTV.setOnClickListener(this);
checkoutTV.setOnClickListener(this);
discountTV.setOnClickListener(this);
homeTV.setOnClickListener(this);
searchIcon.setOnClickListener(this);
cartLayout.setOnClickListener(this);
}
private void setExpandableListViewClickListener() {
listViewExpRight.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition,
long id) {
utils.printLog("GroupClicked", " Id = " + id);
int childCount = parent.getExpandableListAdapter().getChildrenCount(groupPosition);
if (!isLoggedIn()) {
if (childCount < 1) {
if (groupPosition == 0) {
utils.switchFragment(new FragLogin());
} else if (groupPosition == 1) {
utils.switchFragment(new FragRegister());
} else if (groupPosition == 2) {
utils.switchFragment(new FragContactUs());
} else {
recreate();
}
drawer.closeDrawer(GravityCompat.END);
}
} else {
if (childCount < 1) {
changeFragment(103 + groupPosition);
drawer.closeDrawer(GravityCompat.END);
}
}
return false;
}
});
listViewExpLeft.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
int childCount = parent.getExpandableListAdapter().getChildrenCount(groupPosition);
if (childCount < 1) {
MenuCategory textChild = (MenuCategory) parent.getExpandableListAdapter()
.getGroup(groupPosition);
moveToProductFragment(textChild.getMenuCategoryId());
utils.printLog("InsideChildClick", "" + textChild.getMenuCategoryId());
}
return false;
}
});
}
private void setExpandableListViewChildClickListener() {
listViewExpLeft.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
int childPosition, long id) {
MenuSubCategory subCategory = (MenuSubCategory) parent.getExpandableListAdapter()
.getChild(groupPosition, childPosition);
moveToProductFragment(subCategory.getMenuSubCategoryId());
utils.printLog("InsideChildClick", "" + subCategory.getMenuSubCategoryId());
return true;
}
});
listViewExpRight.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
String str = parent.getExpandableListAdapter().getGroup(groupPosition).toString();
UserSubMenu userSubMenu = (UserSubMenu) parent.getExpandableListAdapter()
.getChild(groupPosition, childPosition);
if (str.contains("Information") || str.contains("معلومات")) {
Bundle bundle = new Bundle();
bundle.putString("id", userSubMenu.getUserSubMenuCode());
utils.switchFragment(new FragShowText(), bundle);
} else if (str.contains("اللغة") || str.contains("Language")) {
recreate();
} else if (str.contains("دقة") || str.contains("Currency")) {
makeDefaultCurrencyCall(userSubMenu.getUserSubMenuCode());
}
utils.printLog("InsideChildClick", "" + userSubMenu.getUserSubMenuCode());
drawer.closeDrawer(GravityCompat.END);
return false;
}
});
}
private void setupToolbar(Context context) {
ViewCompat.setLayoutDirection(appbarBottom, ViewCompat.LAYOUT_DIRECTION_RTL);
ViewCompat.setLayoutDirection(appbarTop, ViewCompat.LAYOUT_DIRECTION_RTL);
String imgPath = Preferences
.getSharedPreferenceString(appContext, LOGO_KEY, DEFAULT_STRING_VAL);
utils.printLog("Product Image = " + imgPath);
if (!imgPath.isEmpty()) {
Picasso.with(getApplicationContext()).load(imgPath)
.into(logoIcon);
}
logoIcon.setOnClickListener(this);
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
actionbarToggle();
drawer.addDrawerListener(mDrawerToggle);
drawerIconLeft.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
drawer.openDrawer(GravityCompat.START);
drawerIconLeft.setScaleX(1);
drawerIconLeft.setImageResource(R.drawable.ic_arrow_back_black);
}
}
});
drawerIconRight.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (drawer.isDrawerOpen(GravityCompat.END)) {
drawer.closeDrawer(GravityCompat.END);
} else {
drawer.openDrawer(GravityCompat.END);
}
}
});
}
private void initViews() {
drawerIconLeft = findViewById(R.id.drawer_icon_left);
drawerIconRight = findViewById(R.id.drawer_icon_right);
logoIcon = findViewById(R.id.logo_icon);
searchIcon = findViewById(R.id.search_icon);
cartLayout = findViewById(R.id.cart_layout);
counterTV = findViewById(R.id.actionbar_notification_tv);
drawer = findViewById(R.id.drawer_layout);
listViewExpLeft = findViewById(R.id.expandable_lv_left);
listViewExpRight = findViewById(R.id.expandable_lv_right);
appbarBottom = findViewById(R.id.appbar_bottom);
appbarTop = findViewById(R.id.appbar_top);
myAccountTV = findViewById(R.id.my_account_tv);
discountTV = findViewById(R.id.disc_tv);
checkoutTV = findViewById(R.id.checkout_tv);
homeTV = findViewById(R.id.home_tv);
searchView = findViewById(R.id.search_view);
}
#Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else if (drawer.isDrawerOpen(GravityCompat.END)) {
drawer.closeDrawer(GravityCompat.END);
} else {
super.onBackPressed();
}
}
private void initRightMenuData() {
headerListRight = new ArrayList<>();
hashMapRight = new HashMap<>();
String[] notLoggedInMenu = {findStringByName("login_text"),
findStringByName("action_register_text"),
findStringByName("contact_us_text")};
String[] loggedInMenu = {findStringByName("account"), findStringByName("edit_account_text"),
findStringByName("action_change_pass_text"),
findStringByName("order_history_text"),
findStringByName("logout"), findStringByName("contact_us_text")};
List<UserSubMenu> userSubMenusList = new ArrayList<>();
if (isLoggedIn()) {
for (int i = 0; i < loggedInMenu.length; i++) {
headerListRight.add(loggedInMenu[i]);
hashMapRight.put(headerListRight.get(i), userSubMenusList);
}
} else {
for (int i = 0; i < notLoggedInMenu.length; i++) {
headerListRight.add(notLoggedInMenu[i]);
hashMapRight.put(headerListRight.get(i), userSubMenusList);
}
}
String responseStr = "";
if (getIntent().hasExtra(KEY_EXTRA)) {
responseStr = getIntent().getStringExtra(KEY_EXTRA);
utils.printLog("ResponseInInitData", responseStr);
try {
JSONObject responseObject = new JSONObject(responseStr);
boolean success = responseObject.optBoolean("success");
if (success) {
try {
JSONObject homeObject = responseObject.getJSONObject("home");
JSONArray slideshow = homeObject.optJSONArray("slideshow");
AppConstants.setSlideshowExtra(slideshow.toString());
JSONArray menuRight = homeObject.optJSONArray("usermenu");
for (int z = 0; z < menuRight.length(); z++) {
List<UserSubMenu> userSubMenuList = new ArrayList<>();
JSONObject object = menuRight.getJSONObject(z);
headerListRight.add(object.optString("name"));
JSONArray childArray = object.optJSONArray("children");
for (int y = 0; y < childArray.length(); y++) {
JSONObject obj = childArray.optJSONObject(y);
userSubMenuList.add(new UserSubMenu(obj.optString("code"),
obj.optString("title"), obj.optString("symbol_left"),
obj.optString("symbol_right")));
}
hashMapRight.put(headerListRight.get(headerListRight.size() - 1), userSubMenuList);
utils.printLog("AfterHashMap", "" + hashMapRight.size());
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
utils.showErrorDialog("Error Getting Data From Server");
utils.printLog("SuccessFalse", "Within getCategories");
}
} catch (JSONException e) {
e.printStackTrace();
utils.printLog("JSONObjEx_MainAct", responseStr);
}
} else {
utils.printLog("ResponseExMainActivity", responseStr);
throw new IllegalArgumentException("Activity cannot find extras " + KEY_EXTRA);
}
}
private void initLeftMenuData() {
headerListLeft = new ArrayList<>();
hashMapLeft = new HashMap<>();
String responseStr = "";
if (getIntent().hasExtra(KEY_EXTRA)) {
responseStr = getIntent().getStringExtra(KEY_EXTRA);
utils.printLog("ResponseInMainActivity", responseStr);
try {
JSONObject responseObject = new JSONObject(responseStr);
utils.printLog("JSON_Response", "" + responseObject);
boolean success = responseObject.optBoolean("success");
if (success) {
try {
JSONObject homeObject = responseObject.getJSONObject("home");
JSONArray menuCategories = homeObject.optJSONArray("categoryMenu");
utils.printLog("Categories", menuCategories.toString());
for (int i = 0; i < menuCategories.length(); i++) {
JSONObject menuCategoryObj = menuCategories.getJSONObject(i);
JSONArray menuSubCategoryArray = menuCategoryObj.optJSONArray(
"children");
List<MenuSubCategory> childMenuList = new ArrayList<>();
for (int j = 0; j < menuSubCategoryArray.length(); j++) {
JSONObject menuSubCategoryObj = menuSubCategoryArray.getJSONObject(j);
MenuSubCategory menuSubCategory = new MenuSubCategory(
menuSubCategoryObj.optString("child_id"),
menuSubCategoryObj.optString("name"));
childMenuList.add(menuSubCategory);
}
MenuCategory menuCategory = new MenuCategory(menuCategoryObj.optString(
"category_id"), menuCategoryObj.optString("name"),
menuCategoryObj.optString("icon"), childMenuList);
headerListLeft.add(menuCategory);
hashMapLeft.put(headerListLeft.get(i), menuCategory.getMenuSubCategory());
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
utils.printLog("ResponseExMainActivity", responseStr);
throw new IllegalArgumentException("Activity cannot find extras " + KEY_EXTRA);
}
}
private void actionbarToggle() {
mDrawerToggle = new ActionBarDrawerToggle(MainActivity.this, drawer,
R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
drawerIconLeft.setImageResource(R.drawable.ic_list_black);
drawerIconLeft.setScaleX(-1);
}
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
}
};
}
#Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.logo_icon) {
recreate();
} else if (id == R.id.my_account_tv) {
utils.switchFragment(new Dashboard());
} else if (id == R.id.disc_tv) {
utils.printLog("From = Main Act");
Bundle bundle = new Bundle();
bundle.putString("from", "mainActivity");
utils.switchFragment(new FragProduct(), bundle);
} else if (id == R.id.checkout_tv) {
if (isLoggedIn()) {
utils.switchFragment(new FragCheckout());
} else {
AlertDialog alertDialog = utils.showAlertDialogReturnDialog("Continue As",
"Select the appropriate option");
alertDialog.setButton(DialogInterface.BUTTON_POSITIVE,
"As Guest", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
utils.switchFragment(new FragCheckout());
}
});
alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
"Login", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
utils.switchFragment(new FragLogin());
}
});
alertDialog.setButton(DialogInterface.BUTTON_NEUTRAL,
"Register", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
utils.switchFragment(new FragRegister());
}
});
alertDialog.show();
}
} else if (id == R.id.home_tv) {
recreate();
} else if (id == R.id.search_icon) {
startActivityForResult(new Intent(context, SearchActivity.class), SEARCH_REQUEST_CODE);
} else if (id == R.id.cart_layout) {
Bundle bundle = new Bundle();
bundle.putString("midFix", "cartProducts");
utils.switchFragment(new FragCartDetail(), bundle);
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SEARCH_REQUEST_CODE || requestCode == CURRENCY_REQUEST_CODE
|| requestCode == LANGUAGE_REQUEST_CODE) {
if (data != null) {
String responseStr = data.getStringExtra("result");
utils.printLog("ResponseIs = " + responseStr);
if (responseStr != null) {
if (resultCode == Activity.RESULT_OK) {
JSONObject response;
if (!isJSONString(responseStr)) {
try {
response = new JSONObject(responseStr);
if (requestCode == CURRENCY_REQUEST_CODE) {
JSONObject object = response.optJSONObject("currency");
Preferences.setSharedPreferenceString(appContext
, CURRENCY_SYMBOL_KEY
, object.optString("symbol_left")
+ object.optString("symbol_right")
);
recreate();
} else if (requestCode == LANGUAGE_REQUEST_CODE) {
JSONObject object = response.optJSONObject("language");
Preferences.setSharedPreferenceString(appContext
, LANGUAGE_KEY
, object.optString("code")
);
recreate();
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
if (requestCode == SEARCH_REQUEST_CODE) {
if (responseStr.isEmpty())
return;
Bundle bundle = new Bundle();
bundle.putString("id", responseStr);
bundle.putString("from", "fromSearch");
utils.switchFragment(new FragProduct(), bundle);
} else {
utils.showAlertDialog("Alert", responseStr);
}
}
} else if (resultCode == FORCED_CANCEL) {
utils.printLog("WithinSearchResult", "If Success False" + responseStr);
} else if (resultCode == Activity.RESULT_CANCELED) {
utils.printLog("WithinSearchResult", "Result Cancel" + responseStr);
}
}
}
}
}
}
MainFragment
public class MainFrag extends MyBaseFragment {
public MainFrag() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.frag_main, container, false);
initUtils();
initViews(view);
utils.setupSlider(mPager, indicator, pb, true, true);
RecyclerView.LayoutManager mLayoutManager =
new LinearLayoutManager(getActivity()
, LinearLayoutManager.VERTICAL, false);
mRecyclerView.setLayoutManager(mLayoutManager);
List<String> keysList = prepareData();
if (keysList.size() > 0 && !keysList.isEmpty()) {
mRecyclerView.setAdapter(new MainFragmentAdapter(keysList));
}
return view;
}
private void initViews(View view) {
mRecyclerView = view.findViewById(R.id.parent_recycler_view);
mPager = view.findViewById(R.id.pager);
indicator = view.findViewById(R.id.indicator);
pb = view.findViewById(R.id.loading);
}
private List<String> prepareData() {
String responseStr = getHomeExtra();
List<String> keysStr = new ArrayList<>();
try {
JSONObject responseObject = new JSONObject(responseStr);
utils.printLog("JSON_Response", "" + responseObject);
boolean success = responseObject.optBoolean("success");
if (success) {
JSONObject homeObject = responseObject.optJSONObject("home");
JSONObject modules = homeObject.optJSONObject("modules");
Iterator<?> keys = modules.keys();
while (keys.hasNext()) {
String key = (String) keys.next();
keysStr.add(key);
utils.printLog("KeyStr", key);
}
utils.printLog("ModuleSize", modules.toString());
} else {
utils.printLog("SuccessFalse", "Within getCategories");
}
} catch (JSONException e) {
e.printStackTrace();
utils.printLog("JSONEx_MainFragTest", responseStr);
}
return keysStr;
}
}
I removed global variables and some method because of length.
You should not use recreate() method here, as it forces the activity to reload everything. Just use intent to call your Home activity, that should solve this issue.
From the hint of #Akash Khatri I use to switch to mainFragment and its fine now. Actually #Akash is right recreate(); everything at present But as I was setting main fragment in OnCreate of MainActivity. So, It was hard to notice that Android first recreate everything and in no time It call the main Fragment.
I do not using Intent to start the same activity again Because Switching fragment is more convenient. And also I pass Some extras to it that I will loose With new Intent.

Categories