通过模仿AsyncTask的封装方式,实现一个后台预读数据的线程,(使用AsyncTask有导致应用FC的风险)

本文介绍了一种改进的预读任务实现方案,通过使用单线程池来替代默认的AsyncTask执行方式,确保任务以先进先出的方式执行,适用于启动时预加载数据的应用场景。
package com.app.fantasticbaby;


import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;


import android.os.AsyncTask.Status;
import android.os.Handler;
import android.os.Message;
import android.os.Process;


public abstract class PreReadTask<Params, Progress, Result> {
private static final String LOG_TAG = "FifoAsyncTask";  
private static final ThreadFactory sThreadFactory = new ThreadFactory(){
private final AtomicInteger  mCount = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
// TODO Auto-generated method stub

return new Thread(r,"PreReadTask #" + mCount.getAndIncrement());
}

};
//只有一个工作线程的线程池
//对比AsyncTask我们实际只修改了一个地方
//我们把PreReadTask的的线程池设置成只有一个工作线程,并且带有一个无边界的缓冲队列,
//这一个工作线程以先进先出的顺序不断从缓冲队列中取出并执行任务
private static final ExecutorService sExecutor = Executors.newSingleThreadExecutor(sThreadFactory);
private static final int MESSAGE_POST_RESULT = 0x1;  
    private static final int MESSAGE_POST_PROGRESS = 0x2;  
    private static final int MESSAGE_POST_CANCEL = 0x3;  
    private static final InternalHandler sHandler = new InternalHandler();
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture; 
    private volatile Status mStatus = Status.PENDING;
    
    /** 
     * Indicates the current status of the task. Each status will be set only once 
     * during the lifetime of a task. 
     */  
    public enum Status {  
        /** 
         * Indicates that the task has not been executed yet. 
         */  
        PENDING,  
        /** 
         * Indicates that the task is running. 
         */  
        RUNNING,  
        /** 
         * Indicates that {@link FifoAsyncTask#onPostExecute} has finished. 
         */  
        FINISHED,  
    }  
    
    /** 
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread. 
     */  
    public PreReadTask() {  
        mWorker = new WorkerRunnable<Params, Result>() {  
            public Result call() throws Exception {  
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
                return doInBackground(mParams);  
            }  
        };  
        mFuture = new FutureTask<Result>(mWorker) {  
            @Override  
            protected void done() {  
                Message message;  
                Result result = null;  
  
                try {  
                    result = get();  
                } catch (InterruptedException e) {  
                    android.util.Log.w(LOG_TAG, e);  
                } catch (ExecutionException e) {  
                    throw new RuntimeException("An error occured while executing doInBackground()",  
                            e.getCause());  
                } catch (CancellationException e) {  
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
                            new PreReadTaskResult<Result>(PreReadTask.this, (Result[]) null));  
                    message.sendToTarget();  
                    return;  
                } catch (Throwable t) {  
                    throw new RuntimeException("An error occured while executing "  
                            + "doInBackground()", t);  
                }  
  
                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
                        new PreReadTaskResult<Result>(PreReadTask.this, result));  
                message.sendToTarget();  
            }  
        };  
    }  
    
    /** 
     * Returns the current status of this task. 
     * 
     * @return The current status. 
     */  
    public final Status getStatus() {  
        return mStatus;  
    }  
    
    /** 
     * Override this method to perform a computation on a background thread. The 
     * specified parameters are the parameters passed to {@link #execute} 
     * by the caller of this task. 
     * 
     * This method can call {@link #publishProgress} to publish updates 
     * on the UI thread. 
     * 
     * @param params The parameters of the task. 
     * 
     * @return A result, defined by the subclass of this task. 
     * 
     * @see #onPreExecute() 
     * @see #onPostExecute 
     * @see #publishProgress 
     */  
    protected abstract Result doInBackground(Params... params); 
    
    /** 
     * Runs on the UI thread before {@link #doInBackground}. 
     * 
     * @see #onPostExecute 
     * @see #doInBackground 
     */  
    protected void onPreExecute() {  
    }  
  
    /** 
     * Runs on the UI thread after {@link #doInBackground}. The 
     * specified result is the value returned by {@link #doInBackground} 
     * or null if the task was cancelled or an exception occured. 
     * 
     * @param result The result of the operation computed by {@link #doInBackground}. 
     * 
     * @see #onPreExecute 
     * @see #doInBackground 
     */  
    @SuppressWarnings({"UnusedDeclaration"})  
    protected void onPostExecute(Result result) {  
    }  
    
    /** 
     * Runs on the UI thread after {@link #publishProgress} is invoked. 
     * The specified values are the values passed to {@link #publishProgress}. 
     * 
     * @param values The values indicating progress. 
     * 
     * @see #publishProgress 
     * @see #doInBackground 
     */  
    @SuppressWarnings({"UnusedDeclaration"})  
    protected void onProgressUpdate(Progress... values) {  
    }  
  
    /** 
     * Runs on the UI thread after {@link #cancel(boolean)} is invoked. 
     * 
     * @see #cancel(boolean) 
     * @see #isCancelled() 
     */  
    protected void onCancelled() {  
    }  
  
    /** 
     * Returns <tt>true</tt> if this task was cancelled before it completed 
     * normally. 
     * 
     * @return <tt>true</tt> if task was cancelled before it completed 
     * 
     * @see #cancel(boolean) 
     */  
    public final boolean isCancelled() {  
        return mFuture.isCancelled();  
    }  
    
    /** 
     * Attempts to cancel execution of this task.  This attempt will 
     * fail if the task has already completed, already been cancelled, 
     * or could not be cancelled for some other reason. If successful, 
     * and this task has not started when <tt>cancel</tt> is called, 
     * this task should never run.  If the task has already started, 
     * then the <tt>mayInterruptIfRunning</tt> parameter determines 
     * whether the thread executing this task should be interrupted in 
     * an attempt to stop the task. 
     * 
     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this 
     *        task should be interrupted; otherwise, in-progress tasks are allowed 
     *        to complete. 
     * 
     * @return <tt>false</tt> if the task could not be cancelled, 
     *         typically because it has already completed normally; 
     *         <tt>true</tt> otherwise 
     * 
     * @see #isCancelled() 
     * @see #onCancelled() 
     */  
    public final boolean cancel(boolean mayInterruptIfRunning) {  
        return mFuture.cancel(mayInterruptIfRunning);  
    }  
    
    /** 
     * Waits if necessary for the computation to complete, and then 
     * retrieves its result. 
     * 
     * @return The computed result. 
     * 
     * @throws CancellationException If the computation was cancelled. 
     * @throws ExecutionException If the computation threw an exception. 
     * @throws InterruptedException If the current thread was interrupted 
     *         while waiting. 
     */  
    public final Result get() throws InterruptedException, ExecutionException {  
        return mFuture.get();  
    }  
    
    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,  
    ExecutionException, TimeoutException {  
return mFuture.get(timeout, unit);  
}  
    
    public final PreReadTask<Params, Progress, Result> execute(Params... params) {  
        if (mStatus != Status.PENDING) {  
            switch (mStatus) {  
                case RUNNING:  
                    throw new IllegalStateException("Cannot execute task:"  
                            + " the task is already running.");  
                case FINISHED:  
                    throw new IllegalStateException("Cannot execute task:"  
                            + " the task has already been executed "  
                            + "(a task can be executed only once)");  
            }  
        }  
  
        mStatus = Status.RUNNING;  
  
        onPreExecute();  
  
        mWorker.mParams = params;  
        sExecutor.execute(mFuture);  
  
        return this;  
    }  
    
    /** 
     * This method can be invoked from {@link #doInBackground} to 
     * publish updates on the UI thread while the background computation is 
     * still running. Each call to this method will trigger the execution of 
     * {@link #onProgressUpdate} on the UI thread. 
     * 
     * @param values The progress values to update the UI with. 
     * 
     * @see #onProgressUpdate 
     * @see #doInBackground 
     */  
    protected final void publishProgress(Progress... values) {  
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,  
                new PreReadTaskResult<Progress>(this, values)).sendToTarget();  
    }  
  
    private void finish(Result result) {  
        if (isCancelled()) result = null;  
        onPostExecute(result);  
        mStatus = Status.FINISHED;  
    }  
    
    
    private static class InternalHandler extends Handler {  
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
        @Override  
        public void handleMessage(Message msg) {  
            PreReadTaskResult result = (PreReadTaskResult) msg.obj;  
            switch (msg.what) {  
                case MESSAGE_POST_RESULT:  
                    // There is only one result  
                    result.mTask.finish(result.mData[0]);  
                    break;  
                case MESSAGE_POST_PROGRESS:  
                    result.mTask.onProgressUpdate(result.mData);  
                    break;  
                case MESSAGE_POST_CANCEL:  
                    result.mTask.onCancelled();  
                    break;  
            }  
        }  
    }  
    
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
        Params[] mParams;  
    }  
    
    private static class PreReadTaskResult<Data> {  
        final PreReadTask mTask;  
        final Data[] mData;  
  
        PreReadTaskResult(PreReadTask task, Data... data) {  
            mTask = task;  
            mData = data;  
        }  
    }  
    

}



在空闲时对获取成本较高的数据(如要读取本地或网络资源)进行预读是提高性能的有效手段。为了给用户带来更好的交互体验,提高响应性,很多网络应用(如新闻阅读类应用)都在启动的时候进行预读,把网络数据缓存到sdcard或者内存中。

打开应用之后会有一个欢迎界面,在打开欢迎界面的同时我们在后台启动预读线程,预读下一个Activity需要显示的数据,预读数据保存到一个静态的Hashmap中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值