gpt4 book ai didi

java.lang.OutOfMemoryError 与 ArrayList.addAll()

转载 作者:行者123 更新时间:2023-12-01 09:09:16 24 4
gpt4 key购买 nike

我有一个线程列表,我已对其进行分页以使用无限滚动,我遇到的问题(以及我的用户)是 OutOfMemoryError: Failed to allocate a [x] byte allocation with [y] free bytes and [z] until OOM.每个用户的 x、y 和 z 属性都不同,但错误的原因总是在同一个地方,就是当我刷新帖子时。我完全超出了我的能力范围,因为我不知道如何优化我的代码或使其不会发生。因为这是我的应用程序目前最大的崩溃。我已经发布了我的PostFragment下面请参阅refreshPosts(ArrayList<Posts> newObjects)方法,因为这就是崩溃发生的地方。

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();
}


}

再次提前致谢。

最佳答案

您的问题基本上可以归结为:

private void refreshPosts(ArrayList<Posts> newObjects) {

postList.addAll(newObjects);
populateUIWithData();

}

列表只能变大,不能变小。如果服务器有很多很多帖子,那么 OutOfMemory 几乎是不可避免的。

解决此问题的一种方法是使用 LRU(最近最少使用)缓存。您可以使用一个实用程序类:android.util.LruCache

LRU 缓存本质上是一个映射。项目使用 key 存储,就像 ID 一样。使用 LRU 缓存,您可以放入新项目,但一旦达到预先确定的限制,旧项目就会开始被推出,以便为新项目腾出空间。

这会节省内存,但会为您生成更多管理代码。

您的适配器不会有帖子列表,而只会有帖子 ID 列表。这应该更容易内存。

当用户滚动并且您收集更多帖子时,您可以将帖子 ID 添加到列表中,并使用帖子 ID 将帖子映射到 LRU 缓存中。

当您绑定(bind)到列表项 View 时,您可以使用 LRU 缓存中帖子的 ID 来查找帖子。

  • 如果有的话,那就太好了。这称为缓存命中。将帖子绑定(bind)到列表项 View 。

  • 如果没有,则缓存未命中。您还有一些工作要做。

    • 启动服务器请求以按 ID 检索帖子。我看到您当前的代码仅检索帖子 block ,因此您需要一些新的服务器代码。

    • 请求完成后,将帖子放入 LRU 缓存中,并使用 adapter.notifyItemChanged() 让适配器知道您的项目已更改。除非用户滚动到它之外,否则 RecyclerView 应尝试再次与列表项 View 绑定(bind)。这次,您应该获得缓存命中。

这是基本思想。我会编写一些代码,但我仍然有很多问题,因为我看不到您的模型类、数据工厂和适配器类。

一旦你让它工作,你必须调整缓存的限制,使其足够低,不会超出内存,但又足够高,使你的命中/未命中率不接近于零。

顺便说一句,我注意到您犯了一个错误,即每次收到帖子 block 时都创建一个新适配器并将其交给RecyclerView。您应该创建适配器一次,保留对它的引用并更新它。有一个方法可以添加帖子 block ,然后调用 notifyDataSetChanged()

<小时/>

节省内存的另一个想法是使用文本压缩。如果问题更多是由于帖子的平均大小较大而不是帖子数量较多,那么除了 LRU 缓存之外,您还可以探索这个想法。

这个概念是,您可以获取超过一定大小的帖子,使用 ZipOutputStream 将它们写入缓冲区,然后将缓冲区保存在内存中。当需要显示帖子时,您可以使用 ZipInputStream 读取缓冲区来解压缩文本。这里的问题是性能,因为压缩/解压缩非常消耗 CPU 资源。但如果问题确实很长,那么可能需要考虑这种方法。

<小时/>

更好的方法:仅将帖子的第一部分保存为列表中的“概述”显示。当用户单击列表项时,从服务器检索整个帖子并将其显示在另一个页面中。

关于java.lang.OutOfMemoryError 与 ArrayList.addAll(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41020954/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com