android 线程那点事

在操做系统中,线程是操做系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,而且线程的建立和销毁都会有相应的开销,当系统中存在大量的线程时,系统会经过时间片轮转的方式调度每一个线程,在这么多线程中有一个被称为主线程,主线程是指进程所拥有的线程,在JAVA中默认状况下一个进程只有一个线程,这个线程就是主线程。主线程主要处理界面交互相关的逻辑,由于用户随时会和界面发生交互,所以主线程在任什么时候候都必须有比较高的响应速度,不然就会产生一种界面卡顿的感受。为了保持较高的响应速度,这就要求主线程中不能执行耗时的任务,这个时候子线程就派上用场了。子线程也叫工做线程,除了主线程之外的线程都是子线程。html

Android中的线程java

Android沿用了JAVA的线程模型,其中的线程也分为主线程和子线程,其中主线程又叫UI线程。在Android系统中,在默认状况下,一个应用程序内的各个组件(如Activity、BroadcastReceiver、Service)都会在同一个进程(Process)里执行,且由此进程的主线程负责执行。若是有特别指定(经过android:process),也可让特定组件在不一样的进程中运行。不管组件在哪个进程中运行,默认状况下,他们都由此进程的主线程负责执行。主线程既要处理Activity组件的UI事件,又要处理Service后台服务工做,一般会忙不过来。为了解决此问题,主线程能够建立多个子线程来处理后台服务工做,而自己专心处理UI画面的事件。子线程的任务则是执行耗时任务,好比网络请求,I/O操做等。从Android4.0开始系统要求网络访问必须在子线程中进行,不然网络访问将会失败并抛出NetWorkOnMainThreadException这个异常,这样作是为了不主线程因为被耗时操做阻塞从而出现ANR现象。android

为何会出现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中开启一个子线程无非仍是这两种方法ide

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中的守护线程和垃圾回收线程堆裁剪守护进程等在运行。


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



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(),因此上述的子线程才有机会越俎代庖。

子线程如何与主线程通讯

一、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(来自网络的文字); } }); } }); } });

二、 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>

三、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); } }); } });

四、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(); } }

五、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。



本文同步分享在 博客“xiangzhihong8”(CSDN)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。