担心原文消失,做此记录,感谢

https://www.cnblogs.com/net168/p/4075126.html

前言

  很久很久以前就听说了,每一个android的应用程序都会分别运行在一个独立的dalvik虚拟机进程中,而在每个虚拟机在启动时会运行一个UI主线程(Main Thread),而为啥叫UI主线程而不是AI主线程或者是BI主线程呢?因为它要处理全部和UI相关的事件;因为Android系统采用的是UI单线程模型,只能由UI主线程对其进行UI操作,如果子线程抱着众人拾柴火焰高的觉悟来帮忙UI主线程更新UI界面的话,对不起哦~Android系统就会报错的。粗俗点讲就是:我们只能通过UI主线程来蹂躏UI界面,但是其他线程来的话会被告弓虽女干滴。。

  那么现在问题来了!鉴于近来挖掘机那么火,我也不好意思继续问这个问题了。。。嗯嗯~网络操作之类耗时操作就像挖掘机那样,我们在下载文件的时候一样跟挖掘机挖个大坑一样需要一定的时间;当挖掘机司机挖好一个大坑要找老板反馈工作完成一样,我们下载好一个文件自然要马上告诉屏幕前苦逼等待的用户们,谁知道他们多着急想看**.avi呢;但是你在挖坑时好意思叫老板在旁边看你吗?老板分分钟为几千万上下的事忙着呢~所以嘛同理,对于网络操作,我们当然也不能在UI主线程中进行网络操作,因为这样会阻塞主线程造成界面卡死,也会造成ANR(应用程序无响应)。我们应该把文件下载、文件读取诸如此类的耗时操作放到子线程中去进行,等到子线程耗时操作完成时通知UI界面做出响应。

不要在UI主线程中进行耗时操作

  如果你不信邪一定要在UI主线程进行下载文件、加载大文件之类的耗时操作。如下代码:

private Button btn;
//onCreate之类的生命周期的方法就是允许在UI主线程中
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);   btn = (Button) findViewById(R.id.btn);   btn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
      downLoad();//调用UI主线程的下载函数
    }
  });
} private void downLoad(){
  try {
    Thread.sleep(10000);//休眠10秒,模拟网络文件下载耗时操作
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
}

你会发现界面卡主了10秒:(模拟下载操作的按钮为深色,说明按钮一直为按下状态)

如果这时候你手比较管不住的话,虽然点几下界面,没事~Androi系统会马上送你一份ANR大礼哦,而且还不用998元耶!

小结一个:不要在UI主线程中进行耗时操作,你可能会疑问什么是UI主线程,UI主线程主要运行的就是Activity、Service等里面的生命周期方法,所以不要在生命周期方法如onCreate()中进行下载这些大事件。对于耗时操作,我们应该新建一个子线程并交给他处理,但是还需要注意一点。

不要在子线程中更新UI界面

  既然我们说下载文件要在子线程中进行,那么我们就新建一个子线程把下载操作放到里面进行咯,代码如下:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);   btn = (Button) findViewById(R.id.btn);
  text = (TextView) findViewById(R.id.text);   btn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
      new Thread(){
        @Override
        public void run() {
          //在子线程中进行下载操作
          try {
            Thread.sleep(10000);//休眠10秒,模拟耗时操作
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          text.setText("下载完成");//设置TextView,通知UI界面下载完成
        }
      }.start();
    }
  });
}

10秒后,你觉得会在UI界面完美显示“下载完成”么?一般,出现这个才符合Androi系统的一贯作风

并且在Log中报错如下

小弟英语其实很废柴,但是隐隐约约有人告诉我:这不是叫只能在主线程中更新UI吗?不信,金山翻译一下去呀。。。。

小结一个:不要在子线程中更新UI界面,这样会导致android系统报错、应用崩溃退出。UI界面时单线程模式,我们只能通过UI主线程中对UI的界面进行相关的更新,千万不要越线办事,你要记住的是~UI界面是UI主线程的老婆,你们这些子线程谁都别想动!

利用Thread+Handler进行异步处理

  那么问题来了,现在我们需要进行耗时操作(例如下载文件)时不能在主线程执行,我们又需要在UI界面通知用户我们活干完了不能再子线程中执行。这似乎是一个棘手的热山芋呀,幸好谷歌给我们提供了一个救我们于危难之中的Handler,一个能让主线程监听子线程发送来消息的东东,至于Handler的实现原理我会在后面的文章详细介绍,现在我们只需要先了解Handler的用法。

private Button btn;
private TextView text; private Handler handler = new Handler(){
  private int process = 0;
  @Override
  public void handleMessage(Message msg) {
    switch(msg.what){
    case 0://更细下载进度
      process += 1;
      text.setText("下载" + process + "%");//在主线程中更新UI界面
      break;
    case 1://提示下载完成
      text.setText("下载完成");//在主线程中更新UI界面
      break;
    default:
      break;
    }
  }
};
//onCreate之类的生命周期的方法就是允许在UI主线程中
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);   btn = (Button) findViewById(R.id.btn);
  text = (TextView) findViewById(R.id.text);   btn.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
    new Thread(){
      @Override
      public void run() {
        //在子线程中进行下载操作
        for(int i = 0; i < 100; i++){
          try {
            Thread.sleep(200);//休眠0.2秒,模拟耗时操作
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          handler.sendEmptyMessage(0);//发送消息到handler,通知下载进度
        }
        handler.sendEmptyMessage(1);//发送消失到handler,通知主线程下载完成
        }
      }.start();
    }
  });
}

这里来解释一下Handler的使用方法:

1、我们为了不阻塞主线程,将下载任务通过子线程来执行。

new Thread(){
  @Override
  public void run() {
    //在子线程中进行下载操作
    for(int i = 0; i < 100; i++){
      try {
        Thread.sleep(200);//休眠0.2秒,模拟耗时操作
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      handler.sendEmptyMessage(0);//发送消息到handler,通知下载进度
    }
    handler.sendEmptyMessage(1);//发送消失到handler,通知主线程下载完成
  }
}.start();

2、当子线程需要跟主线程交流时,也就是当子线程要跟UI主线程说:亲,偶下载文件到80%了或者偶已经把文件下载完成了!执行这句代码

handler.sendEmptyMessage(1);//发送消失到handler,通知主线程下载完成

3、当发送空消息之后,在Handler将会收到子线程发来的消息,触发回调方法handlerMessage(),我们就在这里对UI界面进行更新,这个回调方法是运行在UI主线程的

@Override
public void handleMessage(Message msg) {
  switch(msg.what){
  case 0://更细下载进度
    process += 1;
    text.setText("下载" + process + "%");//在主线程中更新UI界面
    break;
  case 1://提示下载完成
    text.setText("下载完成");//在主线程中更新UI界面
    break;
  default:
    break;
  }
}

4、最后,UI界面更新成功!(图嘛,我这里就不上了。。。。)

小结一个:对于比较耗时间的任务,我们一般需要放在子线程中执行;当子线程更新UI界面时,子线程可以通过Handler来通知主线程更新,一般通过发送消息来触发handlerMessage()这个回调方法来执行UI界面的更新。

进一步简略de操作:handler.post方法和view.post方法

  但是如果你觉得每次都要重写handlerMessage()比较麻烦,我们完全可以用更加简略的方法来解决我们的需求,就是用handler中的post方法。代码如下

new Thread(){
  @Override
  public void run() {
    //在子线程中进行下载操作
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    handler.post(new Runnable() {
      @Override
      public void run() {
        text.setText("下载完成");
      }
    });//发送消失到handler,通知主线程下载完成
  }
}.start();

这样处理的话我们就可以不用重写handlerMessage()方法了,适合子线程与主线程进行较为单一的交流。但在这里我们要强调的一点的是,post里面的Runnable还是在UI主线程中运行的,而不会另外开启线程运行,千万不要在Runnable的run()里面进行耗时任务,不然到时又ANR了可别找我哦。。

如果你有时候连handler都不想搞,还可以这样写代码滴。

我们只需要把handler换成View组件进行post,更新任务自然会加载到UI主线程中进行处理。

text.post(new Runnable() {
  @Override
  public void run() {
    text.setText("下载完成");
  }
});//发送消失到handler,通知主线程下载完成

至于Handler机制以及这两种post的原理,我将会在后面的博客文章中专题介绍,这里只提供一个使用方法而已。

Android:异步处理之Handler+Thread的应用的更多相关文章

  1. 解析Android消息处理机制:Handler/Thread/Looper & MessageQueue

    解析Android消息处理机制 ——Handler/Thread/Looper & MessageQueue Keywords: Android Message HandlerThread L ...

  2. Android:异步处理之Handler+Thread的应用(一)

    前言 很久很久以前就听说了,每一个android的应用程序都会分别运行在一个独立的dalvik虚拟机进程中,而在每个虚拟机在启动时会运行一个UI主线程(Main Thread),而为啥叫UI主线程而不 ...

  3. Android:异步处理之Handler、Looper、MessageQueue之间的恩怨(三)

    前言 如果你在阅读本文之前,你不知道Handler在Android中为何物,我建议你先看看本系列的第一篇博文<Android:异步处理之Handler+Thread的应用(一)>:我们都知 ...

  4. Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面

    Android应用的开发过程中需要把繁重的任务(IO,网络连接等)放到其他线程中异步执行,达到不阻塞UI的效果. 下面将由浅入深介绍Android进行异步处理的实现方法和系统底层的实现原理. 本文介绍 ...

  5. Android异步处理系列文章四篇之一使用Thread+Handler实现非UI线程更新UI界面

    目录: Android异步处理一:使用Thread+Handler实现非UI线程更新UI界面Android异步处理二:使用AsyncTask异步更新UI界面Android异步处理三:Handler+L ...

  6. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

  7. Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...

  8. Android 之异步任务(AsyncTask,Handler,Message,looper)

    AsyncTask: 3个类型(Params,Progress和Result),4个步骤(onPreExecute(),doInBackground(Params…),onProgressUpdate ...

  9. Android 异步消息处理机制终结篇 :深入理解 Looper、Handler、Message、MessageQueue四者关系

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.概述 我们知道更新UI操作我们需要在UI线程中操作,如果在子线程中更新UI会发生异常可能导致崩溃,但是在UI线程中进行耗时操作又会导致ANR,这 ...

随机推荐

  1. Set学习笔记

    今天又去看了一下STL里的set,来这里水一下博客 What is set? set的本质是一种功能受限的平衡树,不支持重复数字,也就是说如果插入一大堆数字12,输出它的长度还是1 如何定义 定义 s ...

  2. 深入js系列-类型(显式强制转换)

    什么是显式 这里的显式和隐式是以普遍的标准来进行讨论的,你能看出来是怎么回事,那么它对你是"显式",相反你不知道的话,对你就是"隐式" 抽象操作 字符串.数字. ...

  3. [HNOI2015]亚瑟王(概率期望,DP)

    题目大意:很清晰了,不写了. $1\le T\le 444,1\le n\le 220,0\le r\le 132,0<p_i<1,0\le d_i\le 1000$. $p_i$ 和 $ ...

  4. html文本或元素6px间距问题

    html中的文本内容或者是具有inline或inline-block的元素之间会6px的间距, 这是因为html文当中若出现文字分隔符(文字分隔符主要指空格或者换行等), 那么其就会被渲染成一个空格( ...

  5. 使用canvas实现图片预览、缩放(压缩)以及生成文件下载

    参考 https://www.runoob.com/html/html5-canvas.html https://www.cnblogs.com/yuanzhiguo/p/8288822.html h ...

  6. 疯了!同事又问我为什么不能用 isXXX

    最近在做Code Review,写下了这篇文章:代码写成这样,老夫无可奈何!,说多了都是泪啊.. 最近又有人同事跑过来质疑我: 为什么变量名取名不能用 isXXX 这种方式,这样有什么问题?! 醉了, ...

  7. Kubeadm部署安装kubernetes1.12.1

    1.环境准备(这里是master) CentOS 7.6 两台配置如下,自己更改主机名,加入hosts, master和node 名字不能一样 # hostname master # hostname ...

  8. K8S之traefik高级特性

    Traefik Traefik是一个用Golang开发的轻量级的Http反向代理和负载均衡器.由于可以自动配置和刷新backend节点,目前可以被绝大部分容器平台支持,例如Kubernetes,Swa ...

  9. 【神经网络与深度学习】生成式对抗网络GAN研究进展(五)——Deep Convolutional Generative Adversarial Nerworks,DCGAN

    [前言]      本文首先介绍生成式模型,然后着重梳理生成式模型(Generative Models)中生成对抗网络(Generative Adversarial Network)的研究与发展.作者 ...

  10. gcd 题解

    gcd Little White learned the greatest common divisor, so she plan to solve a problem: given \(x, n,\ ...