zoukankan      html  css  js  c++  java
  • android入门 — 多线程(一)

      android中的一些耗时操作,例如网络请求,如果不能及时响应,就会导致主线程被阻塞,出现ANR,非常影响用户体验,所以一些耗时的操作,我们会想办法放在子线程中去完成。

      android的UI操作并不是线程安全的,所以多个线程并发操作UI组件的时候,则可能导致线程安全问题。为了解决这个问题,android只允许UI线程修改UI组件。

      

    public class MainActivity extends AppCompatActivity
    {
        TextView textView;
        Button changeText;
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            changeText = (Button)findViewById(R.id.btn);
            textView = (TextView)findViewById(R.id.textView);
            changeText.setOnClickListener(this);
        }
    
        public void change(View view)
        {
            switch (view.getId())
            {
                case R.id.btn:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText("被改变了");
                        }
                    }).start();
                    break;
                default:
                    break;
            }
        }
    }
    

      在xml中定义了一个按钮和一个TextView,当点击按钮的时候,开启子线程去更改TextView中的文字,但是在编译的时候是无法通过的,因为不允许在子线程中直接对UI线程中组件进行操作。

      需要借用Handler来实现子线程更新UI组件的功能。

    public class MainActivity extends AppCompatActivity
    {
    
        private TextView textView;
        private Handler handler = new Handler()
        {
            //接收的是消息队列中的msg
            public void handleMessage(Message msg)
            {
                switch (msg.what)
                {
                    case 0x0001:
                        int index = msg.arg1;
                        textView.setText(index + "");
                        break;
                }
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView)findViewById(R.id.textView1);
        }
    
        public void changeNumber(View view)
        {
            switch (view.getId())
            {
                case R.id.btn:
                    new Thread()
                    {
                        public void run()
                        {
                            for(int i = 0; i < 10; i++)
                            {
                                Message msg = new Message();
                                //msg.what是必不可少的,需要用来做判定
                                msg.what = 0x0001;
                                msg.arg1 = i;
                                handler.sendMessage(msg);
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }.start();
                    break;
            }
        }
    }
    

      

    Handler中的组件

    Message:Handler接收和处理的消息对象。

    Looper:每个线程只能拥有一个Looper,它的loop方法负责读取MessageQueue中的消息,读到消息之后将消息交给发送该消息的Handler进行处理。

    private Looper()
    {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
    

      从Looper的构造器的源码中可以看到,初始化Looper的时候会创建一个与之关联的MessageQueue。

    MessageQueue:消息队列,采用先进先出的方式来管理Message。程序创建Looper对象的时候,会在它的构造器中创建MessageQueue对象。

      android采用的是MessageQueue机制保证线程间通信。

      MessageQueue是一个消息队列,用来存放通过Handler发布的消息,Android在第一次启动程序的时候会默认为UI线程创建一个关联的消息队列,用来管理程序的组件,如Activity、BroadcastReceiver、Service等。

    Handler:它的作用是发送消息和处理消息,程序使用Handler发送消息的时候,发送的消息必须被送到指定的MessageQueue。也就是说,如果希望Handler能够正常工作,当前线程必须有一个MessageQueue,否则消息就没有保存。不过由于MessageQueue是由Looper管理的,也就是说,如果希望Handler正常工作,必须在当前线程中有一个Looper对象,为了保证当前线程中有Looper对象,分为两种情况。

      1.在UI线程中,系统初始化了Looper对象,只需要手动创建Handler即可,然后可以进行消息的发送和接收。

      2.在子线程中,必须自己创建一个Looper对象,并启动它。创建的时候,调用prepare()方法即可。

    public static final void prepare()
    {
        if(sThreadLocal.get() != null)
        {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    

      上面是prepare()方法的源码。

      有了Looper()之后,需要调用loop()方法来启动它。loop()方法使用一个死循环不断取出MessageQueue中的消息,并将取出的消息分给该消息对于的Handler进行处理。

    Handler消息传递机制

      工作线程通过handler对象和主线程进行通信

      Handler对象所有工作都在主线程中执行

      Handler类需要实现handleMessage()方法,来处理消息队列中取出的Message对象

      handleMessage()方法由主线程调用,可以在需要的时候更新UI线程,但是必须确保此方法快速完成,因为其他的UI操作会等待它完成才能执行

      

  • 相关阅读:
    Docker 我的常用命令
    Docker 已运行容器修改配置
    Docker .NET Core项目run起来
    Docker 安装运行mssql-server-linux
    Docker Mssql 还原数据库到Linux
    Nginx(四)------nginx 负载均衡
    nginx(二)------nginx.conf 配置文件
    Nginx(一)------简介与安装
    MySQL主从复制作用和原理
    设计模式:策略模式
  • 原文地址:https://www.cnblogs.com/Mr24/p/6888874.html
Copyright © 2011-2022 走看看