程序员人生 网站导航

[置顶] [Android] 任意时刻从子线程切换到主线程的实现

栏目:综合技术时间:2014-12-15 08:58:07

========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
网站:www.qiujuer.net
开源库:Genius-Android
转载请注明出处:http://blog.csdn.net/qiujuer/article/details/41599383
========================================================

引入

在Android开发中常常会遇到网络要求数据库数据准备等1些耗时的操作;而这些操作是不允许在主线程中进行的。由于这样会梗塞主线程致使程序出现未响应情况。

所以只能另起1个子线程进行这些耗时的操作,完成后再显示到界面。尽人皆知,界面等控件操作只能在主线程中完成;所以不可避免的需要从子线程切换到主线程

方法

对这样的情况在Android 中比较常见的是使用AsynTask类或 Handler来进行线程切换;而其中AsynTask是官方封装的类,较为简单,效力也比较可以,但是其实不合适所有的情况,最少我使用了1两次后就再也没有使用了。使用 Handler可以说是最万能的方式,其原理是消息循环,在主线程中建立Handler 变量时,就会启动Handler消息循环,1个个的处理消息队列中的任务。但是其也有辣手的时候;其辣手的地方就是麻烦。

每次都需要去建立1个 Handler 类,然后使用voidhandleMessage(Messagemsg) 方法把消息取出来进行界面操作,而其中还要遇到参数的传递等问题,说起来真的是挺麻烦的。

想法

既然有着这么多的问题,但是又有其的优势,我们何不自行封装1次呢?

这里我梳理1下思路:

  1. 还是使用 Handler进行线程切换
  2. 在子线程中能通过简单的调用就切换到主线程进行工作
  3. 在子线程切换到主线程时,子线程进入阻塞直到主线程履行完成(知道为何有这样的需求么?)
  4. 1定要保证其效力
  5. 主线程的履行要有时间限制,不能履行太长时间致使主线程阻塞

我能想到的就是这些;观众老爷们咋样?可否还有需求?

说干就干,梳理1下实现方法

  • 使用Handler 实现,既然这样那末主方法固然就是采取继承Handler 来实现
  • 而要简单同时又要能随时进入方法 那末对外采取静态方法是个不错的选择
  • 而要保证效力的话,那就不能让Handler 的消息队列过于太多,但是又要满足能随时调用,那末采取外部 Queue
  • 更具情况有阻塞与不阻塞子线程两种情况,那末采取两个 Queue吧,分开来好1点
  • 要保证不能长时间在主线程履行那末对队列的履行1定要有时间限制加1个时间变量吧
  • 固然最后斟酌了1下,既然要简单那末传入参数采取Runnable 是很爽的

万事俱备,只欠东风了;好了进入下1环节。

CodeTime

首先我们建立1个ToolKit类:
public class ToolKit { /** * Asynchronously * * @param runnable Runnable Interface */ public static void runOnMainThreadAsync(Runnable runnable) { } /** * Synchronously * * @param runnable Runnable Interface */ public static void runOnMainThreadSync(Runnable runnable) { } }

两个对外的方法简单来讲就是这样了;但是其功能实现就需要使用继承Handler了。

建立类HandlerPoster,继承自Handler:

final class HandlerPoster extends Handler { private final int ASYNC = 0x1; private final int SYNC = 0x2; private final Queue<Runnable> asyncPool; private final Queue<SyncPost> syncPool; private final int maxMillisInsideHandleMessage; private boolean asyncActive; private boolean syncActive; HandlerPoster(Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage; asyncPool = new LinkedList<>(); syncPool = new LinkedList<>(); } void dispose() { this.removeCallbacksAndMessages(null); this.asyncPool.clear(); this.syncPool.clear(); } void async(Runnable runnable) { synchronized (asyncPool) { asyncPool.offer(runnable); if (!asyncActive) { asyncActive = true; if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException("Could not send handler message"); } } } } void sync(SyncPost post) { synchronized (syncPool) { syncPool.offer(post); if (!syncActive) { syncActive = true; if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException("Could not send handler message"); } } } } @Override public void handleMessage(Message msg) { if (msg.what == ASYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { Runnable runnable = asyncPool.poll(); if (runnable == null) { synchronized (asyncPool) { // Check again, this time in synchronized runnable = asyncPool.poll(); if (runnable == null) { asyncActive = false; return; } } } runnable.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException("Could not send handler message"); } rescheduled = true; return; } } } finally { asyncActive = rescheduled; } } else if (msg.what == SYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { SyncPost post = syncPool.poll(); if (post == null) { synchronized (syncPool) { // Check again, this time in synchronized post = syncPool.poll(); if (post == null) { syncActive = false; return; } } } post.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException("Could not send handler message"); } rescheduled = true; return; } } } finally { syncActive = rescheduled; } } else super.handleMessage(msg); } }

下面来讲说这个我花了很大时间弄出来的类。

类的变量部份:

两个标识,两个队列,两个履行状态,1个时间限制;很好理解吧?标识为了区分分别是处理那个队列使用;队列固然是装着任务了;履行状态是为了不重复发送消息致使消息队列过量;时间限制这个最好理解了。

下面来讲说方法部份:

构造函数HandlerPoster(Looper_looper,int_maxMillisInsideHandleMessage)

传入两个参数,分别是 Looper,用于初始化到主线程,后面的是时间限制;然后初始化了两个队列。

烧毁函数void_dispose()首先去除掉没有处理的消息,然后清空队列。

添加异步履行方法void_async(Runnable_runnable):

void async(Runnable runnable) { synchronized (asyncPool) { asyncPool.offer(runnable); if (!asyncActive) { asyncActive = true; if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException("Could not send handler message"); } } } }

可以看见进入方法后第1件事儿就是进入同步状态,然后调用asyncPool.offer(runnable);把任务写入到队列。

以后判断当前是不是处于异步任务履行中,如果不是:立刻改变状态,然后发送1个消息给当前Handler,固然不要忘记了传入标识。

固然为了效力其消息的构造也是通过obtainMessage(ASYNC)方法来完成,为的就是不过量建立新的Message,尽可能使用当前队列中空闲的消息。

添加同步履行方法void_sync(SyncPost_post)

void sync(SyncPost post) { synchronized (syncPool) { syncPool.offer(post); if (!syncActive) { syncActive = true; if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException("Could not send handler message"); } } } }

可以看到,这里传入的其实不是Runnable 而是SyncPost这是为了同步而对Runnable进行了1次封装后的类;后面介绍。

一样是进入同步,添加,判断,发送消息。

任务履行者@Override_void_handleMessage(Message_msg):

这里是复写的Handler的消息处理方法,铛铛前Handler消息队列中有消息的时候将会依照顺序1个个的调用该方法。

分段来看:

if (msg.what == ASYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { Runnable runnable = asyncPool.poll(); if (runnable == null) { synchronized (asyncPool) { // Check again, this time in synchronized runnable = asyncPool.poll(); if (runnable == null) { asyncActive = false; return; } } } runnable.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(ASYNC))) { throw new GeniusException("Could not send handler message"); } rescheduled = true; return; } } } finally { asyncActive = rescheduled; } }

进入后首先判断是不是是进行异步处理的消息,如果是那末进入该位置。

进入后我们进行了try_finally有1个变量long_started用于标识开始时间。

当履行1个任务后就判断1次如果超过了每次占用主线程的时间限制,那末不管队列中的任务是不是履行完成都退出,同时发起1个新的消息到Handler循环队列。

while部份,我们从队列取出1个任务,采取Poll方法;判断是不是为空,如果为空进入队列同步块;然后再取1次,再次判断。

如果恰巧在进入同步队列之前有新的任务来了,那末第2次取到确当然就不是 NULL也就会继续履行下去。反之,如果还是为空;那末重置当前队列的状态为false同时跳出循环。

下面来看第2部份:
else if (msg.what == SYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { SyncPost post = syncPool.poll(); if (post == null) { synchronized (syncPool) { // Check again, this time in synchronized post = syncPool.poll(); if (post == null) { syncActive = false; return; } } } post.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= maxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(SYNC))) { throw new GeniusException("Could not send handler message"); } rescheduled = true; return; } } } finally { syncActive = rescheduled; } } else super.handleMessage(msg);

首先还是判断,如果是同步任务消息就进入,如果还是否是 那末只有调用super.handleMessage(msg);了。

从上面的处理部份可以看出来其处理的进程与第1部份可以说是完全1样的。

只不过是从不同队列取出不同的类SyncPost,然后判断履行,和发送不同标识的消息;可以说如果懂了第1部份,这部份是毫无营养的。

这里就有问题了,既然方法操作流程1样,那末同步与异步是在哪里进行辨别的?

这里就要看看SyncPost了:
final class SyncPost { boolean end = false; Runnable runnable; SyncPost(Runnable runnable) { this.runnable = runnable; } public void run() { synchronized (this) { runnable.run(); end = true; try { this.notifyAll(); } catch (Exception e) { e.printStackTrace(); } } } public void waitRun() { if (!end) { synchronized (this) { if (!end) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }

首先看看SyncPost的构造函数:

是否是传入1个Runnable接口?所以说是对Runnable 的简单封装。

可以看见其public_void_run()方法:

在该方法中我们进入了同步块,然后调用Runnable接口的run方法。同时在履行完成后将其中的1个状态变量进行了改变boolean_end=true;

然后调用this.notifyAll();通知等待的部份可以继续了,固然有这样的情况;假设在进入该同步块的时候子线程还未履行到this.wait();部份呢?所以我们为此准备了endtry

然后看看public_void_waitRun()方法:

在这个中,我们首先判断状态,如果状态已变了,那末证明子线程履行到此处时,主线程和履行了void_run()。

所以也就不用进入同步块进行等待了,不然那还不等死啊?反之就进入进行等待直到主线程调用this.notifyAll();


豪情部份

马上进入到完成部份了,组建都完善了那末该进行最后的组装了。

回到类classToolKit

public class ToolKit { private static HandlerPoster mainPoster = null; private static HandlerPoster getMainPoster() { if (mainPoster == null) { synchronized (ToolKit.class) { if (mainPoster == null) { mainPoster = new HandlerPoster(Looper.getMainLooper(), 20); } } } return mainPoster; } /** * Asynchronously * The child thread asynchronous run relative to the main thread, * not blocking the child thread * * @param runnable Runnable Interface */ public static void runOnMainThreadAsync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { runnable.run(); return; } getMainPoster().async(runnable); } /** * Synchronously * The child thread relative thread synchronization operation, * blocking the child thread, * thread for the main thread to complete * * @param runnable Runnable Interface */ public static void runOnMainThreadSync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { runnable.run(); return; } SyncPost poster = new SyncPost(runnable); getMainPoster().sync(poster); poster.waitRun(); } public static void dispose() { if (mainPoster != null) { mainPoster.dispose(); mainPoster = null; } } }

其中就1个静态变量HandlerPoster

然后1个初始化部份HandlerPoster_getMainPoster()这里采取同步的方式进行初始化,用于适应多线程同时调用情况;固然在初始化的时候我们传入了

mainPoster=newHandlerPoster(Looper.getMainLooper(),20); 这里就决定了是在主线程履行的HandlerPoster,同时指定主线程单次运行时间为20毫秒。

在方法void_runOnMainThreadAsync(Runnable_runnable)中:

首先判断调用该方法的是不是是主线程,如果是那还弄到队列中履行干吗?直接履行啊;如果是子线程就调用getMainPoster().async(runnable);追加到队列中履行。

而在方法void_runOnMainThreadSync(Runnable_runnable)中:

public static void runOnMainThreadSync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { runnable.run(); return; } SyncPost poster = new SyncPost(runnable); getMainPoster().sync(poster); poster.waitRun(); }

一样是线程判断,然落后行封装,然后丢进队列中等待履行,而在该方法中调用poster.waitRun();进行等待;直到主线程履行了SyncPost类的run方法。
最后固然留下了1个烧毁方法;妈妈说要学会清算不留垃圾:void_dispose()

OK,完成了

// "Runnable" 类实现其中 "run()" 方法 // "run()" 运行在主线程中,可在其中进行界面操作 // 同步进入主线程,等待主线程处理完成后继续履行子线程 ToolKit.runOnMainThreadSync(Runnable runnable); // 异步进入主线程,无需等待 ToolKit.runOnMainThreadAsync(Runnable runnable);

对外就是这么两个方法,简单便捷啊;大伙试试吧;1个字

代码:

ToolKit.java

HandlerPoster.java

SyncPost.java


开源项目:

Genius-Android


------分隔线----------------------------
------分隔线----------------------------

最新技术推荐