初步认识AsyncTask

在Android程序中执行后台任务是一个普遍的要求,因此,Android为了便于实现后台任务,提供了AsyncTask工具类。使用AsyncTask,可以使应用程序在后台执行任务,并将任务的运行状态或结果显示在UI主界面线程中。

为了使用AsyncTask,需要派生一个AsyncTask的子类,并且需要重写AsyncTask的4个方法,它们是:
(1)protected void onPreExecute( )

在执行后台任务之前需要执行的操作,例如进行一些初始化的操作,在这个操作中,可以直接将信息显示在UI主界面中。由于这个方法是在UI主线程中执行的,因此,这个方法需要简短、高效;

(2)protected Result doInBackground (Params… params)

在这个方法中实现需要在后台执行的任务,这个方法在一个新线程中执行,在通过execute函数调用启动异步任务时传递的参数将传递给这个函数。在这个函数中,通过调用publishProgress方法将任务执行过程中的状态信息传递给UI线程,同时,这个函数的返回值也将被传递给UI线程;

(3)protected void onProgressUpdate (Progress… values)

在后台任务执行的过程中,后台任务可以将中间状态信息传递给这个函数,通过这个函数,可以将后台任务运行的中间状态传递给UI线程;

(4)protected void onPostExecute (Result result)

当后台任务执行完毕后,可以使用这个函数将后台任务的运行结果通过UI线程显示在主界面中。

AsyncTask类是支持三个泛型的类,因此,在派生AsyncTask类的子类时,需要指出三个类型:
第一个数据类型是传递给doInBackground方法的Params的类型、
第二个数据类型是传递给onProgressUpdate方法的Progress的类型、
第三个数据类型是传递给onPostExecute方法的Result数据类型。
对于不需要使用的参数,我们都可以传递Void类型到AsyncTask泛型。

上面介绍了这么多基础内容,可能你对如何使用AsyncTask还是比较模糊,下面举个例子来说明如何使用AsyncTask来执行后台任务。我们AsyncTask实现实时显示日期时间的例子。运行效果如图所示:
点击停止按钮,将显示如图所示的界面:
在这里插入图片描述
在这里插入图片描述
我们使用Toast显示一个简单的信息框以告知用户当前时钟的状态。
创建工程,并修改相应文件。首先修改res/layout/activity_main.xml文件,使之显示一个TextView组件和一个Button组件,修改后的文件内容如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.ttt.ex19background01.MainActivity" >

    <TextView
        android:id="@+id/id_textview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="24sp" />
    
    <Button 
        android:id="@+id/id_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="@string/text_button_stop"
    />
    </RelativeLayout>

当然,还需要修改res/values/strings.xml文件,在其中定义几个字符串引用,修改后的文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">Ex19Background01</string>
    <string name="action_settings">Settings</string>

    <string name="text_button_start">启动</string>
    <string name="text_button_stop">停止</string>
    
</resources>

现在修改MainActivity.java文件,修改后的文件内容如下:

  package com.ttt.ex19background02;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Locale;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    import android.support.v7.app.ActionBarActivity;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    public class MainActivity extends ActionBarActivity implements OnClickListener {
    	private TextView tv;
    	private Button btn;
    	
    	private AtomicBoolean started = new AtomicBoolean();
    	private Date d;
    	private SimpleDateFormat sdf;
    	
    	private MyAsyncTask mat;
    
    	@Override
    	protected void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		
    		started.set(false);
    		d = new Date();
    		
    		tv = (TextView)this.findViewById(R.id.id_textview);
    		sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
    		String ds = sdf.format(d);
    		tv.setText(ds);
    		
    		btn = (Button)this.findViewById(R.id.id_button);
    		btn.setOnClickListener(this);
    	}
    
    	@Override
    	public boolean onCreateOptionsMenu(Menu menu) {
    		// Inflate the menu; this adds items to the action bar if it is present.
    		getMenuInflater().inflate(R.menu.main, menu);
    		return true;
    	}
    	
    	@Override
    	protected void onResume() {
    		super.onResume();
    		
    		if (started.get() == false) {
    			started.set(true);
    			mat = new MyAsyncTask();
    			mat.execute();
    		}
    	}
    	
    	@Override
    	protected void onPause() {
    		super.onPause();
    		
    		if (started.get() == true) {
    			started.set(false); 
    		}
    	}
    
    	@Override
    	public boolean onOptionsItemSelected(MenuItem item) {
    		// Handle action bar item clicks here. The action bar will
    		// automatically handle clicks on the Home/Up button, so long
    		// as you specify a parent activity in AndroidManifest.xml.
    		int id = item.getItemId();
    		if (id == R.id.action_settings) {
    			return true;
    		}
    		return super.onOptionsItemSelected(item);
    	}
    
    	@Override
    	public void onClick(View v) {
    		if (started.get() == true) {
    			started.set(false);
    			btn.setText(R.string.text_button_start);
    		}
    		else {
    			started.set(true);
    			mat = new MyAsyncTask();
    			mat.execute();
    			btn.setText(R.string.text_button_stop);
    		}
    	}
    	
    	private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
    		@Override
    		protected void onPreExecute() {
    			Toast.makeText(MainActivity.this, "开始实时显示时间", 
    Toast.LENGTH_SHORT).show();
    		}
    		
    		@Override
    		protected Void doInBackground(Void... params) {
    			while(started.get() == true) {
    				try {
    					Thread.sleep(1000);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    					return null;
    				}
    				publishProgress();
    			}
    			return null;
    		}
    		
    		@Override
    		protected void onProgressUpdate (Void... values) {
    			d.setTime(System.currentTimeMillis());
    			String ds = sdf.format(d);
    			tv.setText(ds);
    		}
    		
    		@Override
    		protected void onPostExecute (Void result) {
    			Toast.makeText(MainActivity.this, "停止实时显示时间", 
    Toast.LENGTH_SHORT).show();
    		}
    	}
    }

在这个类的onCreate回调函数中,我们获得界面上组件的引用,并设置对按钮点击的响应处理接口。
我们重点看看MyAsyncTask类的实现。在MyAsyncTask类中,由于4个需要重写的方法中均没有参数,因此,在AsyncTask的泛型中,我们使用了:AsyncTask<Void,
Void,
Void>来表示MyAsyncTask类的三个方法:doInBackground、onProgressUpdate和onPostExecute都不需要参数(Void就是“无”的意思)。

现在看看onPreExecute方法的实现:

@Override
protected void onPreExecute() {
	Toast.makeText(MainActivity.this, "开始实时显示时间",    Toast.LENGTH_SHORT).show();
}

由于这个方法是在UI线程中执行的,因此,在这个方法中,我们可以非常放心的执行界面信息修改,在这里,我们使用Toast显示一个简短的信息;在看看doInBackground方法的实现:

@Override
protected Void doInBackground(Void... params) {
	while(started.get() == true) {
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
			return null;
		}
		publishProgress();
	}
	return null;
}

在这个函数中,当started为true时,我们每隔1秒钟调用一次publishProgress()函数,进而调用onProgressUpdate函数来修改界面上显示的信息。注意,由于onProgressUpdate也是在UI线程中执行的,因此,我们也可以放心的修改界面上显示的信息:

@Override
protected void onProgressUpdate (Void... values) {
	d.setTime(System.currentTimeMillis());
	String ds = sdf.format(d);
	tv.setText(ds);
}

最后,当在某个地方started被设置为false时,导致doInBackground结束运行,进而系统将调用onPostExecute函数,在这里,我们再次使用Toast显示一个简短的信息:

@Override
protected void onPostExecute (Void result) {
	Toast.makeText(MainActivity.this, "停止实时显示时间", Toast.LENGTH_SHORT).show();
}

编写完成MyAsyncTask类后,为了启动时钟线程的运行,我们在Activity的onResume回调函数中创建了MyAsyncTask类的对象,并调用其execute方法启动后台线程开始运行,进而将修改界面上的时钟显示。

当然,当我们结束应用程序时,需要停止后台线程的执行。因此,在Activity的onPause回调函数中,我们将stated置为false进而停止后台线程的运行而达到停止时钟显示的目的。

这里还需说明的是,注意程序中的started变量,由于在UI线程及MyAsyncTask这两个线程中均需要访问这个变量,为了保证该变量数据的完整性,我们使用了java.util.concurrent.atomic.AtomicBoolean这个类型的变量。

通过将started定义为AtomicBoolean类型的,我们可以使用Java JDK提供的并发控制机制来保证started变量在被多个线程使用时的数据完整性。

类似的,对于在多线程中需要使用到的一个原始数据类型,我们都可以使用java.util.concurrent.atomic包中的类型来保证数据的完整性。

现在运行这个程序,即可成功

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值