AsyncTask源码解析,简单使用

也是很久没有更新博客,今天看完AsyncTask源码,把学到的东西分享一下


AsyncTask 介绍


一个处理异步操作的类。

为了更方便的在子线程中更新UI,Android 1.5版本引入了AsyncTask类,使子线程非常灵活方便的切换到UI线程(主线程);



AsyncTask  主要方法介绍

onProExecute()
这个方法会在任务执行之前调用,一般都是做初始化,dialog提示这类的操作。

doInBackground()
这个方法会在子线程中运行,不进行UI更新操作,我们应该把耗时的任务放到这个里面执行。任务过程中的结果,继续给onProgressUpdate调用进行UI更新操作。如果doInBackground第三个参数是void,则不用返回结果

onProgressUpdate()
这个方法可以更新UI,根据doInBackground过程中结果,做相应的UI处理。

onPostExecute()
当后台的任务执行完毕,调用这个方法,根据return返回来的结果做出相应的处理,比如,关闭对话框,Toast提示等等。。





分析AsyncTask源码

首先我们从AsyncTask.execute()方法入手,因为这是启动异步的入口
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
    
发现三个参数Params,Progerss.Result,一个executeonExecutor()方法
Params 传入参数
Progersss, 任务更新进度值
Result     任务执行完毕的返回值

 在进入executeonExecutor()方法
 @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            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;
        exec.execute(mFuture);

        return this;
    }

发现AsyncTask一个核心方法onProExecute().这就是为什么onProExecute()方法会第一个运行。在接着一个exec.execute(mFuture)方法出现 并且里面带一个mFuture参数。先不管mFuture参数,直接看exec.

exec这个参数是通过executeOnExecutor(Executorexec,Params params)方法传过来的,那么Executor又是哪里赋值呢,继续往上面走,你会发现其实这个Executor是通过executeOnExecutor(sDefaultExecutor,params)中的sDefaultExecutor赋值,然后我们找到sDefaultExecutor
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    
sDefaultExecutor通过SerialExecutor()赋值。这里就清楚了 其实exec这个参数间接就是SerialExecutor。然后我们在回到上面的exec.execute(mFuture)方法,也就是SerialExecutor.execute()方法

public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }



发现一个r.run(),r是execute(Runable r)传过来的,往回看因为SerialExecutot.execute(Runable r) = exec.execute(mFuture);所以这个mFuture就是execute(Runable r)里面的参数。那么mFuture又是什么呢!

	private final FutureTask<Result> mFuture;



理所当然mFuture.run()
 public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

活捉一直c.call()那么问题来了c = callable ,callable又是什么鬼。不要慌车到山前必有路。其实就是mFuture做初始化的时候传过来的,

   mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

  
只不过这个callable有点坏,伪装成了mWorker,那就让我们揭开这个小家伙的面纱吧!
 mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

原来如此,是这个doInBackground(mParams)作祟,AsyncTask第二个主要方法终于是看到了。那么第三个还会远么?当然是否定的。继续往下面看,因为doInBackground(mParams)是子线程,那怎么跟UI线程交互呢,二话不说先调用publishProgerss();
@WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

子线程跟UI线程通信怎么少得了Handler, 发送MESSAGE_POST_PROGRESS,  接收消息
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;


AsyncTask第三个方法也出来,onProgressUpdate(result,mDate)是不是很兴奋马上就能真相大白了。


不浮躁回到刚刚那个mWorker初始化的地方,你会发现除了有一个doInBackground()方法外,还有一个 finally不管怎么样都会实现里面的代码 postRsult (result),到底是怎么一个重要的角色,风雨无阻呢!
   private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
发送一条MESSAGE_POST_RESULT,然后接收信息
       @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) 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;
            }
        }

mTask.finish();一看就知道这个是AsyncTask

  final AsyncTask mTask;


  private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
一切真相大白,AsyncTask第四个方法 onPostExecute(result);






AsyncTask简单使用

  1. class DownloadTask extends AsyncTask<Void, Integer, Boolean> {  
  2.   
  3.     @Override  
  4.     protected void onPreExecute() {  
  5.         progressDialog.show();  
  6.     }  
  7.   
  8.     @Override  
  9.     protected Boolean doInBackground(Void... params) {  
  10.         try {  
  11.             while (true) {  
  12.                 int downloadPercent = doDownload();  
  13.                 publishProgress(downloadPercent);  
  14.                 if (downloadPercent >= 100) {  
  15.                     break;  
  16.                 }  
  17.             }  
  18.         } catch (Exception e) {  
  19.             return false;  
  20.         }  
  21.         return true;  
  22.     }  
  23.   
  24.     @Override  
  25.     protected void onProgressUpdate(Integer... values) {  
  26.         progressDialog.setMessage("当前下载进度:" + values[0] + "%");  
  27.     }  
  28.   
  29.     @Override  
  30.     protected void onPostExecute(Boolean result) {  
  31.         progressDialog.dismiss();  
  32.         if (result) {  
  33.             Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();  
  34.         } else {  
  35.             Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();  
  36.         }  
  37.     }  
  38. }  



new DownloadTask().execute(); 
使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
使用Python来安装geopandas包时,由于geopandas依赖于几个其他的Python库(如GDAL, Fiona, Pyproj, Shapely等),因此安装过程可能需要一些额外的步骤。以下是一个基本的安装指南,适用于大多数用户: 使用pip安装 确保Python和pip已安装: 首先,确保你的计算机上已安装了Python和pip。pip是Python的包管理工具,用于安装和管理Python包。 安装依赖库: 由于geopandas依赖于GDAL, Fiona, Pyproj, Shapely等库,你可能需要先安装这些库。通常,你可以通过pip直接安装这些库,但有时候可能需要从其他源下载预编译的二进制包(wheel文件),特别是GDAL和Fiona,因为它们可能包含一些系统级的依赖。 bash pip install GDAL Fiona Pyproj Shapely 注意:在某些系统上,直接使用pip安装GDAL和Fiona可能会遇到问题,因为它们需要编译一些C/C++代码。如果遇到问题,你可以考虑使用conda(一个Python包、依赖和环境管理器)来安装这些库,或者从Unofficial Windows Binaries for Python Extension Packages这样的网站下载预编译的wheel文件。 安装geopandas: 在安装了所有依赖库之后,你可以使用pip来安装geopandas。 bash pip install geopandas 使用conda安装 如果你正在使用conda作为你的Python包管理器,那么安装geopandas和它的依赖可能会更简单一些。 创建一个新的conda环境(可选,但推荐): bash conda create -n geoenv python=3.x anaconda conda activate geoenv 其中3.x是你希望使用的Python版本。 安装geopandas: 使用conda-forge频道来安装geopandas,因为它提供了许多地理空间相关的包。 bash conda install -c conda-forge geopandas 这条命令会自动安装geopandas及其所有依赖。 注意事项 如果你在安装过程中遇到任何问题,比如编译错误或依赖问题,请检查你的Python版本和pip/conda的版本是否是最新的,或者尝试在不同的环境中安装。 某些库(如GDAL)可能需要额外的系统级依赖,如地理空间库(如PROJ和GEOS)。这些依赖可能需要单独安装,具体取决于你的操作系统。 如果你在Windows上遇到问题,并且pip安装失败,尝试从Unofficial Windows Binaries for Python Extension Packages网站下载相应的wheel文件,并使用pip进行安装。 脚本示例 虽然你的问题主要是关于如何安装geopandas,但如果你想要一个Python脚本来重命名文件夹下的文件,在原始名字前面加上字符串"geopandas",以下是一个简单的示例: python import os # 指定文件夹路径 folder_path = 'path/to/your/folder' # 遍历文件夹中的文件 for filename in os.listdir(folder_path): # 构造原始文件路径 old_file_path = os.path.join(folder_path, filename) # 构造新文件名 new_filename = 'geopandas_' + filename # 构造新文件路径 new_file_path = os.path.join(folder_path, new_filename) # 重命名文件 os.rename(old_file_path, new_file_path) print(f'Renamed "{filename}" to "{new_filename}"') 请确保将'path/to/your/folder'替换为你想要重命名文件的实际文件夹路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值