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

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

public abstract class AsyncTask<Params, Progress, Result>

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

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

布局代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <Button
android:id="@+id/executeButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=" Start "/>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</ScrollView> </LinearLayout>

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<String, Integer, String>{ @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:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <Button
android:id="@+id/executeButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=" Start "/>
<Button
android:id="@+id/cancelButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:enabled="false"
android:text=" cancel "/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:progress="0"
android:max="100"
style="?android:attr/progressBarStyleHorizontal"/>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</ScrollView> </LinearLayout>

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<String, Integer, String>{ @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 queue
dispatch_queue_t myBackgroundQueue;
myBackgroundQueue = dispatch_queue_create("com.company.subsystem.task", NULL); // 异步处理
// Dispatch a private queue
dispatch_async(myBackgroundQueue, ^(void) {
// do some time consuming things here
}); // Release queue。iOS6以后系统自动管理内存。
dispatch_release(myBackgroundQueue); // 获取global queue
dispatch_queue_t myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ); // 异步操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, )
, ^(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 <UIKit/UIKit.h>

@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();
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();
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();
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();
NSLog(@"Task: 100%% is completed.");
dispatch_async(dispatch_get_main_queue(), ^{
self.progressBar.progress = .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有着广大的用户群体,而且在不断的进步。所以不必把两个平台完全独立或者对立起来。对于开发者来说得到实惠才是最重要的!

移动开发iOS&Android对比学习--异步处理的更多相关文章

  1. Xamarin体验:使用C#开发iOS/Android应用

    Xamarin是Mono创始人Miguel de Icaza创建的公司,旨在让开发者可以用C#编写iOS, Android, Mac应用程序,也就是跨平台移动开发.   简介 Xamarin是基于Mo ...

  2. VS2015安装开发ios android

    前几天很火,装了一下,结果是不是太满意,装了VS2015只是多了一个android和ios的模版,最终还是要装xamarin ,最后装了个xamarin ,然后破解 破解地址:http://www.c ...

  3. React Native之原生模块的开发(Android)学习笔记

      目录 1.为什么我们需要原生模块开发 2.开发Android原生模块的主要流程 3.原生模块开发实战   1.为什么我们需要原生模块开发? 我们在用RN开发App的时候,有时候需要用到一些原生模块 ...

  4. [android开发篇] android apidemo 学习网址

    http://www.2cto.com/special/ApiDemos/ApiDemos/type-160-10.html

  5. javascript开发 ios和android app的简单介绍

    先看几个名词解释: nodejs ionic,Cordova,phoneGap,anjularjs react-native,reactjs nodeJs 的介绍参见这里,写的很好http://www ...

  6. Android开发技术周报183学习记录

    Android开发技术周报183学习记录 教程 Android性能优化来龙去脉总结 记录 一.性能问题常见 内存泄漏.频繁GC.耗电问题.OOM问题. 二.导致性能问题的原因 1.人为在ui线程中做了 ...

  7. 突破瓶颈,对比学习:Eclipse开发环境与VS开发环境的调试对比

    曾经看了不少Java和Android的相关知识,不过光看不练易失忆,所以,还是写点文字,除了加强下记忆,也证明我曾经学过~~~ 突破瓶颈,对比学习: 学习一门语言,开发环境很重,对于VS的方形线条开发 ...

  8. [Hadoop 周边] 浅谈大数据(hadoop)和移动开发(Android、IOS)开发前景【转】

    原文链接:http://www.d1net.com/bigdata/news/345893.html 先简单的做个自我介绍,我是云6期的,黑马相比其它培训机构的好偶就不在这里说,想比大家都比我清楚: ...

  9. ios和android一并学习的体会

    如果说为什么要同时学习这两种不同的移动平台,其实有一定的“闲”的因素在里面. 相对于ios,android我是早半年接触的.最开始学习的时候也就是j2ee学习的延续,通过看视频连带看书学了大概一个月的 ...

随机推荐

  1. Converter(转换器)与Formatter(格式化) ,Validator(验证器)

    Converter(转换器)与Formatter(格式化)都可以用于将一种对象类型转换为另一种对象类型.Converter是通用元件,可以在应用程序的任意层中使用,而Fotermatter这是专门为W ...

  2. OD 实验(十四) - 内嵌补丁

    内嵌补丁(inline patch): 内嵌补丁指在程序文件中把补丁代码写入文件里面达到破解的目的 如果修改某行语句会影响后面的语句,例如某语句占用 3 个字节,修改完变为 5 个字节,会覆盖后面的语 ...

  3. 移动终端App测试点归纳

    以下所有测试最后必须在真机上完整的执行. 1 安装.卸载测试 1.1 在真机上.第三方软件(xy苹果助手.91.安卓助手)的安装与卸载 1.2 安装在手机卡上 或 SD卡上 (不同的IOS和安卓版本) ...

  4. Android SQLite最简单demo实现(增删查改)

    本来不太想写这篇博客的,但是看到网上的关于android数据库操作的博文都讲得很详细,对于像我这样的新手入门了解SQLite的基本操作有一定难度,所以我参考了网上的一些博客文章,并自己亲自摸索了一遍, ...

  5. 关于OpenGL Framebuffer Object、glReadPixels与离屏渲染

    最近写论文需要用到离屏渲染(主要是因为模型太大普通窗口绘制根本做不了),于是翻阅了红宝书查了下相关api和用法.中文版的红宝书可读性有点差,很多地方翻译地晦涩,但好歹读起来比较快,主要相关章节为第8章 ...

  6. ZooKeeper架构

    ZooKeeper服务器端运行于两种模式下:独立模式(standalone)和仲裁模式(quorum).独立模式几乎与其术语所描述的一样:有一个单独的服务器,ZooKeeper状态无法复制.在仲裁模式 ...

  7. ffmpeg编译选项汇总

    编译禁用“jack” 和 “crystalhd” : --disable-crystalhd--disable-indev=jack ================================= ...

  8. ROS 禁止公网暴力破解SSH FTP

    最简单的彻底禁止公网访问SSH FTP端口 1 2 /ip firewall filter add chain=input protocol=tcp dst-port=21-22 src-addres ...

  9. Android开发实战之底部Dialog弹出效果

    在Android开发中,有很多情况下我们需要使用到对话框,遗憾的是,安卓自带的对话框样式不能满足我们实际的需要,所以往往需要我们自定义对话框,具体做法:写一个对话框继承自Dialog实现他的一个构造方 ...

  10. Java-CSV文件读取

    import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import ja ...