博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
移动开发iOS&Android对比学习--异步处理
阅读量:6628 次
发布时间:2019-06-25

本文共 12117 字,大约阅读时间需要 40 分钟。

在移动开发里很多时候需要用到异步处理。Android的主线程如果等待超过一定时间的时候直接出现ANR(对不熟悉Android的朋友这里需要解释一下什么叫ANR。ANR就是Application Not Responding,应用无响应的意思。系统在应用一段时间无响应的时候会弹出这个对话框。用户可以选择继续等待或者强制关闭)。这些还是次要的,最主要的还是心急的用户。让用户长时间等待是得罪他们的最好办法!

Android有一个很简单的办法实现异步处理:AnsyncTask。使用的时候你需要继承一个基类

public abstract class AsyncTask

对java不熟的同学这里需要说明,尖括号里的是类型参数。这是java的一个语法,泛型。这三个参数分别指定的是输入参数的类型、任务执行进度值的类型和任务执行结果的类型。并不是所有的类型都被使用,不用的时候指定为void类型。

最简单的使用就是继承这个类以后Override方法doInBackground(Params... params)。使用的时候实例化你的AsyncTask实现,调用execute(Params... params)方法就开启了异步操作。例如:

布局代码:

 

java代码:

import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;public class AsyncTaskDemo extends Activity {        private Button executeButton;       private TextView textView;         @Override    public void onCreate(Bundle savedInstanceState){        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_demo);                executeButton = (Button)findViewById(R.id.executeButton);        textView = (TextView)findViewById(R.id.textView);                 executeButton.setOnClickListener(new OnClickListener() {                        @Override            public void onClick(View v) {                Log.i("-task-","Task started.");                new MyAsyncTask().execute(" on click");            }        });    }        class MyAsyncTask extends AsyncTask
{ @Override public String doInBackground(String... params){ Log.i("-task-","doInBackground is called" + params[0]); try { // 线程暂停2秒模拟后台任务 Thread.sleep(2000); } catch (Exception e) { return ""; } return "Hello World from AsyncTask"; } // 在doInBackground方法执行之后调用这个方法 public void onPostExecute(String result){ Log.i("-task-", "doPostExecute is called"); textView.setText(result); } }}

运行效果:

 

看到这里读者应该明白,doInBackground方法就是在后台执行的那个异步的方法。

但是AsyncTask的妙处不至于此。大家都知道,在UI线程意外不可以更新UI组件。如果后台线程执行完后一定要通知UI线程,由UI线程根据后台线程的执行结果更新UI组件。AsyncTask的妙处就在于,在它的实现中实现某些方法就可以直接更新UI。当然这些方法是在UI线程中的。这些方法有:

  1. onPreExecute() 在execute方法调用后立即执行,在doInBackground执行前执行。相当于在后台操作开始前做一些准备工作。
  2. doInBackground(Params... params) 在onPreExecute之后执行,处理需要在后台操作的任务。在其中可以通过publishProgress(Progress... values)通知UI线程更新进度。
  3. onProgressUpdate(Progress... values)在调用publishProgress后此方法执行。将进度更新到UI组件上。
  4. onPostExecute(Result result) 在doInBackground执行结束之后,后台执行结果作为参数传入这个方法并根据这个结果更新UI组件。
  5. onCancelled() 取消后台操作的时候调用方法cancel(boolean endTaskDuringExecuting),这个时候onCancelled方法会被调用。同事,isCancelled()方法返回true。在后台任务的执行中需要检查是否已经被取消,尤其是循环中。这样可以保证后台任务可以尽快退出。

下面就依据上面所说更新代码了:

layout:

java:

public class AsyncTaskDemo extends Activity {        private Button executeButton;     private Button cancelButton;    private TextView textView;     private ProgressBar progressBar;        private MyAsyncTask myAsyncTask;        @Override    public void onCreate(Bundle savedInstanceState){        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_demo);                executeButton = (Button)findViewById(R.id.executeButton);        cancelButton = (Button)findViewById(R.id.cancelButton);        textView = (TextView)findViewById(R.id.textView);        progressBar = (ProgressBar)findViewById(R.id.progressBar);                 executeButton.setOnClickListener(new OnClickListener() {                        @Override            public void onClick(View v) {                Log.i("-task-","Task started.");                                // 每次开始的时候必须初始化一次。                myAsyncTask = new MyAsyncTask();                myAsyncTask.execute(" on click");                                cancelButton.setEnabled(true);                executeButton.setEnabled(false);            }        });                cancelButton.setOnClickListener(new OnClickListener() {                        @Override            public void onClick(View arg0) {                Log.i("-task", "Task cancelled");                                // 取消任务                myAsyncTask.cancel(true);            }        });    }        class MyAsyncTask extends AsyncTask
{ @Override protected void onPreExecute() { Log.i("-task-", "onPreExecute is called"); textView.setText("Mission is about to start dude..."); } @Override public String doInBackground(String... params){ Log.i("-task-","doInBackground is called" + params[0]); if (isCancelled()) { return "cancelled"; } try { // 开始进度是0. publishProgress(0); Thread.sleep(2000); publishProgress(30); // 如果有循环的话需要在循环中每次都检查是否任务已经被取消。 if (isCancelled()) { return "cancelled"; } // 线程暂停2秒模拟后台任务 Thread.sleep(2000); publishProgress(100); } catch (Exception e) { return ""; } return "Hello World from AsyncTask"; } @Override protected void onProgressUpdate(Integer...progresses){ Log.i("-task-", "onProgressUpdate is called"); progressBar.setProgress(progresses[0]); textView.setText(progresses[0] + "% is handled..."); } // 在doInBackground方法执行之后调用这个方法 @Override protected void onPostExecute(String result){ Log.i("-task-", "doPostExecute is called"); textView.setText(result); executeButton.setEnabled(true); cancelButton.setEnabled(false); } @Override protected void onCancelled(){ Log.i("-task-", "onCancelled is called"); progressBar.setProgress(0); textView.setText("Cancelled"); executeButton.setEnabled(true); cancelButton.setEnabled(false); } }}

执行结果:

1. 没有取消的

2. 取消了的

这里有一点需要注意。AsyncTask对象,一个只跑一次任务!所以,这里你可以看到,每次在执行一个后台任务的时候都要初始化一次。从这里看到,AsyncTask非常好用。只要把后台任务放到doInBackground方法里,其他的在UI线程上执行的只要按照需要放在其他的方法中就可以了。简直是随用随调,方便已用。

在iOS里最方便的就是GCD了。GCD,Grand Central Dispatch。是OSX10.6 iOS4.0引入的,是一套新的编写并行程序的方法,是基于c开发的开源框架。GCD的便携很大程度上依赖于block。block就类似于lambda表达式。所以GCD的写法非常简单。GCD的异步操作是基于线程的,只不过是有GCD去管理这些线程,以发挥多核心CPU的优势。开发者就不用花时间在具体的线程管理的细节上。

block的例子:

void (^blockDemo)(void) = ^{      // do something here...  }

关于block更详细的知识,请到自行补脑。

如果说block是异步任务的子弹的话,那么那把枪就是dispatch queue。dispatch queue可以是串行执行的也可以是并行执行的。串行dispatch queue按照先进先出的顺序执行任务,并行的dispatch queue只是按照这样的方式开始任务,至于具体的执行顺序会随具体的执行条件变化。

GCD包括三中queue:

  1. Main queue:系统定义,用dispatch_get_main_queue获取。提交到这个queue的任务会在UI线程中执行。
  2. Global queue:系统定义,用dispatch_get_global_queue获取。global queue就是并行queue,一共有四种优先级。
  3. Private queue:用 dispatch_queue_create创建。默认为串行执行。可以用DISPATCH_QUEUE_CONCURRENT指定为并行队列(只在iOS5或以后的系统中有效)。

下面通过代码展示使用GCD实现异步操作有多简单:

// 创建一个private queuedispatch_queue_t myBackgroundQueue;myBackgroundQueue = dispatch_queue_create("com.company.subsystem.task", NULL); // 异步处理// Dispatch a private queuedispatch_async(myBackgroundQueue, ^(void) {    // do some time consuming things here});// Release queue。iOS6以后系统自动管理内存。dispatch_release(myBackgroundQueue);// 获取global queuedispatch_queue_t myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 异步操作dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {    // do some time consuming things here});

只要给你的queue dispatch_async一个任务(block)就可以。

由此可见,用dispatch queue实现异步比Android的AsyncTask简单了很多。只需要简单的几句话就可以实现。不用实现抽象类什么的繁琐操作。当你调用dipatch_async后,代码立即返回分发出去的任务由系统分配资源在后台异步执行。

参考上面Android的例子,在dispatch queue执行的过程中要更新异步任务执行进度怎么办呢,要在界面上现实执行了百分之多少,progress bar要现实对应的进度怎么办呢?UI组件的更新需要在UI线程中执行。这个时候就需要用到上面说的main queue了。我们上面已经说过使用dispatch_get_main_queue方法来获取main queue也就是UI线程的queue。下面通过一个简单的例子说明在dispatch queue异步操作中如何更新UI组件。

dispatch_async(myBackgroundQueue, ^(void) { // 异步操作在这里。。。     dispatch_async(dispatch_get_main_queue(), ^{         // 操作UI组件等。。。    });});

另外需要说明一点:如果一个变量需要在queue中操作,准确的说是在block中操作的话,需要有一个特别的声明:在常用的声明前加__block。

那么上面在Android中示例用ObjC的GCD该如何实现呢?下面给出答案。

界面布局:

头文件代码:

#import 
@interface CMRootViewController : UIViewController{@private __block BOOL _cancelTask;}@property (assign, nonatomic, readonly) __block BOOL cancelled;@property (strong, nonatomic) dispatch_queue_t taskQueue;@property (weak, nonatomic) IBOutlet UIProgressView *progressBar;@property (weak, nonatomic) IBOutlet UILabel *progressLabel;@property (weak, nonatomic) IBOutlet UIButton *startButton;@property (weak, nonatomic) IBOutlet UIButton *cancelButton;- (IBAction)startAction:(id)sender;- (IBAction)cancelAction:(id)sender;@end

源文件:

#import "CMRootViewController.h"@implementation CMRootViewController- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];    if (self) {        // 初始化线程        self.taskQueue = dispatch_queue_create("com.queue.demo", NULL);        _cancelTask = NO;        _cancelled = NO;            }    return self;}- (void)viewDidLoad{    [super viewDidLoad];    self.title = @"Queue Demo";    self.progressBar.progress = .0f;    self.progressLabel.text = @"0%";}- (IBAction)startAction:(id)sender {    NSLog(@"Start button is clicked.");        _startButton.enabled = NO;    _cancelButton.enabled = YES;    _cancelled = NO;    _cancelTask = NO;        // 这里用sleep方法暂停线程,代表那些费时的操作。    // 在任务执行的过程中check是否已经取消了操作,尤其在循环里。这样可以尽快停止后台操作    dispatch_async(self.taskQueue, ^{        if (_cancelTask) {            NSLog(@"Task cancelled");            _cancelButton.enabled = NO;            _startButton.enabled = YES;            _cancelled = YES;            return;        }        sleep(2);        NSLog(@"Task: 10%% is completed.");        dispatch_async(dispatch_get_main_queue(), ^{            self.progressBar.progress = .1f;            self.progressLabel.text = @"10%";        });                if (_cancelTask) {            NSLog(@"Task cancelled");            _cancelButton.enabled = NO;            _startButton.enabled = YES;            _cancelled = YES;            return;        }        sleep(3);        NSLog(@"Task: 60%% is completed.");        dispatch_async(dispatch_get_main_queue(), ^{            self.progressBar.progress = .6f;            self.progressLabel.text = @"60%";        });                if (_cancelTask) {            NSLog(@"Task cancelled");            _cancelButton.enabled = NO;            _startButton.enabled = YES;            _cancelled = YES;            return;        }        sleep(2);        NSLog(@"Task: 80%% is completed.");        dispatch_async(dispatch_get_main_queue(), ^{            self.progressBar.progress = .8f;            self.progressLabel.text = @"80%";        });                if (_cancelTask) {            NSLog(@"Task cancelled");            _cancelButton.enabled = NO;            _startButton.enabled = YES;            _cancelled = YES;            return;        }        sleep(1);        NSLog(@"Task: 100%% is completed.");        dispatch_async(dispatch_get_main_queue(), ^{            self.progressBar.progress = 1.f;            self.progressLabel.text = @"100%";        });            });}- (IBAction)cancelAction:(id)sender {    NSLog(@"Cancel button is clicked.");    _cancelTask = YES;        _startButton.enabled = YES;    _cancelButton.enabled = NO;}@end

 

Android和iOS对比着学习可以对这两个平台的各种技术留下深刻的印象。毕竟都是移动平台,大多数的语言之外的东西有很多东西都是想通的。Android有着广大的用户群体,而且在不断的进步。所以不必把两个平台完全独立或者对立起来。对于开发者来说得到实惠才是最重要的!

 

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!
本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/3411112.html
,如需转载请自行联系原作者
你可能感兴趣的文章
Radware:IP欺诈等让网络攻击难以防范
查看>>
基于Token认证的WebSocket连接
查看>>
【Solidity】2.合约的结构体 - 深入理解Solidity
查看>>
《C语言及程序设计》实践参考——二分法解方程
查看>>
java thread中的wait()和notify()
查看>>
2016最新搜索引擎优化(SEO)重点要素
查看>>
【IOS-COCOS2D-X 游戏开发之二】【必看篇】总结阐述COCOS2D-X与COCOS2D-IPHONE区别;
查看>>
eoLinker-API_Shop_通讯服务类API调用的代码示例合集:短信服务、手机号归属地查询、电信基站查询等...
查看>>
前端面试回忆录 - 滴滴篇 - 凉面
查看>>
jxl导入Excel 切割List 并使用MyBatis批量插入数据库
查看>>
小程序开发总结
查看>>
Tomcat监听器设计思路
查看>>
管理ORACLE实例
查看>>
Confluence 6 MySQL 数据库设置准备
查看>>
Ruby 中 0/0.0 = NaN
查看>>
局域网访问Apache服务器
查看>>
JavaScript 闭包
查看>>
Spark算子:RDD行动Action操作(3)–aggregate、fold、lookup
查看>>
java获取当前时间前一周、前一月、前一年的时间
查看>>
话说WEB开发之页面重绘和回流
查看>>