Итак. Есть задача, есть отдельная активити для ленты новостей. Т.е. пользователь прокручивает ленту и на экране выводятся посты с картинками и текстом. Всё это грузится с определённого сайта и пользователь может листать ленту бесконечно прокрутив 100-200 или даже 1000 постов с картинками.
Я решил использовать для этих целей UIL, так как из описания понял, что она не только загружает картинки с интернета, но и управляет кэшем и сама выгружает из памяти то что не видно в данный момент на экране.
Но проблема в том, что после загрузки 30-40 картинок дальше ничего не грузилось (просто вместо картинок была заглушка "Ошибка". А в логе писалось про outOfMemory. Я решил попробовать другие библиотеки, в том числе и Picasso, Fresco. Но проблема оставалась.
Изначально алгоритм был таков - посты загружались пачками по 10 штук сразу в контейнер (разметку для каждого поста я брал из отдельного лайоута и подключал при помощи LayoutInflater). Потом подумав, что может проблема в этом способе, я решил использовать RecycleView с адаптером. Но это тоже не помогло. Теперь я вообще переписал всё с нуля и для чистоты эксперимента решил загружать на экран только картинки, без лишней разметки, адаптеров, инфлаттеров и прочего. Только картинки, которые грузятся в динамически создаваемые ImageView.
И всё равно проблема та же. Попробовав настройки UIL, я вроде бы добился более менее приемлемого результата — теперь уже грузится не 30-40 картинок, а 100-130. Но этого мало, надо чтобы можно было грузить бесконечное число картинок.
Если я правильно понимаю, то картинки просто не удаляются из памяти, когда я дальше прокручиваю ленту и из-за этого память переполняется. На одном форуме мне сказали, что подобные библиотеки (я правда в тот момент спрашивал про Picasso, но суть та же) не предназначены для решения подобных задач и надо реализовывать выгрузку и загрузку картинок из памяти самостоятельно.
Скажите, что я делаю не так?
package com.freescribbler.freescribbler;
import android.app.Activity;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
import com.nostra13.universalimageloader.cache.disc.naming.HashCodeFileNameGenerator;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.utils.StorageUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.util.ArrayList;
public class HomeActivity extends Activity {
serverAPI serverAPI1; //Класс запросов к серверу
SharedPreferences myPref; //Настройки доступа к серверу
final String USER_CONNECTID = "user_connectid"; //ID
final String USER_TOKEN = "user_token"; //Токен
int lastPost = 0;
Boolean morePostLoad = false;
int Counter; //Счётчик выведенных постов
ImageLoader imageLoader;
int imgCount=0;
ArrayList
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
serverAPI1 = new serverAPI(); //инициализируем класс
loadLoginData();//загружаем данные доступа к серверу
Counter = 0; //инициализируем счётчик
imageLoader = ImageLoader.getInstance(); // Получили экземпляр
imageLoader.init(ImageLoaderConfiguration.createDefault(HomeActivity.this));
File cacheDir = StorageUtils.getCacheDirectory(this);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
//.diskCacheExtraOptions(480, 800, null)
.threadPoolSize(3) // default
.threadPriority(Thread.NORM_PRIORITY - 2) // default
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
//.memoryCache(new LruMemoryCache(5 * 1024 * 1024))
//.memoryCacheSize(5 * 1024 * 1024)
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiskCache(cacheDir)) // default
.diskCacheSize(20 * 1024 * 1024)
//.diskCacheFileCount(100)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
.writeDebugLogs()
.build();
//Picasso.with(HomeActivity.this).setIndicatorsEnabled(true); //Включаем дебаг у пикассо
new AsyncTaskGetPosts().execute();// Запускаем асинхронное выполнение загрузки данных с сервера, с последующим выводом постов
Button loadMore = (Button) findViewById(R.id.loadMoreButton);
loadMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (morePostLoad) {
new AsyncTaskGetPostsMore().execute();
}
}
});
}
//Загружаем данные для запросов к серверу
void loadLoginData() {
myPref = getSharedPreferences("myPref", MODE_PRIVATE);
//Проверяем, существует ли токен
if (myPref.contains(USER_TOKEN)) {
serverAPI1.token = myPref.getString(USER_TOKEN, "");
serverAPI1.connectid = myPref.getString(USER_CONNECTID, "");
Log.e("Токен существует", serverAPI1.token);
} else {
serverAPI1.token = null;
serverAPI1.connectid = null;
Toast toast = Toast.makeText(getApplicationContext(),
"Пользователь не залогинен", Toast.LENGTH_LONG);
toast.show();
Log.e("Токена нет", "null");
}
}
//Здесь мы получаем от сервера картинки
public class AsyncTaskGetPosts extends AsyncTask
@Override
//То что выполнится по завершении фонового процесса
protected void onPostExecute(String result) {
super.onPostExecute(result);
//вызываем метод, где обрабатываем JSON и выводим картинки
makePosts(result);
morePostLoad = true;
}
}
//Здесь мы получаем от сервера картинки
public class AsyncTaskGetPostsMore extends AsyncTask
@Override
//То что выполнится по завершении фонового процесса
protected void onPostExecute(String result) {
super.onPostExecute(result);
//вызываем метод, где обрабатываем JSON и выводим картинки
makePosts(result);
morePostLoad = true;
}
}
//Метод вывода постов
void makePosts(String postData) {
try {
//Создаём объект JSON
JSONObject jsnResp = new JSONObject(postData);
JSONArray jposts = jsnResp.getJSONArray("data");
lastPost = jsnResp.getInt("lastpost");
//Цикл вывода картинок
for (int i = 0; i < jposts.length(); i++) {
JSONObject curentPost = new JSONObject(jposts.getString(i));
String picURL; //переменная для хранения ссылок на картинки
//Узнаём дал ли сервер картинку
if (curentPost.getString("pics").equals("-1")) {
//При негативном результате ссылка будет null
Log.d("PICS", "is -1");
picURL = null;
} else {
//Позитивный результат
JSONArray picsInPost = curentPost.getJSONArray("pics");
JSONObject picTest = new JSONObject(picsInPost.getString(0));
//Вытаскиваем ссылку для картинки
picURL = picTest.getString("picname");
//Создаём ImageView
Log.d("IMGCOUNT", String.valueOf(imgCount));
iv.add(new ImageView(HomeActivity.this));
LinearLayout lL = (LinearLayout) findViewById(R.id.linearL);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
lL.addView(iv.get(imgCount), lp); //Вставляем его в нужный Layout
//Загружаем картинку из интернета в ImageView
/*Picasso.with(HomeActivity.this)
.load(picURL) //ссылка
.placeholder(R.drawable.loading) //картинка загрузки
.error(R.drawable.error) //картинка ошибки
.into(iv); //сюда мы грузим картинку*/
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.loading) // resource or drawable
.showImageOnFail(R.drawable.error) // resource or drawable
//.resetViewBeforeLoading(false) // default
.cacheInMemory(true) // default
.cacheOnDisk(true) // default
.imageScaleType(ImageScaleType.EXACTLY) // default
.bitmapConfig(Bitmap.Config.RGB_565) // default
.build();
imageLoader.displayImage(picURL, iv.get(imgCount), options);
imgCount++;
}
Log.d("COUNTER", String.valueOf(Counter));
Counter++;
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
Ответ
Ваша проблема в том, что вы используете ScrollView и в нём отображаете ваши картинки, добавляя их в разметку. Минус этого подхода в том, что ScrollView не убирает из памяти элементы разметки, которые не видны на экране. Отсюда и постоянно растущее потребление памяти и закономерный крах.
Выход - использовать RecyclerView вместо ScrollView, ибо он убирает из памяти неотображаемые элементы разметки и, таким образом, не увеличивает в прогрессии объём потребляемой памяти.
Комментариев нет:
Отправить комментарий