Android 子线程中操作UI【原创】
2015-01-30 00:00
453 查看
开发Android的都知道,自2.2之后,谷歌工程师规定了子线程不允许更新UI,原因说起来也简单,可以想一想,如果多个子线程同时操作UI,那么该听谁的呢,所以,现在在子线程中写关于操作UI的代码,运行就回报异常。
我们都知道耗时的操作,例如数据库读写大量数据,网络请求,这些都比较耗时间,一般情况下都会放入子线程里面操作,这样不会阻塞UI。但是每次当我们操作完这些数据之后,就要更新UI的数据,但是上面说了,子线程是不允许操作UI的,所以我们都想到了handler,创建一个handler对象,重写handlermessage方法,在里面过滤比对消息,再对UI进行操作。
可以想一下,每次new一个线程操作完数据,还得创建Message对象与handler对象,实在是麻烦啊。怎么办,有人说用
AsyncTask,AsyncTask是官方封装的一个比较好的异步操作,但是一些大牛貌似不喜欢用,我写了不少次,后来也不想去写了,每次都要定义后传入参数类型,返回值类型等,代码稍微变动,改起来也是有点麻烦。
Thread用起来灵活方便,现在就是刷新UI有点小麻烦,针对这个问题,参考一下网上资料与思路,整理完善了一个工具类,可以直接在子线程里刷新UI,一起来看看把
原理也好理解,就是创建一个Runnable的任务队列,需要执行的时候把消息发送到handler,然后子啊handlerMessage里面从队列中取出Runnable任务执行,这里面有一个异步与同步的方法,异步好理解,传入一个Runnable接口实现类就可以了,那么同步UIKitSyncPost呢,下面是对Runnable的一个简单的封装,实现同步。
最后是三个公开的方法,如下代码
当我们要在子线程中刷新UI的时候,只需要在子线程中调用,这三个公开的方法即可,
runOnMainThreadAsync是异步,调用这个方法,主线程不会阻塞会直接调用Runnable中的刷新UI代码,不会与主线程干扰。
runOnMainThreadSync是同步,调用这个方法主线程会阻塞
最后一个带参数的同步方法设置了阻塞时间,这样可以防止主线程阻塞时间过久导致ANR错误。
看下调用方式吧:
上面代码就是一个在子线程里弹出一个Dialog的用法,无需再写handler与Message对象了,用起来爽歪歪。
对这个感兴趣的,可以看看代码注释。
我们都知道耗时的操作,例如数据库读写大量数据,网络请求,这些都比较耗时间,一般情况下都会放入子线程里面操作,这样不会阻塞UI。但是每次当我们操作完这些数据之后,就要更新UI的数据,但是上面说了,子线程是不允许操作UI的,所以我们都想到了handler,创建一个handler对象,重写handlermessage方法,在里面过滤比对消息,再对UI进行操作。
可以想一下,每次new一个线程操作完数据,还得创建Message对象与handler对象,实在是麻烦啊。怎么办,有人说用
AsyncTask,AsyncTask是官方封装的一个比较好的异步操作,但是一些大牛貌似不喜欢用,我写了不少次,后来也不想去写了,每次都要定义后传入参数类型,返回值类型等,代码稍微变动,改起来也是有点麻烦。
Thread用起来灵活方便,现在就是刷新UI有点小麻烦,针对这个问题,参考一下网上资料与思路,整理完善了一个工具类,可以直接在子线程里刷新UI,一起来看看把
/* add 增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 offer 添加一个元素并返回true 如果队列已满,则返回false poll 移除并返问队列头部的元素 如果队列为空,则返回null peek 返回队列头部的元素 如果队列为空,则返回null put 添加一个元素 如果队列满,则阻塞 take 移除并返回队列头部的元素 如果队列为空,则阻塞 */ package com.xdroid.utils; import java.util.LinkedList; import java.util.Queue; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; /** * Created by Robin * on 2015-01-28 10:31:15. */ final class UIKitHandlerPoster extends Handler { private static final int ASYNC = 0x1; //异步 private static final int SYNC = 0x2; //同步 private final Queue<Runnable> mAsyncPool; //异步Runnable任务队列 private final Queue<UIKitSyncPost> mSyncPool; //同步Runnable任务队列 private final int mMaxMillisInsideHandleMessage; //占用主线程的时间限制 private boolean isAsyncActive; //当前是否处于异步任务执行中 private boolean isSyncActive; //当前是否处于同步任务执行中 UIKitHandlerPoster(Looper looper, int maxMillisInsideHandleMessage) { super(looper); this.mMaxMillisInsideHandleMessage = maxMillisInsideHandleMessage; mAsyncPool = new LinkedList<Runnable>(); mSyncPool = new LinkedList<UIKitSyncPost>(); } /** * 去除掉没有处理的消息,从队列彻底移除所有元素 */ void dispose() { this.removeCallbacksAndMessages(null); this.mAsyncPool.clear(); this.mSyncPool.clear(); } /** * 加锁添加一个异步Runnable任务 * @param runnable */ void async(Runnable runnable) { synchronized (mAsyncPool) { mAsyncPool.offer(runnable); //添加一个Runnable任务并返回true 如果队列已满,则返回false if (!isAsyncActive) { //判断当前是否处于异步任务执行中,如果不是:立刻改变状态,然后发送一个消息给当前Handler isAsyncActive = true; if (!sendMessage(obtainMessage(ASYNC))) { throw new XDroidException("Could not send handler message"); } } } } /** * 加锁添加一个同步任务 * @param post */ void sync(UIKitSyncPost post) { synchronized (mSyncPool) { mSyncPool.offer(post); if (!isSyncActive) { isSyncActive = true; if (!sendMessage(obtainMessage(SYNC))) { throw new XDroidException("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 = mAsyncPool.poll(); //移除并返问队列头部的Runnable任务 如果队列为空,则返回null if (runnable == null) { synchronized (mAsyncPool) { // Check again, this time in synchronized runnable = mAsyncPool.poll(); if (runnable == null) { isAsyncActive = false; return; } } } runnable.run(); long timeInMethod = SystemClock.uptimeMillis() - started; //任务执行结束时间与任务开始时间差 if (timeInMethod >= mMaxMillisInsideHandleMessage) { //当执行一个任务后就判断一次如果超过了每次占用主线程的时间限制,那么不管队列中的任务是否执行完成都退出,同时发起一个新的消息到Handler循环队列 if (!sendMessage(obtainMessage(ASYNC))) { throw new XDroidException("Could not send handler message"); } rescheduled = true; return; } } } finally { isAsyncActive = rescheduled; } } else if (msg.what == SYNC) { boolean rescheduled = false; try { long started = SystemClock.uptimeMillis(); while (true) { UIKitSyncPost post = mSyncPool.poll(); if (post == null) { synchronized (mSyncPool) { // Check again, this time in synchronized post = mSyncPool.poll(); if (post == null) { isSyncActive = false; return; } } } post.run(); long timeInMethod = SystemClock.uptimeMillis() - started; if (timeInMethod >= mMaxMillisInsideHandleMessage) { if (!sendMessage(obtainMessage(SYNC))) { throw new XDroidException("Could not send handler message"); } rescheduled = true; return; } } } finally { isSyncActive = rescheduled; } } else super.handleMessage(msg); } }
原理也好理解,就是创建一个Runnable的任务队列,需要执行的时候把消息发送到handler,然后子啊handlerMessage里面从队列中取出Runnable任务执行,这里面有一个异步与同步的方法,异步好理解,传入一个Runnable接口实现类就可以了,那么同步UIKitSyncPost呢,下面是对Runnable的一个简单的封装,实现同步。
package com.xdroid.utils; /** * 同步任务类,对Runnable的简单封装 * Created by Robin * on 2015-01-28 10:32:05. */ final class UIKitSyncPost { private Runnable mRunnable; private boolean isEnd = false; //是否执行结束 UIKitSyncPost(Runnable runnable) { this.mRunnable = runnable; } /** * 加锁执行Runnable任务 */ public void run() { if (!isEnd) { synchronized (this) { if (!isEnd) { mRunnable.run(); isEnd = true; try { this.notifyAll(); //唤醒其他所有线程 } catch (Exception e) { e.printStackTrace(); } } } } } /** * 等待状态,等待其他线程执行完毕 */ public void waitRun() { if (!isEnd) { synchronized (this) { if (!isEnd) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } /** * 等待状态,等待其他线程执行完毕 * @param time 等待时间 * @param cancel 是否取消 */ public void waitRun(int time, boolean cancel) { if (!isEnd) { synchronized (this) { if (!isEnd) { try { this.wait(time); } catch (InterruptedException e) { e.printStackTrace(); } finally { if (!isEnd && cancel) isEnd = true; } } } } } }
最后是三个公开的方法,如下代码
package com.xdroid.utils; import android.os.Looper; /** * UI工具类,用于在子线程中操作UI * Created by Robin * on 2015-01-28 10:21:53. */ final public class UIKit { private static UIKitHandlerPoster mainPoster = null; private static UIKitHandlerPoster getMainPoster() { if (mainPoster == null) { synchronized (UIKit.class) { if (mainPoster == null) { mainPoster = new UIKitHandlerPoster(Looper.getMainLooper(), 20); //决定是在主线程执行的HandlerPoster,同时指定主线程单次运行时间为20毫秒 } } } return mainPoster; } /** * Asynchronously * The child thread asynchronous run relative to the main thread, * not blocking the child thread * 子线程异步运行相对于主线程 * 不阻塞子线程 * 在子线程中调用,直接操作UI,不阻塞子线程,即异步切换到主线程,无需等待 * @param runnable Runnable Interface */ public static void runOnMainThreadAsync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { //首先判断调用该方法的是否是主线程,如果是,就直接执行;如果是子线程就调用getMainPoster().async(runnable);追加到队列中执行 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 * 子线程相对线程同步操作 * 阻塞子线程 * 等待主线程执行完毕 * 在子线程中操作UI,同时阻塞子线程 * @param runnable Runnable Interface */ public static void runOnMainThreadSync(Runnable runnable) { if (Looper.myLooper() == Looper.getMainLooper()) { //首先判断调用该方法的是否是主线程,如果是,就直接执行;如果是子线程就调用getMainPoster().async(runnable);追加到队列中执行 runnable.run(); return; } UIKitSyncPost poster = new UIKitSyncPost(runnable); getMainPoster().sync(poster); poster.waitRun(); //进行等待;直到主线程执行了SyncPost类的run方法。 } /** * Synchronously * The child thread relative thread synchronization operation, * blocking the child thread, * thread for the main thread to complete * But the child thread just wait for the waitTime long. * * @param runnable Runnable Interface * @param waitTime wait for the main thread run Time * @param cancel on the child thread cancel the runnable task */ public static void runOnMainThreadSync(Runnable runnable, int waitTime, boolean cancel) { if (Looper.myLooper() == Looper.getMainLooper()) { //如果当前是主线程 runnable.run(); return; } UIKitSyncPost poster = new UIKitSyncPost(runnable); getMainPoster().sync(poster); poster.waitRun(waitTime, cancel); } public static void dispose() { if (mainPoster != null) { mainPoster.dispose(); mainPoster = null; } } }
当我们要在子线程中刷新UI的时候,只需要在子线程中调用,这三个公开的方法即可,
runOnMainThreadAsync是异步,调用这个方法,主线程不会阻塞会直接调用Runnable中的刷新UI代码,不会与主线程干扰。
runOnMainThreadSync是同步,调用这个方法主线程会阻塞
最后一个带参数的同步方法设置了阻塞时间,这样可以防止主线程阻塞时间过久导致ANR错误。
看下调用方式吧:
package com.example.test2; import com.lidroid.xutils.ViewUtils; import com.lidroid.xutils.view.annotation.ViewInject; import com.xdroid.utils.UIKit; import android.app.Activity; import android.app.AlertDialog; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.TextView; public class MainActivity extends Activity { @ViewInject(R.id.tv) private TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewUtils.inject(this); new Thread(){ @Override public void run() { UIKit.runOnMainThreadAsync(new Runnable() { @Override public void run() { AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this); builder.setTitle("hah"); builder.create().show(); } }); }; }.start(); } }
上面代码就是一个在子线程里弹出一个Dialog的用法,无需再写handler与Message对象了,用起来爽歪歪。
对这个感兴趣的,可以看看代码注释。
相关文章推荐
- Android中使用Handler和异步任务(AsyncTack)来为UI线程执行费时操作
- android 在子线程中操作UI 导致Fragment显示空白问题
- Android中使用Handler和异步任务(AsyncTack)来为UI线程执行费时操作
- Android中为什么主线程更新UI,子线程执行耗时操作?
- Android 子线程中操作UI
- android UI 操作 不要在子线程中操作UI
- Android开发之子线程操作UI的几种方法
- Android 子线程中进行UI操作遇到的小问题
- android 访问网络不能在主线程中进行以及在线程中操作UI的解决方法
- 为什么说android UI操作不是线程安全的 分类: Android 2014-09-23 21:08 1357人阅读 评论(0) 收藏 举报 目录(?)[+] 可能在非UI线程中刷新界面的时候,U
- Android 子线程中进行UI操作(非发送消息)
- android-----在子线程中更新UI操作的方法
- 为什么Android的UI操作是线程不安全的
- Android进程等级分类、UI线程操作方式
- Android UI 线程执行操作的三种方式
- android中子线程操作UI的问题
- Android 子线程操作更新UI方法
- android子线程操作ui
- android UI跨线程操作
- Android中更新UI的线程:Thread 、Handler 、Loopper 、TimerTask等