3

次の非同期タスクがあります。これは、指定された 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);
        }
    }

に例外は見られませんが、null として返されるdoInBackgroundこともありますが、非常に断続的です。bm4 つの画像があり、そのうちの 3 つは毎回完全に正常に読み込まれますが、bm割り当てのブレークポイントに到達した場合にのみ読み込まれるのは 1 つだけで、作業を行うのに十分な時間が与えられますか?

バックグラウンドスレッドで実行する必要があると思ったdoInBackgroundので、常に画像を取得するか、例外を取得する必要がありますか?

4

1 に答える 1

2

注: アプリがビットマップ用のネイティブ バッキング メモリを使い果たしているだけの場合、このアプローチは役に立ちません。ビットマップ、特にハニカム以前の問題について説明が難しい場合は、Dalvik ヒープとネイティブ バッキング メモリとの関係を理解することの重要性をいくら強調してもしすぎることはありません。これに関する Dubroy 氏の議論は、私にとって非常に役に立ちました

そして、上記のあなたの質問に対する私の答えを試みました。. . それを証明することはできませんが、スレッドセーフではないという非常に強い疑いがあります。フェッチ後に画像操作をするときにこれを叩きます。上記の例のように、複数の画像ファイルをリクエストし、それらが到着したときにそれらを処理すると、OutOfMemory私がキャッチしたエラーは、ヒープと利用可能なネイティブバッキングメモリの両方が正常であることを発見するためだけです(それぞれ> 100kと> 100M)。そして、フェッチが機能することもあります(説明したように)が、機能しないこともあります。一部のデバイスでは、他のデバイスよりも堅牢です。これがなぜなのかという話を考え出すよう求められたとき、OS のネイティブ ライブラリがそれ自体を利用する場合と利用しない場合があるデバイスによっては画像処理ハードウェア (jpg エンコーダーなど) が存在し、他のデバイスでは存在しない可能性があると想像します。次に、これらのハードウェアのボトルネックがスレッドセーフではないことをすぐに非難します。証拠に似たものはほとんどありません。とにかく、私のテスト安定版 (約 12 個) のすべてのデバイスで確実に動作することがわかった唯一のアプローチは、ビットマップ操作部分とシングルスレッドを分離することです。

AsyncTask上記の例では、実際にネットワークからファイルをフェッチし、それらをストレージのどこかに書き込むために引き続き使用します (生のバイト ストリーム)。がAsyncTask完了する (つまり、そのデリゲートを呼び出すonPostExecution) と、以下の Poster クラスのようなことを行うことができます。

私のアクティビティ (複数のダウンロード リクエストを作成する場所) では、最初に UI スレッドでインスタンス化されるクラスにグローバル エグゼキュータを作成します。

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 スレッド内) には「ビジー」なドローアブルがあります。デリゲートが呼び出されると (ファイルがダウンロードされ、このスレッドによって Drawable に変換された後、指定した Drawable レシーバーにロードする準備が整います。任意の数の画像を並行してダウンロードできます。これにより、バックグラウンド スレッドが一度に 1 つの画像のみを処理します。幸いなことに、画像処理を行うために UI スレッドを拘束しません。

(注意: UI スレッドが実際に Drawable受信側に置くには、Handler呼び出し側クラス (デリゲートとして設定するクラス) に が必要です)。完全を期すために、次のようになります。ViewLayout

mHandler.post(new Runnable() {
    @Override
    public void run() {
        aItem.getButton().setBackgroundDrawable(aDrawable);
        aItem.getButton().postInvalidate();
}
});

おそらく、これはすべて役立つかもしれませんが、そうではないかもしれません。しかし、あなたが提起した素晴らしい質問に対する決定的な答えを聞きたいです.

于 2012-11-15T19:42:30.800 に答える