对于想了解AndroidHandlerThread和IntentService的读者,本文将提供新的信息,并且为您提供关于AlarmManager,WakeLockandIntentService、A
对于想了解Android HandlerThread 和 IntentService的读者,本文将提供新的信息,并且为您提供关于AlarmManager, WakeLock and IntentService、Android HandlerThread、Android HandlerThread FD 数量优化、Android HandlerThread 详解的有价值信息。
本文目录一览:- Android HandlerThread 和 IntentService
- AlarmManager, WakeLock and IntentService
- Android HandlerThread
- Android HandlerThread FD 数量优化
- Android HandlerThread 详解
Android HandlerThread 和 IntentService
HandlerThread
HandlerThread 继承了 Thread, 它是一种可以使用 Handler 的 Thread,它实现也很简单,就是在 run 中通过 Looper.prepare () 来创建消息队列,并且通过 Looper.loop () 来开启消息循环,这样再实际使用中就允许在 HandlerThread 中创建 Handle 了。
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
//Thread.IsAlive属性 ,表示该线程当前是否为可用状态
//如果线程已经启动,并且当前没有任何异常的话,则是true,否则为false
//Start()后,线程不一定能马上启动起来,也许CPU正在忙其他的事情,但迟早是会启动起来的!
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
@NonNull
public Handler getThreadHandler() {
if (mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
public int getThreadId() {
return mTid;
}
}
IntentService
IntentService 是一种特殊的 Service,它继承了 Service 并且它是一个抽象类,因此必须创建它的子类才能使用 IntentService
IntentService 可用于执行后台耗时的任务,当任务执行后它会自动停止,同事由于 IntentService 是服务的原因,这导致它的优先级比单纯的线程要高很多,所以 IntentService 比较适合执行一些高级优先级的后台任务,因为它的优先级高不容易被系统杀死。
在实现上,IntentService 封装了 HandlerThread 和 Handler,这一点可以从它的 onCreate 方法中看出来。
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
使用:
class IntentServiceDemo : IntentService("IntentServiceDemo") {
private val TAG = "zmm"
/* (non-Javadoc)
* @see android.app.IntentService#onCreate()
*/
override fun onCreate() {
Log.e(TAG, "=>onCreate")
super.onCreate()
}
override fun onStartCommand(@Nullable intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG, "=>onStartCommand")
return super.onStartCommand(intent, flags, startId)
}
/* (non-Javadoc)
* @see android.app.IntentService#onDestroy()
*/
override fun onDestroy() {
Log.e(TAG, "=>onDestroy")
super.onDestroy()
}
override fun onHandleIntent(arg0: Intent?) {
try {
Log.e(TAG, "IntentService 线程:" + Thread.currentThread().id)
Thread.sleep(2000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
var mServiceIntent = Intent(this@MainActivity, IntentServiceDemo::class.java)
startService(mServiceIntent)
AlarmManager, WakeLock and IntentService
在 Android 开发中,我们经常会用到 AlarmManager 来做一些类似于 Linux cron job 的工作。我们以下面的代码为例来说明整个流程:
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, WakeUpDemoReceiver.class);
i.setAction("WakeUpDemo");
PendingIntent pi =
PendingIntent.getBroadcast(context, "WakeUpDemo", i, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 100000, pi);
上面的代码说明了 AlarmManager 在定时时间到了之后会广播一个 Intent 出来。这个 Intent 将会被一个
BroadcastReceiver 的实现类来处理:
public class WakeUpDemoReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ("WakeUpDemo".equals(action)) {
WakeLockManager lock = WakeLockManager.getInstance(context);
lock.acquire();
Intent demo = new Intent("DemoService");
eventIn.setClass(context, DemoIntentService.class);
context.startService(demo);
}
}
}
这里引入了 WakeLockManager。当定时时间到了之后,Android 系统会被 AlarmManager 唤醒,然后通过 Intent 传递到 WakeUpDemoReceiver。这里为什么要用 WakeLock?原因在于我们在 onReceive 方法里面启动了一个 DemoIntentService。我们一般把一些耗时的工作放到了这个 DemoIntentService 里面去执行。如果不在 onReceive 开始的时候加锁,系统有可能在启动 DemoIntentService 之前重新在进入睡眠状态。
一旦我们获取了锁,那么 CPU 就会处于 running 的状态,如果不尽快的释放锁,那么对电源的消耗是非常大的。那么我们应该在什么地方释放那?在 DemoIntentService 里面。
public class DemoIntentService extends IntentService {
public DemoIntentService (String name) {
super(name);
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
WakeLockManager lock = WakeLockManager.getInstance(this);
lock.release();
super.onDestroy();
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return (START_REDELIVER_INTENT);
}
@Override
protected void onHandleIntent(Intent intent) {
handleIntent(intent);
}
}
当 DemoIntentService 处理完 Intent 事件之后就会调用 onDestroy,这样就保证了我们自己的逻辑工作做完之后立刻释放锁。
WakeLockManager 是一个简单的 WakeLock 的包装类。这里就不在说明。
下面的几个链接可以帮助我们从其他的角度来理解上面所说的流程。
http://www.androidguys.com/2009/04/02/wake-up-with-the-alarm/
http://stackoverflow.com/questions/7665713/whats-the-best-between-alarmmanager-and-handlerwakelock
https://github.com/commonsguy/cw-advandroid/tree/master/SystemServices/Alarm
Android HandlerThread
package com.example.handlertest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
HandlerThread handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
Bundle b = new Bundle();
b.putString("name", "johnson");
b.putInt("age", 12);
msg.setData(b);
msg.sendToTarget();
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
Bundle b = msg.getData();
String n = (String) b.get("name");
System.out.println(n);
}
public MyHandler() {
}
public MyHandler(Looper looper) {
super(looper);
}
}
}
Android HandlerThread FD 数量优化
背景:
FD 泄露通常会造成 OOM、ANR 甚至 Crash。
Android 作为 Linux 系统的一部份,在 Linux 中 “一切都是文件”,从小到一块儿内存区域大到进程、Socket、线程、磁盘文件等都有自身的文件描述符。我们在保证 Socket、文件句柄、IO 操作不存在泄露的情况下,是否还有其他方面可以优化?
答案是肯定的,不然也不会写这么一篇文章了,那我们今天的主角表面上 HandlerThread 优化,实际上是通过共享 HandlerThread 减少 FD 的创建。
在 Android 系统中,创建一个 HandlerThread 通常会创建 2 个 FD,一个是线程的 FD,一个是 MessageQueue 的 FD,前者是系统机制,后者是为了 epoll 队列监控使用。这些 eventfd 由 HandlerThread 创建,每个 HandlerThread 会创建 eventfd 和 epollfd 两个 fd,在 Android 5 上甚至会创建三个 FD,pipe_in、pipe_out 和 epollfd。
原理:
换种思路,让所有的 Handler 共享同一个 HandlerThread,所有的消息都交给同一个 Looper 去监控,等到执行时间到的时候再转发到其他线程
public class ShareLooperHandler extends Handler implements Runnable {
static final HandlerThread sHandlerThread = new HandlerThread("LightHandlerThread");
static {
sHandlerThread.start();
}
private static final int MSG_QUIT = Integer.MAX_VALUE;
private AbstractQueue<Message> queue = null;
private final Thread thread;
private volatile boolean isQuited = false;
public ShareLooperHandler(String threadName, Callback callback) {
super(sHandlerThread.getLooper(), callback);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
this.queue = new LinkedTransferQueue<>();
} else {
this.queue = new LinkedBlockingQueue<>();
}
this.thread = new Thread(Thread.currentThread().getThreadGroup(), this, threadName, 512*1024);
this.thread.start();
}
public ShareLooperHandler(String threadName) {
this(threadName, null);
}
@Override
public void dispatchMessage(Message msg) {
Message message = Message.obtain(this);
message.copyFrom(msg);
queue.offer(message);
}
public void doHandleMessage(Message msg) {
super.dispatchMessage(msg);
}
@Override
public void run() {
while (!isQuited) {
Message msg = queue.poll();
if (msg == null) {
continue;
}
if(msg.what == Integer.MIN_VALUE){
break;
}
doHandleMessage(msg);
msg.recycle();
}
}
public Thread getThread() {
return thread;
}
public boolean isQuited() {
return isQuited;
}
public void quit(){
sendEmptyMessage(MSG_QUIT);
isQuited = true;
}
}
评价
优点:
避免了创建更多 FD
缺点:
这种依然是有缺点的,主要是同步屏障降对所有 Handler 开启,当然使用同步屏障的情况实际并不多,理论上也不会有人使用共享 ThreadHandler 去实现同步屏障。
Android HandlerThread 详解
相信大家都比较熟悉了,从名字上看是一个带有 Handler 消息循环机制的一个线程,比一般的线程多了消息循环的机制,可以说是 Handler + Thread 的结合,从源码上看也是如此的设计,一般情况下如果需要子线程和主线程之间相互交互,可以用 HandlerThread 来设计,这比单纯的 Thread 要方便,而且更容易管理,因为大家都知道Thread 的生命周期在一些情况下是不可控制的,比如直接 new Thread().start() 这种方式在项目中是不推荐使用的,实际上 Android 的源码中也有很多地方用到了 HandlerThread,下面我将分析一下 HandlerThread 用法以及源码解析。
使用示例
[plain] view plaincopy
// 实例对象,参数为线程名字
HandlerThread handlerThread = new HandlerThread("handlerThread");
// 启动线程
handlerThread.start();
// 参数为 HandlerThread 内部的一个 looper
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
注意:这个使用的顺序是不能更改的!!!,因为如果不先让子线程 start 起来,那么创建主线程的 handler 的参数 getLooper 是获取不到的,这一点可以看源码就清楚。
Demo 详解
这里模拟在子线程下载东西,然后和主线程之间进行通信。主线程知道了下载开始和下载结束的时间,也就能及时改变界面 UI。 首先是 DownloadThread 类,继承于 HandlerThread,用于下载。
[plain] view plaincopy
public class DownloadThread extends HandlerThread{
private static final String TAG = "DownloadThread";
public static final int TYPE_START = 2;//通知主线程任务开始
public static final int TYPE_FINISHED = 3;//通知主线程任务结束
private Handler mUIHandler;//主线程的Handler
public DownloadThread(String name) {
super(name);
}
/*
- 执行初始化任务
- */
@Override
protected void onLooperPrepared() {
Log.e(TAG, "onLooperPrepared: 1.Download线程开始准备");
super.onLooperPrepared();
}
//注入主线程Handler
public void setUIHandler(Handler UIhandler) {
mUIHandler = UIhandler;
Log.e(TAG, "setUIHandler: 2.主线程的handler传入到Download线程");
}
//Download线程开始下载
public void startDownload() {
Log.e(TAG, "startDownload: 3.通知主线程,此时Download线程开始下载");
mUIHandler.sendEmptyMessage(TYPE_START);
//模拟下载
Log.e(TAG, "startDownload: 5.Download线程下载中...");
SystemClock.sleep(2000);
Log.e(TAG, "startDownload: 6.通知主线程,此时Download线程下载完成");
mUIHandler.sendEmptyMessage(TYPE_FINISHED);
}
}
然后是
MainActivity 部分,UI 和处理消息。
[plain] view plaincopy
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private DownloadThread mHandlerThread;//子线程
private Handler mUIhandler;//主线程的Handler
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化,参数为线程的名字
mHandlerThread = new DownloadThread("mHandlerThread");
//调用start方法启动线程
mHandlerThread.start();
//初始化Handler,传递mHandlerThread内部的一个looper
mUIhandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//判断mHandlerThread里传来的msg,根据msg进行主页面的UI更改
switch (msg.what) {
case DownloadThread.TYPE_START:
//不是在这里更改UI哦,只是说在这个时间,你可以去做更改UI这件事情,改UI还是得在主线程。
Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");
break;
case DownloadThread.TYPE_FINISHED:
Log.e(TAG, "7.主线程知道Download线程下载完成了...这时候可以更改主界面UI,收工");
break;
default:
break;
}
super.handleMessage(msg);
}
};
//子线程注入主线程的mUIhandler,可以在子线程执行任务的时候,随时发送消息回来主线程
mHandlerThread.setUIHandler(mUIhandler);
//子线程开始下载
mHandlerThread.startDownload();
}
@Override
protected void onDestroy() {
//有2种退出方式
mHandlerThread.quit();
//mHandlerThread.quitSafely(); 需要API>=18
super.onDestroy();
}
}
运行的Log日志如下
源码解析
先来看下构造函数相关的:
[plain] view plaincopy
int mPriority;//优先级
int mTid = -1;
Looper mLooper;//自带的Looper
private @Nullable Handler mHandler;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
- Constructs a HandlerThread.
- @param name
- @param priority The priority to run the thread at. The value supplied must be from
- {@link android.os.Process} and not from java.lang.Thread.
/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
这里有两个构造方法,一个 HandlerThread(String name),一个 HandlerThread(String name, int priority),我们可以自己设定线程的名字以及优先级。注意!是 Process 里的优先级而不是Thread 的。
[plain] view plaincopy
/* - Call back method that can be explicitly overridden if needed to execute some
- setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
这里面有一个方法 onLooperPrepared(),在实际中,我们可以重写这个方法做一些初始化的操作,这个 run() 是重点。
run 方法中首先获取线程 id,然后就调用了 Looper.prepare 方法创建一个 Looper,接着调用了 Looper.myLooper 方法获取到了当前线程的 Looper。
接着通过
notifyAll 通知等带唤醒,这里的等待是在 HandlerThread 的 getLooper 方法里调用的 wait 方法,getLooper 方法是为了获取该 HandlerThread 中的 Looper。
如果在没调用
HandlerThread 的 start 方法开启线程前就调用 getLooper 方法就通过 wait 方法暂时先进入等待,等到 run 方法运行后再进行唤醒。唤醒之后 run 方法中继续设置了构造函数中传入的优先级,接着调用了onLooperPrepared 方法,该方法是个空实现,该方法是为了在 Looper 开启轮询之前如果要进行某些设置,可以复写该方法。
最后调用
Looper.loop开启轮询。退出的时候,将 mTid = -1;
[plain] view plaincopy
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
这个方法是获取当前的 Looper,可以看到如果没有获取的时候就一直等待直到获取,而前面也提到了获取到了就唤醒了所有的线程,看来这是线程的等待-唤醒机制应用。
[plain] view plaincopy
public Handler getThreadHandler() {
if (
56c
mHandler == null) {
mHandler = new Handler(getLooper());
}
return mHandler;
}
这个是获取 HandlerThread 绑定的 Looper 线程的 Handler
[plain] view plaincopy
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
可以看到这两个方法去退出线程的 Looper 循环,那么这两个方法有什么区别呢,实际上都是调用了 MessageQueue 的 quit() 方法,源码如下:
[plain] view plaincopy
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was prevIoUsly false.
nativeWake(mPtr);
}
}
可以看到: 当我们调用 quit 方法的时候,实际上执行了 MessageQueue 中的 removeAllMessagesLocked 方法,该方法的作用是把 56c MessageQueue 消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过 sendMessageDelayed 或通过 postDelayed 等方法发送的需要延迟执行的消息,只要不是立即执行的消息都是延迟消息)还是非延迟消息。
而 quitSafely 方法时,实际上执行了 MessageQueue 中的 removeAllFutureMessagesLocked 方法,通过名字就可以看出,该方法只会清空 MessageQueue 消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让 Handler 去处理,quitSafely 相比于 quit 方法安全之处在于清空消息之前会派发所有的非延迟消息,一句话,就是清除未来需要执行的消息。
这两个方法有一个共同的特点就是:Looper 不再接收新的消息了,消息循环就此结束,此时通过 Handler 发送的消息也不会在放入消息杜队列了,因为消息队列已经退出了。应用这2个方法的时候需要注意的是:quit 方法从 API 1 就开始存在了,比较早,而 quitSafely 直到 API 18 才添加进来.
总结
如果经常要开启线程,接着又是销毁线程,这是很耗性能的,
HandlerThread 很好的解决了这个问题;
HandlerThread 由于异步操作是放在 Handler 的消息队列中的,所以是串行的,但只适合并发量较少的耗时操作。
HandlerThread 用完记得调用退出方法。
注意使用 handler 避免出现内存泄露
今天关于Android HandlerThread 和 IntentService的分享就到这里,希望大家有所收获,若想了解更多关于AlarmManager, WakeLock and IntentService、Android HandlerThread、Android HandlerThread FD 数量优化、Android HandlerThread 详解等相关知识,可以在本站进行查询。
本文标签: