package com.qozix.mapview.tiles; import java.lang.ref.WeakReference; import java.util.LinkedList; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import android.os.AsyncTask; class TileRenderTask extends AsyncTask<Void, MapTile, Void> { private final ExecutorService executor = Executors.newCachedThreadPool(); private final WeakReference<TileManager> reference; private final AtomicInteger numberOfTilesToRender = new AtomicInteger(); // package level access TileRenderTask( TileManager tm ) { super(); reference = new WeakReference<TileManager>( tm ); } @Override protected void onPreExecute() { final TileManager tileManager = reference.get(); if ( tileManager != null ) { tileManager.onRenderTaskPreExecute(); } } @Override protected Void doInBackground( Void... params ) { // have we been stopped or dereffed? TileManager tileManager = reference.get(); // if not go ahead, but check again in each iteration if ( tileManager != null ) { // avoid concurrent modification exceptions by duplicating LinkedList<MapTile> renderList = tileManager.getRenderList(); // start rendering, checking each iteration if we need to break out for ( MapTile m : renderList ) { // check again if we've been stopped or gc'ed tileManager = reference.get(); if ( tileManager == null ) { return null; } // quit if we've been forcibly stopped if ( tileManager.getRenderIsCancelled() ) { return null; } // quit if task has been cancelled or replaced if ( isCancelled() ) { return null; } // create new AsyncTask for each tile and render them new AsyncTask<MapTile, Void, MapTile>() { @Override protected MapTile doInBackground(MapTile... params) { numberOfTilesToRender.incrementAndGet(); TileManager tileManager = reference.get(); if ( tileManager == null ) { return null; } // quit if we've been forcibly stopped if ( tileManager.getRenderIsCancelled() ) { return null; } // quit if task has been cancelled or replaced if ( isCancelled() ) { return null; } MapTile mapTile = params[0]; //decode the map tile bitmap tileManager.decodeIndividualTile(mapTile); return mapTile; } @Override protected void onPostExecute(MapTile mapTile) { if (mapTile == null) { //sometimes that is null... return; } TileManager tileManager = reference.get(); if(tileManager != null) { // if not cancelled render the tile if(!tileManager.getRenderIsCancelled()) { tileManager.renderIndividualTile(mapTile); } //when we have finished every render task, inform the manager if(numberOfTilesToRender.decrementAndGet() == 0) { tileManager.onRenderTaskPostExecute(); } } } }.executeOnExecutor(executor, m); } } return null; } @Override protected void onCancelled() { executor.shutdown(); // Disable new tasks from being submitted try { // Wait a while for existing tasks to terminate if (!executor.awaitTermination(30, TimeUnit.SECONDS)) { executor.shutdownNow(); // Cancel currently executing tasks // Wait a while for tasks to respond to being cancelled if (!executor.awaitTermination(30, TimeUnit.SECONDS)) System.err.println("Pool did not terminate"); } } catch (InterruptedException ie) { // (Re-)Cancel if current thread also interrupted executor.shutdownNow(); // Preserve interrupt status Thread.currentThread().interrupt(); } // have we been stopped or dereffed? TileManager tileManager = reference.get(); // if not go ahead but check other cancel states if ( tileManager != null ) { tileManager.onRenderTaskCancelled(); } } }