应用程序的启动运行就是一个进程的触发,我们知道进程是由线程组成的。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);//结束后发送空消息提醒主线程结束
}
}
}
效果如下