Android中的线程、进程和消息处理机制

应用程序的启动运行就是一个进程的触发,我们知道进程是由线程组成的。Handler是Android中用来进行线程间的通信的。

1)Android进程分类

a.前台进程是用户当前正在使用的进程。只有一些前台进程可以在任何时候都存在。只有在系统内存无法维持当前进程运行时才会被结束,也可以手动降级(即关闭)。

如果有以下的情形的那么就是前台进程:

这个进程运行着一个正在和用户交互的Activity(这个Activity的onResume()方法被调用)。

这个进程里有绑定到当前正在和用户交互的Activity的一个Service。

这个进程里有一个onReceive()方法的BroadCastReiver对象。

b.可见进程是没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。

如果一个进程满足以下任一条件,即视为可见进程。

不在前台、但仍对用户可见的Activity(已调用其onPause() 方法)。例如,如果前台Activity 启动了一个对话框。

托管绑定到可见(或前台)Activity的 Service

可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

c.服务进程是正在运行已使用 startService() 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

d.后台进程是目前对用户不可见的 Activity 的进程(已调用 Activity 的 onStop() 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。

e.空进程是为了再次运行提高速度,不用开辟进程空间,只需将数据装载进来就可以了。

2)进程的优先级

Android为了管理内存释放有限的空间,会根据优先级杀死优先级低的进程。从高到低依次为:

                   空进程—>后台进程—>服务进程—>可见进程—>前台进程

3)Android线程

 a)Android是单线程模式

 b)Android进程在启动时,会创建一个主线程(UI线程)去处理UI相关操作,为了高效率考虑,主线程不是线程安全的。

当一个程序第一次启动时,ANDROID会同时启动一个对应的主线程(MAIN THREAD),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

什么叫线程不安全呢?如果主线程在处理UI的时候子线程也想处理UI将导致主线程阻塞。

  c)子线程不能直接操作主线程内的控件,UI控件只能由UI线程去操作。

  d)若要实现线程间通信,需要依赖于Android消息机制等技术

4)Handler

Android消息机制,如果子线程需要处理UI控件,必须借助Handler发送消息给主线程,由主线程完成处理操作(这里需要注意,子线程中发送的消息其实就是一个消息Message,我一开始以为要发送一个意图,把自己想做的处理发给主线程,实际上仅仅是发送一个“提醒”,当然可以附带数据,然后具体处理UI还是在主线程中写代码。我的意思是,如果我想让你帮我做件事,我只说帮我做件事,但不说做什么事,你却做了一件具体的事。)。

Handler 的使用涉及到四个组件:Message、MessageQueue、Looper、Handler

Message:消息,线程通信中携带标记信息和数据信息

MessageQueue:消息队列,子线程发给主线程的消息可能有多个,但不能同时处理,把消息按先后顺序存放,挨个取出执行

Looper:消息管理者,Looper以无限循环的模式不断从消息队列中取出消息并传给Handler

Handler:消息处理者,通过Handler发送消息,亦通过Handler处理消息

 

如何使用:

子线程发消息到主线程:

在主线程中新建一个Handler对象;

在子线程中使用主线程中创建的Handler对象发送一个消息(消息中需要包含数据和标识信息);

在主线程中的Handler对象中重写handlerMessage()方法,在该方法中写入需要进行的UI操作。

主线程发消息到子线程:

在子线程中获取当前线程的Looper(主线程不需要手动获取)Looper.prepare()

在子线程中新建Handler对象

在主线程中获取子线程中的Handler对象并发送一个消息

在子线程Handler类中重写handlerMessage()方法,在该方法中写入需要进行的操作

在子线程中启动Looper:Looper.loop()

 

关于Message的新建:

Message message = new Message();

MessageQueue(消息队列)中最多存在50个消息,官方建议不使用创建的方法新建。如果消息很多,每个消息都新建一个新的消息,数量足够多将导致垃圾过多内存溢出。

Message message = Message.obtain();

obtain()方法中会对消息队列中使用过的消息进行判空,如果有用过的消息就将新消息赋值给它,如果没有才新建消息。

handler.obtainMessage()方法与Message.obtain()相似,前者其实回调了后者,效果相同

Message对于信息的携带

可同时携带三个整型和一个Object类型数据(arg1,arg2,what,obj)

其中what是一个唯一标识本条信息的数据,一般以16进制形式赋值。

此外,还可以通过以下方法新建Message

Message message = Message.obtain(handler,what, obj);

Message message = Message.obtain(handler,what, arg1,arg2);

Message message = Message.obtain(handler,what, arg1,arg2,obj);

这些方法都对Message做了不同程度的封装,使用起来更加方便

 

Handler发送消息

handler.sendEmptyMessage(what)发送即时空消息

handler.sendEmptyMessageAtTime(what,uptimeMillis)特定时间发送空消息

handler.sendEmptyMessageDelayed(what,delayMillis)特定延迟发送空消息

空消息甚至不需要新建Message,直接在方法中填入what值

handler.sendMessage(message)发送即时消息

handler.sendMessageAtTime(message,uptimeMillis)特定时间发送消息

handler.sendMessageDelayed(message,delayMillis)特定延迟发送消息

Handler接收并处理消息

handleMessage(Message msg)重写该方法传回一个Message直接调用其中的参数

 

关于Looper和MessageQueue:

使用消息机制并不需要手动创建Looper和MessageQueue,Looper的构造函数是私有的,而在其中调用了MessageQueue的构造方法。主线程在创建过程中,系统会自动为其添加Looper与Message Queue,子线程默认不会添加Looper与Message Queue,在子线程中使用Handler就必须先获取子线程的Looper(同时也就获取了MessageQueue),并在Handler代码结束后启动Looper。

下面模拟一个下载弹窗来做一下测试,布局文件如下

<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="com.briup.download.MainActivity" >

    <Button
        android:id="@+id/start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        android:onClick="startDown" />

</RelativeLayout>
Activity类文件如下

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Toast;

@SuppressLint("HandlerLeak")
public class MainActivity extends Activity {
	private ProgressDialog dialog;//进度条弹出框
	private int progress = 0;//进度
	private int max = 100;//最大进度
	public final static int WHAT = 0x123;//标识信息
	private Handler handler = new Handler() {//主线程中不需要手动获取Looper
		public void handleMessage(Message msg) {//消息处理
			if (msg.what == WHAT) {//消息分类
				dialog.setProgress(msg.arg1);//将传回的数据设置给进度
			}
			if(msg.what==0x111){//消息分类
				dialog.dismiss();//完成后隐藏进度
				Toast.makeText(MainActivity.this,"Done DownLoad", Toast.LENGTH_SHORT).show();
				progress = 0;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
	}

	public void startDown(View v) {
		new DownThread().start();//启动子线程
		showDialog();
	}

	private void showDialog() {//设定进度弹出框
		dialog = new ProgressDialog(this);
		dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		dialog.setIcon(R.drawable.ic_launcher);
		dialog.setTitle("正在下载");
		dialog.setProgress(0);
		dialog.show();
	}

	class DownThread extends Thread {
		@Override
		public void run() {
			while (progress <= max) {
				try {
					sleep(100);//隔100毫秒发一次消息
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				Message message = Message.obtain();//新建Message
				message.arg1 = progress;//携带数据
				message.what = WHAT;//设置表示数据
				handler.sendMessage(message);//发送消息
				progress++;
			}
			handler.sendEmptyMessage(0x111);//结束后发送空消息提醒主线程结束
		}
	}
}
效果如下




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值