java Picasso 在列表视图中向上滚动时不断重新加载图像,加载缓慢
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27771646/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Picasso keeps reloading images while scrolling upwards in listview, loads slowly
提问by 24x7
I have been searching SO threads for answers but couldn't figure out my issue from previous discussion. I have a listview which loads about 50 images (it used to be about 100 but this was barely loading any images at all). After grabbing my JSON content (including image URL) from an api endpoint, through an adapter, my code puts it inside the listview.
我一直在搜索 SO 主题的答案,但无法从之前的讨论中找出我的问题。我有一个加载大约 50 张图像的列表视图(它曾经是大约 100 张,但几乎没有加载任何图像)。通过适配器从 api 端点获取我的 JSON 内容(包括图像 URL)后,我的代码将其放入列表视图中。
Currently, with 50 images, picasso will load one image at a time as I scroll down on the feed. I feel as if keeping the scroll fixed on one item in the listview will make that image load faster. As I scroll up, however, it puts the placeholder back in and reloads the image again. Is there a way to solve this issue?
目前,有 50 张图片,当我向下滚动提要时,毕加索将一次加载一张图片。我觉得好像将滚动固定在列表视图中的一个项目上会使图像加载速度更快。然而,当我向上滚动时,它将占位符放回并再次重新加载图像。有没有办法解决这个问题?
public class MainActivity extends Activity {
private List<Post> myPosts = new ArrayList<Post>();
protected String[] mBlogPostTitles;
public static final String TAG = MainActivity.class.getSimpleName();//prints name of class without package name
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(isNetworkAvailable()) {
GetBlogPostsTask getBlogPostsTask = new GetBlogPostsTask(); // new thread
getBlogPostsTask.execute();// don't call do in background directly
}else{
Toast.makeText(this, "Network is unavailable", Toast.LENGTH_LONG).show();
}
}
public boolean isNetworkAvailable() {
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if(networkInfo != null && networkInfo.isConnected()){
isAvailable = true;
}
return isAvailable;
}
private void populateListView() {
ArrayAdapter<Post> adapter = new MyListAdapter();
ListView list = (ListView) findViewById(R.id.postsListView);
list.setAdapter(adapter);
}
private class MyListAdapter extends ArrayAdapter<Post>{
public MyListAdapter() {
super(MainActivity.this, R.layout.item_view, myPosts);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// make sure we have a view to work with
View itemView = convertView;
if (itemView == null) {
itemView = getLayoutInflater().inflate(R.layout.item_view, parent,false);
}
//find the post to work with
Post currentPost = myPosts.get(position);
Context context = itemView.getContext();
String imageURL = currentPost.getImage();
if(imageURL == null || imageURL.isEmpty()){
ImageView imageView = (ImageView) itemView.findViewById(R.id.item_image);
imageView.setVisibility(View.GONE);
}else{
ImageView imageView = (ImageView) itemView.findViewById(R.id.item_image);
Picasso.with(context)
.load(imageURL)
.tag(context)
.placeholder(R.drawable.kanye8080s)
.error(R.drawable.stadiumarcadium)
.into(imageView);
imageView.setVisibility(View.VISIBLE);
}
//Username
TextView userText = (TextView) itemView.findViewById(R.id.item_txtUser);
userText.setText(currentPost.getUser());
//Time of post
TextView timeText = (TextView) itemView.findViewById(R.id.item_txtTime);
timeText.setText("" + currentPost.getTime());
//The actual post
TextView postText = (TextView) itemView.findViewById(R.id.item_txtPost);
postText.setText("" + currentPost.getPost());
//The actual post
TextView likesText = (TextView) itemView.findViewById(R.id.item_txtLikes);
likesText.setText("" + currentPost.getLikes());
return itemView;
}
}
private class GetBlogPostsTask extends AsyncTask<Object, Void, List> {
@Override
protected List doInBackground(Object[] params) {
int responseCode = -1;//need to have this variable outside scope of try/catch block
JSONObject jsonResponse = null;
StringBuilder builder = new StringBuilder();
HttpClient client = new DefaultHttpClient();
HttpGet httpget = new HttpGet(""); /// api endpoint redacted
try {
HttpResponse response = client.execute(httpget);
StatusLine statusLine = response.getStatusLine();
responseCode = statusLine.getStatusCode();
if(responseCode == HttpURLConnection.HTTP_OK){ //could have used just 200 value
HttpEntity entity = response.getEntity();
InputStream content = entity.getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content));
String line;
while((line = reader.readLine()) != null){
builder.append(line);
}
jsonResponse = new JSONObject(builder.toString());
JSONArray jsonPosts = jsonResponse.getJSONArray("posts");
for(int i=0; i < jsonPosts.length(); i++ ){
JSONObject jsonPost = jsonPosts.getJSONObject(i);
int post_id = Integer.parseInt(jsonPost.getString("id"));
String post_user = jsonPost.getString("user");
String post_account = jsonPost.getString("account");
int post_time = Integer.parseInt(jsonPost.getString("time"));
String post_post = jsonPost.getString("post");
String post_image = jsonPost.getString("image");
int post_likes = Integer.parseInt(jsonPost.getString("likes"));
myPosts.add(new Post(post_id, post_user, post_account, post_time, post_post, post_image, "profile picture here", post_likes));
}
}else{
Log.i(TAG, "Unsuccessful HTTP Response Code: " + responseCode);
}
}
catch (MalformedURLException e){
Log.e(TAG, "Exception caught");
}
catch (IOException e){
Log.e(TAG, "Exception caught");
}
catch (Exception e){//must be in this order, this is the last, general catch
Log.e(TAG, "Exception caught", e);
}
return null;
}
@Override
protected void onPostExecute(List result) {
// call populateListView method here
populateListView();
super.onPostExecute(result);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
EDIT:
编辑:
I have updated my code into a view holder pattern, created two separate views (one for a post with an image, one for a post with just text) and also included Picasso's new scroll detection capabilities.
我已将代码更新为视图持有者模式,创建了两个单独的视图(一个用于带有图像的帖子,一个用于仅包含文本的帖子),并且还包括毕加索的新滚动检测功能。
I have seen an improvement in some of the images loading quicker, at least when the view is focused while scrolling, the image is more likely to load now. However, on scroll up those same images that were once loaded, disappear. It feels as if Picasso is only loading 4-5 images at a time and replacing the ones already loaded to make room. My updated code is below:
我已经看到一些图像加载速度的改进,至少在滚动时聚焦视图时,图像现在更有可能加载。但是,向上滚动那些曾经加载过的相同图像,就会消失。感觉好像毕加索一次只加载 4-5 张图像并替换已经加载的图像以腾出空间。我更新的代码如下:
public class MainActivity extends Activity {
private List<Post> myPosts = new ArrayList<Post>();
protected String[] mBlogPostTitles;
public static final String TAG = MainActivity.class.getSimpleName();//prints name of class without package name
...
private void populateListView() {
Activity activity = MainActivity.this;
ArrayAdapter<Post> adapter = new MyListAdapter();
ListView list = (ListView) findViewById(R.id.postsListView);
list.setAdapter(adapter);
list.setOnScrollListener(new SampleScrollListener(activity));
}
private class MyListAdapter extends ArrayAdapter<Post>{
public MyListAdapter() {
super(MainActivity.this, R.layout.item_view, myPosts);
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
String imageURL = myPosts.get(position).getImage();
if(imageURL == null || imageURL.isEmpty()){
return 1; // text based
}else{
return 0; // image based
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
PostViewHolder holder;
int type = getItemViewType(position);
View itemView = convertView;
// make sure we have a view to work with
if (itemView == null) {
holder = new PostViewHolder();
if(type == 1) {
itemView = getLayoutInflater().inflate(R.layout.item_view, parent, false);
} else {
itemView = getLayoutInflater().inflate(R.layout.image_post_view, parent, false);
holder.image = (ImageView) itemView.findViewById(R.id.item_image);
}
holder.user = (TextView) itemView.findViewById(R.id.item_txtUser);
holder.time = (TextView) itemView.findViewById(R.id.item_txtTime);
holder.post = (TextView) itemView.findViewById(R.id.item_txtPost);
holder.likes = (TextView) itemView.findViewById(R.id.item_txtLikes);
itemView.setTag(holder);
} else {
holder = (PostViewHolder) itemView.getTag();
}
//find the post to work with
Post currentPost = myPosts.get(position);
if(type != 1) {
Context context = itemView.getContext();
String imageURL = currentPost.getImage();
Picasso.with(context).setIndicatorsEnabled(true);
//Picasso.with(context).setLoggingEnabled(true);
Picasso.with(context)
.load(imageURL)
.tag(context)
.placeholder(R.drawable.kanye8080s)
//.skipMemoryCache()
.error(R.drawable.stadiumarcadium)
.fit()
.into(holder.image);
}
//Username
holder.user.setText(currentPost.getUser());
//Time of post
holder.time.setText("" + currentPost.getTime());
//The actual post
holder.post.setText(currentPost.getPost());
//Likes for the post
holder.likes.setText("" + currentPost.getLikes());
return itemView;
}
}
public class SampleScrollListener implements AbsListView.OnScrollListener {
private final Context context;
public SampleScrollListener(Context context) {
this.context = context;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
final Picasso picasso = Picasso.with(context);
if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
picasso.resumeTag(context);
} else {
picasso.pauseTag(context);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
// Do nothing.
}
}
...
}
Where is the issue coming from? Should I be preloading these images somehow in the cache? While I have already looked into Picasso's new priority feature, should I be telling Picasso somehow to load images in the order in which they appear in my listview? Any ideas? How can I "keep" images that have already been loaded on scroll up?
问题从何而来?我应该以某种方式在缓存中预加载这些图像吗?虽然我已经研究了毕加索的新优先级功能,但我是否应该告诉毕加索以它们在我的列表视图中出现的顺序加载图像?有任何想法吗?如何在向上滚动时“保留”已加载的图像?
回答by Pb Studies
use resize with picasso
使用毕加索调整大小
Picasso.with(context)
.load(imageURL)
.tag(context)
.placeholder(R.drawable.kanye8080s)
.error(R.drawable.stadiumarcadium)
.into(imageView)
.resize(x,y);
//This would definitely help
//这肯定会有帮助
回答by Steve M
The size of the memory cache of Picasso is limited so it does not generate out of memory errors when scrolling long lists. Once the images are out of the memory cache, the placeholder will be displayed while the image is reloaded from either the disk cache or the network.
Picasso 的内存缓存大小是有限的,因此在滚动长列表时不会产生内存不足的错误。一旦图像从内存缓存中取出,当从磁盘缓存或网络重新加载图像时,将显示占位符。
The disk cache is enabled by default so the reload time should be very fast. You can use setIndicatorsEnabled(true)
to see where the images are being loaded from.
默认情况下启用磁盘缓存,因此重新加载时间应该非常快。您可以使用setIndicatorsEnabled(true)
查看图像的加载位置。
If you are finding that Picasso is reloading the images from network, this is probably a problem with the HTTP headers being sent from the server. I don't believe Picasso actually caches the images on disk itself, instead relying on the HTTP layer, which will obey a no-cache header, and will reload from network if the expire time elapses.
如果您发现 Picasso 正在从网络重新加载图像,这可能是从服务器发送的 HTTP 标头的问题。我不相信毕加索实际上将图像缓存在磁盘本身上,而是依赖于 HTTP 层,该层将遵循无缓存标头,并在过期时间过后从网络重新加载。
回答by GreyBeardedGeek
I'd look at two things.
我会看两件事。
Number one is the sizes of the images being loaded. I don't know what the default maximum cache size is in Picasso, but it sounds like you may be exceeding it with just a few images, causing the others to be evicted from the cache.
第一个是正在加载的图像的大小。我不知道 Picasso 中默认的最大缓存大小是多少,但听起来您可能只用几张图像就超过了它,从而导致其他图像从缓存中被逐出。
Number two is probably not the core issue, but also contributes to performance. You are doing a lot findViewById() calls, which are fairly expensive. Look into the "ViewHolder" pattern for "caching" those lookups.
第二个可能不是核心问题,但也有助于性能。您正在执行很多 findViewById() 调用,这些调用相当昂贵。查看“ViewHolder”模式以“缓存”这些查找。
Edit - see Jake Wharton's answer to a similar questionfor more detail
编辑 -有关更多详细信息,请参阅Jake Wharton 对类似问题的回答
回答by Krishna Chauhan
I would suggest you to use GLIDE for image loading. Since GLIDE is fast and with its cache loading feature you can get super fast image loading, With GLIDE you get lot of features..
我建议您使用 GLIDE 进行图像加载。由于 GLIDE 速度快,并且具有缓存加载功能,因此您可以获得超快的图像加载,使用 GLIDE,您可以获得很多功能。
Download here https://github.com/bumptech/glide
回答by Aniket Narvankar
Use :
利用 :
recyclerview.getRecycledViewPool().setMaxRecycledViews(0, 0);
This solved my issue
这解决了我的问题