您的位置:首页 > 产品设计 > UI/UE

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

2015-09-21 17:46 381 查看
为什么只是点击一个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。方便快捷
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: