android 线程那点事
在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销,当系统中存在大量的线程时,系统会通过时间片轮转的方式调度每个线程,在这么多线程中有一个被称为主线程,主线程是指进程所拥有的线程,在JAVA中默认情况下一个进程只有一个线程,这个线程就是主线程。主线程主要处理界面交互相关的逻辑,因为用户随时会和界面发生交互,因此主线程在任何时候都必须有比较高的响应速度,否则就会产生一种界面卡顿的感觉。为了保持较高的响应速度,这就要求主线程中不能执行耗时的任务,这个时候子线程就派上用场了。子线程也叫工作线程,除了主线程以外的线程都是子线程。
Android中的线程
Android沿用了JAVA的线程模型,其中的线程也分为主线程和子线程,其中主线程又叫UI线程。在Android系统中,在默认情况下,一个应用程序内的各个组件(如Activity、BroadcastReceiver、Service)都会在同一个进程(Process)里执行,且由此进程的主线程负责执行。如果有特别指定(通过android:process),也可以让特定组件在不同的进程中运行。无论组件在哪一个进程中运行,默认情况下,他们都由此进程的主线程负责执行。主线程既要处理Activity组件的UI事件,又要处理Service后台服务工作,通常会忙不过来。为了解决此问题,主线程可以创建多个子线程来处理后台服务工作,而本身专心处理UI画面的事件。子线程的任务则是执行耗时任务,比如网络请求,I/O操作等。从Android4.0开始系统要求网络访问必须在子线程中进行,否则网络访问将会失败并抛出NetWorkOnMainThreadException这个异常,这样做是为了避免主线程由于被耗时操作阻塞从而出现ANR现象。
为什么会出现ANR
Android希望UI线程能根据用户的要求做出快速响应,如果UI线程花太多时间处理后台的工作,当UI事件发生时,让用户等待时间超过5秒而未处理,Android系统就会给用户显示ANR提示信息。主线程除了处理UI事件之外,还要处理Broadcast消息。所以在BroadcastReceiver的onReceive()函数中,不宜占用太长的时间,否则导致主线程无法处理其它的Broadcast消息或UI事件。如果占用时间超过10秒,Android系统就会给用户显示ANR提示信息。解决办法自然还是解放UI主线程,将耗时操作交给子线程,避免阻塞。
Android中也有main()方法
刚接触Android的开发者可能会因为找不到Java程序的执行入口main()方法而觉得疑惑,其实Android中当然是也有main()方法的(如下),它被包装在源码中的ActivityThread类里。ActivityThread为应用程序的主线程类,所有的Apk程序都有且仅有一个ActivityThread类,程序的入口为该类中的static main()方法,ActivityThread所在的线程即为UI线程或主线程。Activity从main()方法开始执行,调用prepareMain()为UI线程创建一个消息队列(MessageQueue)。然后创建一个ActivityThread对象,在ActivityThread的初始化代码中会创建一个H(Handler)对象和一个ApplicationThread(Binder)对象。其中Binder负责接收远程AmS的IPC调用,接收到调用后,则通过Hander把消息发送到消息队列,UI主线程会异步地从消息队列中取出消息并执行相应操作,比如start,pause,stop等。接着UI主线程调用Looper.loop()方法进入消息循环体,进入后就会不断地从消息队列中读取并处理消息。
public static final void main(String[] args) { SamplingProfilerIntegration.start(); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); if (Process.supportsProcesses()) { throw new RuntimeException("Main thread loop unexpectedly exited"); } thread.detach(); String name = (thread.mInitialApplication != null) ? thread.mInitialApplication.getPackageName() : "<unknown>"; Slog.i(TAG, "Main thread of " + name + " is now exiting"); } }
public static final void main(String[] args) { SamplingProfilerIntegration.start(); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); if (Process.supportsProcesses()) { throw new RuntimeException("Main thread loop unexpectedly exited"); } thread.detach(); String name = (thread.mInitialApplication != null) ? thread.mInitialApplication.getPackageName(); Slog.i(TAG, "Main thread of " + name + " is now exiting"); }}<strong> </strong>
Android中的子线程
Android中开启一个子线程无非还是这两种方法
1:继承Thread类
public class MyThread extends Thread { public void run(){ } } public class MyThread extends Thread { public void run(){ } } newMyThread().start();
2:实现Runnable接口
public class MyRunnable implements Runnable{ @Override public void run(){ //TODOAuto-generatedmethodstub } } public class MyRunnable implements Runnable{ @Override public void run(){ //TODOAuto-generatedmethodstub } } new MyThread().start();
Android APK程序中都有哪些线程?
通过debug,我们可以捕获当前应用程序中的线程(如下图),其中蓝色选中部分即为当前应用程序的主线程,当前程序中还运行了三个Binder,每个Binder对象都对应一个线程,这些Binder线程主要负责接收Linux Binder驱动发送的IPC调用。除此以外还有Java中的守护线程和垃圾回收线程堆裁剪守护进程等在运行。

程序中自定义Thread和UI线程的区别是什么?
自定义Thread和UI线程的区别在于,UI线程是从ActivityThread运行的,在该类中的main()方法中,已经使用Looper.prepareMainLooper()为该线程添加了Looper对象,即已经为该线程创建了消息队列(MessageQueue),因此,程序员才可以在Activity中定义Hander对象(因为声明Hander对象时,所在的线程必须已经创建了MessageQueue)。而普通的自定义Thread是一个裸线程,因此,不能直接在Thread中定义Hander对象,从使用场景的角度讲,即不能直接给Thread对象发消息,但却可以给UI线程发消息。
子线程为什么不能更新UI
因为UI访问是没有加锁的,在多个线程中访问UI是不安全的,如果有多个子线程都去更新UI,会导致界面不断改变而混乱不堪。所以最好的解决办法就是只有一个线程有更新UI的权限,所以这个时候就只能有一个线程振臂高呼:放开那女孩,让我来!那么最合适的人选只能是主线程。
子线程也可以更新UI
SurfaceView是 android 里唯一一个可以在子线程更新的控件。SurfaceView可以在主线程之外的线程中向屏幕绘图。这样可以避免画图任务繁重的时候造成主线程阻塞,从而提高了程序的反应速度。当需要快速,主动地更新View的UI,或者当前渲染代码阻塞GUI线程的时间过长的时候,SurfaceView就是解决上述问题的最佳选择。
子线程可以更新除SurfaceView以外的UI
子线程更新UI?没错,不信下面的代码跑一遍试试,并不会报错,而且正确显示。
public class MainActivity extends AppCompatActivity { private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView=(TextView)findViewById(R.id.textView); new Thread(new Runnable() { @Override public void run() { mTextView.setText("Child Thread"); } }).start(); } }
这是为什么呢?一个应用程序中有一个主线程和若干个子线程,而线程的检查工作是由ViewRoot完成的。ViewRoot是什么呢?可以简单的理解为Window和View之前的桥梁或者纽带。而ViewRoot的创建是在onResume()之后才完成的,也就是说在onResume()之前,系统本身是无法区分当前线程到底是主线程还是子线程,而上面的代码中UI的更新操作在onCreate()中完成,先于onResume(),所以上述的子线程才有机会越俎代庖。
子线程如何与主线程通信
1、Activity.runOnUiThread(Runnable)
mHandle.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { // 耗时操作 loadNetWork(); MainActivity.this.runOnUiThread(new Runnable() { @Override public void run() { mTextView.setText(来自网络的文字); } }); } }); } });
2、 View.post(Runnable)
mHandle.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { // 耗时操作 loadNetWork(); mTextView.post(new Runnable() { @Override public void run() { mTextView.setText(来自网络的文字); } }); } }); } })<span style="font-family: Arial, Helvetica, sans-serif;">;</span>
3、View.postDelayed(Runnable,long)
mHandle.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { // 耗时操作 loadNetWork(); mTextView.postDelayed(new Runnable() { @Override public void run() { mTextView.setText(来自网络的文字); } }, 10); } }); } });
4、Handler(子线程调用Handler的
handle.sendMessage(msg);
Handler handle = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); mTextView.setText(来自网络的文字); } }; class MyThread extends Thread { @Override public void run() { // 耗时操作 loadNetWork(); Message msg = new Message(); handle.sendMessage(msg); super.run(); } }
5、AsyncTask
主线程调用:
aTask ak = new aTask(); ak.execute(); AsyncTask private class aTask extends AsyncTask { //后台线程执行时 @Override protected Object doInBackground(Object... params) { // 耗时操作 return loadNetWork(); } //后台线程执行结束后的操作,其中参数result为doInBackground返回的结果 @Override protected void onPostExecute(Object result) { super.onPostExecute(result); mTextView.setText(result); } }
总结
最后来个总结,Android中的线程延续了JAVA的设计模型,默认一个应用程序只有一个主线程,主线程的开启是在Activity的main()方法。主线程实际上是一个死循环,不断的循环处理系统以及其他子线程发来的消息。主线程的绑定是在DecorView初始化的时候,也就是生命周期的onResume()之后。主线程主要处理UI操作,和Broadcast相关消息,主线程如果长时间无法响应,将出现ANR,为了避免ANR,耗时操作一般都开启子线程处理。子线程处理完再发消息通知主线程来改变UI。
android 线程那点事的更多相关文章
- android线程间通讯
近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图 ...
- 优化 Android 线程和后台任务开发
在 Android 开发中,你不应该做任何阻碍主线程的事情.但这究竟意味着什么呢?在这次海湾 Android 开发者大会讲座中,Ari Lacenski 认为对于长时间运行或潜在的复杂任务要特别小心. ...
- 【Android开发那点破事】打开APP加载页面实现
今天的破事呢就说说APP加载页面的实现.一般情况下,当APP打开的时候,我们需要做很多事情,比如检查网络连接啊,初始化一些配置啊等等.我们可以让这些事情在APP完全打开之前做完,然后呢在打开的过程中显 ...
- 浅析Android线程模型一 --- 转
摘要:随着中国移动在8月份相继发布基于Google Android的OPhone平台和手机网上应用商店Mobile Market,以及各大手机生产厂商在2009年北京国际通信展?上展出了各自基于And ...
- [Android开发那点破事]解决android.os.NetworkOnMainThreadException
[Android开发那点破事]解决android.os.NetworkOnMainThreadException 昨天和女朋友换了手机,我的iPhone 4S 换了她得三星I9003.第一感觉就是好卡 ...
- android线程 Handler Message Queue AsyncTask线程模型 线程交互 + 修改Button样式 示例 最终easy整合版
首先原谅我把文章的标题写的这么长.其实我还嫌弃它短了因为 写不下去了所以我就不写了.因为我实在不知道该怎么定义这篇文章的标题或许应该叫 "乱谈"比较合适. 这样可能还体现了 ...
- Android线程管理之ThreadLocal理解及应用场景
前言: 最近在学习总结Android的动画效果,当学到Android属性动画的时候大致看了下源代码,里面的AnimationHandler存取使用了ThreadLocal,激起了我很大的好奇心以及兴趣 ...
- Android线程管理之Thread使用总结
前言 最近在一直准备总结一下Android上的线程管理,今天先来总结一下Thread使用. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Executo ...
- Android线程管理之ExecutorService线程池
前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...
随机推荐
- Android的AIDL机制
Android 接口定义语言 (AIDL) AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似. 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的 ...
- springMVC源码分析--HttpRequestHandlerAdapter(四)
上一篇博客springMVC源码分析--HandlerAdapter(一)中我们主要介绍了一下HandlerAdapter接口相关的内容,实现类及其在DispatcherServlet中执行的顺序,接 ...
- Apache shiro集群实现 (二) shiro 的INI配置
Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...
- MPAndroidChart——饼图
MPAndroidChart--饼图 MPAndroidChart是安卓下的一个开源图形库,很多效果,简单看几个效果图 Github地址:https://github.com/PhilJay/MPAn ...
- 使用Android Studio向GitHub提交代码
使用Android Studio向GitHub提交代码 在GitHub上创建一个仓库 填写仓库名称.描述.类型(公有.私有)等信息,点击创建 到此,成功创建了我们的仓库,并且初始化创建了README. ...
- paypal的IPN机制
paypal对接时发现有这么一个机制,看起来还不错,起到了防止篡改欺诈行为,保证了通信的安全性,但会增加几次通信.
- Dynamics CRM 插件注册时报Assembly must be registered in isolation的解决方法
在插件注册的时候经常会遇到"Assembly must be registered in isolation"的问题导致无法注册,之前经常会被同事或者朋友问到这个问题,遇到这个问题 ...
- 【Netty源码学习】BootStrap
BootStrap是客户端的启动类,其主要功能就是设置必要的参数然后启动客户端. 实现如下: Bootstrap b = new Bootstrap(); b.group(group) .channe ...
- shell入门之函数应用
最近在学习shell编程,文中若有错误的地方还望各位批评指正. 先来看一个简单的求和函数 #!/bin/bash #a test about function f_sum 7 8 function f ...
- 深入理解Express.js
转自:http://xvfeng.me/posts/understanding-expressjs/ 英文原文更赞:http://evanhahn.com/understanding-express- ...