Android开发之多线程

Android开发之多线程

近来,小编在学习了Android开发中的多线程之后,自己又去了解了一下关于Android中消息机制,感觉Android的消息机制特别有趣,故在这里和大家分享一下


首先我们先从最基本的开始:如何在Android中实现多线程?

在讲述这个之前,我们可以先来看看Java中是如何实现多线程的:

Java实现多线程可以通过两种方式:1.通过Thread类 2.通过Runnable接口

我们可以看看这里两个的源码:

可以看到,其实Thread类也是继承了Runnable接口,从而实现多线程。

而这里也给我们讲述了显示多线程的两种方式。


那么,我们在Android中实现多线程的方式,其实和java大致相同,那么先来用Java中的方式来试试多线程。假设我们现在有个场景:在界面上实现计数器的功能,如

那么按照java中的方法,我们需要先重写Thread类中的run方法,然后再主线程中实例化这个Thread子类,再调用其start方法。当然,在Android中,我们是MainActivity中调用start方法。以下便是代码:

1.activity_main.xml文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/countername"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="58dp"
        android:layout_marginTop="100dp"
        android:text="计数器" />

    <TextView
        android:id="@+id/showCounter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/countername"
        android:layout_alignParentRight="true"
        android:layout_marginRight="70dp"
        android:text="0" />

</RelativeLayout>

其中,我们在界面上放置里两个TextView 组件,其中showCounter为我们要选择的用来计数的组件,效果如图

2.runThread类(继承了Thread类)

package com.example.b;

import android.widget.TextView;

public class runThread extends Thread{

	//showCounter组件
	TextView showCounter;

	public runThread(TextView showCounter) {
		super();
		this.showCounter = showCounter;
	}

	//重写run方法,实现每一秒,showCounter上的数字增加1,直到99
	public void run(){
		int i = 0;
		while(i<100){
			showCounter.setText(""+i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}
	
}

3.MainActivity:

package com.example.b;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //找出showCounter组件
        TextView showCounter = (TextView)this.findViewById(R.id.showCounter);
        
        //创建线程
        runThread run = new runThread(showCounter);
        run.start();
        
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}


现在我们来运行一下这段代码,我们会发现,程序会报错,错误则是

即,说明只有主线程才能去更新UI界面,其余的子线程都不可操作UI界面。

这其实是Android与java不同的一点,在Android中,我们称主线程为UI线程,而对界面的更新操作只能由UI线程来做,大家可以这样想象:假设我们把界面看成一家商店,而UI线程看成这家商店的店面员工。每当用户有要求,需要购买东西时,员工总要第一时间内处理,同时又不能打乱商品顺序,否则就会乱套,那唯一最好的方式,就是由一个员工来处理用户所提的所有要求,这个员工就是UI线程。如若,请了多名员工(多个子线程)来同时处理事务,结果可能就是整个商店就是乱的。

但是,仅仅靠UI线程一个员工来打理整个商店,肯定是处理不过来的。所以我们势必需要多个子线程来分工,但是不能乱套,那么我们可以想到,由UI线程来调控所有要处理的事物,然后将这些事物分给多个员工(多个子线程),由他们具体处理这些任务,处理完之后,将处理的结果告知UI线程,由UI线程来把最终的商品给予用户。

事实上,Android就是这样处理的。而处理的方法就采用的Handler机制。


在Android中,主线程永远处于无线循环的过程,这样做是为了保证界面始终存在。如同最初学C语言时,为了让黑框框里面有一个菜单界面,我们就采用了while循环,以保证菜单界面的存在。那么Android中也一样。

但是Android不仅要展示界面,同时还要处理用户在界面上产生的各种消息,例如用户点击了某个组件等,故需要去处理这样的消息,那么,也就需要一个消息队列MessageQueue,存储所有消息;一个轮询器Looper来不断检查MessageQueue中是否有消息,同时将消息分配。

首先,我们来看一下,什么是Message?

可以看到,Message充当了一个容器,这个容器装载了事物所有处理的内容。

而Message中本身包含了

public int what;

public int arg1;

public int arg2;

public Object obj;

4个属性,其中obj是用于装载我们所需要的对象,what则是用于用户去记录这是哪个子线程处理的。


光有容器还不够,我们还需要知道怎么处理容器里面的信息,这时候就需要Handler了。

在Handler中,我们有两个很重要的方法

一个是 public void handleMessage(Message msg) { },这个方法用于告知如何处理msg中的信息

另一个就是 public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0); },这个方法用于向MessageQueue发送Handler所要处理的消息。

而在Message中,有个target属性,这个属性是用来存储Handler对象的。

那么Android的整个处理方式也就清楚了。


总结一下:

首先,由子线程来写好Message中的信息内容,并同时,通过handler来向MessageQueue中发送这个Message。

之后,Looper在MessageQueen中不断轮询,当轮询到我们所发的Message时,则将其出队,并且通过target来找到是由哪个handler所发,由Looper调用handler的handleMessage()方法来处理Message中的信息内容。


以下便是代码部分:

1.activity_main.xml文件与之前相同,不做改变

2.Handler类

package com.example.painter;

import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class handler extends Handler{

	TextView showCounter;

	//处理消息
	public void handleMessage(Message msg) {
		String text = (String)msg.obj;
		showCounter.setText(text);
    }


	public handler(TextView showCounter) {
		super();
		this.showCounter = showCounter;
	}
	
}


3.线程类

package com.example.painter;

import android.os.Message;
import android.util.Log;
import android.widget.TextView;

public class runthread extends Thread{

	handler handle;

	public runthread(handler handle) {
		super();
		this.handle = handle;
	}

	public void run(){
		int i = 0;
		while(i<100){
			//获得消息,并写好message中内容,这里采用了回收机制
			Message msg = handle.obtainMessage();
			msg.obj = ""+(i++);	
			//Log.i("run", ""+i);
			
			//发送消息
			handle.sendMessage(msg);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}


4.MainActivity:

package com.example.painter;

import android.os.Bundle;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_draw);
		
		//找出组件		
		TextView showCounter = (TextView)this.findViewById(R.id.showCounter);

		//创建handler类
		handler handle = new handler(showCounter);

		//创建线程类
		runthread r = new runthread(handle);
		r.start();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.draw, menu);
		return true;
	}

}

这样就在Android中实现对界面更改的多线程了。

大家是不是会感到特别美妙呢,Android通过Handler实现了子线程对UI组件操控,同时又没有影响到UI线程,做到了“一石二鸟”,当然了,小编这里也只是简单介绍了一下,其中还有更加深厚的地方,大家不妨可以去深入了解Android的消息机制。

发布于 2019-01-21