Android中AsyncTask(异步执行任务)的使用

发布时间:2016-12-7 22:33:25 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"Android中AsyncTask(异步执行任务)的使用",主要涉及到Android中AsyncTask(异步执行任务)的使用方面的内容,对于Android中AsyncTask(异步执行任务)的使用感兴趣的同学可以参考一下。

   AsyncTask(异步执行任务)很常用,在侧边菜单栏,下拉刷新中都有用到~~下面看一下介绍吧     在开发过程中经常会用到多线程,开启单独的线程来处理费时的操作,避免阻塞主线程(UI线程)。在子线程中通过Handler类,采用消息机制来更新主线程,和主UI线程进行通信。但是太多的子线程会给系统带来巨大的负担,随之带来一些性能问题。所以出现了AsyncTask。      Android中的AsyncTask类其实就是一个执行大开销的辅助类。其基本思路是:发起一个异步任务,在子线程中进行执行。当任务线程执行过程中,显示一个进度框,可以根据任务的执行情况发出进度来更新UI线程中的进度框,当任务将结果返回到主线程中,此时将进度框关闭。       AsyncTask底层是一个线程池,所以在处理异步任务的数据量特别大的时候,就显得很有优势。     在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。在单线程模型中始终要记住两条法则:  1. 不要阻塞UI线程  2. 确保只在UI线程中访问Android UI工具包      当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。     比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后,是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员  Android提供了几种在其他线程中访问UI线程的方法: Activity.runOnUiThread( Runnable )  View.post( Runnable )  View.postDelayed( Runnable, long )  Hanlder  这些类或方法同样会使你的代码很复杂很难理解。然而当你需要实现一些很复杂的操作并需要频繁地更新UI时这会变得更糟糕。  为了解决这个问题,Android 1.5提供了一个工具类:AsyncTask 它使创建需要与用户界面交互的长时间运行的任务变得更简单,不需要借助线程和Handler即可实现。  AsyncTask是抽象类,它定义了三种泛型类型: Params,Progress,Result   Params 启动任务执行的输入参数,比如HTTP请求的URL。    Progress 后台任务执行的百分比。    Result 后台执行任务最终返回的结果,比如String。  AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用(即用户不可直接调用,而应由系统调用),开发者需要做的就是实现这些方法。  1) 子类化AsyncTask  2) 实现AsyncTask中定义的下面一个或几个方法  (a)onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。  (b)doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。  (c)onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。  (d)onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.  为了正确的使用AsyncTask类,以下是几条必须遵守的准则:  1) Task的实例必须在UI thread中创建  2) execute方法必须在UI thread中调用  3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法  4) 该task只能被执行一次,否则多次调用时将会出现异常  整个流程:        AsyncTask的整个调用过程都是从execute方法开始的(主线程中调用),一旦在主线程中调用execute方法,就会调用onPreExecute方法,在这里可以做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化.同样也可以通过onProgressUpdate方法给用户一个进度条的显示更新,增加用户体验;最后通过onPostExecute方法,将在doInBackground得到的结果来处理操作UI。doInBackground任务执行的结果作为此方法的参数返回。     下面给出一个例子是一个侧边菜单栏的例子:     package com.example.renrenslidemenudemo; import android.app.Activity; import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.View.OnTouchListener; import android.view.WindowManager; import android.widget.LinearLayout; public class MainActivity extends Activity implements OnTouchListener { /** * 滚动显示和隐藏menu时,手指滑动需要达到的速度。 */ public static final int SNAP_VELOCITY = 200;//有两种方式跳转页面,一是滑动速度超过200,二是滑动的距离超过屏幕的一半 /** * 屏幕宽度值。 */ private int screenWidth; /** * menu最多可以滑动到的左边缘。值由menu布局的宽度来定,marginLeft到达此值之后,不能再减少。 */ private int leftEdge; /** * menu最多可以滑动到的右边缘。值恒为0,即marginLeft到达0之后,不能增加。 */ private int rightEdge = 0; /** * menu完全显示时,留给content的宽度值。 */ private int menuPadding = 80; /** * 主内容的布局。 */ private View content; /** * menu的布局。 */ private View menu; /** * menu布局的参数,通过此参数来更改leftMargin的值。 */ private LinearLayout.LayoutParams menuParams; /** * 记录手指按下时的横坐标。 */ private float xDown; /** * 记录手指移动时的横坐标。 */ private float xMove; /** * 记录手机抬起时的横坐标。 */ private float xUp; /** * menu当前是显示还是隐藏。只有完全显示或隐藏menu时才会更改此值,滑动过程中此值无效。 */ private boolean isMenuVisible; /** * 用于计算手指滑动的速度。 */ private VelocityTracker mVelocityTracker;//VelocityTracker速度追踪器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initValues(); content.setOnTouchListener(this); } /** * 初始化一些关键性数据。包括获取屏幕的宽度,给content布局重新设置宽度,给menu布局重新设置宽度和偏移距离等。 */ private void initValues() { WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE); screenWidth = window.getDefaultDisplay().getWidth(); content = findViewById(R.id.content); menu = findViewById(R.id.menu); menuParams = (LinearLayout.LayoutParams) menu.getLayoutParams(); // 将menu的宽度设置为屏幕宽度减去menuPadding menuParams.width = screenWidth - menuPadding; // 左边缘的值赋值为menu宽度的负数 leftEdge = -menuParams.width;//menu左边距离屏幕左边的距离 // menu的leftMargin设置为左边缘的值,这样初始化时menu就变为不可见 menuParams.leftMargin = leftEdge;//menu左边距离屏幕左边的距离 // 将content的宽度设置为屏幕宽度 content.getLayoutParams().width = screenWidth; } @Override public boolean onTouch(View v, MotionEvent event) { createVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下时,记录按下时的横坐标 xDown = event.getRawX(); break; case MotionEvent.ACTION_MOVE: // 手指移动时,对比按下时的横坐标,计算出移动的距离,来调整menu的leftMargin值,从而显示和隐藏menu xMove = event.getRawX(); int distanceX = (int) (xMove - xDown); if (isMenuVisible) { menuParams.leftMargin = distanceX; } else { menuParams.leftMargin = leftEdge + distanceX; } if (menuParams.leftMargin < leftEdge) { menuParams.leftMargin = leftEdge; } else if (menuParams.leftMargin > rightEdge) { menuParams.leftMargin = rightEdge; } menu.setLayoutParams(menuParams); break; case MotionEvent.ACTION_UP://主要是针对第二种速度超过200时,跳转界面的判断,还有当拉动距离超过半屏幕手放开时,自动跳转的 // 手指抬起时,进行判断当前手势的意图,从而决定是滚动到menu界面,还是滚动到content界面 xUp = event.getRawX(); if (wantToShowMenu()) { if (shouldScrollToMenu()) { scrollToMenu(); } else { scrollToContent(); } } else if (wantToShowContent()) { if (shouldScrollToContent()) { scrollToContent(); } else { scrollToMenu(); } } recycleVelocityTracker();//回收利用方法,将速度追踪器回收利用 break; } return true; } /** * 判断当前手势的意图是不是想显示content。如果手指移动的距离是负数,且当前menu是可见的,则认为当前手势是想要显示content。 * * @return 当前手势想显示content返回true,否则返回false。 */ private boolean wantToShowContent() { return xUp - xDown < 0 && isMenuVisible; } /** * 判断当前手势的意图是不是想显示menu。如果手指移动的距离是正数,且当前menu是不可见的,则认为当前手势是想要显示menu。 * * @return 当前手势想显示menu返回true,否则返回false。 */ private boolean wantToShowMenu() { return xUp - xDown > 0 && !isMenuVisible; } /** * 判断是否应该滚动将menu展示出来。如果手指移动距离大于屏幕的1/2,或者手指移动速度大于SNAP_VELOCITY, * 就认为应该滚动将menu展示出来。 * * @return 如果应该滚动将menu展示出来返回true,否则返回false。 */ private boolean shouldScrollToMenu() { return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判断是否应该滚动将content展示出来。如果手指移动距离加上menuPadding大于屏幕的1/2, * 或者手指移动速度大于SNAP_VELOCITY, 就认为应该滚动将content展示出来。 * * @return 如果应该滚动将content展示出来返回true,否则返回false。 */ private boolean shouldScrollToContent() { return xDown - xUp + menuPadding > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 将屏幕滚动到menu界面,滚动速度设定为30. */ private void scrollToMenu() { new ScrollTask().execute(30); } /** * 将屏幕滚动到content界面,滚动速度设定为-30. */ private void scrollToContent() { new ScrollTask().execute(-30); } /** * 创建VelocityTracker对象,并将触摸content界面的滑动事件加入到VelocityTracker当中。 * * @param event * content界面的滑动事件 */ private void createVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 获取手指在content界面滑动的速度。 * * @return 滑动速度,以每秒钟移动了多少像素值为单位。 */ private int getScrollVelocity() { mVelocityTracker.computeCurrentVelocity(1000); int velocity = (int) mVelocityTracker.getXVelocity(); return Math.abs(velocity); } /** * 回收VelocityTracker对象。 */ private void recycleVelocityTracker() { mVelocityTracker.recycle();//回收利用 mVelocityTracker = null; } class ScrollTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer... speed) { int leftMargin = menuParams.leftMargin; // 根据传入的速度来滚动界面,当滚动到达左边界或右边界时,跳出循环。 while (true) { leftMargin = leftMargin + speed[0]; if (leftMargin > rightEdge) { leftMargin = rightEdge; break; } if (leftMargin < leftEdge) { leftMargin = leftEdge; break; } publishProgress(leftMargin);//实时的将进度更新到UI主界面 // 为了要有滚动效果产生,每次循环使线程睡眠20毫秒,这样肉眼才能够看到滚动动画。 sleep(20); } if (speed[0] > 0) {//跳出循环年后,根据传来的速度的正负值判断menu是显示还是隐藏,负数代表隐藏,反之 isMenuVisible = true; } else { isMenuVisible = false; } return leftMargin; } @Override protected void onProgressUpdate(Integer... leftMargin) { menuParams.leftMargin = leftMargin[0]; menu.setLayoutParams(menuParams); } /* * * onPostExecute(Result), * 在doInBackground 执行完成后, * onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread. * */ @Override protected void onPostExecute(Integer leftMargin) { menuParams.leftMargin = leftMargin; menu.setLayoutParams(menuParams); } } /** * 使当前线程睡眠指定的毫秒数。 * * @param millis * 指定当前线程睡眠多久,以毫秒为单位 */ private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } }     截图就不在这里贴出来了~~ 在接下来的侧边菜单栏中还会有相关介绍~~    

上一篇:基于HTML5的超级玛丽游戏demo
下一篇:【代码】Android ViewFlow 实现左右滑动

相关文章

相关评论