Android开发实践:线程与异步任务
基于移动客户端的软件特别强调实时性,Android程序更是如此,任何一个程序超过5s没有响应,都会被系统强制杀掉。而且Android也不允许在UI线程中进行任何网络操作,否则就会产生NetworkOnMainThreadException 异常。因此,凡是耗时的操作,都不应该直接出现在UI线程中。今天,我通过最简单直观地示例总结下Android开发中最常用的两种处理耗时操作的方法:一个是线程,另一个是异步任务。
首先,看看示例效果,点击Download后,进度条每1秒中增加1%,直到增加到100%。
我将分别用两种方式实现这个功能。
首先,给出 XML 的布局文件:
(1) 线程(Thread,Runnable)
第一种方法是通过线程的形式来实现,代码如下:
public class DownloadRunnable implements Runnable { private RunnableStateListener mStateListener; private String mURL; public static interface RunnableStateListener { public void onRunnableUpdate(int progress); public void onRunnableComplete(boolean isSuccess); } public DownloadRunnable(RunnableStateListener listener, String url ) { mStateListener = listener; mURL = url; } @Override public void run() { Log.d("DownloadTask", "Begin download, the URL is " + mURL ); for( int i=1; i<=100; i++ ) { mStateListener.onRunnableUpdate(i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); mStateListener.onRunnableComplete(false); return; } } mStateListener.onRunnableComplete(true); }}
Android/Java中,线程是通过 new Thread(Runnable runnable).start(); 来创建和执行的,所以可以先定义一个类实现 Runnalbe 接口,在 run 函数中完成耗时的操作。本类中,定义RunnableStateListener ,是为了方便线程与外界(调用者)交流,将线程中的任务运行状态传递到外界。
(2) 异步任务(AsyncTask)
另一种方法则是采用异步任务来实现,代码如下:
public class DownloadTask extends AsyncTask{ private TaskStateListener mTaskStateListener; public static interface TaskStateListener { public void onTaskUpdate(int progress); public void onTaskComplete(boolean isSuccess); } public DownloadTask(TaskStateListener listener) { mTaskStateListener = listener; } @Override protected Boolean doInBackground(String ... params ) Log.d("DownloadTask", "Begin download, the URL is " + params[0] ); for( int i=1; i<=100; i++ ) { //会回调onProgressUpdate super.publishProgress(i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); return Boolean.FALSE; } } return Boolean.TRUE; } @Override protected void onProgressUpdate(Integer... values) { mTaskStateListener.onTaskUpdate(values[0]); super.onProgressUpdate(values); } @Override protected void onPostExecute(Boolean result) { mTaskStateListener.onTaskComplete(result); }}
异步任务与线程的实现方式有很大不同,异步任务主要通过实例化AsyncTask类来实现,该类有三个接口,doInBackground,该函数是任务的主体部分,将耗时的操作可以放在这里;onProgressUpdate是由系统回调的函数,当doInBackground主体中调用了publishProgress后,则会进入onProgressUpdate更新当前任务的状态,因此,在这里可以通过本类定义的TaskStateListener将状态传递给外界(调用者);而onPostExecute则是在doInBackground主体任务return(结束)后由系统回调。
AsyncTask的原型定义如下:AsyncTask
AsyncTask 通过 new AsyncTask().execute() 来启动,其中 execute 的参数会被传递给 doInBackground 函数。AsyncTask可以通过 getStatus 来获取当前任务的执行状态,通过 cancel来取消。
(3) MainActivity 的实现
MainActivity 的代码如下,我把两种方式的代码都集成在里面了。
public class MainActivity extends Activity implements TaskStateListener,RunnableStateListener { private TextView mProgressShow; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mProgressShow = (TextView)findViewById(R.id.TextShow); mProgressBar = (ProgressBar)findViewById(R.id.ProgressBar); } public void onClickDownLoad(View v) { //new DownloadTask(this).execute("blog.ticktick.51cto.com"); new Thread(new DownloadRunnable(this,"blog.ticktick.51cto.com")).start(); } @Override public void onTaskUpdate(int progress) { mProgressShow.setText(progress+"%"); mProgressBar.setProgress(progress); } @Override public void onTaskComplete(boolean isSuccess) { if( isSuccess ) { Toast.makeText(this,"Download Complete",Toast.LENGTH_LONG).show(); } else { Toast.makeText(this,"Download Failed",Toast.LENGTH_LONG).show(); } } @Override public void onRunnableUpdate(final int progress) { this.runOnUiThread(new Runnable() { @Override public void run() { mProgressShow.setText(progress+"%"); mProgressBar.setProgress(progress); } }); } @Override public void onRunnableComplete(final boolean isSuccess) { this.runOnUiThread(new Runnable() { @Override public void run() { if( isSuccess ) { Toast.makeText(MainActivity.this,"Download Complete",Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this,"Download Failed",Toast.LENGTH_LONG).show(); } } }); }}
这里注意,由线程类回调的onRunnableUpdate和onRunnableComplete函数中,通过this.runOnUiThread的方式在更新UI,而由AsynTask回调的则不需要采用这种方式,因为Android不允许非UI线程改变UI元素,所以必须通过runOnUiThread的方式来更新,而AsynTask则是在内部通过handle收发消息的方式自动切换到了UI线程,所以可以直接更新UI。
关于Android开发中常用的两种耗时操作的处理方式就总结到这儿了,主要通过一个简单的示例程序示范了Runnable和AsynTask的使用方法,工程代码见文章后面的附件。有不清楚的地方,欢迎留言或者来信lujun.hust@gmail.com交流,或者关注我的新浪微博 @卢_俊 获取最新的文章和资讯。
更多相关文章
- Android中子线程真的不能更新UI吗?
- Android中Handler的简析
- Android中的Looper类&Handler &Message
- Android中 AsyncTask的使用
- android的5种数据存储方式
- android线程间通信之handler
- Android(安卓)Handler Looper
- Android简明开发教程十九:线程 Bezier曲线
- Android之网络请求5————OkHttp源码2:发送请求