天天看点

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()方法进入消息循环体,进入后就会不断地从消息队列中读取并处理消息。

android中的子线程

android中开启一个子线程无非还是这两种方法

1:继承thread类

2:实现runnable接口

android apk程序中都有哪些线程?

通过debug,我们可以捕获当前应用程序中的线程(如下图),其中蓝色选中部分即为当前应用程序的主线程,当前程序中还运行了三个binder,每个binder对象都对应一个线程,这些binder线程主要负责接收linux binder驱动发送的ipc调用。除此以外还有java中的守护线程和垃圾回收线程堆裁剪守护进程等在运行。

android 线程那点事

paste_image.png

程序中自定义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?没错,不信下面的代码跑一遍试试,并不会报错,而且正确显示。

这是为什么呢?一个应用程序中有一个主线程和若干个子线程,而线程的检查工作是由viewroot完成的。viewroot是什么呢?可以简单的理解为window和view之前的桥梁或者纽带。而viewroot的创建是在onresume()之后才完成的,也就是说在onresume()之前,系统本身是无法区分当前线程到底是主线程还是子线程,而上面的代码中ui的更新操作在oncreate()中完成,先于onresume(),所以上述的子线程才有机会越俎代庖。

子线程如何与主线程通信

1、activity.runonuithread(runnable)

2、 view.post(runnable)

3、view.postdelayed(runnable,long)

4、handler(子线程调用handler的

handle.sendmessage(msg);

5、asynctask

主线程调用:

总结

最后来个总结,android中的线程延续了java的设计模型,默认一个应用程序只有一个主线程,主线程的开启是在activity的main()方法。主线程实际上是一个死循环,不断的循环处理系统以及其他子线程发来的消息。主线程的绑定是在decorview初始化的时候,也就是生命周期的onresume()之后。主线程主要处理ui操作,和broadcast相关消息,主线程如果长时间无法响应,将出现anr,为了避免anr,耗时操作一般都开启子线程处理。子线程处理完再发消息通知主线程来改变ui。

下一篇: Squid

继续阅读