7、以后进先出方式加载图片

以网格/瀑布流的方式显示网络图片,理想的解决方案需要有下面几个方面:

  • 保持响应灵敏的UI
  • 在应用程序UI线程之外处理网络和磁盘IO操作
  • 对于listview,需要支持视图回收
  • 快速显示图片的缓存机制
  • 更优的请求加载顺序
    • 问题背景:用户快速下拉时,希望图片优先加载当前屏幕的图片。而不是按照顺序加载。

AsyncTask的问题

  1. 生命周期和内存泄漏

    "当Activity结束或者退出应用时AsyncTask会一直执行doInBackground()方法直到方法执行结束,这可能会导致在onPostExecute时view不存在而导致崩溃溃,以及可能的内存泄露"。

    这个需要开发者在onDestory()时调用cancel()来取消任务。

  2. cancel不能正常取消的问题

    首先调用cancel终止AsyncTask的原理是对执行异步任务的线程调用interrupt()函数。 每个线程内部都有一个boolean型变量表示线程的中断状态,true代表线程处于中断状态,false表示未处于中断状态。 而interrupt()方法的作用只是用来改变线程的中断状态(把线程的中断状态改为true,即被中断)。因此interrupt()方法代表着外界希望中断此线程,只是希望,具体怎么处理还是线程内部来做,一般情况下interrupt()方法可以使处于阻塞状态的线程抛出InterruptedException从而结束阻塞状态或则判断Thread.interrupted()来处理逻辑。所以如果你的AsyncTask后台任务有未做中断的处理肯定会一直执行这个线程。

    这需要自己在doInbackground里进行线程是否已经中断的判断,由自己来规避是否还要继续执行。

  3. Activity意外重启,状态消失问题

    另一个问题就是在屏幕旋转等造成Activity重新创建时AsyncTask数据丢失的问题。当Activity销毁并创新创建后,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。导致onPostExecute()没有任何作用。

    这个是由于界面销毁导致的,其实跟AsyncTask是没有太大关系的。 看到网上一个解决的方法,是基于类似EventBus的事件通知机制。

    // XXAsyncTask.java
    @Override
     protected String doInBackground(Void... params) {
         Random random = new Random();
         final long sleep = random.nextInt(10);
         try {
             Thread.sleep(10 * 6000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         return "Slept for " + sleep + " seconds";
     }
    
     @Override
     protected void onPostExecute(String result) {
         MyBus.getInstance().post(new AsyncTaskResultEvent(result));
     }
    
     // XXActivity.java
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.otto_layout);
    
         findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
             @Override public void onClick(View v) {
                 new MyAsyncTask().execute();
             }
         });
    
         MyBus.getInstance().register(this);
     }
    
     @Override
     protected void onDestroy() {
         MyBus.getInstance().unregister(this);
         super.onDestroy();
     }
    
     @Subscribe
     public void onAsyncTaskResult(AsyncTaskResultEvent event) {
         Toast.makeText(this, event.getResult(), Toast.LENGTH_LONG).show();
     }
    
  4. 错误处理问题

    AsyncTask没有对发生的一些异常进行处理,你只能在onBackground里进行一些判断,但之外的一些异常情况发生你都无法了解,比如线程异常退出等。

  5. 多个任务的管理问题

    如果需要多个后台任务,需要新建多个AsyncTask来执行任务,在需要退出的时候你需要对每一个都进行一定的处理来避免内存泄露以及UI问题,这是一个很麻烦的事情。

  6. 串行还是并行

    • <1.6:串行
    • >=1.6&&<=2.3:并行
    • >=3.0:默认是串行,但是开发者可以自定义,自己提供exec,实现并行处理

如何实现后进先出

主要是自己实现特定的线程池。

线程类

public class LIFOTask extends FutureTask<Object> implements
        Comparable<LIFOTask> {
    private static long counter = 0;
    private final long priority;

    public LIFOTask(Runnable runnable) {
        super(runnable, new Object());
        priority = counter++;
    }

    public long getPriority() {
        return priority;
    }

    @Override
    public int compareTo(LIFOTask other) {
        return priority > other.getPriority() ? -1 : 1;
    }
}

线程池类:

public class NetworkThreadPool {
    private static LIFOThreadPoolProcessor pool = new LIFOThreadPoolProcessor(3);

    public static Future<?> submitTask(LIFOTask task) {
        return pool.submitTask(task);
    }
}

线程处理类:

public class LIFOThreadPoolProcessor {
    private BlockingQueue<Runnable> opsToRun = new PriorityBlockingQueue<Runnable>(
            64, new Comparator<Runnable>() {
        @Override
        public int compare(Runnable r0, Runnable r1) {
            if (r0 instanceof LIFOTask && r1 instanceof LIFOTask) {
                LIFOTask l0 = (LIFOTask) r0;
                LIFOTask l1 = (LIFOTask) r1;
                return l0.compareTo(l1);
            }
            return 0;
        }
    });

    private ThreadPoolExecutor executor;

    public LIFOThreadPoolProcessor(int threadCount) {
        executor = new ThreadPoolExecutor(threadCount, threadCount, 0,
                TimeUnit.SECONDS, opsToRun);
    }

    public Future<?> submitTask(LIFOTask task) {
        return executor.submit(task);
    }

    public void clear() {
        executor.purge();
    }
}

实际的调用就很简单,只要NetworkThreadPool.submitTask(task);

如何处理闪切的?

主要是通过设置ImageDrawable的值,在下次再加载的时候,先判断时候已经在加载中了。

results matching ""

    No results matching ""