导航:首页 > 操作系统 > android子线程looper

android子线程looper

发布时间:2025-03-10 14:10:52

㈠ 这些关于 Handler 的知识点你都知道吗

在安卓面试中,关于Handler的问题是必考的,那么这些关于Handler的知识点你都知道吗?让我们逐一探索Handler的奥秘。



Handler的基本原理


关于Handler的原理,大家都应该有所了解,一张图就可以说明。



子线程中使用Handler


在子线程中使用Handler需要先执行两个操作:Looper.prepare和Looper.loop。为何需要这两个操作?因为Handler的构造函数会对Looper进行判断,如果ThreadLocal获取的Looper为空,就会报错。那么,Looper.prepare做了什么?它创建了Looper并设置给ThreadLocal,每个Thread只能有一个Looper,否则会抛出异常。而Looper.loop则是开始读取MessageQueue中的消息并执行。



MessageQueue如何等待消息


Looper.loop实际上就是开始读取MessageQueue中的消息。如果MessageQueue中没有消息,Looper会做什么?它会等待消息。那么,它是如何等待的呢?通过Looper.loop方法,我们知道是MessageQueue.next()来获取消息的,如果没有消息,会阻塞在这里。具体来说,MessageQueue.next调用了native方法nativePollOnce。



为什么不用wait而用epoll机制


android 2.2及以前,确实使用java的wait/notify进行阻塞等待消息。但随着版本更新,改为使用epoll机制,主要原因是需要处理native侧的事件,仅使用Java的wait/notify不够。具体改动见commit记录。



线程与Handler、Looper、MessageQueue的关系


一个线程对应一个Looper,一个Looper对应一个MessageQueue,多个Handler共享一个MessageQueue。



多个线程给MessageQueue发消息的线程安全保证


保证线程安全的方式是为MessageQueue加锁。



Handler消息延迟处理


Handler处理延迟消息的机制在post一个延迟消息时,会将uptimeMillis和delayMillis相加作为when参数进行顺序排序。执行流程包括创建Message并设置参数,将其加入MessageQueue,等待执行。



View.post与Handler.post的区别


View.post与Handler.post本质上都通过Handler进行消息处理。区别在于View.post最终也是通过Handler.post执行,其执行过程包含在ViewRootImpl的实现中。



Handler引发的内存泄漏


内存泄漏问题与Handler息息相关,通常涉及内存管理与生命周期,如何排查与避免需要综合考虑。



非UI线程是否可以操作View


在非UI线程操作View时,确实存在限制。这主要是因为ViewRootImpl在主线程创建,检查创建线程与当前线程是否一致,因此非主线程无法直接操作UI。



本文总结了Handler相关的关键知识点,希望对你的学习和面试有所帮助。更多资源和面试题整理在GitHub上,欢迎查阅。

㈡ Android之Looper使用

Looper是Android中的一个类,用于为线程提供消息循环。在Android中,主线程已经默认开启了一个Looper,因此可以直接使用Handler来发送消息。但是对于其他线程,如果需要使用Handler来发送消息,就需要先创建一个Looper。

以下是使用Looper的步骤:

1. 在子线程中创建一个Looper对象,并调用Looper的prepare()方法和Looper的loop()方法,这样就可以为该线程创建一个消息循环。

```java
public class MyThread extends Thread {
public Handler mHandler;

public void run() {
// 创建Looper对象
Looper.prepare();

// 创建Handler对象
mHandler = new Handler() {
public void handleMessage(Message msg) {
// 处理消息
}
};

// 进入消息循环
Looper.loop();
}
}
```

2. 在主线程或其他线程中,可以通过Handler向该线程发送消息。

```java
MyThread thread = new MyThread();
thread.start();

// 向子线程发送消息
thread.mHandler.sendEmptyMessage(1);
```

在使用完Looper之后,需要调用Looper的quit()方法来退出消息循环。

```java
Looper.myLooper().quit();
```

需要注意的是,Looper是一个轮询消息队列的无限循环,如果没有消息需要处理,会一直阻塞在loop()方法处,因此需要谨慎使用,避免出现死循环或内存泄漏等问题。

㈢ 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相关的资料

热点内容
去哪里找加密的便签 浏览:19
武汉订酒店旅馆哪个app平台好 浏览:114
c语言在线编译器字节流 浏览:317
带队伍pdf 浏览:951
电脑如何形成服务器 浏览:522
javaprint换行 浏览:785
华为手机压缩文件夹怎么操作 浏览:99
c语言垃圾堆这个文件夹可以删吗 浏览:758
websocketssl加密 浏览:924
系统类算法 浏览:657
ai算法血检t细胞衰老 浏览:473
如何查看文件夹是否安装过程 浏览:195
编译原理画出下面的状态转换图 浏览:40
倩女幽魂什么服务器比较火 浏览:464
内江市程序员招聘 浏览:462
程序员老师身份 浏览:50
手机生兼职app的哪个比较靠谱 浏览:569
lua编译有什么用 浏览:352
买了服务器如何架设 浏览:931
如何运用mex函数编译c 浏览:898