androidlooper
❶ Android-Looper
Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR?
首先我们来看造成ANR的原因:
1.当前的事件没有机会得到处理(即主线程正在处理前一个事件,没有及时的完成或者looper被某种原因阻塞住了)
2.当前的事件正在处理,但没有及时完成
我们再来看一下APP的入口ActivityThread的main方法:
显而易见的,如果main方法中没有looper进行死循环,那么主线程一运行完毕就会退出,会导致直接崩溃,还玩什么!
现在我们知道了消息循环的必要性,那为什么这个死循环不会造成ANR异常呢?
我们知道Android 的是由事件驱动的,looper.loop() 不断地接收事件、处理事件,每一个点击触摸或者说Activity的生命周期都是运行在 Looper的控制之下,如果它停止了,应用也就停止了。只能是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它,这也就是我们为什么不能在UI线程中处理耗时操作的原因。
主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,唤醒主线程,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗。
初始化当前线程和Looper,这样可以在实际开始启动循环(loop())之前创建一个Handler并且关联一个looper。确保在先调用这个方法,然后调用loop()方法,并且通过调用quit()结束。
这里面的入参boolean表示Looper是否允许退出,true就表示允许退出,对于false则表示Looper不允许退出。
初始化当前当前线程的looper。并且标记为一个程序的主Looper。由Android环境来创建应用程序的主Looper。因此这个方法不能由咱们来调用。另请参阅prepare()
这里的sThreadLocal.get()是和prepare(boolean)方法里面的sThreadLocal.set(new Looper(quitAllowed));一一对应的。而在prepareMainLooper()方法里面。
退出循环
将终止(loop()方法)而不处理消息队列中的任何更多消息。在调用quit()后,任何尝试去发送消息都是失败的。例如Handler.sendMessage(Message)方法将返回false。因为循环终止之后一些message可能会被无法传递,所以这个方法是不安全的。可以考虑使用quitSafely()方法来确保所有的工作有序地完成。
安全退出循环
调用quitSafely()方法会使循环结束,只要消息队列中已经被传递的所有消息都将被处理。然而,在循环结束之前,将来不会提交处理延迟消息。
调用退出后,所有尝试去发送消息都将失败。就像调用Handler.sendMessage(Message)将返回false。
❷ Android如何保证一个线程最多只能有一个Looper
Looper的构造方法为private,所以不能直接使用其构造方法创建。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}1234
要想在当前线程创建Looper,需使用Looper的prepare方法,Looper.prepare()。
如果现在要我们来实现Looper.prepare()这个方法,我们该怎么做?我们知道,Android中一个线程最多只能有一个Looper,若在已有Looper的线程中调用Looper.prepare()会抛出RuntimeException(“Only one Looper may be created per thread”)。面对这样的需求,我们可能会考虑使用一个HashMap,其中Key为线程ID,Value为与线程关联的Looper,再加上一些同步机制,实现Looper.prepare()这个方法,代码如下:
❸ android looper 怎么理解
Android
中
Looper
和
handler
的联系及处理
最近一直在看
Android
内核的代码。把
Looper
及
Handler
这一节先分享一下:
Handle
主要起一个代理中介的作用,具体对象要向底层发请求,得通过
Handler
,
Handler
把这个请求放进对应的
MessageQueue
(消息队列)中,而
MessageQueue
是由
Looper
来维护的。
Looper
中有一个循环,循环读取
MessageQueue
中的请求,发给相应的底层处理(这个过程有点复杂)。当底层
有信息返回时,
也会先放进
MessageQueue
中,
然后对应的
Looper
遍历,
交给对
应的
Handler
处理。
Handler
找出对应的回调函数或其他已注册的相关方法,让
其进行处理,最终在界面有一个反应。
以下是我写的一个
demo
,
主要是如何关联
Looper
、
Handler
、
以及对应的线程
(
MessageQueue
是
Looper
的一个成员)。
1.
测试方法入口:
testLooper1.java
package com.linquan.test.loop1;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class testLooper1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d(MyConstant.TAG, "main start...");
new MyThread().start();
Message msg = new Message();
msg.what = MyConstant.CONSTANT3;
while(MyThread.h == null){
Log.d(MyConstant.TAG, "handler is not init ! please wait a
monment....");
try {
Thread.sleep(300);//
由于这里是测试,
所以加上此行代码,
避免出错。
实
际中多为用户触发事件,此时
handle
应该已经初始化了。
} catch (InterruptedException e) {
e.printStackTrace();
❹ android中looper的实现原理,为什么调用looper.prepare就在当前线程关联了一个lo
实际上:消息发送和计划任务提交之后,它们都会进入某线程的消息队列中,我们可以把这个线程称之为目标线程。不论是主线程还是子线程都可以成为目标线程。上例中之所以在主线程中处理消息,是因为我们要更新UI,按照android中的规定我们必须由主线程更新UI。所以我们让主线程成为了目标线程。
那么如何控制让某个线程成为目标线程呢?
这就引出了Looper的概念。Android系统中实现了消息循环机制,Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。Android系统中的通过Looper帮助线程维护着一个消息队列和消息循环。通过Looper.myLooper()得到当前线程的Looper对象,通过Looper.getMainLooper()得到当前进程的主线程的Looper对象。
前面提到每个线程都可以有自己的消息队列和消息循环,然而我们自己创建的线程默认是没有消息队列和消息循环的(及Looper),要想让一个线程具有消息处理机制我们应该在线程中先调用Looper.prepare()来创建一个Looper对象,然后调用Looper.loop()进入消息循环。如上面的源码所示。
当我们用Handler的构造方法创建Handler对象时,指定handler对象与哪个具有消息处理机制的线程(具有Looper的线程)相关联,这个线程就成了目标线程,可以接受消息和计划任务了。Handler中的构造方法如下:
[java] view
plainprint?
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
public Handler() {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = null;
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
在上述的计时器的例子中,之所以可以在主线程中处理消息而我们自己并没有调用Looper.prepare()等方法,是因为Android系统在Activity启动时为其创建一个消息队列和消息循环,当我们用无参的Handler构造方法创建对象时又用了当前线程的Looper对象,及将handler与主线程中的Looper对象进行了关联。
android中是使用Looper机制来完成消息循环的,但每次创建线程时都先初始化Looper比较麻烦,因此Android为我们提供了一个HandlerThread类,他封装了Looper对象,是我们不用关心Looper的开启和释放问题。
不管是主线程还是其他线程只要有Looper的线程,别的线程就可以向这个线程的消息队列中发送消息和任务。
我们使用HandlerThread类代替上一篇文章中的子线程,并用HandlerThread类中的Looper对象构造Handler,则接受消息的目标线程就不是主线程了,而是HandlerThread线程。代码如下:
[java] view
plainprint?
public class clockActivity extends Activity {
/** Called when the activity is first created. */
private String TAG="clockActivity";
private Button endButton;
private TextView textView;
private int timer=0;
private boolean isRunning=true;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
endButton=(Button)findViewById(R.id.endBtn);
textView=(TextView)findViewById(R.id.textview);
endButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
isRunning=false;
}
});
HandlerThread thread=new HandlerThread("myThread");
handler=new Handler(thread.getLooper());//与HandlerThread中的Looper对象关联
thread.start();
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
if(isRunning){
textView.setText("走了"+timer+"秒");
timer++;
handler.postDelayed(this, 1000);//提交任务r,延时1秒执行
}
}
};
handler.postDelayed(r, 1000);
}
}
public class clockActivity extends Activity {
/** Called when the activity is first created. */
private String TAG="clockActivity";
private Button endButton;
private TextView textView;
private int timer=0;
private boolean isRunning=true;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
endButton=(Button)findViewById(R.id.endBtn);
textView=(TextView)findViewById(R.id.textview);
endButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
isRunning=false;
}
});
HandlerThread thread=new HandlerThread("myThread");
handler=new Handler(thread.getLooper());//与HandlerThread中的Looper对象关联
thread.start();
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
if(isRunning){
textView.setText("走了"+timer+"秒");
timer++;
handler.postDelayed(this, 1000);//提交任务r,延时1秒执行
}
}
};
handler.postDelayed(r, 1000);
}
}
此时处理任务会在handlerThread线程中完成。当然这个例子会出线异常:依然是因为在非主线程中更新了UI。这样做只是为了大家能够理解这种机制。
深入理解Android消息处理机制对于应用程序开发非常重要,也可以让我们对线程同步有更加深刻的认识,希望这篇文章可以对朋友们有所帮助。
❺ Android 系统运行机制 【Looper】【Choreographer】篇
目录:
1 MessageQueue next()
2 Vsync
3 Choreographer doFrame
4 input
系统是一个无限循环的模型, Android也不例外,进程被创建后就陷入了无限循环的状态
系统运行最重要的两个概念:输入,输出。
Android 中输入 输出 的往复循环都是在 looper 中消息机制驱动下完成的
looper 的循环中, messageQueue next 取消息进行处理, 处理输入事件, 进行输出, 完成和用户交互
应用生命周期内会不断 产生 message 到 messageQueue 中, 有: java层 也有 native层
其中最核心的方法就是 messageQueue 的 next 方法, 其中会先处理 java 层消息, 当 java 层没有消息时候, 会执行 nativePollOnce 来处理 native 的消息 以及监听 fd 各种事件
从硬件来看, 屏幕不会一直刷新, 屏幕的刷新只需要符合人眼的视觉停留机制
24Hz , 连续刷新每一帧, 人眼就会认为画面是流畅的
所以我们只需要配合上这个频率, 在需要更新 UI 的时候执行绘制操作
如何以这个频率进行绘制每一帧: Android 的方案是 Vsync 信号驱动。
Vsync 信号的频率就是 24Hz , 也就是每隔 16.6667 ms 发送一次 Vsync 信号提示系统合成一帧。
监听屏幕刷新来发送 Vsync 信号的能力,应用层 是做不到的, 系统是通过 jni 回调到 Choreographer 中的 Vsync 监听, 将这个重要信号从 native 传递到 java 层。
总体来说 输入事件获取 Vsync信号获取 都是先由 native 捕获事件 然后 jni 到 java 层实现业务逻辑
执行的是 messageQueue 中的关键方法: next
next 主要的逻辑分为: java 部分 和 native 部分
java 上主要是取java层的 messageQueue msg 执行, 无 msg 就 idleHandler
java层 无 msg 会执行 native 的 pollOnce@Looper
native looper 中 fd 监听封装为 requestQueue, epoll_wait 将 fd 中的事件和对应 request 封装为 response 处理, 处理的时候会调用 fd 对应的 callback 的 handleEvent
native 层 pollOnce 主要做的事情是:
vsync 信号,输入事件, 都是通过这样的机制完成的。
epoll_wait 机制 拿到的 event , 都在 response pollOnce pollInner 处理了
这里的 dispatchVsync 从 native 回到 java 层
native:
java:
收到 Vsync 信号后, Choreographer 执行 doFrame
应用层重要的工作几乎都在 doFrame 中
首先看下 doFrame 执行了什么:
UI 线程的核心工作就在这几个方法中:
上述执行 callback 的过程就对应了图片中 依次处理 input animation traversal 这几个关键过程
执行的周期是 16.6ms, 实际可能因为一些 delay 造成一些延迟、丢帧
input 事件的整体逻辑和 vsync 类似
native handleEvent ,在 NativeInputEventReceiver 中处理事件, 区分不同事件会通过 JNI
走到 java 层,WindowInputEventReceiver 然后进行分发消费
native :
java:
input事件的处理流程:
输入event deliverInputEvent
deliver的 input 事件会来到 InputStage
InputStage 是一个责任链, 会分发消费这些 InputEvent
下面以滑动一下 recyclerView 为例子, 整体逻辑如下:
vsync 信号到来, 执行 doFrame,执行到 input 阶段
touchEvent 消费, recyclerView layout 一些 ViewHolder
scroll 中 fill 结束,会执行 一个 recyclerView viewProperty 变化, 触发了invalidate
invalidate 会走硬件加速, 一直到达 ViewRootImpl , 从而将 Traversal 的 callback post choreographer执行到 traversal 阶段就会执行
ViewRootImpl 执行 performTraversal , 会根据目前是否需要重新layout , 然后执行layout, draw 等流程
整个 input 到 traversal 结束,硬件绘制后, sync 任务到 GPU , 然后合成一帧。
交给 SurfaceFlinger 来显示。
SurfaceFlinger 是系统进程, 每一个应用进程是一个 client 端, 通过 IPC 机制,client 将图像显示工作交给 SurfaceFlinger
launch 一个 app:
❻ android里面所说的looper是什么意思啊
Looper即:有消息循环的线程。
在Android里线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新概念。主线程(UI线程)就是一个消息循环的线程。针对这种消息循环的机制,引入一个新的机制Handle,有消息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有对应的处理,消息的发送和清除,消息的处理,把这些都封装在Handle里面,注意Handle只是针对那些有Looper的线程,不管是UI线程还是子线程,只要有Looper,就可以往消息队列里面添加东西,并做相应的处理。