我有以下异步任务,它应该只从给定的URL加载图像。图像确实存在,我可以访问它们
private class FetchVehicleImage extends AsyncTask<String, Integer, Bitmap>
{
private ProgressBar mSpinner;
private ImageView mImage;
private String imagesBaseUrl = "http://mywebsite.net/images/";
private URL url = null;
@Override
protected void onPreExecute()
{
mImage = (ImageView) findViewById(R.id.vehicle_image);
mSpinner = (ProgressBar) findViewById(R.id.vehicle_image_progress_bar);
mSpinner.setIndeterminate(true);
mSpinner.setVisibility(View.VISIBLE);
mImage.setVisibility(View.GONE);
}
@Override
protected Bitmap doInBackground(String... strings)
{
Bitmap bm = null;
try
{
url = new URL(imagesBaseUrl + strings[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
bm = BitmapFactory.decodeStream(is);
}
catch (Exception e)
{
e.printStackTrace();
}
return bm;
}
protected void onPostExecute(final Bitmap result)
{
if (result != null)
{
mImage.setImageBitmap(result);
}
mImage.setVisibility(View.VISIBLE);
mSpinner.setVisibility(View.GONE);
}
}
我从未在doInBackground
中看到异常,但有时候bm
会返回null,但它非常间歇性。我有4个图像,其中3个图像每次加载完全正常,但是如果我在bm
任务上遇到一个断点,假设有足够的时间来完成其工作,那么只会加载一个图像?
我认为doInBackground
应该在后台线程上运行,因此我应该总是获取图像,或者获得异常?
答案 0 :(得分:2)
N.B。如果您的应用程序仅仅为其位图耗尽了本机后备内存,那么这种方法将无济于事。如果你有难以解释的位图问题,特别是在Honeycomb之前,我不能夸大理解Dalvik堆与本机后备内存之间关系的重要性。杜布罗先生对此的讨论对我非常有帮助 - 值得一直聆听Dubroy's Heap Presentation
然后,我试图回答你上面的问题。 。 。我无法证明这一点,但我非常怀疑它不是线程安全的。我在抓取后进行图像处理时就点击了这个。与上面的示例一样,当我请求多个图像文件并在它们到达时处理它们时,我得到OutOfMemory
错误,我发现它只是发现堆和可用的本机后备内存都很好(分别>&gt; 100k和> 100M)。并且,有时获取工作(如您所述),但有时不工作。在某些设备上,它比其他设备更强大。当被要求发明故事的原因时,我想,在某些设备上可能存在图像处理硬件(例如jpg编码器)而不是其他设备,OS的本地库可能会或可能不会利用它们。然后,我立即着手将这些硬件瓶颈归咎于不是线程安全的 - 所有这些都没有任何类似证据的最小碎片。无论如何,我发现的唯一方法是在我的测试稳定(大约十几个)中的所有设备 - 可靠 - 是隔离位图操作部分和单线程。
在上面的示例中,您仍然可以使用AsyncTask
实际从网络中获取文件,并将它们写入某个位置的存储(原始字节流)。当AsyncTask
完成时(即调用onPostExecution
的代理人),那么您可以执行类似我的海报课程。
在我的活动中(我发出了多个下载请求),我在类中创建了一个全局执行器,它最初在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中)具有'busy'drawable。当委托被调用时(在文件下载并被该线程转换为Drawable之后,它就可以加载到你指定的任何Drawable接收器中。可以并行下载任意数量的图像,这保证了后台线程只会一次处理一个图像。很幸运的是,它不占用UI线程来进行图像处理
(注意,你的调用类中仍然需要一个Handler
(将自己设置为委托的那个)让UI线程实际将Drawable 放入接收{{1} }} / View
/其它)。对于尝试完整性,可能如下所示:
Layout
也许这一切都有帮助,也许不是。但我会爱听到你提出的优秀问题的确切答案。