励精图治---Concurrency---GUI设计

为什么只是点击一个button, 可以看到按键变化很快? 添加了按键事件listener,并在里头dosomething的时候有时会有无响应的感觉?

因为特么GUI工具包都被实现为单线程.---------->因为单线程能保证时间有序进行.多线程不稳定的多,开发难度大.现在都是单线程了.


此文不必较真,有个别类是基于swing写的。就看那么个意思。有些许GUI了解就达到目的了


EDT: 事件分发线程  Event Dispatch Thread


EDT负责GUI drawing, 事件分发处理. 因此,当onClickListener中有一个长时间运行的任务,那么会导致界面无响应.

那么对长时间运行的任务,必须要将这个任务委托给分线程,让分线程去处理这个任务, 这可以迅速释放控制器给EDT。从而保证GUI的迅速响应。


用单线程实现线程中的任务调度

范例

public class SingleThread {
    private static final ExecutorService exec = Executors.newSingleThreadExecutor(new SingleThreadFactory());
    
    private static volatile Thread theSingleThread;
    private static class SingleThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            theSingleThread = new Thread(r);
            return theSingleThread;
        }
    }
    public static boolean isEventDispatchThread () {
        return Thread.currentThread() == theSingleThread;
    }
    
    public static void invokeLater(Runnable task) {
        exec.execute(task);
    }
    
    public static void invokeAndWait(Runnable task) throws InterruptedException,  InvocationTargetException {
        Future f = exec.submit(task);
        try {
            f.get();
        } catch (ExecutionException e) {
            throw new InvocationTargetException(e);
        }
    }
}


用newSingleThreadExecutor来实现任务的提交。还是比较靠谱的。起码是单线程。安全。

适用:任务时间短,不需要被并发执行,


GUI事件流程


在鼠标点击后,事件的处理流程是这样的。。。这里只要这里doSomething访问的是线程安全的或者GUI对象,而且,时间短的话,那么这里跟线程相关的问题都可以忽略。


面对长时间任务

使用线程接力的方法是比较可取的。

直接看范例。

mSearchView.setOnClickListener(
    new OnClickListener() {
        @Override
        public void onClick(View v) {
            mSearchView.setText("start");//#--------------->第一部分在GUIExecutor中
            backgroundExec.execute(new Runnable() {
                public void run() {
                    try {
                        doSomethingBig();//#--------------->第二部分在backgroundExec中
                    } finally {
                        GuiExecutor.instance().execute(new Runnable() {
                            public void run() {
                                mSearchView.setText("end");//#--------------->第三部分在GUIExecutor中
                            }
                        });
                    }
                }
            });
        }
    });
    
这种做法就叫线程接力。如果这里还需要用Future,那么,用get就可以在得到结果后继续操作。


利用Future取消线程
1. 设置mayInterruptIfRuning为true
2. 调用cancel


Future<?> runningTask;
mSubMitBT.setOnClickListener(
    new OnClickListener() {
        @Override
        public void onClick(View v) {
            if(runningTask == null){
                runningTask = backgroundExec.submit(new Runnable() {
                    public void run() {
                        while(hasWork()){
                            if(Thread.currentThread().isInterrupted()){
                                cleanup();
                                break;
                            }
                            doSomething();
                        }
                    }
                });
            }
        }
    });
mCancelBT.setOnClickListener(
    new OnClickListener() {
        @Override
        public void onClick(View v) {
            if(runningTask != null) runningTask.cancel();
        }
    });
    
利用Future更新完成状态和进度
先看一段FutureTask的源码
public class FutureTask<V> implements RunnableFuture<V> {
    ......
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;//#这里可以传入callable
        this.state = NEW;       // ensure visibility of callable
    }


    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }


    public boolean isCancelled() {//#通过isCanceled检查是否中止
        return state >= CANCELLED;
    }


    public boolean isDone() {//#通过isCanceled检查是否结束
        return state != NEW;
    }


    ......
    
    protected void done() { }


    ......


}
FutureTask实现了RunnableFuture,RunnableFuture继承了Future
public abstract class BackgroundTask<V> implements Runnable, Future<V> {
    private final FutureTask<V> computation = new Computation();
    
    private class Computation extends FutureTask<V> {
        public Computation() {
            super(new Callable<V>() {
                public V call() throws Exception {
                    return BackgroundTask.this.compute();//#这里的Callable会传入给callable,在run的时候会被调用。
                }
            });
        }
        protected final void done() {
            GuiExecutor.instance().execute(new Runnable() {
                public void run() {
                    V value = null;
                    Throwable thrown = null;
                    boolean cancelled = false;
                    try {
                        value = get();//#阻塞式获取结果
                    } catch (ExceptionException e) {
                        thrown = e.getCause();
                    } catch (CancellationException e) {
                        cancelled = true;
                    } finally {
                        onCompletion(value, thrown, cancelled);
                    }
                }
            });//#   GuiExecutor这个类是自定义的。是针对swing写。写这个代码的意义在于意会下那么个意思。不要较真
        }
        protected void setProgress(final int cur, final int max) {
            GuiExecutor.instance().execute(new Runnable() {
                public void run() {
                    onProgress(cur, max);
                }
            });
        }
        protected abstract V computer() throws Exceptions;//#这里是要被实现的,在实现的过程中可以调用setProgress来更新进度
        protected void onCompletion(V result, Throwable exe, boolean cancelled) {}
        protected void onProgress(final int cur, final int max) {}
    }
}




总结
1. GUI设计大部分都是单线程的。也可以自己写。要够严谨才行。
2. 长时间运行的任务,一定要放在分线程里头
3. 线程接力就是把一个人任务拆开执行。UI的归一个线程,逻辑的归一个线程。这是为了避免计算造成阻塞。影响响应
4. EDT中的处理的任务要迅速
5. 创建一个自己的类去处理。Future和newSingleThreadExecutor搭配使用有奇效哦。
6. 库的访问,System.loadLibrary什么的,都要放在同一个线程中执行。android里头一般就是static。方便快捷


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值