GVKun编程网logo

Android HandlerThread 和 IntentService

2

对于想了解AndroidHandlerThread和IntentService的读者,本文将提供新的信息,并且为您提供关于AlarmManager,WakeLockandIntentService、A

对于想了解Android HandlerThread 和 IntentService的读者,本文将提供新的信息,并且为您提供关于AlarmManager, WakeLock and IntentService、Android HandlerThread、Android HandlerThread FD 数量优化、Android HandlerThread 详解的有价值信息。

本文目录一览:

Android HandlerThread 和 IntentService

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

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

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 数量优化

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 详解

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 详解等相关知识,可以在本站进行查询。

本文标签: