N.B. Если ваше приложение просто исчерпало собственную память для своих растровых изображений, то этот подход не поможет. Если у вас возникают проблемы с растровыми изображениями, особенно с предварительными сотами, я не могу переоценить важность понимания отношения кучи Dalvik к собственной памяти. Обсуждение этого вопроса было исключительно полезным для меня - стоит прослушать всю дорогу через Dubroy's Heap Presentation
И затем мой ответ на ваш вопрос выше. , , Я не могу это доказать, но у меня очень сильное подозрение, что оно не является потокобезопасным. Я ударил это, когда манипулирование изображениями после извлечения. Как и в примере выше, когда я запрашиваю несколько файлов изображений и обрабатываю их по мере их поступления, я получаю ошибки OutOfMemory
, которые я улавливаю, только чтобы обнаружить, что и куча, и доступная собственная резервная память хороши (> 100 тыс. И > 100M соответственно). И, иногда, извлечения работает (как вы описали), но иногда нет. На некоторых устройствах он более устойчив, чем другие. Когда вы призвали придумать историю, почему это так, я полагаю себе, что на некоторых устройствах могут быть аппаратные средства обработки изображений (например, jpg-кодеры), а не другие, которые могут или не могут использоваться в собственных библиотеках ОС. Затем я сразу же обвиняю эти узкие места в аппаратных средствах за то, что они не являются потокобезопасными - все без малейшего клочка чего-либо, напоминающего доказательства. Во всяком случае, единственный подход, который я нашел, работает на всех устройствах моей тестовой стабильной (около дюжины) - надежно - заключается в том, чтобы изолировать части обработки растровых изображений и однопоточность.
В приведенном выше примере вы по-прежнему будете использовать AsyncTask
для фактического извлечения файлов из сети и записи их в хранилище где-то (поток необработанного байта). Когда AsyncTask
завершает работу (т. Е. Вызывает его делегат для), вы можете сделать что-то вроде моего класса Poster ниже.
В моей деятельности (где я сделать несколько запросов на загрузку), создать глобальный исполнитель в классе, который инстанцированный сначала в UI-Thread:
public ExecutorService mImagePipelineTask = null; // Thread to use for pipelining images (overlays, etc.)
А затем инициализировать его:
mImagePipelineTask = Executors.newSingleThreadExecutor();
Затем я обходится без использования AsyncTask
, чтобы получить контроль над количеством потоков в пуле Thread
. Мои асинхронные биты выглядят следующим образом:
public class PosterImage extends HashMap<String, Object> {
private final String TAG = "DEBUG -- " + ClassUtils.getShortClassName(this.getClass());
private PosterImageDelegate mPosterDelegate = null;
private Drawable mBusyDrawable = null;
private Drawable mErrorDrawable = null;
private ExecutorService mImagePipelineTask = null;
/*
* Globals
*/
Context mContext = null;
/*
* Constructors
*/
public PosterImage() {
}
public PosterImage(PlaygroundActivity aContext) {
mContext = aContext;
mImagePipelineTask = aContext.mImagePipelineTask;
mBusyDrawable = mContext.getResources().getDrawable(R.drawable.loading);
mErrorDrawable = mContext.getResources().getDrawable(R.drawable.load_error);
}
Затем некоторые фрагменты, которые вам, вероятно, не волнуют. , ,а затем некоторые инициализации вещи, например, как установить наш делегат (вы хотите интерфейс PosterImageDelegate, конечно):
public void setPosterDelegate(PosterImageDelegate aPosterDelegate) {
mPosterDelegate = aPosterDelegate;
}
и затем биты, которые делают манипуляции с изображениями, а также в качестве побочного эффекта, используйте BitmapFactory
(и Drawable
) классов. Для того, чтобы использовать эту функцию, вы экземпляр объекта PosterImage, установить себя в качестве делегата, а затем вызвать этот парень:
public Drawable getPreformattedFileAsync() {
if(mFetchFileTask == null) {
Log.e(TAG, " -- Task is Null!!, Need to start an executor");
return(mErrorDrawable);
}
Runnable job = new Runnable() {
public void run() {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
Thread.currentThread().yield();
if(mPosterDelegate != null) {
Drawable retDrawable = getPreformattedFile();
if(retDrawable != null) {
mPosterDelegate.onDrawableRequest(retDrawable);
} else {
mPosterDelegate.onDrawableRequest(mErrorDrawable);
}
}
}
};
mImagePipelineTask.execute(job);
return(mBusyDrawable);
}
public Drawable getPreformattedFile() {
Drawable ret = null;
try {
FileInputStream in = new FileInputStream(preformattedFileName());
ret = Drawable.createFromStream(in, null);
// do something interesting with the Drawable
} catch(OutOfMemoryError e) {
System.gc();
e.printStackTrace();
// Will return null on its own
} catch(Exception e) {
Log.e(TAG, "Trouble reading PNG file ["+e+"]");
}
return(ret);
}
Когда это возвращается, вызывающий объект (в UI-Thread) имеет «занята» вытяжку. Когда делегат получает вызов (после загрузки файла и преобразования в Drawable этим потоком, он готов к загрузке в любой назначаемый вами Drawable-приемник. Любое количество изображений может быть загружено параллельно, и это гарантирует, что фоновый поток буду только когда-либо процесс один образом в то время. к счастью, он делает не подвязать нить пользовательского интерфейса, чтобы сделать процесс обработки изображений
(NB вам все еще нужен в своем классе вызывающего в Handler
(тот, который ставит перед собой как делегат), чтобы поток пользовательского интерфейса фактически помещал Drawable в принимающий View
/Layout
/независимо от того, что угодно). Для попытки завершенности это может выглядеть так:
mHandler.post(new Runnable() {
@Override
public void run() {
aItem.getButton().setBackgroundDrawable(aDrawable);
aItem.getButton().postInvalidate();
}
});
Возможно, все это помогает, возможно, нет. Но я бы люблю, чтобы услышать окончательный ответ на отличный вопрос, который вы ставите.
Возможно, эта статья может пролить некоторый свет http://foo.jasonhudgins.com/2010/05/limitations-of-asynctask.html, но в вашем случае только 4 изображения вряд ли применимы. Я бы пошел с обычной Thread (override run()) и runOnUiTread в этом случае возможно –