使用异步数据(RPC)的GWT Google Visualization

时间:2015-01-23 08:46:13

标签: java gwt google-visualization gwt-rpc

在仔细搜索了正确的工作实用程序之后,我终于放弃并解决了我面临的问题:如何在{{{>>如何呈现异步加载的数据(RPC) 3}}图表本身是通过异步Google Visualization for GWT加载并行执行两个请求吗?

或者换句话说:无论图表数据是在加载Visualization库之前还是之后到达,我们如何确保图表呈现能够正常工作?

两个限制较少(或不那么雄心勃勃)的场景非常简单。

简单方案#1:使用静态数据

使用Visualization不依赖于RPC数据很简单。这是因为只有一个异步请求:加载Visualization库。因此,在加载Visualization库之前,数据不会到达的风险。

示例代码段:

public class MyViewImpl implements MyView {
    private Panel main;

    public MyViewImpl() {
        this.main = new FlowPanel();

        Runnable onVizLoaded = new Runnable() {
            @Override
            public void run() {
                DataTable data = DataTable.create();
                Options options = Options.create();

                // Populate data and options based on static data here...

                // Instantiate and attach new chart.
                LineChart lineChart = new LineChart(data, options);
                main.add(lineChart);
            }
        };
        // Load Google Visualization asynchronously.
        VisualizationUtils.loadVisualizationApi(onVizLoaded, LineChart.PACKAGE);
    }

    // View logic goes here...
}

可以轻松加载Visualization库,并在加载完成后创建并呈现LineChart。考虑一下负载特性的概述:

  Viz load: |---?---|
Chart data:         |
    Render:          |--->

简单方案#2:串行运行请求

可以在通过RPC加载图表数据时利用上面的代码段。但是,以下意味着在Visualization库加载并准备好之前,不会获取图表数据。你可能会对这种性能影响感到满意 - 我不是!

  1. 主要是因为我在可视化库的加载时间内没有无控制,因此在获取数据之前无法控制延迟
  2. 我不喜欢我的View来指示应用程序行为。应用程序逻辑属于Presenter。
  3. 示例代码段:

    public class MyViewImpl implements MyView {
        private Panel main;
    
        public MyViewImpl() {
            this.main = new FlowPanel();
    
            Runnable onVizLoaded = new Runnable() {
                @Override
                public void run() {
                    // Make RPC call.
                    ClientFactory.getService().getData(new AsyncCallback<MyResult>() {
                        @Override
                        public void onSuccess(MyResult result) {
                            DataTable data = DataTable.create();
                            Options options = Options.create();
    
                            // Populate data from RPC result.
                            data.addColumn(ColumnType.DATE);
                            data.addRow();
                            data.setValue(0, 0, result.getDate());
                            // ... Etc.
    
                            // Set options.
                            options.setWidth(500);
                            // ... Etc.
    
                            // Instantiate and attach new chart.
                            LineChart lineChart = new LineChart(data, options);
                            main.add(lineChart);
                        }
    
                        @Override
                        public void onFailure(Throwable caught) {
                            // Handle RPC error.
                        }
                    });
                }
            };
            // Load Google Visualization asynchronously.
            VisualizationUtils.loadVisualizationApi(onVizLoaded, LineChart.PACKAGE);
        }
    
        // View logic goes here...
    }
    

    负载特性说明了问题:

      Viz load: |---?---|
    Chart data:          |----~----|
        Render:                     |--->
    

    所需方案:并行运行

    所需方案将具有以下特征,可视化库的加载速度比数据快:

      Viz load: |---?---|
    Chart data: |----~----|
        Render:            |--->
    

    或者数据加载速度比可视化库快:

      Viz load: |---?---|
    Chart data: |--~--|
        Render:          |--->
    

    问题是:如何?

1 个答案:

答案 0 :(得分:0)

以下策略将符合要求:

  1. 请求并行运行
  2. 完成两项请求后立即呈现图表
  3. 应用程序逻辑(RPC调用)保留在Presenter中。
  4. 示例代码段:演示者

    public class MyPresenter implements MyView.Presenter {
        private MyView view;
    
        // ... Constructor etc. goes here...
    
        @Override
        public void render() {
            // Make RPC call immediately when Presenter should begin rendering.
            ClientFactory.getService().getData(new AsyncCallback<MyResult>() {
                @Override
                public void onSuccess(MyResult result) {
                    // Pass data to view for rendering (when appropriate.)
                    view.render(result);
                }
    
                @Override
                public void onFailure(Throwable caught) {
                    // Handle RPC error.
                }
            });
        }
    }
    

    示例代码段:ViewImpl

    public class MyViewImpl implements MyView {
        private Panel main;
        private AsyncChart<LineChart> asyncLineChart;
    
        public MyViewImpl() {
            this.main = new FlowPanel();
    
            // Runnable wrapper (see next snippet.)
            this.asyncLineChart = new AsyncChart<LineChart>() {
                @Override
                public LineChart onAttach(DataTable data, Options options) {
                    // Instantiate and attach new chart.
                    LineChart lineChart = new LineChart(data, options);
                    main.add(lineChart);
    
                    return lineChart;
                }
            };
    
            // Load Google Visualization asynchronously.
            VisualizationUtils.loadVisualizationApi(this.asyncLineChart, LineChart.PACKAGE);
        }
    
        @Override
        public void render(final MyResult result) { // Invoked from Presenter (see above snippet.)
            // Schedule rendering to be invoked ASAP (but not before Visualization library is loaded.)
            this.asyncLineChart.enqueueWriter(new AsyncChartWriter() {
                @Override
                public void onWrite(DataTable data, Options options) {
                    // Populate data from RPC result.
                    data.addColumn(ColumnType.DATE);
                    data.addRow();
                    data.setValue(0, 0, result.getDate());
                    // ... Etc.
    
                    // Set options.
                    options.setWidth(500);
                    // ... Etc.
                }
            });
        }
    }
    

    现在为了肉!

    示例代码段:AsyncChart 准备复制。

    import com.google.gwt.visualization.client.DataTable;
    import com.google.gwt.visualization.client.VisualizationUtils;
    import com.google.gwt.visualization.client.visualizations.corechart.CoreChart;
    import com.google.gwt.visualization.client.visualizations.corechart.Options;
    
    
    /**
     * Wrapping {@link Runnable} to use with the load methods of  {@link VisualizationUtils} 
     * allowing a chart to be populated asynchronously (e.g., via RPC.)
     * 
     *  This wrapper handles the process of deferred attachment of the chart via the 
     *  {@link #onAttach(DataTable, Options)} method.
     *  
     *  Chart data is set / updated by means of a {@link AsyncChartWriter} mutating the 
     *  passed {@link DataTable} and {@link Options} instances.
     *  
     *  {@link AsyncChartWriter} can be reassigned (via {@link #enqueueWriter(AsyncChartWriter)}) 
     *  in order to redraw the chart.
     * 
     * @param <T>   The concrete chart type.
     */
    public abstract class AsyncChart<T extends CoreChart> implements Runnable {
        public interface AsyncChartWriter {
            void onWrite(DataTable data, Options options);
        }
    
        private enum State {
            NEW,
            LOADED,
            ATTACHED
        }
    
        private State state;
        private T chart;
        private AsyncChartWriter enqueuedWriter;
    
        public AsyncChart() {
            this.state = State.NEW;
        }
    
        public abstract T onAttach(DataTable data, Options options);
    
        /**
         * Enqueues a writer to populate or manipulate the chart. This will happen immediately 
         * if the visualization API is already loaded and will otherwise be deferred.
         * 
         * @param writer    The writer to enqueue.
         */
        public void enqueueWriter(AsyncChartWriter writer) {
            this.enqueuedWriter = writer;
            processWriter();
        }
    
        /* (non-Javadoc)
         * @see java.lang.Runnable#run()
         * 
         * Invoked when the visualization API has loaded.
         */
        @Override
        public void run() {
            this.state = State.LOADED;
            if (this.enqueuedWriter != null) {
                processWriter();
            }
        }
    
        private void processWriter() {
            if (this.state == State.LOADED || this.state == State.ATTACHED) {
                DataTable data = DataTable.create();
                Options options = CoreChart.createOptions();
                this.enqueuedWriter.onWrite(data, options);
                if (this.state == State.LOADED) {
                    this.chart = onAttach(data, options); // Instantiate and attach.
                    this.state = State.ATTACHED;
                } else {
                    this.chart.draw(data, options); // Redraw already attached chart.
                }
                this.enqueuedWriter = null;
            }
            // If state = NEW do nothing until run() is invoked (and state has changed to LOADED.) 
        }
    }
    

    享受并行负载。