android入门 — 多线程(一)

摘要:
Android中一些耗时的操作,如网络请求,如果无法及时响应,将导致主线程被阻塞,并发生ANR,这将极大地影响用户体验。因此,我们将找到方法在子线程中完成一些耗时的操作。为了解决这个问题,Android只允许UI线程修改UI组件。Android使用MessageQueue机制来确保线程间的通信。MessageQueue是一个消息队列,用于存储通过处理程序发布的消息。Android在首次启动程序时,默认情况下会为UI线程创建一个关联的消息队列,用于管理程序的组件,如Activity、BroadcastReceiver、Service等。

android入门 — 多线程(一)第1张

  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操作会等待它完成才能执行

android入门 — 多线程(一)第2张

  

android入门 — 多线程(一)第3张

免责声明:文章转载自《android入门 — 多线程(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java中的名称命名规范ROS探索总结(十二)——坐标系统下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

asp.net多线程(收藏)

1.ASP.NET多线程技术解析 http://developer.51cto.com/art/200907/138426.htm 2.(原创)asp.net利用多线程执行长时间的任务,客户端显示出任务的执行进度的示例(二) 3.多线程范例 http://blog.csdn.net/gloomyboyo/archive/2006/08/09/104218...

高德APP启动耗时剖析与优化实践(iOS篇)

前言最近高德地图APP完成了一次启动优化专项,超预期将双端启动的耗时都降低了65%以上,iOS在iPhone7上速度达到了400毫秒以内。就像产品们用后说的,快到不习惯。算一下每天为用户省下的时间,还是蛮有成就感的,本文做个小结。 (文中配图均为多才多艺的技术哥哥手绘)   启动阶段性能多维度分析 要优化,首先要做到的是对启动阶段的各个性能纬度做分析,...

java多线程案例

packagecom.alibaba.yuntu.me.biz.district.service.impl.MyUtis; importcom.alibaba.fastjson.JSONObject; importcom.alibaba.yuntu.me.common.base.util.HttpUtil; importlombok.SneakyThro...

Python多线程----线程池以及线程实现异步任务

Python多线程----线程池 需求:假设我们现在有一个多线程项目,每有一个用户连接进来,我们的服务器就会创建一个线程。而我们的服务器最多能够承载100个线程,再多就会崩溃。为了防止恶意用户伪装真实用户构建大量的访问来让我们的服务器崩溃,现在需要对线程数量进行限制,一共只有100个线程,并且当一个用户访问结束以后线程会自动归还,等待下一个用户访问。如果1...

web 服务的基础介绍

1>web 服务的访问流程          1.电脑浏览器网页上输入请求的地址          2.服务器接收到请求          3.服务器响应请求          4.将响应的数据返回给客户端 2>  apache 的三种工作模型(面试)               select ;work;event           2...

OpenMP 线程同步之临界区

多核/多线程编程中肯定会用到同步互斥操作。除了互斥变量以为,就是临界区。 临界区是指在用一时刻只允许一个线程执行的一段用{...},包围的代码段。 在OpenMP中临界区声明方法如下: #pragma omp critical [(name)] //[]表示名字可选 { //需要同一时刻只能有一个线程访问的代码 } 如下面的代码: 1 #include &...