Repository: census-instrumentation/opencensus-go
Pull request #432 (2018-02-09 03:25:56)

DO NOT SQUASH merged #370 & #349


Patch:
diff --git a/README.md b/README.md
index 786a50d..00ca76d 100644
--- a/README.md
+++ b/README.md
@@ -113,7 +113,7 @@ Create and load measures with units:
 
 [embedmd]:# (stats.go measure)
 ```go
-videoSize, err := stats.NewMeasureInt64("my.org/video_size", "processed video size", "MB")
+videoSize, err := stats.Int64("my.org/video_size", "processed video size", "MB")
 if err != nil {
 	log.Fatal(err)
 }
@@ -129,18 +129,6 @@ if m == nil {
 }
 ```
 
-Delete measure (this can be useful when replacing a measure by
-another measure with the same name):
-
-[embedmd]:# (stats.go deleteMeasure)
-```go
-if err := stats.DeleteMeasure(m); err != nil {
-	log.Fatal(err)
-}
-```
-However, it is an error to delete a Measure that's used by at least one View. The
-View using the Measure has to be unregistered first.
-
 ### Creating an aggregation
 
 Currently 4 types of aggregations are supported. The CountAggregation is used to count
@@ -151,19 +139,10 @@ sample values.
 
 [embedmd]:# (stats.go aggs)
 ```go
-distAgg := stats.DistributionAggregation([]float64{0, 1 << 32, 2 << 32, 3 << 32})
-countAgg := stats.CountAggregation{}
-sumAgg := stats.SumAggregation{}
-meanAgg := stats.MeanAggregation{}
-```
-
-### Create an aggregation window
-
-Use Cumulative to continuously aggregate the recorded data.
-
-[embedmd]:# (stats.go windows)
-```go
-cum := stats.Cumulative{}
+distAgg := view.DistributionAggregation([]float64{0, 1 << 32, 2 << 32, 3 << 32})
+countAgg := view.CountAggregation{}
+sumAgg := view.SumAggregation{}
+meanAgg := view.MeanAggregation{}
 ```
 
 ### Creating, registering and unregistering a view
@@ -172,18 +151,17 @@ Create and register a view:
 
 [embedmd]:# (stats.go view)
 ```go
-view, err := stats.NewView(
+v, err := view.New(
 	"my.org/video_size_distribution",
 	"distribution of processed video size over time",
 	nil,
 	videoSize,
 	distAgg,
-	cum,
 )
 if err != nil {
 	log.Fatalf("cannot create view: %v", err)
 }
-if err := stats.RegisterView(view); err != nil {
+if err := view.Register(v); err != nil {
 	log.Fatal(err)
 }
 ```
@@ -192,7 +170,7 @@ Find view by name:
 
 [embedmd]:# (stats.go findView)
 ```go
-v := stats.FindView("my.org/video_size_distribution")
+v = view.Find("my.org/video_size_distribution")
 if v == nil {
 	log.Fatalln("view not found")
 }
@@ -202,7 +180,7 @@ Unregister view:
 
 [embedmd]:# (stats.go unregisterView)
 ```go
-if err = stats.UnregisterView(v); err != nil {
+if err = view.Unregister(v); err != nil {
 	log.Fatal(err)
 }
 ```
@@ -214,7 +192,7 @@ a duration less than a certain minimum (maybe 1s) should have no effect.
 
 [embedmd]:# (stats.go reportingPeriod)
 ```go
-stats.SetReportingPeriod(5 * time.Second)
+view.SetReportingPeriod(5 * time.Second)
 ```
 
 ### Recording measurements
@@ -234,7 +212,7 @@ Users need to subscribe to a view in order to retrieve collected data.
 
 [embedmd]:# (stats.go subscribe)
 ```go
-if err := view.Subscribe(); err != nil {
+if err := v.Subscribe(); err != nil {
 	log.Fatal(err)
 }
 ```
@@ -245,7 +223,7 @@ Subscribed views' data will be exported via the registered exporters.
 ```go
 // Register an exporter to be able to retrieve
 // the data from the subscribed views.
-stats.RegisterExporter(&exporter{})
+view.RegisterExporter(&exporter{})
 ```
 
 An example logger exporter is below:
@@ -255,7 +233,7 @@ An example logger exporter is below:
 
 type exporter struct{}
 
-func (e *exporter) ExportView(vd *stats.ViewData) {
+func (e *exporter) ExportView(vd *view.Data) {
 	log.Println(vd)
 }
 
diff --git a/examples/grpc/exporter/exporter.go b/examples/grpc/exporter/exporter.go
index c89391c..eb1ee4f 100644
--- a/examples/grpc/exporter/exporter.go
+++ b/examples/grpc/exporter/exporter.go
@@ -17,7 +17,7 @@ package exporter
 import (
 	"log"
 
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/trace"
 )
 
@@ -26,7 +26,7 @@ import (
 type Exporter struct{}
 
 // ExportView logs the view data.
-func (e *Exporter) ExportView(vd *stats.ViewData) {
+func (e *Exporter) ExportView(vd *view.Data) {
 	log.Println(vd)
 }
 
diff --git a/examples/grpc/helloworld_client/main.go b/examples/grpc/helloworld_client/main.go
index ab14f97..dc1ecde 100644
--- a/examples/grpc/helloworld_client/main.go
+++ b/examples/grpc/helloworld_client/main.go
@@ -24,7 +24,7 @@ import (
 	pb "go.opencensus.io/examples/grpc/proto"
 	ocgrpc "go.opencensus.io/plugin/grpc"
 	"go.opencensus.io/plugin/grpc/grpcstats"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/zpages"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
@@ -41,7 +41,7 @@ func main() {
 
 	// Register stats and trace exporters to export
 	// the collected data.
-	stats.RegisterExporter(&exporter.Exporter{})
+	view.RegisterExporter(&exporter.Exporter{})
 
 	// Subscribe to collect client request count.
 	if err := grpcstats.RPCClientRequestCountView.Subscribe(); err != nil {
@@ -62,7 +62,7 @@ func main() {
 	if len(os.Args) > 1 {
 		name = os.Args[1]
 	}
-	stats.SetReportingPeriod(time.Second)
+	view.SetReportingPeriod(time.Second)
 	for {
 		r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
 		if err != nil {
diff --git a/examples/grpc/helloworld_server/main.go b/examples/grpc/helloworld_server/main.go
index 51b98d0..2ad2f46 100644
--- a/examples/grpc/helloworld_server/main.go
+++ b/examples/grpc/helloworld_server/main.go
@@ -25,7 +25,7 @@ import (
 	pb "go.opencensus.io/examples/grpc/proto"
 	ocgrpc "go.opencensus.io/plugin/grpc"
 	"go.opencensus.io/plugin/grpc/grpcstats"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/zpages"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
@@ -47,7 +47,7 @@ func main() {
 	go func() { log.Fatal(http.ListenAndServe(":8081", nil)) }()
 	// Register stats and trace exporters to export
 	// the collected data.
-	stats.RegisterExporter(&exporter.Exporter{})
+	view.RegisterExporter(&exporter.Exporter{})
 
 	// Subscribe to collect server request count.
 	if err := grpcstats.RPCServerRequestCountView.Subscribe(); err != nil {
diff --git a/examples/stats/helloworld/main.go b/examples/stats/helloworld/main.go
index 2c64bc6..4e48fc5 100644
--- a/examples/stats/helloworld/main.go
+++ b/examples/stats/helloworld/main.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // Command helloworld is an example program that collects data for
-// video size over a time window.
+// video size.
 package main
 
 import (
@@ -23,6 +23,7 @@ import (
 	"time"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 func main() {
@@ -30,35 +31,34 @@ func main() {
 
 	// Register an exporter to be able to retrieve
 	// the data from the subscribed views.
-	stats.RegisterExporter(&exporter{})
+	view.RegisterExporter(&exporter{})
 
 	// Create measures. The program will record measures for the size of
 	// processed videos and the nubmer of videos marked as spam.
-	videoSize, err := stats.NewMeasureInt64("my.org/measure/video_size", "size of processed videos", "MBy")
+	videoSize, err := stats.Int64("my.org/measure/video_size", "size of processed videos", "MBy")
 	if err != nil {
 		log.Fatalf("Video size measure not created: %v", err)
 	}
 
 	// Create view to see the processed video size
 	// distribution over 10 seconds.
-	view, err := stats.NewView(
+	v, err := view.New(
 		"my.org/views/video_size",
 		"processed video size over time",
 		nil,
 		videoSize,
-		stats.DistributionAggregation([]float64{0, 1 << 16, 1 << 32}),
-		stats.Cumulative{},
+		view.DistributionAggregation([]float64{0, 1 << 16, 1 << 32}),
 	)
 	if err != nil {
 		log.Fatalf("Cannot create view: %v", err)
 	}
 
 	// Set reporting period to report data at every second.
-	stats.SetReportingPeriod(1 * time.Second)
+	view.SetReportingPeriod(1 * time.Second)
 
 	// Subscribe will allow view data to be exported.
 	// Once no longer need, you can unsubscribe from the view.
-	if err := view.Subscribe(); err != nil {
+	if err := v.Subscribe(); err != nil {
 		log.Fatalf("Cannot subscribe to the view: %v", err)
 	}
 
@@ -73,6 +73,6 @@ func main() {
 
 type exporter struct{}
 
-func (e *exporter) ExportView(vd *stats.ViewData) {
+func (e *exporter) ExportView(vd *view.Data) {
 	log.Println(vd)
 }
diff --git a/examples/stats/prometheus/main.go b/examples/stats/prometheus/main.go
index 890040b..11f616c 100644
--- a/examples/stats/prometheus/main.go
+++ b/examples/stats/prometheus/main.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // Command prometheus is an example program that collects data for
-// video size over a time window. Collected data is exported to Prometheus.
+// video size. Collected data is exported to Prometheus.
 package main
 
 import (
@@ -25,6 +25,7 @@ import (
 
 	"go.opencensus.io/exporter/prometheus"
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 func main() {
@@ -34,23 +35,22 @@ func main() {
 	if err != nil {
 		log.Fatal(err)
 	}
-	stats.RegisterExporter(exporter)
+	view.RegisterExporter(exporter)
 
 	// Create measures. The program will record measures for the size of
 	// processed videos and the number of videos marked as spam.
-	videoCount, err := stats.NewMeasureInt64("my.org/measures/video_count", "number of processed videos", "")
+	videoCount, err := stats.Int64("my.org/measures/video_count", "number of processed videos", "")
 	if err != nil {
 		log.Fatalf("Video count measure not created: %v", err)
 	}
 
 	// 1. Create view to see the number of processed videos cumulatively.
-	viewCount, err := stats.NewView(
+	viewCount, err := view.New(
 		"video_count",
 		"number of videos processed over time",
 		nil,
 		videoCount,
-		stats.CountAggregation{},
-		stats.Cumulative{},
+		view.CountAggregation{},
 	)
 	if err != nil {
 		log.Fatalf("Cannot create view: %v", err)
@@ -64,19 +64,18 @@ func main() {
 
 	// Create measures. The program will record measures for the size of
 	// processed videos and the number of videos marked as spam.
-	videoSize, err := stats.NewMeasureInt64("my.org/measures/video_size_cum", "size of processed video", "MBy")
+	videoSize, err := stats.Int64("my.org/measures/video_size_cum", "size of processed video", "MBy")
 	if err != nil {
 		log.Fatalf("Video size measure not created: %v", err)
 	}
 
 	// 2. Create view to see the amount of video processed
-	viewSize, err := stats.NewView(
+	viewSize, err := view.New(
 		"video_cum",
 		"processed video size over time",
 		nil,
 		videoSize,
-		stats.DistributionAggregation([]float64{0, 1 << 16, 1 << 32}),
-		stats.Cumulative{},
+		view.DistributionAggregation([]float64{0, 1 << 16, 1 << 32}),
 	)
 	if err != nil {
 		log.Fatalf("Cannot create view: %v", err)
@@ -89,7 +88,7 @@ func main() {
 	}
 
 	// Set reporting period to report data at every second.
-	stats.SetReportingPeriod(1 * time.Second)
+	view.SetReportingPeriod(1 * time.Second)
 
 	// Record some data points...
 	go func() {
diff --git a/examples/stats/stackdriver/main.go b/examples/stats/stackdriver/main.go
index 22608b2..9019683 100644
--- a/examples/stats/stackdriver/main.go
+++ b/examples/stats/stackdriver/main.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // Command stackdriver is an example program that collects data for
-// video size over a time window. Collected data is exported to
+// video size. Collected data is exported to
 // Stackdriver Monitoring.
 package main
 
@@ -25,6 +25,7 @@ import (
 
 	"go.opencensus.io/exporter/stackdriver"
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 func main() {
@@ -46,34 +47,33 @@ func main() {
 	if err != nil {
 		log.Fatal(err)
 	}
-	stats.RegisterExporter(exporter)
+	view.RegisterExporter(exporter)
 
 	// Create measures. The program will record measures for the size of
 	// processed videos and the nubmer of videos marked as spam.
-	videoSize, err := stats.NewMeasureInt64("my.org/measure/video_size", "size of processed videos", "MBy")
+	videoSize, err := stats.Int64("my.org/measure/video_size", "size of processed videos", "MBy")
 	if err != nil {
 		log.Fatalf("Video size measure not created: %v", err)
 	}
 
 	// Create view to see the processed video size cumulatively.
-	view, err := stats.NewView(
+	v, err := view.New(
 		"my.org/views/video_size_cum",
 		"processed video size over time",
 		nil,
 		videoSize,
-		stats.DistributionAggregation([]float64{0, 1 << 16, 1 << 32}),
-		stats.Cumulative{},
+		view.DistributionAggregation([]float64{0, 1 << 16, 1 << 32}),
 	)
 	if err != nil {
 		log.Fatalf("Cannot create view: %v", err)
 	}
 
 	// Set reporting period to report data at every second.
-	stats.SetReportingPeriod(1 * time.Second)
+	view.SetReportingPeriod(1 * time.Second)
 
 	// Subscribe will allow view data to be exported.
 	// Once no longer need, you can unsubscribe from the view.
-	if err := view.Subscribe(); err != nil {
+	if err := v.Subscribe(); err != nil {
 		log.Fatalf("Cannot subscribe to the view: %v", err)
 	}
 
diff --git a/exporter/prometheus/prometheus.go b/exporter/prometheus/prometheus.go
index c4cf12c..06afa6a 100644
--- a/exporter/prometheus/prometheus.go
+++ b/exporter/prometheus/prometheus.go
@@ -27,7 +27,7 @@ import (
 	"sync"
 
 	"go.opencensus.io/internal"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 
 	"github.com/prometheus/client_golang/prometheus"
@@ -86,22 +86,11 @@ func newExporter(o Options) (*Exporter, error) {
 }
 
 var _ http.Handler = (*Exporter)(nil)
-var _ stats.Exporter = (*Exporter)(nil)
-
-func allowedWindowType(v *stats.View) bool {
-	// TODO: (@rakyll, @odeke-em): Only the cumulative window will
-	// be exported in this version. Support others in the future.
-	// See Issue https://github.com/census-instrumentation/opencensus-go/issues/214
-	_, ok := v.Window().(stats.Cumulative)
-	return ok
-}
+var _ view.Exporter = (*Exporter)(nil)
 
-func (c *collector) registerViews(views ...*stats.View) {
+func (c *collector) registerViews(views ...*view.View) {
 	count := 0
 	for _, view := range views {
-		if !allowedWindowType(view) {
-			continue
-		}
 		sig := viewSignature(c.opts.Namespace, view)
 		c.registeredViewsMu.Lock()
 		_, ok := c.registeredViews[sig]
@@ -145,7 +134,7 @@ func (o *Options) onError(err error) {
 // DistributionData will be Histogram Metric, and MeanData
 // will be Summary Metric. Please note the Summary Metric from
 // MeanData does not have any quantiles.
-func (e *Exporter) ExportView(vd *stats.ViewData) {
+func (e *Exporter) ExportView(vd *view.Data) {
 	if len(vd.Rows) == 0 {
 		return
 	}
@@ -169,14 +158,14 @@ type collector struct {
 	// appended to on every Export invocation, from
 	// stats. These views are cleared out when
 	// Collect is invoked and the cycle is repeated.
-	viewData map[string]*stats.ViewData
+	viewData map[string]*view.Data
 
 	registeredViewsMu sync.Mutex
 	// registeredViews maps a view to a prometheus desc.
 	registeredViews map[string]*prometheus.Desc
 }
 
-func (c *collector) addViewData(vd *stats.ViewData) {
+func (c *collector) addViewData(vd *view.Data) {
 	c.registerViews(vd.View)
 	sig := viewSignature(c.opts.Namespace, vd.View)
 
@@ -207,10 +196,6 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
 	viewData := c.cloneViewData()
 
 	for _, vd := range viewData {
-		if !allowedWindowType(vd.View) {
-			continue
-		}
-
 		sig := viewSignature(c.opts.Namespace, vd.View)
 		c.registeredViewsMu.Lock()
 		desc := c.registeredViews[sig]
@@ -228,30 +213,30 @@ func (c *collector) Collect(ch chan<- prometheus.Metric) {
 
 }
 
-func (c *collector) toMetric(desc *prometheus.Desc, view *stats.View, row *stats.Row) (prometheus.Metric, error) {
-	switch agg := view.Aggregation().(type) {
-	case stats.CountAggregation:
-		data := row.Data.(*stats.CountData)
+func (c *collector) toMetric(desc *prometheus.Desc, v *view.View, row *view.Row) (prometheus.Metric, error) {
+	switch agg := v.Aggregation().(type) {
+	case view.CountAggregation:
+		data := row.Data.(*view.CountData)
 		return prometheus.NewConstMetric(desc, prometheus.CounterValue, float64(*data), tagValues(row.Tags)...)
 
-	case stats.DistributionAggregation:
-		data := row.Data.(*stats.DistributionData)
+	case view.DistributionAggregation:
+		data := row.Data.(*view.DistributionData)
 		points := make(map[float64]uint64)
 		for i, b := range agg {
 			points[b] = uint64(data.CountPerBucket[i])
 		}
 		return prometheus.NewConstHistogram(desc, uint64(data.Count), data.Sum(), points, tagValues(row.Tags)...)
 
-	case stats.MeanAggregation:
-		data := row.Data.(*stats.MeanData)
+	case view.MeanAggregation:
+		data := row.Data.(*view.MeanData)
 		return prometheus.NewConstSummary(desc, uint64(data.Count), data.Sum(), make(map[float64]float64), tagValues(row.Tags)...)
 
-	case stats.SumAggregation:
-		data := row.Data.(*stats.SumData)
+	case view.SumAggregation:
+		data := row.Data.(*view.SumData)
 		return prometheus.NewConstMetric(desc, prometheus.UntypedValue, float64(*data), tagValues(row.Tags)...)
 
 	default:
-		return nil, fmt.Errorf("aggregation %T is not yet supported", view.Aggregation())
+		return nil, fmt.Errorf("aggregation %T is not yet supported", v.Aggregation())
 	}
 }
 
@@ -275,7 +260,7 @@ func newCollector(opts Options, registrar *prometheus.Registry) *collector {
 		reg:             registrar,
 		opts:            opts,
 		registeredViews: make(map[string]*prometheus.Desc),
-		viewData:        make(map[string]*stats.ViewData),
+		viewData:        make(map[string]*view.Data),
 	}
 }
 
@@ -287,11 +272,11 @@ func tagValues(t []tag.Tag) []string {
 	return values
 }
 
-func viewName(namespace string, v *stats.View) string {
+func viewName(namespace string, v *view.View) string {
 	return namespace + "_" + internal.Sanitize(v.Name())
 }
 
-func viewSignature(namespace string, v *stats.View) string {
+func viewSignature(namespace string, v *view.View) string {
 	var buf bytes.Buffer
 	buf.WriteString(viewName(namespace, v))
 	for _, k := range v.TagKeys() {
@@ -300,11 +285,11 @@ func viewSignature(namespace string, v *stats.View) string {
 	return buf.String()
 }
 
-func (c *collector) cloneViewData() map[string]*stats.ViewData {
+func (c *collector) cloneViewData() map[string]*view.Data {
 	c.mu.Lock()
 	defer c.mu.Unlock()
 
-	viewDataCopy := make(map[string]*stats.ViewData)
+	viewDataCopy := make(map[string]*view.Data)
 	for sig, viewData := range c.viewData {
 		viewDataCopy[sig] = viewData
 	}
diff --git a/exporter/stackdriver/stackdriver.go b/exporter/stackdriver/stackdriver.go
index 9c4e017..05e87db 100644
--- a/exporter/stackdriver/stackdriver.go
+++ b/exporter/stackdriver/stackdriver.go
@@ -24,7 +24,7 @@ package stackdriver // import "go.opencensus.io/exporter/stackdriver"
 import (
 	"time"
 
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/trace"
 	"google.golang.org/api/option"
 	monitoredrespb "google.golang.org/genproto/googleapis/api/monitoredres"
@@ -92,7 +92,7 @@ func NewExporter(o Options) (*Exporter, error) {
 
 // ExportView exports to the Stackdriver Monitoring if view data
 // has one or more rows.
-func (e *Exporter) ExportView(vd *stats.ViewData) {
+func (e *Exporter) ExportView(vd *view.Data) {
 	e.statsExporter.ExportView(vd)
 }
 
diff --git a/exporter/stackdriver/stats.go b/exporter/stackdriver/stats.go
index 151aab3..a9cee1f 100644
--- a/exporter/stackdriver/stats.go
+++ b/exporter/stackdriver/stats.go
@@ -29,7 +29,7 @@ import (
 	"time"
 
 	"go.opencensus.io/internal"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 
 	monitoring "cloud.google.com/go/monitoring/apiv3"
@@ -102,8 +102,8 @@ func newStatsExporter(o Options) (*statsExporter, error) {
 		createdViews: make(map[string]*metricpb.MetricDescriptor),
 		taskValue:    getTaskValue(),
 	}
-	e.bundler = bundler.NewBundler((*stats.ViewData)(nil), func(bundle interface{}) {
-		vds := bundle.([]*stats.ViewData)
+	e.bundler = bundler.NewBundler((*view.Data)(nil), func(bundle interface{}) {
+		vds := bundle.([]*view.Data)
 		e.handleUpload(vds...)
 	})
 	e.bundler.DelayThreshold = e.o.BundleDelayThreshold
@@ -113,7 +113,7 @@ func newStatsExporter(o Options) (*statsExporter, error) {
 
 // ExportView exports to the Stackdriver Monitoring if view data
 // has one or more rows.
-func (e *statsExporter) ExportView(vd *stats.ViewData) {
+func (e *statsExporter) ExportView(vd *view.Data) {
 	if len(vd.Rows) == 0 {
 		return
 	}
@@ -141,8 +141,8 @@ func getTaskValue() string {
 }
 
 // handleUpload handles uploading a slice
-// of ViewData, as well as error handling.
-func (e *statsExporter) handleUpload(vds ...*stats.ViewData) {
+// of Data, as well as error handling.
+func (e *statsExporter) handleUpload(vds ...*view.Data) {
 	if err := e.upload(vds); err != nil {
 		e.onError(err)
 	}
@@ -164,15 +164,10 @@ func (e *statsExporter) onError(err error) {
 	log.Printf("Failed to export to Stackdriver Monitoring: %v", err)
 }
 
-func (e *statsExporter) upload(vds []*stats.ViewData) error {
+func (e *statsExporter) upload(vds []*view.Data) error {
 	ctx := context.Background()
 
 	for _, vd := range vds {
-		if _, ok := vd.View.Window().(stats.Cumulative); !ok {
-			// TODO(jbd): Only Cumulative window will be exported to Stackdriver in this version.
-			// Support others when custom delta metrics are supported.
-			continue
-		}
 		if err := e.createMeasure(ctx, vd); err != nil {
 			return err
 		}
@@ -186,7 +181,7 @@ func (e *statsExporter) upload(vds []*stats.ViewData) error {
 	return nil
 }
 
-func (e *statsExporter) makeReq(vds []*stats.ViewData, limit int) []*monitoringpb.CreateTimeSeriesRequest {
+func (e *statsExporter) makeReq(vds []*view.Data, limit int) []*monitoringpb.CreateTimeSeriesRequest {
 	var reqs []*monitoringpb.CreateTimeSeriesRequest
 	var timeSeries []*monitoringpb.TimeSeries
 
@@ -198,11 +193,6 @@ func (e *statsExporter) makeReq(vds []*stats.ViewData, limit int) []*monitoringp
 	}
 
 	for _, vd := range vds {
-		if _, ok := vd.View.Window().(stats.Cumulative); !ok {
-			// TODO(jbd): Only Cumulative window will be exported to Stackdriver in this version.
-			// Support others when custom delta metrics are supported.
-			continue
-		}
 		for _, row := range vd.Rows {
 			ts := &monitoringpb.TimeSeries{
 				Metric: &metricpb.Metric{
@@ -233,20 +223,18 @@ func (e *statsExporter) makeReq(vds []*stats.ViewData, limit int) []*monitoringp
 
 // createMeasure creates a MetricDescriptor for the given view data in Stackdriver Monitoring.
 // An error will be returned if there is already a metric descriptor created with the same name
-// but it has a different aggregation, window or keys.
-func (e *statsExporter) createMeasure(ctx context.Context, vd *stats.ViewData) error {
+// but it has a different aggregation or keys.
+func (e *statsExporter) createMeasure(ctx context.Context, vd *view.Data) error {
 	e.createdViewsMu.Lock()
 	defer e.createdViewsMu.Unlock()
 
 	m := vd.View.Measure()
 	agg := vd.View.Aggregation()
-	window := vd.View.Window()
 	tagKeys := vd.View.TagKeys()
 	viewName := vd.View.Name()
 
 	if md, ok := e.createdViews[viewName]; ok {
-		// Check agg, window and keys.
-		return equalAggWindowTagKeys(md, agg, window, tagKeys)
+		return equalAggTagKeys(md, agg, tagKeys)
 	}
 
 	metricName := monitoring.MetricMetricDescriptorPath(e.o.ProjectID, namespacedViewName(viewName, true))
@@ -254,7 +242,7 @@ func (e *statsExporter) createMeasure(ctx context.Context, vd *stats.ViewData) e
 		Name: metricName,
 	})
 	if err == nil {
-		if err := equalAggWindowTagKeys(md, agg, window, tagKeys); err != nil {
+		if err := equalAggTagKeys(md, agg, tagKeys); err != nil {
 			return err
 		}
 		e.createdViews[viewName] = md
@@ -268,26 +256,19 @@ func (e *statsExporter) createMeasure(ctx context.Context, vd *stats.ViewData) e
 	var valueType metricpb.MetricDescriptor_ValueType
 
 	switch agg.(type) {
-	case stats.CountAggregation:
+	case view.CountAggregation:
 		valueType = metricpb.MetricDescriptor_INT64
-	case stats.SumAggregation:
+	case view.SumAggregation:
 		valueType = metricpb.MetricDescriptor_DOUBLE
-	case stats.MeanAggregation:
+	case view.MeanAggregation:
 		valueType = metricpb.MetricDescriptor_DISTRIBUTION
-	case stats.DistributionAggregation:
+	case view.DistributionAggregation:
 		valueType = metricpb.MetricDescriptor_DISTRIBUTION
 	default:
 		return fmt.Errorf("unsupported aggregation type: %T", agg)
 	}
 
-	switch window.(type) {
-	case stats.Cumulative:
-		metricKind = metricpb.MetricDescriptor_CUMULATIVE
-	case stats.Interval:
-		metricKind = metricpb.MetricDescriptor_DELTA
-	default:
-		return fmt.Errorf("unsupported window type: %T", window)
-	}
+	metricKind = metricpb.MetricDescriptor_CUMULATIVE
 
 	md, err = createMetricDescriptor(ctx, e.c, &monitoringpb.CreateMetricDescriptorRequest{
 		Name: monitoring.MetricProjectPath(e.o.ProjectID),
@@ -309,7 +290,7 @@ func (e *statsExporter) createMeasure(ctx context.Context, vd *stats.ViewData) e
 	return nil
 }
 
-func newPoint(v *stats.View, row *stats.Row, start, end time.Time) *monitoringpb.Point {
+func newPoint(v *view.View, row *view.Row, start, end time.Time) *monitoringpb.Point {
 	return &monitoringpb.Point{
 		Interval: &monitoringpb.TimeInterval{
 			StartTime: &timestamp.Timestamp{
@@ -325,17 +306,17 @@ func newPoint(v *stats.View, row *stats.Row, start, end time.Time) *monitoringpb
 	}
 }
 
-func newTypedValue(view *stats.View, r *stats.Row) *monitoringpb.TypedValue {
+func newTypedValue(vd *view.View, r *view.Row) *monitoringpb.TypedValue {
 	switch v := r.Data.(type) {
-	case *stats.CountData:
+	case *view.CountData:
 		return &monitoringpb.TypedValue{Value: &monitoringpb.TypedValue_Int64Value{
 			Int64Value: int64(*v),
 		}}
-	case *stats.SumData:
+	case *view.SumData:
 		return &monitoringpb.TypedValue{Value: &monitoringpb.TypedValue_DoubleValue{
 			DoubleValue: float64(*v),
 		}}
-	case *stats.MeanData:
+	case *view.MeanData:
 		return &monitoringpb.TypedValue{Value: &monitoringpb.TypedValue_DistributionValue{
 			DistributionValue: &distributionpb.Distribution{
 				Count: int64(v.Count),
@@ -351,8 +332,8 @@ func newTypedValue(view *stats.View, r *stats.Row) *monitoringpb.TypedValue {
 				BucketCounts: []int64{0, int64(v.Count)},
 			},
 		}}
-	case *stats.DistributionData:
-		bounds := view.Aggregation().(stats.DistributionAggregation)
+	case *view.DistributionData:
+		bounds := vd.Aggregation().(view.DistributionAggregation)
 		return &monitoringpb.TypedValue{Value: &monitoringpb.TypedValue_DistributionValue{
 			DistributionValue: &distributionpb.Distribution{
 				Count: v.Count,
@@ -411,16 +392,7 @@ func newLabelDescriptors(keys []tag.Key) []*labelpb.LabelDescriptor {
 	return labelDescriptors
 }
 
-func equalAggWindowTagKeys(md *metricpb.MetricDescriptor, agg stats.Aggregation, window stats.Window, keys []tag.Key) error {
-	var w stats.Window
-
-	switch md.MetricKind {
-	case metricpb.MetricDescriptor_DELTA:
-		w = stats.Interval{}
-	case metricpb.MetricDescriptor_CUMULATIVE:
-		w = stats.Cumulative{}
-	}
-
+func equalAggTagKeys(md *metricpb.MetricDescriptor, agg view.Aggregation, keys []tag.Key) error {
 	aggType := reflect.TypeOf(agg)
 	if aggType.Kind() == reflect.Ptr { // if pointer, find out the concrete type
 		aggType = reflect.ValueOf(agg).Elem().Type()
@@ -428,25 +400,17 @@ func equalAggWindowTagKeys(md *metricpb.MetricDescriptor, agg stats.Aggregation,
 	var aggTypeMatch bool
 	switch md.ValueType {
 	case metricpb.MetricDescriptor_INT64:
-		aggTypeMatch = aggType == reflect.TypeOf(stats.CountAggregation{})
+		aggTypeMatch = aggType == reflect.TypeOf(view.CountAggregation{})
 	case metricpb.MetricDescriptor_DOUBLE:
-		aggTypeMatch = aggType == reflect.TypeOf(stats.SumAggregation{})
+		aggTypeMatch = aggType == reflect.TypeOf(view.SumAggregation{})
 	case metricpb.MetricDescriptor_DISTRIBUTION:
-		aggTypeMatch = aggType == reflect.TypeOf(stats.MeanAggregation{}) || aggType == reflect.TypeOf(stats.DistributionAggregation{})
+		aggTypeMatch = aggType == reflect.TypeOf(view.MeanAggregation{}) || aggType == reflect.TypeOf(view.DistributionAggregation{})
 	}
 
 	if !aggTypeMatch {
 		return fmt.Errorf("stackdriver metric descriptor was not created with aggregation type %T", aggType)
 	}
 
-	winType := reflect.TypeOf(window)
-	if winType.Kind() == reflect.Ptr { // if pointer, find out the concrete type
-		winType = reflect.ValueOf(window).Elem().Type()
-	}
-	if winType != reflect.TypeOf(w) {
-		return fmt.Errorf("stackdriver metric descriptor was not created with window type %T", w)
-	}
-
 	if len(md.Labels) != len(keys)+1 {
 		return errors.New("stackdriver metric descriptor was not created with the view labels")
 	}
diff --git a/internal/readme/source.md b/internal/readme/source.md
index 2feb321..4ac0f8f 100644
--- a/internal/readme/source.md
+++ b/internal/readme/source.md
@@ -80,13 +80,6 @@ Retrieve measure by name:
 
 [embedmd]:# (stats.go findMeasure)
 
-Delete measure (this can be useful when replacing a measure by
-another measure with the same name):
-
-[embedmd]:# (stats.go deleteMeasure)
-However, it is an error to delete a Measure that's used by at least one View. The
-View using the Measure has to be unregistered first.
-
 ### Creating an aggregation
 
 Currently 4 types of aggregations are supported. The CountAggregation is used to count
@@ -97,12 +90,6 @@ sample values.
 
 [embedmd]:# (stats.go aggs)
 
-### Create an aggregation window
-
-Use Cumulative to continuously aggregate the recorded data.
-
-[embedmd]:# (stats.go windows)
-
 ### Creating, registering and unregistering a view
 
 Create and register a view:
diff --git a/internal/readme/stats.go b/internal/readme/stats.go
index bbce1cb..4462873 100644
--- a/internal/readme/stats.go
+++ b/internal/readme/stats.go
@@ -21,6 +21,7 @@ import (
 	"time"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 // README.md is generated with the examples here by using embedmd.
@@ -30,7 +31,7 @@ func statsExamples() {
 	ctx := context.Background()
 
 	// START measure
-	videoSize, err := stats.NewMeasureInt64("my.org/video_size", "processed video size", "MB")
+	videoSize, err := stats.Int64("my.org/video_size", "processed video size", "MB")
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -46,44 +47,33 @@ func statsExamples() {
 
 	_ = m
 
-	// START deleteMeasure
-	if err := stats.DeleteMeasure(m); err != nil {
-		log.Fatal(err)
-	}
-	// END deleteMeasure
-
 	// START aggs
-	distAgg := stats.DistributionAggregation([]float64{0, 1 << 32, 2 << 32, 3 << 32})
-	countAgg := stats.CountAggregation{}
-	sumAgg := stats.SumAggregation{}
-	meanAgg := stats.MeanAggregation{}
+	distAgg := view.DistributionAggregation([]float64{0, 1 << 32, 2 << 32, 3 << 32})
+	countAgg := view.CountAggregation{}
+	sumAgg := view.SumAggregation{}
+	meanAgg := view.MeanAggregation{}
 	// END aggs
 
 	_, _, _, _ = distAgg, countAgg, sumAgg, meanAgg
 
-	// START windows
-	cum := stats.Cumulative{}
-	// END windows
-
 	// START view
-	view, err := stats.NewView(
+	v, err := view.New(
 		"my.org/video_size_distribution",
 		"distribution of processed video size over time",
 		nil,
 		videoSize,
 		distAgg,
-		cum,
 	)
 	if err != nil {
 		log.Fatalf("cannot create view: %v", err)
 	}
-	if err := stats.RegisterView(view); err != nil {
+	if err := view.Register(v); err != nil {
 		log.Fatal(err)
 	}
 	// END view
 
 	// START findView
-	v := stats.FindView("my.org/video_size_distribution")
+	v = view.Find("my.org/video_size_distribution")
 	if v == nil {
 		log.Fatalln("view not found")
 	}
@@ -92,13 +82,13 @@ func statsExamples() {
 	_ = v
 
 	// START unregisterView
-	if err = stats.UnregisterView(v); err != nil {
+	if err = view.Unregister(v); err != nil {
 		log.Fatal(err)
 	}
 	// END unregisterView
 
 	// START reportingPeriod
-	stats.SetReportingPeriod(5 * time.Second)
+	view.SetReportingPeriod(5 * time.Second)
 	// END reportingPeriod
 
 	// START record
@@ -106,7 +96,7 @@ func statsExamples() {
 	// END record
 
 	// START subscribe
-	if err := view.Subscribe(); err != nil {
+	if err := v.Subscribe(); err != nil {
 		log.Fatal(err)
 	}
 	// END subscribe
@@ -114,7 +104,7 @@ func statsExamples() {
 	// START registerExporter
 	// Register an exporter to be able to retrieve
 	// the data from the subscribed views.
-	stats.RegisterExporter(&exporter{})
+	view.RegisterExporter(&exporter{})
 	// END registerExporter
 }
 
@@ -122,7 +112,7 @@ func statsExamples() {
 
 type exporter struct{}
 
-func (e *exporter) ExportView(vd *stats.ViewData) {
+func (e *exporter) ExportView(vd *view.Data) {
 	log.Println(vd)
 }
 
diff --git a/plugin/grpc/grpcstats/client_handler.go b/plugin/grpc/grpcstats/client_handler.go
index ad35029..ed02a4e 100644
--- a/plugin/grpc/grpcstats/client_handler.go
+++ b/plugin/grpc/grpcstats/client_handler.go
@@ -19,7 +19,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	istats "go.opencensus.io/stats"
+	ocstats "go.opencensus.io/stats"
 	"go.opencensus.io/tag"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc/grpclog"
@@ -72,11 +72,11 @@ func (h *ClientStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
 	encoded := tag.Encode(ts)
 	ctx = stats.SetTags(ctx, encoded)
 	ctx, _ = tag.New(ctx,
-		tag.Upsert(keyMethod, methodName(info.FullMethodName)),
+		tag.Upsert(KeyMethod, methodName(info.FullMethodName)),
 	)
 	// TODO(acetechnologist): should we be recording this later? What is the
 	// point of updating d.reqLen & d.reqCount if we update now?
-	istats.Record(ctx, RPCClientStartedCount.M(1))
+	ocstats.Record(ctx, RPCClientStartedCount.M(1))
 
 	return context.WithValue(ctx, grpcClientRPCKey, d)
 }
@@ -106,7 +106,7 @@ func (h *ClientStatsHandler) handleRPCOutPayload(ctx context.Context, s *stats.O
 		return
 	}
 
-	istats.Record(ctx, RPCClientRequestBytes.M(int64(s.Length)))
+	ocstats.Record(ctx, RPCClientRequestBytes.M(int64(s.Length)))
 	atomic.AddInt64(&d.reqCount, 1)
 }
 
@@ -119,7 +119,7 @@ func (h *ClientStatsHandler) handleRPCInPayload(ctx context.Context, s *stats.In
 		return
 	}
 
-	istats.Record(ctx, RPCClientResponseBytes.M(int64(s.Length)))
+	ocstats.Record(ctx, RPCClientResponseBytes.M(int64(s.Length)))
 	atomic.AddInt64(&d.respCount, 1)
 }
 
@@ -136,7 +136,7 @@ func (h *ClientStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
 	reqCount := atomic.LoadInt64(&d.reqCount)
 	respCount := atomic.LoadInt64(&d.respCount)
 
-	m := []istats.Measurement{
+	m := []ocstats.Measurement{
 		RPCClientRequestCount.M(reqCount),
 		RPCClientResponseCount.M(respCount),
 		RPCClientFinishedCount.M(1),
@@ -147,11 +147,11 @@ func (h *ClientStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
 		s, ok := status.FromError(s.Error)
 		if ok {
 			ctx, _ = tag.New(ctx,
-				tag.Upsert(keyStatus, s.Code().String()),
+				tag.Upsert(KeyStatus, s.Code().String()),
 			)
 		}
 		m = append(m, RPCClientErrorCount.M(1))
 	}
 
-	istats.Record(ctx, m...)
+	ocstats.Record(ctx, m...)
 }
diff --git a/plugin/grpc/grpcstats/client_metrics.go b/plugin/grpc/grpcstats/client_metrics.go
index 879bfaf..8d858e1 100644
--- a/plugin/grpc/grpcstats/client_metrics.go
+++ b/plugin/grpc/grpcstats/client_metrics.go
@@ -19,6 +19,7 @@ import (
 	"log"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 )
 
@@ -26,40 +27,22 @@ import (
 // Client connection needs to use a ClientStatsHandler in order to enable collection.
 var (
 	// Available client measures
-	RPCClientErrorCount       *stats.MeasureInt64
-	RPCClientRoundTripLatency *stats.MeasureFloat64
-	RPCClientRequestBytes     *stats.MeasureInt64
-	RPCClientResponseBytes    *stats.MeasureInt64
-	RPCClientStartedCount     *stats.MeasureInt64
-	RPCClientFinishedCount    *stats.MeasureInt64
-	RPCClientRequestCount     *stats.MeasureInt64
-	RPCClientResponseCount    *stats.MeasureInt64
+	RPCClientErrorCount       *stats.Int64Measure
+	RPCClientRoundTripLatency *stats.Float64Measure
+	RPCClientRequestBytes     *stats.Int64Measure
+	RPCClientResponseBytes    *stats.Int64Measure
+	RPCClientStartedCount     *stats.Int64Measure
+	RPCClientFinishedCount    *stats.Int64Measure
+	RPCClientRequestCount     *stats.Int64Measure
+	RPCClientResponseCount    *stats.Int64Measure
 
 	// Predefined client views
-	RPCClientErrorCountView       *stats.View
-	RPCClientRoundTripLatencyView *stats.View
-	RPCClientRequestBytesView     *stats.View
-	RPCClientResponseBytesView    *stats.View
-	RPCClientRequestCountView     *stats.View
-	RPCClientResponseCountView    *stats.View
-
-	RPCClientRoundTripLatencyMinuteView *stats.View
-	RPCClientRequestBytesMinuteView     *stats.View
-	RPCClientResponseBytesMinuteView    *stats.View
-	RPCClientErrorCountMinuteView       *stats.View
-	RPCClientStartedCountMinuteView     *stats.View
-	RPCClientFinishedCountMinuteView    *stats.View
-	RPCClientRequestCountMinuteView     *stats.View
-	RPCClientResponseCountMinuteView    *stats.View
-
-	RPCClientRoundTripLatencyHourView *stats.View
-	RPCClientRequestBytesHourView     *stats.View
-	RPCClientResponseBytesHourView    *stats.View
-	RPCClientErrorCountHourView       *stats.View
-	RPCClientStartedCountHourView     *stats.View
-	RPCClientFinishedCountHourView    *stats.View
-	RPCClientRequestCountHourView     *stats.View
-	RPCClientResponseCountHourView    *stats.View
+	RPCClientErrorCountView       *view.View
+	RPCClientRoundTripLatencyView *view.View
+	RPCClientRequestBytesView     *view.View
+	RPCClientResponseBytesView    *view.View
+	RPCClientRequestCountView     *view.View
+	RPCClientResponseCountView    *view.View
 )
 
 // TODO(acetechnologist): This is temporary and will need to be replaced by a
@@ -70,75 +53,69 @@ func defaultClientMeasures() {
 	var err error
 
 	// Creating client measures
-	if RPCClientErrorCount, err = stats.NewMeasureInt64("grpc.io/client/error_count", "RPC Errors", unitCount); err != nil {
+	if RPCClientErrorCount, err = stats.Int64("grpc.io/client/error_count", "RPC Errors", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/error_count: %v", err)
 	}
-	if RPCClientRoundTripLatency, err = stats.NewMeasureFloat64("grpc.io/client/roundtrip_latency", "RPC roundtrip latency in msecs", unitMillisecond); err != nil {
+	if RPCClientRoundTripLatency, err = stats.Float64("grpc.io/client/roundtrip_latency", "RPC roundtrip latency in msecs", unitMillisecond); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/roundtrip_latency: %v", err)
 	}
-	if RPCClientRequestBytes, err = stats.NewMeasureInt64("grpc.io/client/request_bytes", "Request bytes", unitByte); err != nil {
+	if RPCClientRequestBytes, err = stats.Int64("grpc.io/client/request_bytes", "Request bytes", unitByte); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/request_bytes: %v", err)
 	}
-	if RPCClientResponseBytes, err = stats.NewMeasureInt64("grpc.io/client/response_bytes", "Response bytes", unitByte); err != nil {
+	if RPCClientResponseBytes, err = stats.Int64("grpc.io/client/response_bytes", "Response bytes", unitByte); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/response_bytes: %v", err)
 	}
-	if RPCClientStartedCount, err = stats.NewMeasureInt64("grpc.io/client/started_count", "Number of client RPCs (streams) started", unitCount); err != nil {
+	if RPCClientStartedCount, err = stats.Int64("grpc.io/client/started_count", "Number of client RPCs (streams) started", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/started_count: %v", err)
 	}
-	if RPCClientFinishedCount, err = stats.NewMeasureInt64("grpc.io/client/finished_count", "Number of client RPCs (streams) finished", unitCount); err != nil {
+	if RPCClientFinishedCount, err = stats.Int64("grpc.io/client/finished_count", "Number of client RPCs (streams) finished", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/finished_count: %v", err)
 	}
-	if RPCClientRequestCount, err = stats.NewMeasureInt64("grpc.io/client/request_count", "Number of client RPC request messages", unitCount); err != nil {
+	if RPCClientRequestCount, err = stats.Int64("grpc.io/client/request_count", "Number of client RPC request messages", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/request_count: %v", err)
 	}
-	if RPCClientResponseCount, err = stats.NewMeasureInt64("grpc.io/client/response_count", "Number of client RPC response messages", unitCount); err != nil {
+	if RPCClientResponseCount, err = stats.Int64("grpc.io/client/response_count", "Number of client RPC response messages", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/client/response_count: %v", err)
 	}
 }
 
 func defaultClientViews() {
-	RPCClientErrorCountView, _ = stats.NewView(
+	RPCClientErrorCountView, _ = view.New(
 		"grpc.io/client/error_count/cumulative",
 		"RPC Errors",
-		[]tag.Key{keyStatus, keyMethod},
+		[]tag.Key{KeyStatus, KeyMethod},
 		RPCClientErrorCount,
-		aggMean,
-		windowCumulative)
-	RPCClientRoundTripLatencyView, _ = stats.NewView(
+		aggMean)
+	RPCClientRoundTripLatencyView, _ = view.New(
 		"grpc.io/client/roundtrip_latency/cumulative",
 		"Latency in msecs",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCClientRoundTripLatency,
-		aggDistMillis,
-		windowCumulative)
-	RPCClientRequestBytesView, _ = stats.NewView(
+		aggDistMillis)
+	RPCClientRequestBytesView, _ = view.New(
 		"grpc.io/client/request_bytes/cumulative",
 		"Request bytes",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCClientRequestBytes,
-		aggDistBytes,
-		windowCumulative)
-	RPCClientResponseBytesView, _ = stats.NewView(
+		aggDistBytes)
+	RPCClientResponseBytesView, _ = view.New(
 		"grpc.io/client/response_bytes/cumulative",
 		"Response bytes",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCClientResponseBytes,
-		aggDistBytes,
-		windowCumulative)
-	RPCClientRequestCountView, _ = stats.NewView(
+		aggDistBytes)
+	RPCClientRequestCountView, _ = view.New(
 		"grpc.io/client/request_count/cumulative",
 		"Count of request messages per client RPC",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCClientRequestCount,
-		aggDistCounts,
-		windowCumulative)
-	RPCClientResponseCountView, _ = stats.NewView(
+		aggDistCounts)
+	RPCClientResponseCountView, _ = view.New(
 		"grpc.io/client/response_count/cumulative",
 		"Count of response messages per client RPC",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCClientResponseCount,
-		aggDistCounts,
-		windowCumulative)
+		aggDistCounts)
 
 	clientViews = append(clientViews,
 		RPCClientErrorCountView,
@@ -149,142 +126,6 @@ func defaultClientViews() {
 		RPCClientResponseCountView,
 	)
 	// TODO(jbd): Add roundtrip_latency, uncompressed_request_bytes, uncompressed_response_bytes, request_count, response_count.
-
-	RPCClientRoundTripLatencyMinuteView, _ = stats.NewView(
-		"grpc.io/client/roundtrip_latency/minute",
-		"Minute stats for latency in msecs",
-		[]tag.Key{keyMethod},
-		RPCClientRoundTripLatency,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientRequestBytesMinuteView, _ = stats.NewView(
-		"grpc.io/client/request_bytes/minute",
-		"Minute stats for request size in bytes",
-		[]tag.Key{keyMethod},
-		RPCClientRequestBytes,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientResponseBytesMinuteView, _ = stats.NewView(
-		"grpc.io/client/response_bytes/minute",
-		"Minute stats for response size in bytes",
-		[]tag.Key{keyMethod},
-		RPCClientResponseBytes,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientErrorCountMinuteView, _ = stats.NewView(
-		"grpc.io/client/error_count/minute",
-		"Minute stats for rpc errors",
-		[]tag.Key{keyMethod},
-		RPCClientErrorCount,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientStartedCountMinuteView, _ = stats.NewView(
-		"grpc.io/client/started_count/minute",
-		"Minute stats on the number of client RPCs started",
-		[]tag.Key{keyMethod},
-		RPCClientStartedCount,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientFinishedCountMinuteView, _ = stats.NewView(
-		"grpc.io/client/finished_count/minute",
-		"Minute stats on the number of client RPCs finished",
-		[]tag.Key{keyMethod},
-		RPCClientFinishedCount,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientRequestCountMinuteView, _ = stats.NewView(
-		"grpc.io/client/request_count/minute",
-		"Minute stats on the count of request messages per client RPC",
-		[]tag.Key{keyMethod},
-		RPCClientRequestCount,
-		aggMean,
-		windowSlidingMinute)
-	RPCClientResponseCountMinuteView, _ = stats.NewView(
-		"grpc.io/client/response_count/minute",
-		"Minute stats on the count of response messages per client RPC",
-		[]tag.Key{keyMethod},
-		RPCClientResponseCount,
-		aggMean,
-		windowSlidingMinute)
-
-	clientViews = append(clientViews,
-		RPCClientRoundTripLatencyMinuteView,
-		RPCClientRequestBytesMinuteView,
-		RPCClientResponseBytesMinuteView,
-		RPCClientErrorCountMinuteView,
-		RPCClientStartedCountMinuteView,
-		RPCClientFinishedCountMinuteView,
-		RPCClientRequestCountMinuteView,
-		RPCClientResponseCountMinuteView,
-	)
-
-	RPCClientRoundTripLatencyHourView, _ = stats.NewView(
-		"grpc.io/client/roundtrip_latency/hour",
-		"Hour stats for latency in msecs",
-		[]tag.Key{keyMethod},
-		RPCClientRoundTripLatency,
-		aggMean,
-		windowSlidingHour)
-	RPCClientRequestBytesHourView, _ = stats.NewView(
-		"grpc.io/client/request_bytes/hour",
-		"Hour stats for request size in bytes",
-		[]tag.Key{keyMethod},
-		RPCClientRequestBytes,
-		aggMean,
-		windowSlidingHour)
-	RPCClientResponseBytesHourView, _ = stats.NewView(
-		"grpc.io/client/response_bytes/hour",
-		"Hour stats for response size in bytes",
-		[]tag.Key{keyMethod},
-		RPCClientResponseBytes,
-		aggMean,
-		windowSlidingHour)
-	RPCClientErrorCountHourView, _ = stats.NewView(
-		"grpc.io/client/error_count/hour",
-		"Hour stats for rpc errors",
-		[]tag.Key{keyMethod},
-		RPCClientErrorCount,
-		aggMean,
-		windowSlidingHour)
-	RPCClientStartedCountHourView, _ = stats.NewView(
-		"grpc.io/client/started_count/hour",
-		"Hour stats on the number of client RPCs started",
-		[]tag.Key{keyMethod},
-		RPCClientStartedCount,
-		aggMean,
-		windowSlidingHour)
-	RPCClientFinishedCountHourView, _ = stats.NewView(
-		"grpc.io/client/finished_count/hour",
-		"Hour stats on the number of client RPCs finished",
-		[]tag.Key{keyMethod},
-		RPCClientFinishedCount,
-		aggMean,
-		windowSlidingHour)
-	RPCClientRequestCountHourView, _ = stats.NewView(
-		"grpc.io/client/request_count/hour",
-		"Hour stats on the count of request messages per client RPC",
-		[]tag.Key{keyMethod},
-		RPCClientRequestCount,
-		aggMean,
-		windowSlidingHour)
-	RPCClientResponseCountHourView, _ = stats.NewView(
-		"grpc.io/client/response_count/hour",
-		"Hour stats on the count of response messages per client RPC",
-		[]tag.Key{keyMethod},
-		RPCClientResponseCount,
-		aggMean,
-		windowSlidingHour)
-
-	clientViews = append(clientViews,
-		RPCClientRoundTripLatencyHourView,
-		RPCClientRequestBytesHourView,
-		RPCClientResponseBytesHourView,
-		RPCClientErrorCountHourView,
-		RPCClientStartedCountHourView,
-		RPCClientFinishedCountHourView,
-		RPCClientRequestCountHourView,
-		RPCClientResponseCountHourView,
-	)
 }
 
 // initClient registers the default metrics (measures and views)
@@ -294,4 +135,4 @@ func initClient() {
 	defaultClientViews()
 }
 
-var clientViews []*stats.View
+var clientViews []*view.View
diff --git a/plugin/grpc/grpcstats/grpcstats.go b/plugin/grpc/grpcstats/grpcstats.go
index d49e991..3d52a04 100644
--- a/plugin/grpc/grpcstats/grpcstats.go
+++ b/plugin/grpc/grpcstats/grpcstats.go
@@ -21,7 +21,7 @@ import (
 	"strings"
 	"time"
 
-	istats "go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 )
 
@@ -56,26 +56,22 @@ var (
 	rpcMillisBucketBoundaries = []float64{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000}
 	rpcCountBucketBoundaries  = []float64{0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536}
 
-	aggCount      = istats.CountAggregation{}
-	aggMean       = istats.MeanAggregation{}
-	aggDistBytes  = istats.DistributionAggregation(rpcBytesBucketBoundaries)
-	aggDistMillis = istats.DistributionAggregation(rpcMillisBucketBoundaries)
-	aggDistCounts = istats.DistributionAggregation(rpcCountBucketBoundaries)
+	aggCount      = view.CountAggregation{}
+	aggMean       = view.MeanAggregation{}
+	aggDistBytes  = view.DistributionAggregation(rpcBytesBucketBoundaries)
+	aggDistMillis = view.DistributionAggregation(rpcMillisBucketBoundaries)
+	aggDistCounts = view.DistributionAggregation(rpcCountBucketBoundaries)
 
-	windowCumulative    = istats.Cumulative{}
-	windowSlidingHour   = istats.Interval{Duration: 1 * time.Hour, SubIntervals: 6}
-	windowSlidingMinute = istats.Interval{Duration: 1 * time.Minute, SubIntervals: 6}
-
-	keyMethod tag.Key
-	keyStatus tag.Key
+	KeyMethod tag.Key
+	KeyStatus tag.Key
 )
 
 func init() {
 	var err error
-	if keyMethod, err = tag.NewKey("method"); err != nil {
+	if KeyMethod, err = tag.NewKey("method"); err != nil {
 		log.Fatalf("Cannot create method key: %v", err)
 	}
-	if keyStatus, err = tag.NewKey("canonical_status"); err != nil {
+	if KeyStatus, err = tag.NewKey("canonical_status"); err != nil {
 		log.Fatalf("Cannot create canonical_status key: %v", err)
 	}
 	initServer()
diff --git a/plugin/grpc/grpcstats/server_handler.go b/plugin/grpc/grpcstats/server_handler.go
index 942dc01..8573288 100644
--- a/plugin/grpc/grpcstats/server_handler.go
+++ b/plugin/grpc/grpcstats/server_handler.go
@@ -22,7 +22,7 @@ import (
 
 	"golang.org/x/net/context"
 
-	istats "go.opencensus.io/stats"
+	ocstats "go.opencensus.io/stats"
 	"go.opencensus.io/tag"
 	"google.golang.org/grpc/grpclog"
 	"google.golang.org/grpc/stats"
@@ -67,7 +67,7 @@ func (h *ServerStatsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo)
 	}
 	d := &rpcData{startTime: startTime}
 	ctx, _ = h.createTags(ctx, info.FullMethodName)
-	istats.Record(ctx, RPCServerStartedCount.M(1))
+	ocstats.Record(ctx, RPCServerStartedCount.M(1))
 	return context.WithValue(ctx, grpcServerRPCKey, d)
 }
 
@@ -97,7 +97,7 @@ func (h *ServerStatsHandler) handleRPCInPayload(ctx context.Context, s *stats.In
 		return
 	}
 
-	istats.Record(ctx, RPCServerRequestBytes.M(int64(s.Length)))
+	ocstats.Record(ctx, RPCServerRequestBytes.M(int64(s.Length)))
 	atomic.AddInt64(&d.reqCount, 1)
 }
 
@@ -110,7 +110,7 @@ func (h *ServerStatsHandler) handleRPCOutPayload(ctx context.Context, s *stats.O
 		return
 	}
 
-	istats.Record(ctx, RPCServerResponseBytes.M(int64(s.Length)))
+	ocstats.Record(ctx, RPCServerResponseBytes.M(int64(s.Length)))
 	atomic.AddInt64(&d.respCount, 1)
 }
 
@@ -127,7 +127,7 @@ func (h *ServerStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
 	reqCount := atomic.LoadInt64(&d.reqCount)
 	respCount := atomic.LoadInt64(&d.respCount)
 
-	m := []istats.Measurement{
+	m := []ocstats.Measurement{
 		RPCServerRequestCount.M(reqCount),
 		RPCServerResponseCount.M(respCount),
 		RPCServerFinishedCount.M(1),
@@ -138,20 +138,20 @@ func (h *ServerStatsHandler) handleRPCEnd(ctx context.Context, s *stats.End) {
 		s, ok := status.FromError(s.Error)
 		if ok {
 			ctx, _ = tag.New(ctx,
-				tag.Upsert(keyStatus, s.Code().String()),
+				tag.Upsert(KeyStatus, s.Code().String()),
 			)
 		}
 		m = append(m, RPCServerErrorCount.M(1))
 	}
 
-	istats.Record(ctx, m...)
+	ocstats.Record(ctx, m...)
 }
 
 // createTags creates a new tag map containing the tags extracted from the
 // gRPC metadata.
 func (h *ServerStatsHandler) createTags(ctx context.Context, fullinfo string) (context.Context, error) {
 	mods := []tag.Mutator{
-		tag.Upsert(keyMethod, methodName(fullinfo)),
+		tag.Upsert(KeyMethod, methodName(fullinfo)),
 	}
 	if tagsBin := stats.Tags(ctx); tagsBin != nil {
 		old, err := tag.Decode([]byte(tagsBin))
diff --git a/plugin/grpc/grpcstats/server_metrics.go b/plugin/grpc/grpcstats/server_metrics.go
index c0297d2..1e875a7 100644
--- a/plugin/grpc/grpcstats/server_metrics.go
+++ b/plugin/grpc/grpcstats/server_metrics.go
@@ -19,6 +19,7 @@ import (
 	"log"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 )
 
@@ -26,40 +27,22 @@ import (
 // Server needs to use a ServerStatsHandler in order to enable collection.
 var (
 	// Available server measures
-	RPCServerErrorCount        *stats.MeasureInt64
-	RPCServerServerElapsedTime *stats.MeasureFloat64
-	RPCServerRequestBytes      *stats.MeasureInt64
-	RPCServerResponseBytes     *stats.MeasureInt64
-	RPCServerStartedCount      *stats.MeasureInt64
-	RPCServerFinishedCount     *stats.MeasureInt64
-	RPCServerRequestCount      *stats.MeasureInt64
-	RPCServerResponseCount     *stats.MeasureInt64
+	RPCServerErrorCount        *stats.Int64Measure
+	RPCServerServerElapsedTime *stats.Float64Measure
+	RPCServerRequestBytes      *stats.Int64Measure
+	RPCServerResponseBytes     *stats.Int64Measure
+	RPCServerStartedCount      *stats.Int64Measure
+	RPCServerFinishedCount     *stats.Int64Measure
+	RPCServerRequestCount      *stats.Int64Measure
+	RPCServerResponseCount     *stats.Int64Measure
 
 	// Predefined server views
-	RPCServerErrorCountView        *stats.View
-	RPCServerServerElapsedTimeView *stats.View
-	RPCServerRequestBytesView      *stats.View
-	RPCServerResponseBytesView     *stats.View
-	RPCServerRequestCountView      *stats.View
-	RPCServerResponseCountView     *stats.View
-
-	RPCServerServerElapsedTimeMinuteView *stats.View
-	RPCServerRequestBytesMinuteView      *stats.View
-	RPCServerResponseBytesMinuteView     *stats.View
-	RPCServerErrorCountMinuteView        *stats.View
-	RPCServerStartedCountMinuteView      *stats.View
-	RPCServerFinishedCountMinuteView     *stats.View
-	RPCServerRequestCountMinuteView      *stats.View
-	RPCServerResponseCountMinuteView     *stats.View
-
-	RPCServerServerElapsedTimeHourView *stats.View
-	RPCServerRequestBytesHourView      *stats.View
-	RPCServerResponseBytesHourView     *stats.View
-	RPCServerErrorCountHourView        *stats.View
-	RPCServerStartedCountHourView      *stats.View
-	RPCServerFinishedCountHourView     *stats.View
-	RPCServerRequestCountHourView      *stats.View
-	RPCServerResponseCountHourView     *stats.View
+	RPCServerErrorCountView        *view.View
+	RPCServerServerElapsedTimeView *view.View
+	RPCServerRequestBytesView      *view.View
+	RPCServerResponseBytesView     *view.View
+	RPCServerRequestCountView      *view.View
+	RPCServerResponseCountView     *view.View
 )
 
 // TODO(acetechnologist): This is temporary and will need to be replaced by a
@@ -69,75 +52,69 @@ var (
 func defaultServerMeasures() {
 	var err error
 
-	if RPCServerErrorCount, err = stats.NewMeasureInt64("grpc.io/server/error_count", "RPC Errors", unitCount); err != nil {
+	if RPCServerErrorCount, err = stats.Int64("grpc.io/server/error_count", "RPC Errors", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/error_count: %v", err)
 	}
-	if RPCServerServerElapsedTime, err = stats.NewMeasureFloat64("grpc.io/server/server_elapsed_time", "Server elapsed time in msecs", unitMillisecond); err != nil {
+	if RPCServerServerElapsedTime, err = stats.Float64("grpc.io/server/server_elapsed_time", "Server elapsed time in msecs", unitMillisecond); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/server_elapsed_time: %v", err)
 	}
-	if RPCServerRequestBytes, err = stats.NewMeasureInt64("grpc.io/server/request_bytes", "Request bytes", unitByte); err != nil {
+	if RPCServerRequestBytes, err = stats.Int64("grpc.io/server/request_bytes", "Request bytes", unitByte); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/request_bytes: %v", err)
 	}
-	if RPCServerResponseBytes, err = stats.NewMeasureInt64("grpc.io/server/response_bytes", "Response bytes", unitByte); err != nil {
+	if RPCServerResponseBytes, err = stats.Int64("grpc.io/server/response_bytes", "Response bytes", unitByte); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/response_bytes: %v", err)
 	}
-	if RPCServerStartedCount, err = stats.NewMeasureInt64("grpc.io/server/started_count", "Number of server RPCs (streams) started", unitCount); err != nil {
+	if RPCServerStartedCount, err = stats.Int64("grpc.io/server/started_count", "Number of server RPCs (streams) started", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/started_count: %v", err)
 	}
-	if RPCServerFinishedCount, err = stats.NewMeasureInt64("grpc.io/server/finished_count", "Number of server RPCs (streams) finished", unitCount); err != nil {
+	if RPCServerFinishedCount, err = stats.Int64("grpc.io/server/finished_count", "Number of server RPCs (streams) finished", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/finished_count: %v", err)
 	}
-	if RPCServerRequestCount, err = stats.NewMeasureInt64("grpc.io/server/request_count", "Number of server RPC request messages", unitCount); err != nil {
+	if RPCServerRequestCount, err = stats.Int64("grpc.io/server/request_count", "Number of server RPC request messages", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/request_count: %v", err)
 	}
-	if RPCServerResponseCount, err = stats.NewMeasureInt64("grpc.io/server/response_count", "Number of server RPC response messages", unitCount); err != nil {
+	if RPCServerResponseCount, err = stats.Int64("grpc.io/server/response_count", "Number of server RPC response messages", unitCount); err != nil {
 		log.Fatalf("Cannot create measure grpc.io/server/response_count: %v", err)
 	}
 }
 
 func defaultServerViews() {
-	RPCServerErrorCountView, _ = stats.NewView(
+	RPCServerErrorCountView, _ = view.New(
 		"grpc.io/server/error_count/cumulative",
 		"RPC Errors",
-		[]tag.Key{keyMethod, keyStatus},
+		[]tag.Key{KeyMethod, KeyStatus},
 		RPCServerErrorCount,
-		aggCount,
-		windowCumulative)
-	RPCServerServerElapsedTimeView, _ = stats.NewView(
+		aggCount)
+	RPCServerServerElapsedTimeView, _ = view.New(
 		"grpc.io/server/server_elapsed_time/cumulative",
 		"Server elapsed time in msecs",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCServerServerElapsedTime,
-		aggDistMillis,
-		windowCumulative)
-	RPCServerRequestBytesView, _ = stats.NewView(
+		aggDistMillis)
+	RPCServerRequestBytesView, _ = view.New(
 		"grpc.io/server/request_bytes/cumulative",
 		"Request bytes",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCServerRequestBytes,
-		aggDistBytes,
-		windowCumulative)
-	RPCServerResponseBytesView, _ = stats.NewView(
+		aggDistBytes)
+	RPCServerResponseBytesView, _ = view.New(
 		"grpc.io/server/response_bytes/cumulative",
 		"Response bytes",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCServerResponseBytes,
-		aggDistBytes,
-		windowCumulative)
-	RPCServerRequestCountView, _ = stats.NewView(
+		aggDistBytes)
+	RPCServerRequestCountView, _ = view.New(
 		"grpc.io/server/request_count/cumulative",
 		"Count of request messages per server RPC",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCServerRequestCount,
-		aggDistCounts,
-		windowCumulative)
-	RPCServerResponseCountView, _ = stats.NewView(
+		aggDistCounts)
+	RPCServerResponseCountView, _ = view.New(
 		"grpc.io/server/response_count/cumulative",
 		"Count of response messages per server RPC",
-		[]tag.Key{keyMethod},
+		[]tag.Key{KeyMethod},
 		RPCServerResponseCount,
-		aggDistCounts,
-		windowCumulative)
+		aggDistCounts)
 
 	serverViews = append(serverViews,
 		RPCServerErrorCountView,
@@ -148,142 +125,6 @@ func defaultServerViews() {
 		RPCServerResponseCountView)
 
 	// TODO(jbd): Add roundtrip_latency, uncompressed_request_bytes, uncompressed_response_bytes, request_count, response_count.
-
-	RPCServerServerElapsedTimeMinuteView, _ = stats.NewView(
-		"grpc.io/server/server_elapsed_time/minute",
-		"Minute stats for server elapsed time in msecs",
-		[]tag.Key{keyMethod},
-		RPCServerServerElapsedTime,
-		aggDistMillis,
-		windowSlidingMinute)
-	RPCServerRequestBytesMinuteView, _ = stats.NewView(
-		"grpc.io/server/request_bytes/minute",
-		"Minute stats for request size in bytes",
-		[]tag.Key{keyMethod},
-		RPCServerRequestBytes,
-		aggCount,
-		windowSlidingMinute)
-	RPCServerResponseBytesMinuteView, _ = stats.NewView(
-		"grpc.io/server/response_bytes/minute",
-		"Minute stats for response size in bytes",
-		[]tag.Key{keyMethod},
-		RPCServerResponseBytes,
-		aggCount,
-		windowSlidingMinute)
-	RPCServerErrorCountMinuteView, _ = stats.NewView(
-		"grpc.io/server/error_count/minute",
-		"Minute stats for rpc errors",
-		[]tag.Key{keyMethod},
-		RPCServerErrorCount,
-		aggCount,
-		windowSlidingMinute)
-	RPCServerStartedCountMinuteView, _ = stats.NewView(
-		"grpc.io/server/started_count/minute",
-		"Minute stats on the number of server RPCs started",
-		[]tag.Key{keyMethod},
-		RPCServerStartedCount,
-		aggCount,
-		windowSlidingMinute)
-	RPCServerFinishedCountMinuteView, _ = stats.NewView(
-		"grpc.io/server/finished_count/minute",
-		"Minute stats on the number of server RPCs finished",
-		[]tag.Key{keyMethod},
-		RPCServerFinishedCount,
-		aggCount,
-		windowSlidingMinute)
-	RPCServerRequestCountMinuteView, _ = stats.NewView(
-		"grpc.io/server/request_count/minute",
-		"Minute stats on the count of request messages per server RPC",
-		[]tag.Key{keyMethod},
-		RPCServerRequestCount,
-		aggCount,
-		windowSlidingMinute)
-	RPCServerResponseCountMinuteView, _ = stats.NewView(
-		"grpc.io/server/response_count/minute",
-		"Minute stats on the count of response messages per server RPC",
-		[]tag.Key{keyMethod},
-		RPCServerResponseCount,
-		aggCount,
-		windowSlidingMinute)
-
-	serverViews = append(serverViews,
-		RPCServerServerElapsedTimeMinuteView,
-		RPCServerRequestBytesMinuteView,
-		RPCServerResponseBytesMinuteView,
-		RPCServerErrorCountMinuteView,
-		RPCServerStartedCountMinuteView,
-		RPCServerFinishedCountMinuteView,
-		RPCServerRequestCountMinuteView,
-		RPCServerResponseCountMinuteView,
-	)
-
-	RPCServerServerElapsedTimeHourView, _ = stats.NewView(
-		"grpc.io/server/server_elapsed_time/hour",
-		"Hour stats for server elapsed time in msecs",
-		[]tag.Key{keyMethod},
-		RPCServerServerElapsedTime,
-		aggDistMillis,
-		windowSlidingHour)
-	RPCServerRequestBytesHourView, _ = stats.NewView(
-		"grpc.io/server/request_bytes/hour",
-		"Hour stats for request size in bytes",
-		[]tag.Key{keyMethod},
-		RPCServerRequestBytes,
-		aggCount,
-		windowSlidingHour)
-	RPCServerResponseBytesHourView, _ = stats.NewView(
-		"grpc.io/server/response_bytes/hour",
-		"Hour stats for response size in bytes",
-		[]tag.Key{keyMethod},
-		RPCServerResponseBytes,
-		aggCount,
-		windowSlidingHour)
-	RPCServerErrorCountHourView, _ = stats.NewView(
-		"grpc.io/server/error_count/hour",
-		"Hour stats for rpc errors",
-		[]tag.Key{keyMethod},
-		RPCServerErrorCount,
-		aggCount,
-		windowSlidingHour)
-	RPCServerStartedCountHourView, _ = stats.NewView(
-		"grpc.io/server/started_count/hour",
-		"Hour stats on the number of server RPCs started",
-		[]tag.Key{keyMethod},
-		RPCServerStartedCount,
-		aggCount,
-		windowSlidingHour)
-	RPCServerFinishedCountHourView, _ = stats.NewView(
-		"grpc.io/server/finished_count/hour",
-		"Hour stats on the number of server RPCs finished",
-		[]tag.Key{keyMethod},
-		RPCServerFinishedCount,
-		aggCount,
-		windowSlidingHour)
-	RPCServerRequestCountHourView, _ = stats.NewView(
-		"grpc.io/server/request_count/hour",
-		"Hour stats on the count of request messages per server RPC",
-		[]tag.Key{keyMethod},
-		RPCServerRequestCount,
-		aggCount,
-		windowSlidingHour)
-	RPCServerResponseCountHourView, _ = stats.NewView(
-		"grpc.io/server/response_count/hour",
-		"Hour stats on the count of response messages per server RPC",
-		[]tag.Key{keyMethod},
-		RPCServerResponseCount,
-		aggCount,
-		windowSlidingHour)
-
-	serverViews = append(serverViews,
-		RPCServerResponseCountHourView,
-		RPCServerServerElapsedTimeHourView,
-		RPCServerRequestBytesHourView,
-		RPCServerResponseBytesHourView,
-		RPCServerErrorCountHourView,
-		RPCServerStartedCountHourView,
-		RPCServerFinishedCountHourView,
-		RPCServerRequestCountHourView,
-	)
 }
 
 func initServer() {
@@ -291,4 +132,4 @@ func initServer() {
 	defaultServerViews()
 }
 
-var serverViews []*stats.View
+var serverViews []*view.View
diff --git a/plugin/ochttp/stats.go b/plugin/ochttp/stats.go
index 3b16e22..5ca5c6c 100644
--- a/plugin/ochttp/stats.go
+++ b/plugin/ochttp/stats.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 )
 
@@ -30,9 +31,9 @@ var (
 	bytesBucketBoundaries  = []float64{0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296}
 	millisBucketBoundaries = []float64{0, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000}
 
-	aggCount      = stats.CountAggregation{}
-	aggDistBytes  = stats.DistributionAggregation(bytesBucketBoundaries)
-	aggDistMillis = stats.DistributionAggregation(millisBucketBoundaries)
+	aggCount      = view.CountAggregation{}
+	aggDistBytes  = view.DistributionAggregation(bytesBucketBoundaries)
+	aggDistMillis = view.DistributionAggregation(millisBucketBoundaries)
 )
 
 var (
@@ -54,21 +55,19 @@ var (
 	// ClientLatencyDistribution is a view of the latency distribution of all instrumented requests.
 	ClientLatencyDistribution = defaultView(ClientLatency, aggDistMillis)
 	// ClientRequestCountByMethod is a view of response counts by HTTP method.
-	ClientRequestCountByMethod = mustView(stats.NewView(
+	ClientRequestCountByMethod = mustView(view.New(
 		qualify("request_count_by_method"),
 		"Client request count by HTTP method",
 		[]tag.Key{Method},
 		ClientRequest,
-		aggCount,
-		&stats.Cumulative{}))
+		aggCount))
 	// ClientResponseCountByStatusCode is a count of all instrumented HTTP responses HTTP status code.
-	ClientResponseCountByStatusCode = mustView(stats.NewView(
+	ClientResponseCountByStatusCode = mustView(view.New(
 		qualify("response_count_by_status_code"),
 		"Client response count by status code",
 		[]tag.Key{StatusCode},
 		ClientLatency,
-		aggCount,
-		&stats.Cumulative{}))
+		aggCount))
 
 	// Host is the value of the HTTP Host header.
 	Host = key("host")
@@ -81,12 +80,12 @@ var (
 	Method = key("method")
 )
 
-func defaultView(m stats.Measure, agg stats.Aggregation) *stats.View {
-	v, err := stats.NewView(m.Name(), m.Description(), nil, m, agg, stats.Cumulative{})
+func defaultView(m stats.Measure, agg view.Aggregation) *view.View {
+	v, err := view.New(m.Name(), m.Description(), nil, m, agg)
 	if err != nil {
 		panic(err)
 	}
-	if err := stats.RegisterView(v); err != nil {
+	if err := view.Register(v); err != nil {
 		panic(err)
 	}
 	return v
@@ -100,23 +99,23 @@ func key(name string) tag.Key {
 	return k
 }
 
-func int64Measure(name, desc, unit string) *stats.MeasureInt64 {
-	m, err := stats.NewMeasureInt64(qualify(name), desc, unit)
+func int64Measure(name, desc, unit string) *stats.Int64Measure {
+	m, err := stats.Int64(qualify(name), desc, unit)
 	if err != nil {
 		panic(err)
 	}
 	return m
 }
 
-func floatMeasure(name, desc, unit string) *stats.MeasureFloat64 {
-	m, err := stats.NewMeasureFloat64(qualify(name), desc, unit)
+func floatMeasure(name, desc, unit string) *stats.Float64Measure {
+	m, err := stats.Float64(qualify(name), desc, unit)
 	if err != nil {
 		panic(err)
 	}
 	return m
 }
 
-func mustView(v *stats.View, err error) *stats.View {
+func mustView(v *view.View, err error) *view.View {
 	if err != nil {
 		panic(err)
 	}
diff --git a/stats/aggregation.go b/stats/aggregation.go
deleted file mode 100644
index b64adf4..0000000
--- a/stats/aggregation.go
+++ /dev/null
@@ -1,205 +0,0 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package stats
-
-import "time"
-
-// Aggregation represents a data aggregation method. There are several
-// aggregation methods made available in the package such as
-// CountAggregation, SumAggregation, MeanAggregation and
-// DistributionAggregation.
-type Aggregation interface {
-	isAggregation() bool
-	newData() func() AggregationData
-}
-
-// CountAggregation indicates that data collected and aggregated
-// with this method will be turned into a count value.
-// For example, total number of accepted requests can be
-// aggregated by using CountAggregation.
-type CountAggregation struct{}
-
-func (a CountAggregation) isAggregation() bool { return true }
-
-func (a CountAggregation) newData() func() AggregationData {
-	return func() AggregationData { return newCountData(0) }
-}
-
-// SumAggregation indicates that data collected and aggregated
-// with this method will be summed up.
-// For example, accumulated request bytes can be aggregated by using
-// SumAggregation.
-type SumAggregation struct{}
-
-func (a SumAggregation) isAggregation() bool { return true }
-
-func (a SumAggregation) newData() func() AggregationData {
-	return func() AggregationData { return newSumData(0) }
-}
-
-// MeanAggregation indicates that collect and aggregate data and maintain
-// the mean value.
-// For example, average latency in milliseconds can be aggregated by using
-// MeanAggregation.
-type MeanAggregation struct{}
-
-func (a MeanAggregation) isAggregation() bool { return true }
-
-func (a MeanAggregation) newData() func() AggregationData {
-	return func() AggregationData { return newMeanData(0, 0) }
-}
-
-// DistributionAggregation indicates that the desired aggregation is
-// a histogram distribution.
-// An distribution aggregation may contain a histogram of the values in the
-// population. The bucket boundaries for that histogram are described
-// by DistributionAggregation slice. This defines length+1 buckets.
-//
-// If length >= 2 then the boundaries for bucket index i are:
-//
-//     [-infinity, bounds[i]) for i = 0
-//     [bounds[i-1], bounds[i]) for 0 < i < length
-//     [bounds[i-1], +infinity) for i = length
-//
-// If length is 0 then there is no histogram associated with the
-// distribution. There will be a single bucket with boundaries
-// (-infinity, +infinity).
-//
-// If length is 1 then there is no finite buckets, and that single
-// element is the common boundary of the overflow and underflow buckets.
-type DistributionAggregation []float64
-
-func (a DistributionAggregation) isAggregation() bool { return true }
-
-func (a DistributionAggregation) newData() func() AggregationData {
-	return func() AggregationData { return newDistributionData([]float64(a)) }
-}
-
-// aggregatorCumulative indicates that the aggregation occurs over all samples
-// seen since the view collection started.
-type aggregatorCumulative struct {
-	data AggregationData
-}
-
-// newAggregatorCumulative creates an aggregatorCumulative.
-func newAggregatorCumulative(now time.Time, newAggregationValue func() AggregationData) *aggregatorCumulative {
-	return &aggregatorCumulative{
-		data: newAggregationValue(),
-	}
-}
-
-func (a *aggregatorCumulative) isAggregator() bool {
-	return true
-}
-
-func (a *aggregatorCumulative) addSample(v interface{}, now time.Time) {
-	a.data.addSample(v)
-}
-
-func (a *aggregatorCumulative) retrieveCollected(now time.Time) AggregationData {
-	return a.data
-}
-
-// aggregatorInterval indicates that the aggregation occurs over a
-// window of time.
-type aggregatorInterval struct {
-	// keptDuration is the full duration that needs to be kept in memory in
-	// order to retrieve the aggregated data whenever it is requested. Its size
-	// is subDuration*len(entries+1). The actual desiredDuration interval is
-	// slightly shorter: subDuration*len(entries). The extra subDuration is
-	// needed to compute an approximation of the collected stats over the last
-	// desiredDuration without storing every instance with its timestamp.
-	keptDuration    time.Duration
-	desiredDuration time.Duration
-	subDuration     time.Duration
-	entries         []*timeSerieEntry
-	idx             int
-}
-
-const defaultSubIntervals = 5
-
-// newAggregatorInterval creates an aggregatorSlidingTime.
-func newAggregatorInterval(now time.Time, d time.Duration, subIntervalsCount int, newAggregationValue func() AggregationData) *aggregatorInterval {
-	if subIntervalsCount == 0 {
-		subIntervalsCount = defaultSubIntervals
-	}
-	subDuration := d / time.Duration(subIntervalsCount)
-	start := now.Add(-subDuration * time.Duration(subIntervalsCount))
-	var entries []*timeSerieEntry
-	// Keeps track of subIntervalsCount+1 entries in order to approximate the
-	// collected stats without storing every instance with its timestamp.
-	for i := 0; i <= subIntervalsCount; i++ {
-		entries = append(entries, &timeSerieEntry{
-			endTime: start.Add(subDuration),
-			av:      newAggregationValue(),
-		})
-		start = start.Add(subDuration)
-	}
-
-	return &aggregatorInterval{
-		keptDuration:    subDuration * time.Duration(len(entries)),
-		desiredDuration: subDuration * time.Duration(len(entries)-1), // this is equal to d
-		subDuration:     subDuration,
-		entries:         entries,
-		idx:             subIntervalsCount,
-	}
-}
-
-func (a *aggregatorInterval) isAggregator() bool {
-	return true
-}
-
-func (a *aggregatorInterval) addSample(v interface{}, now time.Time) {
-	a.moveToCurrentEntry(now)
-	e := a.entries[a.idx]
-	e.av.addSample(v)
-}
-
-func (a *aggregatorInterval) retrieveCollected(now time.Time) AggregationData {
-	a.moveToCurrentEntry(now)
-
-	e := a.entries[a.idx]
-	remaining := float64(e.endTime.Sub(now)) / float64(a.subDuration)
-	oldestIdx := (a.idx + 1) % len(a.entries)
-
-	e = a.entries[oldestIdx]
-	ret := e.av.multiplyByFraction(remaining)
-
-	for j := 1; j < len(a.entries); j++ {
-		oldestIdx = (oldestIdx + 1) % len(a.entries)
-		e = a.entries[oldestIdx]
-		ret.addOther(e.av)
-	}
-	return ret
-}
-
-func (a *aggregatorInterval) moveToCurrentEntry(now time.Time) {
-	e := a.entries[a.idx]
-	for {
-		if e.endTime.After(now) {
-			break
-		}
-		a.idx = (a.idx + 1) % len(a.entries)
-		e = a.entries[a.idx]
-		e.endTime = e.endTime.Add(a.keptDuration)
-		e.av.clear()
-	}
-}
-
-type timeSerieEntry struct {
-	endTime time.Time
-	av      AggregationData
-}
diff --git a/stats/doc.go b/stats/doc.go
index 577ac0b..7a8a62c 100644
--- a/stats/doc.go
+++ b/stats/doc.go
@@ -14,7 +14,7 @@
 //
 
 /*
-Package stats contains support for OpenCensus stats collection.
+Package stats contains support for OpenCensus stats recording.
 
 OpenCensus allows users to create typed measures, record measurements,
 aggregate the collected data, and export the aggregated data.
@@ -26,7 +26,7 @@ For example, latency, request Mb/s, and response Mb/s are measures
 to collect from a server.
 
 Each measure needs to be registered before being used. Measure
-constructors such as NewMeasureInt64 and NewMeasureFloat64 automatically
+constructors such as Int64 and Float64 automatically
 register the measure by the given name. Each registered measure needs
 to be unique by name. Measures also have a description and a unit.
 
@@ -48,33 +48,6 @@ recording to reduce cost. Recording of measurements is cheap.
 Libraries can always record measurements, and end-users can later decide
 on which measurements they want to collect by registering views. This allows
 libraries to turn on the instrumentation by default.
-
-Views
-
-In order to collect measurements, views need to be defined and registered.
-A view allows recorded measurements to be filtered and aggregated over a time window.
-
-All recorded measurements can be filtered by a list of tags.
-
-OpenCensus provides several aggregation methods: count, distribution, sum and mean.
-Count aggregation only counts the number of measurement points. Distribution
-aggregation provides statistical summary of the aggregated data. Sum distribution
-sums up the measurement points. Mean provides the mean of the recorded measurements.
-Aggregations can either happen cumulatively or over an interval.
-
-Users can dynamically create and delete views.
-
-Libraries can export their own views and claim the view names
-by registering them themselves.
-
-Exporting
-
-Collected and aggregated data can be exported to a metric collection
-backend by registering its exporter.
-
-Multiple exporters can be registered to upload the data to various
-different backends. Users need to unregister the exporters once they
-no longer are needed.
 */
 package stats // import "go.opencensus.io/stats"
 
diff --git a/stats/internal/record.go b/stats/internal/record.go
new file mode 100644
index 0000000..84f6b30
--- /dev/null
+++ b/stats/internal/record.go
@@ -0,0 +1,12 @@
+package internal
+
+import (
+	"time"
+
+	"go.opencensus.io/tag"
+)
+
+type Recorder func(*tag.Map, time.Time, interface{})
+
+// DefaultRecorder will be called for each Record call.
+var DefaultRecorder Recorder = nil
diff --git a/stats/internal/validation.go b/stats/internal/validation.go
new file mode 100644
index 0000000..8fa3ee3
--- /dev/null
+++ b/stats/internal/validation.go
@@ -0,0 +1,14 @@
+package internal
+
+const (
+	MaxNameLength = 255
+)
+
+func IsPrintable(str string) bool {
+	for _, r := range str {
+		if !(r >= ' ' && r <= '~') {
+			return false
+		}
+	}
+	return true
+}
diff --git a/stats/measure.go b/stats/measure.go
index da26de3..2dc5e88 100644
--- a/stats/measure.go
+++ b/stats/measure.go
@@ -15,13 +15,21 @@
 
 package stats
 
+import (
+	"errors"
+	"fmt"
+	"sync"
+
+	"go.opencensus.io/stats/internal"
+)
+
 // Measure represents a type of metric to be tracked and recorded.
 // For example, latency, request Mb/s, and response Mb/s are measures
 // to collect from a server.
 //
 // Each measure needs to be registered before being used.
-// Measure constructors such as NewMeasureInt64 and
-// NewMeasureFloat64 automatically registers the measure
+// Measure constructors such as Int64 and
+// Float64 automatically registers the measure
 // by the given name.
 // Each registered measure needs to be unique by name.
 // Measures also have a description and a unit.
@@ -31,34 +39,46 @@ type Measure interface {
 	Unit() string
 }
 
+var (
+	mu           sync.RWMutex
+	measures     = make(map[string]Measure)
+	errDuplicate = errors.New("duplicate measure name")
+)
+
+func FindMeasure(name string) Measure {
+	mu.RLock()
+	defer mu.RUnlock()
+	if m, ok := measures[name]; ok {
+		return m
+	}
+	return nil
+}
+
+func register(m Measure) (Measure, error) {
+	key := m.Name()
+	mu.Lock()
+	defer mu.Unlock()
+	if stored, ok := measures[key]; ok {
+		return stored, errDuplicate
+	}
+	measures[key] = m
+	return m, nil
+}
+
 // Measurement is the numeric value measured when recording stats. Each measure
-// provides methods to create measurements of their kind. For example, MeasureInt64
+// provides methods to create measurements of their kind. For example, Int64Measure
 // provides M to convert an int64 into a measurement.
 type Measurement struct {
-	v interface{} // int64 or float64
-	m Measure
+	Value   interface{} // int64 or float64
+	Measure Measure
 }
 
-// FindMeasure returns the registered measure associated with name.
-// If no registered measure is not found, nil is returned.
-func FindMeasure(name string) (m Measure) {
-	req := &getMeasureByNameReq{
-		name: name,
-		c:    make(chan *getMeasureByNameResp),
+func checkName(name string) error {
+	if len(name) > internal.MaxNameLength {
+		return fmt.Errorf("measure name cannot be larger than %v", internal.MaxNameLength)
 	}
-	defaultWorker.c <- req
-	resp := <-req.c
-	return resp.m
-}
-
-// DeleteMeasure deletes an existing measure to allow for creation of a new
-// measure with the same name. It returns an error if the measure cannot be
-// deleted, such as one or multiple registered views refer to it.
-func DeleteMeasure(m Measure) error {
-	req := &deleteMeasureReq{
-		m:   m,
-		err: make(chan error),
+	if !internal.IsPrintable(name) {
+		return fmt.Errorf("measure name needs to be an ASCII string")
 	}
-	defaultWorker.c <- req
-	return <-req.err
+	return nil
 }
diff --git a/stats/measure_float64.go b/stats/measure_float64.go
index 2aba6a9..660ceb6 100644
--- a/stats/measure_float64.go
+++ b/stats/measure_float64.go
@@ -15,30 +15,49 @@
 
 package stats
 
-// MeasureFloat64 is a measure of type float64.
-type MeasureFloat64 struct {
+// Float64Measure is a measure of type float64.
+type Float64Measure struct {
 	name        string
 	unit        string
 	description string
 }
 
 // Name returns the name of the measure.
-func (m *MeasureFloat64) Name() string {
+func (m *Float64Measure) Name() string {
 	return m.name
 }
 
 // Description returns the description of the measure.
-func (m *MeasureFloat64) Description() string {
+func (m *Float64Measure) Description() string {
 	return m.description
 }
 
 // Unit returns the unit of the measure.
-func (m *MeasureFloat64) Unit() string {
+func (m *Float64Measure) Unit() string {
 	return m.unit
 }
 
 // M creates a new float64 measurement.
 // Use Record to record measurements.
-func (m *MeasureFloat64) M(v float64) Measurement {
-	return Measurement{m: m, v: v}
+func (m *Float64Measure) M(v float64) Measurement {
+	return Measurement{Measure: m, Value: v}
+}
+
+// Float64 creates a new measure of type Float64Measure. It returns
+// an error if a measure with the same name already exists.
+func Float64(name, description, unit string) (*Float64Measure, error) {
+	if err := checkName(name); err != nil {
+		return nil, err
+	}
+	m := &Float64Measure{
+		name:        name,
+		description: description,
+		unit:        unit,
+	}
+	_, err := register(m)
+	if err != nil {
+		return nil, err
+	} else {
+		return m, err
+	}
 }
diff --git a/stats/measure_int64.go b/stats/measure_int64.go
index db38d93..cf9ddd1 100644
--- a/stats/measure_int64.go
+++ b/stats/measure_int64.go
@@ -15,30 +15,49 @@
 
 package stats
 
-// MeasureInt64 is a measure of type int64.
-type MeasureInt64 struct {
+// Int64Measure is a measure of type int64.
+type Int64Measure struct {
 	name        string
 	unit        string
 	description string
 }
 
 // Name returns the name of the measure.
-func (m *MeasureInt64) Name() string {
+func (m *Int64Measure) Name() string {
 	return m.name
 }
 
 // Description returns the description of the measure.
-func (m *MeasureInt64) Description() string {
+func (m *Int64Measure) Description() string {
 	return m.description
 }
 
 // Unit returns the unit of the measure.
-func (m *MeasureInt64) Unit() string {
+func (m *Int64Measure) Unit() string {
 	return m.unit
 }
 
 // M creates a new int64 measurement.
 // Use Record to record measurements.
-func (m *MeasureInt64) M(v int64) Measurement {
-	return Measurement{m: m, v: v}
+func (m *Int64Measure) M(v int64) Measurement {
+	return Measurement{Measure: m, Value: v}
+}
+
+// Int64 creates a new measure of type Int64Measure. It returns an
+// error if a measure with the same name already exists.
+func Int64(name, description, unit string) (*Int64Measure, error) {
+	if err := checkName(name); err != nil {
+		return nil, err
+	}
+	m := &Int64Measure{
+		name:        name,
+		description: description,
+		unit:        unit,
+	}
+	_, err := register(m)
+	if err != nil {
+		return nil, err
+	} else {
+		return m, err
+	}
 }
diff --git a/stats/record.go b/stats/record.go
new file mode 100644
index 0000000..a4862e7
--- /dev/null
+++ b/stats/record.go
@@ -0,0 +1,32 @@
+// Copyright 2018, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package stats
+
+import (
+	"context"
+	"time"
+
+	"go.opencensus.io/stats/internal"
+	"go.opencensus.io/tag"
+)
+
+// Record records one or multiple measurements with the same tags at once.
+// If there are any tags in the context, measurements will be tagged with them.
+func Record(ctx context.Context, ms ...Measurement) {
+	if internal.DefaultRecorder != nil {
+		internal.DefaultRecorder(tag.FromContext(ctx), time.Now(), ms)
+	}
+}
diff --git a/stats/validation.go b/stats/validation.go
deleted file mode 100644
index 96b4911..0000000
--- a/stats/validation.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package stats
-
-import "fmt"
-
-const (
-	maxNameLength = 255
-)
-
-func checkViewName(name string) error {
-	if len(name) > maxNameLength {
-		return fmt.Errorf("view name cannot be larger than %v", maxNameLength)
-	}
-	if !isPrintable(name) {
-		return fmt.Errorf("view name needs to be an ASCII string")
-	}
-	return nil
-}
-
-func checkMeasureName(name string) error {
-	if len(name) > maxNameLength {
-		return fmt.Errorf("measure name cannot be larger than %v", maxNameLength)
-	}
-	if !isPrintable(name) {
-		return fmt.Errorf("measure name needs to be an ASCII string")
-	}
-	return nil
-}
-
-func isPrintable(str string) bool {
-	for _, r := range str {
-		if !(r >= ' ' && r <= '~') {
-			return false
-		}
-	}
-	return true
-}
diff --git a/stats/view/aggregation.go b/stats/view/aggregation.go
new file mode 100644
index 0000000..66fda60
--- /dev/null
+++ b/stats/view/aggregation.go
@@ -0,0 +1,87 @@
+// Copyright 2017, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package view
+
+// Aggregation represents a data aggregation method. There are several
+// aggregation methods made available in the package such as
+// CountAggregation, SumAggregation, MeanAggregation and
+// DistributionAggregation.
+type Aggregation interface {
+	isAggregation() bool
+	newData() AggregationData
+}
+
+// CountAggregation indicates that data collected and aggregated
+// with this method will be turned into a count value.
+// For example, total number of accepted requests can be
+// aggregated by using CountAggregation.
+type CountAggregation struct{}
+
+func (a CountAggregation) isAggregation() bool { return true }
+
+func (a CountAggregation) newData() AggregationData {
+	return newCountData(0)
+}
+
+// SumAggregation indicates that data collected and aggregated
+// with this method will be summed up.
+// For example, accumulated request bytes can be aggregated by using
+// SumAggregation.
+type SumAggregation struct{}
+
+func (a SumAggregation) isAggregation() bool { return true }
+
+func (a SumAggregation) newData() AggregationData {
+	return newSumData(0)
+}
+
+// MeanAggregation indicates that collect and aggregate data and maintain
+// the mean value.
+// For example, average latency in milliseconds can be aggregated by using
+// MeanAggregation.
+type MeanAggregation struct{}
+
+func (a MeanAggregation) isAggregation() bool { return true }
+
+func (a MeanAggregation) newData() AggregationData {
+	return newMeanData(0, 0)
+}
+
+// DistributionAggregation indicates that the desired aggregation is
+// a histogram distribution.
+// An distribution aggregation may contain a histogram of the values in the
+// population. The bucket boundaries for that histogram are described
+// by DistributionAggregation slice. This defines length+1 buckets.
+//
+// If length >= 2 then the boundaries for bucket index i are:
+//
+//     [-infinity, bounds[i]) for i = 0
+//     [bounds[i-1], bounds[i]) for 0 < i < length
+//     [bounds[i-1], +infinity) for i = length
+//
+// If length is 0 then there is no histogram associated with the
+// distribution. There will be a single bucket with boundaries
+// (-infinity, +infinity).
+//
+// If length is 1 then there is no finite buckets, and that single
+// element is the common boundary of the overflow and underflow buckets.
+type DistributionAggregation []float64
+
+func (a DistributionAggregation) isAggregation() bool { return true }
+
+func (a DistributionAggregation) newData() AggregationData {
+	return newDistributionData([]float64(a))
+}
diff --git a/stats/aggregation_data.go b/stats/view/aggregation_data.go
similarity index 99%
rename from stats/aggregation_data.go
rename to stats/view/aggregation_data.go
index aad7def..572d187 100644
--- a/stats/aggregation_data.go
+++ b/stats/view/aggregation_data.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-package stats
+package view
 
 import (
 	"math"
diff --git a/stats/collector.go b/stats/view/collector.go
similarity index 85%
rename from stats/collector.go
rename to stats/view/collector.go
index 379be97..10584a8 100644
--- a/stats/collector.go
+++ b/stats/view/collector.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-package stats
+package view
 
 import (
 	"sort"
@@ -25,37 +25,34 @@ import (
 
 type collector struct {
 	// signatures holds the aggregations values for each unique tag signature
-	// (values for all keys) to its Window.
-	signatures map[string]aggregator
+	// (values for all keys) to its aggregator.
+	signatures map[string]AggregationData
 	// Aggregation is the description of the aggregation to perform for this
 	// view.
 	a Aggregation
-
-	// window is the window under which the aggregation is performed.
-	w Window
 }
 
 func (c *collector) addSample(s string, v interface{}, now time.Time) {
 	aggregator, ok := c.signatures[s]
 	if !ok {
-		aggregator = c.w.newAggregator(now, c.a.newData())
+		aggregator = c.a.newData()
 		c.signatures[s] = aggregator
 	}
-	aggregator.addSample(v, now)
+	aggregator.addSample(v)
 }
 
 func (c *collector) collectedRows(keys []tag.Key, now time.Time) []*Row {
 	var rows []*Row
 	for sig, aggregator := range c.signatures {
 		tags := decodeTags([]byte(sig), keys)
-		row := &Row{tags, aggregator.retrieveCollected(now)}
+		row := &Row{tags, aggregator}
 		rows = append(rows, row)
 	}
 	return rows
 }
 
 func (c *collector) clearRows() {
-	c.signatures = make(map[string]aggregator)
+	c.signatures = make(map[string]AggregationData)
 }
 
 // encodeWithKeys encodes the map by using values
diff --git a/stats/view/doc.go b/stats/view/doc.go
new file mode 100644
index 0000000..fcaae6e
--- /dev/null
+++ b/stats/view/doc.go
@@ -0,0 +1,47 @@
+// Copyright 2017, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+/*
+Package view contains support for collecting and exposing aggregates over stats.
+
+In order to collect measurements, views need to be defined and registered.
+A view allows recorded measurements to be filtered and aggregated over a time window.
+
+All recorded measurements can be filtered by a list of tags.
+
+OpenCensus provides several aggregation methods: count, distribution, sum and mean.
+Count aggregation only counts the number of measurement points. Distribution
+aggregation provides statistical summary of the aggregated data. Sum distribution
+sums up the measurement points. Mean provides the mean of the recorded measurements.
+Aggregations can either happen cumulatively or over an interval.
+
+Users can dynamically create and delete views.
+
+Libraries can export their own views and claim the view names
+by registering them themselves.
+
+Exporting
+
+Collected and aggregated data can be exported to a metric collection
+backend by registering its exporter.
+
+Multiple exporters can be registered to upload the data to various
+different backends. Users need to unregister the exporters once they
+no longer are needed.
+*/
+package view // import "go.opencensus.io/stats/view"
+
+// TODO(acetechnologist): Add a link to the language independent OpenCensus
+// spec when it is available.
diff --git a/stats/export.go b/stats/view/export.go
similarity index 90%
rename from stats/export.go
rename to stats/view/export.go
index ba82aa9..fac3787 100644
--- a/stats/export.go
+++ b/stats/view/export.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package stats
+package view
 
 import "sync"
 
@@ -25,11 +25,11 @@ var (
 //
 // The ExportView method should return quickly; if an
 // Exporter takes a significant amount of time to
-// process a ViewData, that work should be done on another goroutine.
+// process a Data, that work should be done on another goroutine.
 //
-// The ViewData should not be modified.
+// The Data should not be modified.
 type Exporter interface {
-	ExportView(viewData *ViewData)
+	ExportView(viewData *Data)
 }
 
 // RegisterExporter registers an exporter.
diff --git a/stats/view.go b/stats/view/view.go
similarity index 80%
rename from stats/view.go
rename to stats/view/view.go
index 0b374b4..1ef805d 100644
--- a/stats/view.go
+++ b/stats/view/view.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-package stats
+package view
 
 import (
 	"bytes"
@@ -23,13 +23,15 @@ import (
 	"sync/atomic"
 	"time"
 
+	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/internal"
 	"go.opencensus.io/tag"
 )
 
-// View allows users to filter and aggregate the recorded events
-// over a time window. Each view has to be registered to enable
-// data retrieval. Use NewView to initiate new views.
-// Unregister views once you don't want to collect any more events.
+// View allows users to filter and aggregate the recorded events.
+// Each view has to be registered to enable data retrieval. Use New to
+// initiate new views. Unregister views once you don't want to collect any more
+// events.
 type View struct {
 	name        string // name of View. Must be unique.
 	description string
@@ -38,24 +40,23 @@ type View struct {
 	tagKeys []tag.Key
 
 	// Examples of measures are cpu:tickCount, diskio:time...
-	m Measure
+	m stats.Measure
 
 	subscribed uint32 // 1 if someone is subscribed and data need to be exported, use atomic to access
 
 	collector *collector
 }
 
-// NewView creates a new view with the given name and description.
+// New creates a new view with the given name and description.
 // View names need to be unique globally in the entire system.
 //
 // Data collection will only filter measurements recorded by the given keys.
-// Collected data will be processed by the given aggregation algorithm for
-// the given time window.
+// Collected data will be processed by the given aggregation algorithm.
 //
 // Views need to be subscribed toin order to retrieve collection data.
 //
 // Once the view is no longer required, the view can be unregistered.
-func NewView(name, description string, keys []tag.Key, measure Measure, agg Aggregation, window Window) (*View, error) {
+func New(name, description string, keys []tag.Key, measure stats.Measure, agg Aggregation) (*View, error) {
 	if err := checkViewName(name); err != nil {
 		return nil, err
 	}
@@ -70,7 +71,7 @@ func NewView(name, description string, keys []tag.Key, measure Measure, agg Aggr
 		description: description,
 		tagKeys:     ks,
 		m:           measure,
-		collector:   &collector{make(map[string]aggregator), agg, window},
+		collector:   &collector{make(map[string]AggregationData), agg},
 	}, nil
 }
 
@@ -107,12 +108,6 @@ func (v *View) TagKeys() []tag.Key {
 	return v.tagKeys
 }
 
-// Window returns the timing window being used to collect
-// metrics from this view.
-func (v *View) Window() Window {
-	return v.collector.w
-}
-
 // Aggregation returns the data aggregation method used to aggregate
 // the measurements collected by this view.
 func (v *View) Aggregation() Aggregation {
@@ -120,7 +115,7 @@ func (v *View) Aggregation() Aggregation {
 }
 
 // Measure returns the measure the view is collecting measurements for.
-func (v *View) Measure() Measure {
+func (v *View) Measure() stats.Measure {
 	return v.m
 }
 
@@ -136,10 +131,9 @@ func (v *View) addSample(m *tag.Map, val interface{}, now time.Time) {
 	v.collector.addSample(sig, val, now)
 }
 
-// A ViewData is a set of rows about usage of the single measure associated
-// with the given view during a particular window. Each row is specific to a
-// unique set of tags.
-type ViewData struct {
+// A Data is a set of rows about usage of the single measure associated
+// with the given view. Each row is specific to a unique set of tags.
+type Data struct {
 	View       *View
 	Start, End time.Time
 	Rows       []*Row
@@ -173,3 +167,13 @@ func (r *Row) Equal(other *Row) bool {
 	}
 	return reflect.DeepEqual(r.Tags, other.Tags) && r.Data.equal(other.Data)
 }
+
+func checkViewName(name string) error {
+	if len(name) > internal.MaxNameLength {
+		return fmt.Errorf("view name cannot be larger than %v", internal.MaxNameLength)
+	}
+	if !internal.IsPrintable(name) {
+		return fmt.Errorf("view name needs to be an ASCII string")
+	}
+	return nil
+}
diff --git a/stats/worker.go b/stats/view/worker.go
similarity index 62%
rename from stats/worker.go
rename to stats/view/worker.go
index e04ddcf..0e842df 100644
--- a/stats/worker.go
+++ b/stats/view/worker.go
@@ -13,24 +13,26 @@
 // limitations under the License.
 //
 
-package stats
+package view
 
 import (
-	"context"
 	"errors"
 	"fmt"
 	"time"
 
+	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/internal"
 	"go.opencensus.io/tag"
 )
 
 func init() {
 	defaultWorker = newWorker()
 	go defaultWorker.start()
+	internal.DefaultRecorder = record
 }
 
 type measureRef struct {
-	measure Measure
+	measure stats.Measure
 	views   map[*View]struct{}
 }
 
@@ -48,57 +50,9 @@ var defaultWorker *worker
 
 var defaultReportingDuration = 10 * time.Second
 
-// NewMeasureFloat64 creates a new measure of type MeasureFloat64. It returns
-// an error if a measure with the same name already exists.
-func NewMeasureFloat64(name, description, unit string) (*MeasureFloat64, error) {
-	if err := checkMeasureName(name); err != nil {
-		return nil, err
-	}
-	m := &MeasureFloat64{
-		name:        name,
-		description: description,
-		unit:        unit,
-	}
-
-	req := &registerMeasureReq{
-		m:   m,
-		err: make(chan error),
-	}
-	defaultWorker.c <- req
-	if err := <-req.err; err != nil {
-		return nil, err
-	}
-
-	return m, nil
-}
-
-// NewMeasureInt64 creates a new measure of type MeasureInt64. It returns an
-// error if a measure with the same name already exists.
-func NewMeasureInt64(name, description, unit string) (*MeasureInt64, error) {
-	if err := checkMeasureName(name); err != nil {
-		return nil, err
-	}
-	m := &MeasureInt64{
-		name:        name,
-		description: description,
-		unit:        unit,
-	}
-
-	req := &registerMeasureReq{
-		m:   m,
-		err: make(chan error),
-	}
-	defaultWorker.c <- req
-	if err := <-req.err; err != nil {
-		return nil, err
-	}
-
-	return m, nil
-}
-
-// FindView returns a registered view associated with this name.
+// FindMeasure returns a registered view associated with this name.
 // If no registered view is found, nil is returned.
-func FindView(name string) (v *View) {
+func Find(name string) (v *View) {
 	req := &getViewByNameReq{
 		name: name,
 		c:    make(chan *getViewByNameResp),
@@ -108,14 +62,14 @@ func FindView(name string) (v *View) {
 	return resp.v
 }
 
-// RegisterView registers view. It returns an error if the view is already registered.
+// Register registers view. It returns an error if the view is already registered.
 //
 // Subscription automatically registers a view.
 // Most users will not register directly but register via subscription.
-// Registeration can be used by libraries to claim a view name.
+// Registration can be used by libraries to claim a view name.
 //
 // Unregister the view once the view is not required anymore.
-func RegisterView(v *View) error {
+func Register(v *View) error {
 	req := &registerViewReq{
 		v:   v,
 		err: make(chan error),
@@ -124,10 +78,10 @@ func RegisterView(v *View) error {
 	return <-req.err
 }
 
-// UnregisterView removes the previously registered view. It returns an error
+// Unregister removes the previously registered view. It returns an error
 // if the view wasn't registered. All data collected and not reported for the
 // corresponding view will be lost. The view is automatically be unsubscribed.
-func UnregisterView(v *View) error {
+func Unregister(v *View) error {
 	req := &unregisterViewReq{
 		v:   v,
 		err: make(chan error),
@@ -150,11 +104,8 @@ func (v *View) Subscribe() error {
 	return <-req.err
 }
 
-// Unsubscribe unsubscribes a previously subscribed channel.
+// Unsubscribe unsubscribes a previously subscribed view.
 // Data will not be exported from this view once unsubscription happens.
-// If no more subscriber for v exists and the the ad hoc
-// collection for this view isn't active, data stops being collected for this
-// view.
 func (v *View) Unsubscribe() error {
 	req := &unsubscribeFromViewReq{
 		v:   v,
@@ -179,13 +130,11 @@ func (v *View) RetrieveData() ([]*Row, error) {
 	return resp.rows, resp.err
 }
 
-// Record records one or multiple measurements with the same tags at once.
-// If there are any tags in the context, measurements will be tagged with them.
-func Record(ctx context.Context, ms ...Measurement) {
+func record(tags *tag.Map, now time.Time, ms interface{}) {
 	req := &recordReq{
-		now: time.Now(),
-		tm:  tag.FromContext(ctx),
-		ms:  ms,
+		now: now,
+		tm:  tags,
+		ms:  ms.([]stats.Measurement),
 	}
 	defaultWorker.c <- req
 }
@@ -239,24 +188,22 @@ func (w *worker) stop() {
 	<-w.done
 }
 
-func (w *worker) tryRegisterMeasure(m Measure) error {
-	if ref, ok := w.measures[m.Name()]; ok {
-		if ref.measure != m {
-			return fmt.Errorf("cannot register measure %q; another measure with the same name is already registered", m.Name())
-		}
-		// the measure is already registered so there is nothing to do and the
-		// command is considered successful.
-		return nil
+func (w *worker) getMeasureRef(m stats.Measure) *measureRef {
+	if mr, ok := w.measures[m.Name()]; ok {
+		return mr
 	}
-
-	w.measures[m.Name()] = &measureRef{
+	mr := &measureRef{
 		measure: m,
 		views:   make(map[*View]struct{}),
 	}
-	return nil
+	w.measures[m.Name()] = mr
+	return mr
 }
 
 func (w *worker) tryRegisterView(v *View) error {
+	if err := checkViewName(v.name); err != nil {
+		return err
+	}
 	if x, ok := w.views[v.Name()]; ok {
 		if x != v {
 			return fmt.Errorf("cannot register view %q; another view with the same name is already registered", v.Name())
@@ -267,14 +214,12 @@ func (w *worker) tryRegisterView(v *View) error {
 		return nil
 	}
 
-	// view is not registered and needs to be registered, but first its measure
-	// needs to be registered.
-	if err := w.tryRegisterMeasure(v.Measure()); err != nil {
-		return fmt.Errorf("cannot register view %q: %v", v.Name(), err)
+	if v.Measure() == nil {
+		return fmt.Errorf("cannot register view %q: measure not defined", v.Name())
 	}
 
 	w.views[v.Name()] = v
-	ref := w.measures[v.Measure().Name()]
+	ref := w.getMeasureRef(v.Measure())
 	ref.views[v] = struct{}{}
 
 	return nil
@@ -286,18 +231,16 @@ func (w *worker) reportUsage(start time.Time) {
 			continue
 		}
 		rows := v.collectedRows(start)
-		if isCumulative(v) {
-			s, ok := w.startTimes[v]
-			if !ok {
-				w.startTimes[v] = start
-			} else {
-				start = s
-			}
+		s, ok := w.startTimes[v]
+		if !ok {
+			w.startTimes[v] = start
+		} else {
+			start = s
 		}
 		// Make sure collector is never going
 		// to mutate the exported data.
 		rows = deepCopyRowData(rows)
-		viewData := &ViewData{
+		viewData := &Data{
 			View:  v,
 			Start: start,
 			End:   time.Now(),
@@ -308,20 +251,7 @@ func (w *worker) reportUsage(start time.Time) {
 			e.ExportView(viewData)
 		}
 		exportersMu.Unlock()
-		if !isCumulative(v) {
-			v.clearRows()
-		}
-	}
-}
-
-func isCumulative(v *View) bool {
-	switch v.Window().(type) {
-	case *Cumulative:
-		return true
-	case Cumulative:
-		return true
 	}
-	return false
 }
 
 func deepCopyRowData(rows []*Row) []*Row {
diff --git a/stats/worker_commands.go b/stats/view/worker_commands.go
similarity index 76%
rename from stats/worker_commands.go
rename to stats/view/worker_commands.go
index 525c97a..ae21e75 100644
--- a/stats/worker_commands.go
+++ b/stats/view/worker_commands.go
@@ -13,12 +13,13 @@
 // limitations under the License.
 //
 
-package stats
+package view
 
 import (
 	"fmt"
 	"time"
 
+	"go.opencensus.io/stats"
 	"go.opencensus.io/tag"
 )
 
@@ -26,62 +27,6 @@ type command interface {
 	handleCommand(w *worker)
 }
 
-// getMeasureByNameReq is the command to get a measure given its name.
-type getMeasureByNameReq struct {
-	name string
-	c    chan *getMeasureByNameResp
-}
-
-type getMeasureByNameResp struct {
-	m Measure
-}
-
-func (cmd *getMeasureByNameReq) handleCommand(w *worker) {
-	ref, ok := w.measures[cmd.name]
-	if ok {
-		cmd.c <- &getMeasureByNameResp{ref.measure}
-		return
-	}
-	cmd.c <- &getMeasureByNameResp{nil}
-}
-
-// registerMeasureReq is the command to register a measure with the library.
-type registerMeasureReq struct {
-	m   Measure
-	err chan error
-}
-
-func (cmd *registerMeasureReq) handleCommand(w *worker) {
-	cmd.err <- w.tryRegisterMeasure(cmd.m)
-}
-
-// deleteMeasureReq is the command to delete a measure from the library.
-type deleteMeasureReq struct {
-	m   Measure
-	err chan error
-}
-
-func (cmd *deleteMeasureReq) handleCommand(w *worker) {
-	ref, ok := w.measures[cmd.m.Name()]
-	if !ok {
-		cmd.err <- nil
-		return
-	}
-
-	if ref.measure != cmd.m {
-		cmd.err <- nil
-		return
-	}
-
-	if c := len(ref.views); c > 0 {
-		cmd.err <- fmt.Errorf("cannot delete; measure %q used by %v registered views", cmd.m.Name(), c)
-		return
-	}
-
-	delete(w.measures, cmd.m.Name())
-	cmd.err <- nil
-}
-
 // getViewByNameReq is the command to get a view given its name.
 type getViewByNameReq struct {
 	name string
@@ -127,7 +72,7 @@ func (cmd *unregisterViewReq) handleCommand(w *worker) {
 		return
 	}
 	delete(w.views, cmd.v.Name())
-	ref := w.measures[v.Measure().Name()]
+	ref := w.getMeasureRef(v.Measure())
 	delete(ref.views, v)
 	cmd.err <- nil
 }
@@ -211,14 +156,14 @@ func (cmd *retrieveDataReq) handleCommand(w *worker) {
 type recordReq struct {
 	now time.Time
 	tm  *tag.Map
-	ms  []Measurement
+	ms  []stats.Measurement
 }
 
 func (cmd *recordReq) handleCommand(w *worker) {
 	for _, m := range cmd.ms {
-		ref := w.measures[m.m.Name()]
+		ref := w.getMeasureRef(m.Measure)
 		for v := range ref.views {
-			v.addSample(cmd.tm, m.v, cmd.now)
+			v.addSample(cmd.tm, m.Value, cmd.now)
 		}
 	}
 }
diff --git a/stats/window.go b/stats/window.go
deleted file mode 100644
index e51ddc9..0000000
--- a/stats/window.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package stats
-
-import "time"
-
-// aggregator represents the interface for the aggregators for the various windows.
-type aggregator interface {
-	isAggregator() bool
-	addSample(v interface{}, now time.Time)
-	retrieveCollected(now time.Time) AggregationData
-}
-
-// Window represents a time interval or samples count over
-// which the aggregation occurs.
-type Window interface {
-	isWindow()
-	newAggregator(now time.Time, newAggregationData func() AggregationData) aggregator
-}
-
-// Cumulative is a window that indicates that the aggregation occurs
-// over the lifetime of the view.
-type Cumulative struct{}
-
-func (w Cumulative) isWindow() {}
-
-func (w Cumulative) newAggregator(now time.Time, newAggregationData func() AggregationData) aggregator {
-	return newAggregatorCumulative(now, newAggregationData)
-}
-
-// Interval is a window that indicates that the aggregation occurs over a sliding
-// window of time: last n seconds, minutes, hours.
-type Interval struct {
-	Duration     time.Duration
-	SubIntervals int
-}
-
-func (w Interval) isWindow() {}
-
-func (w Interval) newAggregator(now time.Time, newAggregationData func() AggregationData) aggregator {
-	return newAggregatorInterval(now, w.Duration, w.SubIntervals, newAggregationData)
-}
diff --git a/zpages/rpcz.go b/zpages/rpcz.go
index b0e4217..d6e0a7c 100644
--- a/zpages/rpcz.go
+++ b/zpages/rpcz.go
@@ -28,7 +28,7 @@ import (
 
 	"go.opencensus.io/internal"
 	"go.opencensus.io/plugin/grpc/grpcstats"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 var (
@@ -39,51 +39,19 @@ var (
 	// viewType lists the views we are interested in for RPC stats.
 	// A view's map value indicates whether that view contains data for received
 	// RPCs.
-	viewType = map[*stats.View]bool{
-		grpcstats.RPCClientErrorCountHourView:          false,
-		grpcstats.RPCClientErrorCountMinuteView:        false,
-		grpcstats.RPCClientErrorCountView:              false,
-		grpcstats.RPCClientFinishedCountHourView:       false,
-		grpcstats.RPCClientFinishedCountMinuteView:     false,
-		grpcstats.RPCClientRequestBytesHourView:        false,
-		grpcstats.RPCClientRequestBytesMinuteView:      false,
-		grpcstats.RPCClientRequestBytesView:            false,
-		grpcstats.RPCClientRequestCountHourView:        false,
-		grpcstats.RPCClientRequestCountMinuteView:      false,
-		grpcstats.RPCClientRequestCountView:            false,
-		grpcstats.RPCClientResponseBytesHourView:       false,
-		grpcstats.RPCClientResponseBytesMinuteView:     false,
-		grpcstats.RPCClientResponseBytesView:           false,
-		grpcstats.RPCClientResponseCountHourView:       false,
-		grpcstats.RPCClientResponseCountMinuteView:     false,
-		grpcstats.RPCClientResponseCountView:           false,
-		grpcstats.RPCClientRoundTripLatencyHourView:    false,
-		grpcstats.RPCClientRoundTripLatencyMinuteView:  false,
-		grpcstats.RPCClientRoundTripLatencyView:        false,
-		grpcstats.RPCClientStartedCountHourView:        false,
-		grpcstats.RPCClientStartedCountMinuteView:      false,
-		grpcstats.RPCServerErrorCountHourView:          true,
-		grpcstats.RPCServerErrorCountMinuteView:        true,
-		grpcstats.RPCServerErrorCountView:              true,
-		grpcstats.RPCServerFinishedCountHourView:       true,
-		grpcstats.RPCServerFinishedCountMinuteView:     true,
-		grpcstats.RPCServerRequestBytesHourView:        true,
-		grpcstats.RPCServerRequestBytesMinuteView:      true,
-		grpcstats.RPCServerRequestBytesView:            true,
-		grpcstats.RPCServerRequestCountHourView:        true,
-		grpcstats.RPCServerRequestCountMinuteView:      true,
-		grpcstats.RPCServerRequestCountView:            true,
-		grpcstats.RPCServerResponseBytesHourView:       true,
-		grpcstats.RPCServerResponseBytesMinuteView:     true,
-		grpcstats.RPCServerResponseBytesView:           true,
-		grpcstats.RPCServerResponseCountHourView:       true,
-		grpcstats.RPCServerResponseCountMinuteView:     true,
-		grpcstats.RPCServerResponseCountView:           true,
-		grpcstats.RPCServerServerElapsedTimeHourView:   true,
-		grpcstats.RPCServerServerElapsedTimeMinuteView: true,
-		grpcstats.RPCServerServerElapsedTimeView:       true,
-		grpcstats.RPCServerStartedCountHourView:        true,
-		grpcstats.RPCServerStartedCountMinuteView:      true,
+	viewType = map[*view.View]bool{
+		grpcstats.RPCClientErrorCountView:        false,
+		grpcstats.RPCClientRequestBytesView:      false,
+		grpcstats.RPCClientRequestCountView:      false,
+		grpcstats.RPCClientResponseBytesView:     false,
+		grpcstats.RPCClientResponseCountView:     false,
+		grpcstats.RPCClientRoundTripLatencyView:  false,
+		grpcstats.RPCServerErrorCountView:        true,
+		grpcstats.RPCServerRequestBytesView:      true,
+		grpcstats.RPCServerRequestCountView:      true,
+		grpcstats.RPCServerResponseBytesView:     true,
+		grpcstats.RPCServerResponseCountView:     true,
+		grpcstats.RPCServerServerElapsedTimeView: true,
 	}
 )
 
@@ -93,7 +61,7 @@ func init() {
 			log.Printf("error subscribing to view %q: %v", view.Name(), err)
 		}
 	}
-	stats.RegisterExporter(snapExporter{})
+	view.RegisterExporter(snapExporter{})
 }
 
 // RpczHandler is a handler for /rpcz.
@@ -254,6 +222,7 @@ func (s *statGroup) Less(i, j int) bool {
 // statSnapshot holds the data items that are presented in a single row of RPC
 // stat information.
 type statSnapshot struct {
+	// TODO: compute hour/minute values from cumulative
 	Method           string
 	Received         bool
 	CountMinute      int
@@ -286,7 +255,7 @@ type methodKey struct {
 
 type snapExporter struct{}
 
-func (s snapExporter) ExportView(vd *stats.ViewData) {
+func (s snapExporter) ExportView(vd *view.Data) {
 	received, ok := viewType[vd.View]
 	if !ok {
 		return
@@ -316,7 +285,7 @@ func (s snapExporter) ExportView(vd *stats.ViewData) {
 	for _, row := range vd.Rows {
 		var method string
 		for _, tag := range row.Tags {
-			if tag.Key.Name() == "method" {
+			if tag.Key == grpcstats.KeyMethod {
 				method = tag.Value
 				break
 			}
@@ -330,137 +299,62 @@ func (s snapExporter) ExportView(vd *stats.ViewData) {
 		}
 
 		var (
-			dist  = &stats.DistributionData{}
+			dist  = &view.DistributionData{}
 			sum   float64
 			count float64
 		)
 		switch v := row.Data.(type) {
-		case *stats.CountData:
+		case *view.CountData:
 			sum = float64(*v)
 			count = float64(*v)
-		case *stats.DistributionData:
+		case *view.DistributionData:
 			dist = v
 			sum = v.Sum()
 			count = float64(v.Count)
-		case *stats.MeanData:
+		case *view.MeanData:
 			sum = v.Sum()
 			count = v.Count
-		case *stats.SumData:
+		case *view.SumData:
 			sum = float64(*v)
 			count = float64(*v)
 		}
 
 		// Update field of s corresponding to the view.
 		switch vd.View {
-		case grpcstats.RPCClientErrorCountHourView:
-			s.ErrorsHour = int(count)
-		case grpcstats.RPCClientErrorCountMinuteView:
-			s.ErrorsMinute = int(count)
 		case grpcstats.RPCClientErrorCountView:
 			s.ErrorsTotal = int(count)
 
-		case grpcstats.RPCClientRoundTripLatencyHourView:
-			s.AvgLatencyHour = convertTime(sum / count)
-			s.MaxLatencyHour = convertTime(dist.Max)
-		case grpcstats.RPCClientRoundTripLatencyMinuteView:
-			s.AvgLatencyMinute = convertTime(sum / count)
-			s.MaxLatencyMinute = convertTime(dist.Max)
 		case grpcstats.RPCClientRoundTripLatencyView:
 			s.AvgLatencyTotal = convertTime(sum / count)
 			s.MaxLatencyTotal = convertTime(dist.Max)
 
-		case grpcstats.RPCClientRequestBytesHourView:
-			s.OutputRateHour = computeRate(3600, sum)
-		case grpcstats.RPCClientRequestBytesMinuteView:
-			s.OutputRateMinute = computeRate(60, sum)
 		case grpcstats.RPCClientRequestBytesView:
 			s.OutputRateTotal = computeRate(0, sum)
 
-		case grpcstats.RPCClientResponseBytesHourView:
-			s.InputRateHour = computeRate(3600, sum)
-		case grpcstats.RPCClientResponseBytesMinuteView:
-			s.InputRateMinute = computeRate(60, sum)
 		case grpcstats.RPCClientResponseBytesView:
 			s.InputRateTotal = computeRate(0, sum)
 
-		case grpcstats.RPCClientStartedCountHourView:
-			s.CountHour = int(count)
-			s.RPCRateHour = computeRate(3600, count)
-		case grpcstats.RPCClientStartedCountMinuteView:
-			s.CountMinute = int(count)
-			s.RPCRateMinute = computeRate(60, count)
-
-		case grpcstats.RPCClientFinishedCountHourView:
-			// currently unused
-		case grpcstats.RPCClientFinishedCountMinuteView:
-			// currently unused
-
-		case grpcstats.RPCClientRequestCountHourView:
-		case grpcstats.RPCClientRequestCountMinuteView:
 		case grpcstats.RPCClientRequestCountView:
 			s.CountTotal = int(count)
 			s.RPCRateTotal = computeRate(0, count)
 
-		case grpcstats.RPCClientResponseCountHourView:
-			// currently unused
-		case grpcstats.RPCClientResponseCountMinuteView:
-			// currently unused
 		case grpcstats.RPCClientResponseCountView:
 			// currently unused
 
-		case grpcstats.RPCServerErrorCountHourView:
-			s.ErrorsHour = int(count)
-		case grpcstats.RPCServerErrorCountMinuteView:
-			s.ErrorsMinute = int(count)
 		case grpcstats.RPCServerErrorCountView:
 			s.ErrorsTotal = int(count)
 
-		case grpcstats.RPCServerServerElapsedTimeHourView:
-			s.AvgLatencyHour = convertTime(sum / count)
-			s.MaxLatencyHour = convertTime(dist.Max)
-		case grpcstats.RPCServerServerElapsedTimeMinuteView:
-			s.AvgLatencyMinute = convertTime(sum / count)
-			s.MaxLatencyMinute = convertTime(dist.Max)
 		case grpcstats.RPCServerServerElapsedTimeView:
 			s.AvgLatencyTotal = convertTime(sum / count)
 			s.MaxLatencyTotal = convertTime(dist.Max)
 
-		case grpcstats.RPCServerRequestBytesHourView:
-			s.InputRateHour = computeRate(3600, sum)
-		case grpcstats.RPCServerRequestBytesMinuteView:
-			s.InputRateMinute = computeRate(60, sum)
-		case grpcstats.RPCServerRequestBytesView:
-			s.InputRateTotal = computeRate(0, sum)
-
-		case grpcstats.RPCServerResponseBytesHourView:
-			s.OutputRateHour = computeRate(3600, sum)
-		case grpcstats.RPCServerResponseBytesMinuteView:
-			s.OutputRateMinute = computeRate(60, sum)
 		case grpcstats.RPCServerResponseBytesView:
 			s.OutputRateTotal = computeRate(0, sum)
 
-		case grpcstats.RPCServerStartedCountHourView:
-			s.CountHour = int(count)
-			s.RPCRateHour = computeRate(3600, count)
-		case grpcstats.RPCServerStartedCountMinuteView:
-			s.CountMinute = int(count)
-			s.RPCRateMinute = computeRate(60, count)
-
-		case grpcstats.RPCServerFinishedCountHourView:
-			// currently unused
-		case grpcstats.RPCServerFinishedCountMinuteView:
-			// currently unused
-
-		case grpcstats.RPCServerRequestCountHourView:
-		case grpcstats.RPCServerRequestCountMinuteView:
 		case grpcstats.RPCServerRequestCountView:
 			s.CountTotal = int(count)
 			s.RPCRateTotal = computeRate(0, count)
 
-		case grpcstats.RPCServerResponseCountHourView:
-			// currently unused
-		case grpcstats.RPCServerResponseCountMinuteView:
-			// currently unused
 		case grpcstats.RPCServerResponseCountView:
 			// currently unused
 		}


Test patch:
diff --git a/exporter/prometheus/prometheus_test.go b/exporter/prometheus/prometheus_test.go
index 3e1c061..db9a19c 100644
--- a/exporter/prometheus/prometheus_test.go
+++ b/exporter/prometheus/prometheus_test.go
@@ -20,55 +20,47 @@ import (
 	"time"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 
 	"github.com/prometheus/client_golang/prometheus"
 )
 
-func newView(agg stats.Aggregation, window stats.Window) *stats.View {
-	m, _ := stats.NewMeasureInt64("tests/foo1", "bytes", "byte")
-	view, _ := stats.NewView("foo", "bar", nil, m, agg, window)
+func newView(agg view.Aggregation) *view.View {
+	m, _ := stats.Int64("tests/foo1", "bytes", "byte")
+	view, _ := view.New("foo", "bar", nil, m, agg)
 	return view
 }
 
 func TestOnlyCumulativeWindowSupported(t *testing.T) {
 	// See Issue https://github.com/census-instrumentation/opencensus-go/issues/214.
-	count1 := stats.CountData(1)
-	mean1 := stats.MeanData{
+	count1 := view.CountData(1)
+	mean1 := view.MeanData{
 		Mean:  4.5,
 		Count: 5,
 	}
 	tests := []struct {
-		vds  *stats.ViewData
+		vds  *view.Data
 		want int
 	}{
 		0: {
-			vds: &stats.ViewData{
-				View: newView(stats.CountAggregation{}, stats.Cumulative{}),
+			vds: &view.Data{
+				View: newView(view.CountAggregation{}),
 			},
 			want: 0, // no rows present
 		},
 		1: {
-			vds: &stats.ViewData{
-				View: newView(stats.CountAggregation{}, stats.Cumulative{}),
-				Rows: []*stats.Row{
+			vds: &view.Data{
+				View: newView(view.CountAggregation{}),
+				Rows: []*view.Row{
 					{nil, &count1},
 				},
 			},
 			want: 1,
 		},
 		2: {
-			vds: &stats.ViewData{
-				View: newView(stats.CountAggregation{}, stats.Interval{}),
-				Rows: []*stats.Row{
-					{nil, &count1},
-				},
-			},
-			want: 0,
-		},
-		3: {
-			vds: &stats.ViewData{
-				View: newView(stats.MeanAggregation{}, stats.Cumulative{}),
-				Rows: []*stats.Row{
+			vds: &view.Data{
+				View: newView(view.MeanAggregation{}),
+				Rows: []*view.Row{
 					{nil, &mean1},
 				},
 			},
@@ -136,11 +128,11 @@ func TestCollectNonRacy(t *testing.T) {
 		}()
 
 		for i := 0; i < 1e3; i++ {
-			count1 := stats.CountData(1)
-			mean1 := &stats.MeanData{Mean: 4.5, Count: 5}
-			vds := []*stats.ViewData{
-				{View: newView(stats.MeanAggregation{}, stats.Cumulative{}), Rows: []*stats.Row{{nil, mean1}}},
-				{View: newView(stats.CountAggregation{}, stats.Cumulative{}), Rows: []*stats.Row{{nil, &count1}}},
+			count1 := view.CountData(1)
+			mean1 := &view.MeanData{Mean: 4.5, Count: 5}
+			vds := []*view.Data{
+				{View: newView(view.MeanAggregation{}), Rows: []*view.Row{{nil, mean1}}},
+				{View: newView(view.CountAggregation{}), Rows: []*view.Row{{nil, &count1}}},
 			}
 			for _, v := range vds {
 				exp.ExportView(v)
diff --git a/exporter/stackdriver/example_test.go b/exporter/stackdriver/example_test.go
index eb5d06e..5850ef7 100644
--- a/exporter/stackdriver/example_test.go
+++ b/exporter/stackdriver/example_test.go
@@ -21,7 +21,7 @@ import (
 	"go.opencensus.io/exporter/stackdriver"
 	"go.opencensus.io/plugin/ochttp"
 	"go.opencensus.io/plugin/ochttp/propagation/google"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/trace"
 )
 
@@ -32,7 +32,7 @@ func Example() {
 	}
 
 	// Export to Stackdriver Monitoring.
-	stats.RegisterExporter(exporter)
+	view.RegisterExporter(exporter)
 
 	// Export to Stackdriver Trace.
 	trace.RegisterExporter(exporter)
diff --git a/exporter/stackdriver/stats_test.go b/exporter/stackdriver/stats_test.go
index 5ed0a81..3ffbe70 100644
--- a/exporter/stackdriver/stats_test.go
+++ b/exporter/stackdriver/stats_test.go
@@ -20,9 +20,10 @@ import (
 	"testing"
 	"time"
 
-	monitoring "cloud.google.com/go/monitoring/apiv3"
-	timestamp "github.com/golang/protobuf/ptypes/timestamp"
+	"cloud.google.com/go/monitoring/apiv3"
+	"github.com/golang/protobuf/ptypes/timestamp"
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 	"google.golang.org/api/option"
 	distributionpb "google.golang.org/genproto/googleapis/api/distribution"
@@ -72,46 +73,37 @@ func TestNewExporterSingletonPerProcess(t *testing.T) {
 }
 
 func TestExporter_makeReq(t *testing.T) {
-	m, err := stats.NewMeasureFloat64("test-measure", "measure desc", "unit")
+	m, err := stats.Float64("test-measure", "measure desc", "unit")
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer stats.DeleteMeasure(m)
 
 	key, err := tag.NewKey("test_key")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	cumView, err := stats.NewView("cumview", "desc", []tag.Key{key}, m, stats.CountAggregation{}, stats.Cumulative{})
+	v, err := view.New("testview", "desc", []tag.Key{key}, m, view.CountAggregation{})
 	if err != nil {
 		t.Fatal(err)
 	}
-	if err := stats.RegisterView(cumView); err != nil {
-		t.Fatal(err)
-	}
-	defer stats.UnregisterView(cumView)
 
-	distView, err := stats.NewView("distview", "desc", nil, m, stats.DistributionAggregation([]float64{2, 4, 7}), stats.Interval{})
+	distView, err := view.New("distview", "desc", nil, m, view.DistributionAggregation([]float64{2, 4, 7}))
 	if err != nil {
 		t.Fatal(err)
 	}
-	if err := stats.RegisterView(distView); err != nil {
-		t.Fatal(err)
-	}
-	defer stats.UnregisterView(distView)
 
 	start := time.Now()
 	end := start.Add(time.Minute)
-	count1 := stats.CountData(10)
-	count2 := stats.CountData(16)
-	sum1 := stats.SumData(5.5)
-	sum2 := stats.SumData(-11.1)
-	mean1 := stats.MeanData{
+	count1 := view.CountData(10)
+	count2 := view.CountData(16)
+	sum1 := view.SumData(5.5)
+	sum2 := view.SumData(-11.1)
+	mean1 := view.MeanData{
 		Mean:  3.3,
 		Count: 7,
 	}
-	mean2 := stats.MeanData{
+	mean2 := view.MeanData{
 		Mean:  -7.7,
 		Count: 5,
 	}
@@ -120,19 +112,19 @@ func TestExporter_makeReq(t *testing.T) {
 	tests := []struct {
 		name   string
 		projID string
-		vd     *stats.ViewData
+		vd     *view.Data
 		want   []*monitoringpb.CreateTimeSeriesRequest
 	}{
 		{
-			name:   "count agg + cum timeline",
+			name:   "count agg + timeline",
 			projID: "proj-id",
-			vd:     newTestCumViewData(cumView, start, end, &count1, &count2),
+			vd:     newTestViewData(v, start, end, &count1, &count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{{
 				Name: monitoring.MetricProjectPath("proj-id"),
 				TimeSeries: []*monitoringpb.TimeSeries{
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-1",
 								opencensusTaskKey: taskValue,
@@ -161,7 +153,7 @@ func TestExporter_makeReq(t *testing.T) {
 					},
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-2",
 								opencensusTaskKey: taskValue,
@@ -192,15 +184,15 @@ func TestExporter_makeReq(t *testing.T) {
 			}},
 		},
 		{
-			name:   "sum agg + cum timeline",
+			name:   "sum agg + timeline",
 			projID: "proj-id",
-			vd:     newTestCumViewData(cumView, start, end, &sum1, &sum2),
+			vd:     newTestViewData(v, start, end, &sum1, &sum2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{{
 				Name: monitoring.MetricProjectPath("proj-id"),
 				TimeSeries: []*monitoringpb.TimeSeries{
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-1",
 								opencensusTaskKey: taskValue,
@@ -229,7 +221,7 @@ func TestExporter_makeReq(t *testing.T) {
 					},
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-2",
 								opencensusTaskKey: taskValue,
@@ -260,15 +252,15 @@ func TestExporter_makeReq(t *testing.T) {
 			}},
 		},
 		{
-			name:   "mean agg + cum timeline",
+			name:   "mean agg + timeline",
 			projID: "proj-id",
-			vd:     newTestCumViewData(cumView, start, end, &mean1, &mean2),
+			vd:     newTestViewData(v, start, end, &mean1, &mean2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{{
 				Name: monitoring.MetricProjectPath("proj-id"),
 				TimeSeries: []*monitoringpb.TimeSeries{
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-1",
 								opencensusTaskKey: taskValue,
@@ -309,7 +301,7 @@ func TestExporter_makeReq(t *testing.T) {
 					},
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-2",
 								opencensusTaskKey: taskValue,
@@ -355,7 +347,7 @@ func TestExporter_makeReq(t *testing.T) {
 			name:   "dist agg + time window",
 			projID: "proj-id",
 			vd:     newTestDistViewData(distView, start, end),
-			want:   []*monitoringpb.CreateTimeSeriesRequest{},
+			want:   nil, //TODO: add expectation for distribution
 		},
 	}
 	for _, tt := range tests {
@@ -364,7 +356,10 @@ func TestExporter_makeReq(t *testing.T) {
 				o:         Options{ProjectID: tt.projID},
 				taskValue: taskValue,
 			}
-			resps := e.makeReq([]*stats.ViewData{tt.vd}, maxTimeSeriesPerUpload)
+			resps := e.makeReq([]*view.Data{tt.vd}, maxTimeSeriesPerUpload)
+			if tt.want == nil {
+				t.Skip("Missing expectation")
+			}
 			if got, want := len(resps), len(tt.want); got != want {
 				t.Fatalf("%v: Exporter.makeReq() returned %d responses; want %d", tt.name, got, want)
 			}
@@ -379,25 +374,24 @@ func TestExporter_makeReq(t *testing.T) {
 }
 
 func TestExporter_makeReq_batching(t *testing.T) {
-	m, err := stats.NewMeasureFloat64("test-measure", "measure desc", "unit")
+	m, err := stats.Float64("test-measure/makeReq_batching", "measure desc", "unit")
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer stats.DeleteMeasure(m)
 
 	key, err := tag.NewKey("test_key")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	view, err := stats.NewView("view", "desc", []tag.Key{key}, m, stats.CountAggregation{}, stats.Cumulative{})
+	v, err := view.New("view", "desc", []tag.Key{key}, m, view.CountAggregation{})
 	if err != nil {
 		t.Fatal(err)
 	}
-	if err := stats.RegisterView(view); err != nil {
+	if err := view.Register(v); err != nil {
 		t.Fatal(err)
 	}
-	defer stats.UnregisterView(view)
+	defer view.Unregister(v)
 
 	tests := []struct {
 		name      string
@@ -429,13 +423,13 @@ func TestExporter_makeReq_batching(t *testing.T) {
 		},
 	}
 
-	count1 := stats.CountData(10)
-	count2 := stats.CountData(16)
+	count1 := view.CountData(10)
+	count2 := view.CountData(16)
 
 	for _, tt := range tests {
-		var vds []*stats.ViewData
+		var vds []*view.Data
 		for i := 0; i < tt.iter; i++ {
-			vds = append(vds, newTestCumViewData(view, time.Now(), time.Now(), &count1, &count2))
+			vds = append(vds, newTestViewData(v, time.Now(), time.Now(), &count1, &count2))
 		}
 
 		e := &statsExporter{}
@@ -460,90 +454,62 @@ func TestEqualAggWindowTagKeys(t *testing.T) {
 	tests := []struct {
 		name    string
 		md      *metricpb.MetricDescriptor
-		agg     stats.Aggregation
+		agg     view.Aggregation
 		keys    []tag.Key
-		window  stats.Window
 		wantErr bool
 	}{
 		{
-			name: "count agg + cum",
+			name: "count agg",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_INT64,
 				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
 			},
-			agg:     stats.CountAggregation{},
-			window:  stats.Cumulative{},
+			agg:     view.CountAggregation{},
 			wantErr: false,
 		},
 		{
-			name: "sum agg + cum",
+			name: "sum agg",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_DOUBLE,
 				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
 			},
-			agg:     stats.SumAggregation{},
-			window:  stats.Cumulative{},
+			agg:     view.SumAggregation{},
 			wantErr: false,
 		},
 		{
-			name: "mean agg + cum",
+			name: "mean agg",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_DISTRIBUTION,
 				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
 			},
-			agg:     stats.MeanAggregation{},
-			window:  stats.Cumulative{},
+			agg:     view.MeanAggregation{},
 			wantErr: false,
 		},
 		{
-			name: "distribution agg + cum - mismatch",
+			name: "distribution agg - mismatch",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_DISTRIBUTION,
 				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
 			},
-			agg:     stats.CountAggregation{},
-			window:  stats.Cumulative{},
+			agg:     view.CountAggregation{},
 			wantErr: true,
 		},
 		{
-			name: "mean agg + cum - mismatch",
+			name: "mean agg - mismatch",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_DOUBLE,
 				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
 			},
-			agg:     stats.MeanAggregation{},
-			window:  stats.Cumulative{},
-			wantErr: true,
-		},
-		{
-			name: "distribution agg + delta",
-			md: &metricpb.MetricDescriptor{
-				MetricKind: metricpb.MetricDescriptor_DELTA,
-				ValueType:  metricpb.MetricDescriptor_DISTRIBUTION,
-				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
-			},
-			agg:     stats.DistributionAggregation{},
-			window:  stats.Interval{},
-			wantErr: false,
-		},
-		{
-			name: "distribution agg + cum",
-			md: &metricpb.MetricDescriptor{
-				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
-				ValueType:  metricpb.MetricDescriptor_DISTRIBUTION,
-				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
-			},
-			agg:     stats.DistributionAggregation{},
-			window:  stats.Interval{},
+			agg:     view.MeanAggregation{},
 			wantErr: true,
 		},
 		{
-			name: "distribution agg + cum with keys",
+			name: "distribution agg with keys",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_DISTRIBUTION,
@@ -553,42 +519,39 @@ func TestEqualAggWindowTagKeys(t *testing.T) {
 					{Key: opencensusTaskKey},
 				},
 			},
-			agg:     stats.DistributionAggregation{},
-			window:  stats.Cumulative{},
+			agg:     view.DistributionAggregation{},
 			keys:    []tag.Key{key1, key2},
 			wantErr: false,
 		},
 		{
-			name: "distribution agg + cum with keys -- mismatch",
+			name: "distribution agg with keys -- mismatch",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_DISTRIBUTION,
 			},
-			agg:     stats.DistributionAggregation{},
-			window:  stats.Cumulative{},
+			agg:     view.DistributionAggregation{},
 			keys:    []tag.Key{key1, key2},
 			wantErr: true,
 		},
 		{
-			name: "count agg + cum with pointers",
+			name: "count agg with pointers",
 			md: &metricpb.MetricDescriptor{
 				MetricKind: metricpb.MetricDescriptor_CUMULATIVE,
 				ValueType:  metricpb.MetricDescriptor_INT64,
 				Labels:     []*label.LabelDescriptor{{Key: opencensusTaskKey}},
 			},
-			agg:     &stats.CountAggregation{},
-			window:  &stats.Cumulative{},
+			agg:     &view.CountAggregation{},
 			wantErr: false,
 		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			err := equalAggWindowTagKeys(tt.md, tt.agg, tt.window, tt.keys)
+			err := equalAggTagKeys(tt.md, tt.agg, tt.keys)
 			if err != nil && !tt.wantErr {
-				t.Errorf("equalAggWindowTagKeys() = %q; want no error", err)
+				t.Errorf("equalAggTagKeys() = %q; want no error", err)
 			}
 			if err == nil && tt.wantErr {
-				t.Errorf("equalAggWindowTagKeys() = %q; want error", err)
+				t.Errorf("equalAggTagKeys() = %q; want error", err)
 			}
 
 		})
@@ -605,19 +568,18 @@ func TestExporter_createMeasure(t *testing.T) {
 	}()
 
 	key, _ := tag.NewKey("test-key-one")
-	m, err := stats.NewMeasureFloat64("test-measure", "measure desc", "unit")
+	m, err := stats.Float64("test-measure/TestExporter_createMeasure", "measure desc", "unit")
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer stats.DeleteMeasure(m)
 
-	view, err := stats.NewView("cumview", "desc", []tag.Key{key}, m, stats.CountAggregation{}, stats.Cumulative{})
+	v, err := view.New("testview", "desc", []tag.Key{key}, m, view.CountAggregation{})
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	data := stats.CountData(0)
-	vd := newTestCumViewData(view, time.Now(), time.Now(), &data, &data)
+	data := view.CountData(0)
+	vd := newTestViewData(v, time.Now(), time.Now(), &data, &data)
 
 	e := &statsExporter{
 		createdViews: make(map[string]*metricpb.MetricDescriptor),
@@ -660,30 +622,29 @@ func TestExporter_createMeasure(t *testing.T) {
 }
 
 func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
-	m, err := stats.NewMeasureFloat64("test-measure", "measure desc", "unit")
+	m, err := stats.Float64("test-measure/TestExporter_makeReq_withCustomMonitoredResource", "measure desc", "unit")
 	if err != nil {
 		t.Fatal(err)
 	}
-	defer stats.DeleteMeasure(m)
 
 	key, err := tag.NewKey("test_key")
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	cumView, err := stats.NewView("cumview", "desc", []tag.Key{key}, m, stats.CountAggregation{}, stats.Cumulative{})
+	v, err := view.New("testview", "desc", []tag.Key{key}, m, view.CountAggregation{})
 	if err != nil {
 		t.Fatal(err)
 	}
-	if err := cumView.Subscribe(); err != nil {
+	if err := v.Subscribe(); err != nil {
 		t.Fatal(err)
 	}
-	defer cumView.Unsubscribe()
+	defer v.Unsubscribe()
 
 	start := time.Now()
 	end := start.Add(time.Minute)
-	count1 := stats.CountData(10)
-	count2 := stats.CountData(16)
+	count1 := view.CountData(10)
+	count2 := view.CountData(16)
 	taskValue := getTaskValue()
 
 	resource := &monitoredrespb.MonitoredResource{
@@ -694,19 +655,19 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 	tests := []struct {
 		name   string
 		projID string
-		vd     *stats.ViewData
+		vd     *view.Data
 		want   []*monitoringpb.CreateTimeSeriesRequest
 	}{
 		{
-			name:   "count agg + cum timeline",
+			name:   "count agg timeline",
 			projID: "proj-id",
-			vd:     newTestCumViewData(cumView, start, end, &count1, &count2),
+			vd:     newTestViewData(v, start, end, &count1, &count2),
 			want: []*monitoringpb.CreateTimeSeriesRequest{{
 				Name: monitoring.MetricProjectPath("proj-id"),
 				TimeSeries: []*monitoringpb.TimeSeries{
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-1",
 								opencensusTaskKey: taskValue,
@@ -733,7 +694,7 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 					},
 					{
 						Metric: &metricpb.Metric{
-							Type: "custom.googleapis.com/opencensus/cumview",
+							Type: "custom.googleapis.com/opencensus/testview",
 							Labels: map[string]string{
 								"test_key":        "test-value-2",
 								opencensusTaskKey: taskValue,
@@ -768,7 +729,7 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 				o:         Options{ProjectID: tt.projID, Resource: resource},
 				taskValue: taskValue,
 			}
-			resps := e.makeReq([]*stats.ViewData{tt.vd}, maxTimeSeriesPerUpload)
+			resps := e.makeReq([]*view.Data{tt.vd}, maxTimeSeriesPerUpload)
 			if got, want := len(resps), len(tt.want); got != want {
 				t.Fatalf("%v: Exporter.makeReq() returned %d responses; want %d", tt.name, got, want)
 			}
@@ -782,13 +743,13 @@ func TestExporter_makeReq_withCustomMonitoredResource(t *testing.T) {
 	}
 }
 
-func newTestCumViewData(v *stats.View, start, end time.Time, data1, data2 stats.AggregationData) *stats.ViewData {
+func newTestViewData(v *view.View, start, end time.Time, data1, data2 view.AggregationData) *view.Data {
 	key, _ := tag.NewKey("test-key")
 	tag1 := tag.Tag{Key: key, Value: "test-value-1"}
 	tag2 := tag.Tag{Key: key, Value: "test-value-2"}
-	return &stats.ViewData{
+	return &view.Data{
 		View: v,
-		Rows: []*stats.Row{
+		Rows: []*view.Row{
 			{
 				Tags: []tag.Tag{tag1},
 				Data: data1,
@@ -803,11 +764,11 @@ func newTestCumViewData(v *stats.View, start, end time.Time, data1, data2 stats.
 	}
 }
 
-func newTestDistViewData(v *stats.View, start, end time.Time) *stats.ViewData {
-	return &stats.ViewData{
+func newTestDistViewData(v *view.View, start, end time.Time) *view.Data {
+	return &view.Data{
 		View: v,
-		Rows: []*stats.Row{
-			{Data: &stats.DistributionData{
+		Rows: []*view.Row{
+			{Data: &view.DistributionData{
 				Count:           5,
 				Min:             1,
 				Max:             7,
diff --git a/plugin/grpc/grpcstats/client_handler_test.go b/plugin/grpc/grpcstats/client_handler_test.go
index 39769c1..cd9bd48 100644
--- a/plugin/grpc/grpcstats/client_handler_test.go
+++ b/plugin/grpc/grpcstats/client_handler_test.go
@@ -23,7 +23,7 @@ import (
 
 	"golang.org/x/net/context"
 
-	istats "go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 
 	"google.golang.org/grpc/stats"
@@ -39,8 +39,8 @@ func TestClientDefaultCollections(t *testing.T) {
 	}
 
 	type wantData struct {
-		v    func() *istats.View
-		rows []*istats.Row
+		v    func() *view.View
+		rows []*view.Row
 	}
 	type rpc struct {
 		tags        []tagPair
@@ -73,44 +73,44 @@ func TestClientDefaultCollections(t *testing.T) {
 			},
 			[]*wantData{
 				{
-					func() *istats.View { return RPCClientRequestCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientRequestCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 1, 1, 1, 0),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientResponseCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientResponseCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 1, 1, 1, 0),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientRequestBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientRequestBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 10, 10, 10, 0),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientResponseBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientResponseBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 10, 10, 10, 0),
 						},
@@ -150,34 +150,34 @@ func TestClientDefaultCollections(t *testing.T) {
 			},
 			[]*wantData{
 				{
-					func() *istats.View { return RPCClientErrorCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientErrorCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyStatus, Value: "Canceled"},
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyStatus, Value: "Canceled"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newMeanData(1, 1),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientRequestCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientRequestCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 2, 2, 3, 2.5, 0.5),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientResponseCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientResponseCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 2, 1, 2, 1.5, 0.5),
 						},
@@ -230,63 +230,63 @@ func TestClientDefaultCollections(t *testing.T) {
 			},
 			[]*wantData{
 				{
-					func() *istats.View { return RPCClientErrorCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientErrorCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyStatus, Value: "Canceled"},
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyStatus, Value: "Canceled"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newMeanData(1, 1),
 						},
 						{
 							[]tag.Tag{
-								{Key: keyStatus, Value: "Aborted"},
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyStatus, Value: "Aborted"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newMeanData(1, 1),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientRequestCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientRequestCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 3, 2, 3, 2.666666666, 0.333333333*2),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientResponseCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientResponseCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 3, 1, 2, 1.333333333, 0.333333333*2),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientRequestBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientRequestBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 1, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 8, 1, 65536, 13696.125, 481423542.982143*7),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCClientResponseBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCClientResponseBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 4, 1, 16384, 4864.25, 59678208.25*3),
 						},
@@ -358,7 +358,7 @@ func TestClientDefaultCollections(t *testing.T) {
 }
 
 // containsRow returns true if rows contain r.
-func containsRow(rows []*istats.Row, r *istats.Row) bool {
+func containsRow(rows []*view.Row, r *view.Row) bool {
 	for _, x := range rows {
 		if r.Equal(x) {
 			return true
diff --git a/plugin/grpc/grpcstats/client_metrics_test.go b/plugin/grpc/grpcstats/client_metrics_test.go
index 3756eb8..9d59d87 100644
--- a/plugin/grpc/grpcstats/client_metrics_test.go
+++ b/plugin/grpc/grpcstats/client_metrics_test.go
@@ -20,7 +20,7 @@ import (
 	"runtime"
 	"testing"
 
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 func TestViewsAggregationsConform(t *testing.T) {
@@ -30,7 +30,7 @@ func TestViewsAggregationsConform(t *testing.T) {
 	// https://github.com/census-instrumentation/opencensus-java/blob/2b464864e3dd3f80e8e4c9dc72fccc225444a939/contrib/grpc_metrics/src/main/java/io/opencensus/contrib/grpc/metrics/RpcViewConstants.java#L113-L658
 	// Add any other defined views to be type checked during tests to ensure we don't regress.
 
-	assertTypeOf := func(v *stats.View, wantSample stats.Aggregation) {
+	assertTypeOf := func(v *view.View, wantSample view.Aggregation) {
 		aggregation := v.Aggregation()
 		gotValue := reflect.ValueOf(aggregation)
 		wantValue := reflect.ValueOf(wantSample)
@@ -40,33 +40,17 @@ func TestViewsAggregationsConform(t *testing.T) {
 		}
 	}
 
-	assertTypeOf(RPCClientErrorCountView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientRoundTripLatencyView, stats.DistributionAggregation{})
-	assertTypeOf(RPCClientRequestBytesView, stats.DistributionAggregation{})
-	assertTypeOf(RPCClientResponseBytesView, stats.DistributionAggregation{})
-	assertTypeOf(RPCClientRequestCountView, stats.DistributionAggregation{})
-	assertTypeOf(RPCClientResponseCountView, stats.DistributionAggregation{})
-	assertTypeOf(RPCClientRoundTripLatencyMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientRequestBytesMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientResponseBytesMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientErrorCountMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientStartedCountMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientFinishedCountMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientRequestCountMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientResponseCountMinuteView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientRoundTripLatencyHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientRequestBytesHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientResponseBytesHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientErrorCountHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientStartedCountHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientFinishedCountHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientRequestCountHourView, stats.MeanAggregation{})
-	assertTypeOf(RPCClientResponseCountHourView, stats.MeanAggregation{})
+	assertTypeOf(RPCClientErrorCountView, view.MeanAggregation{})
+	assertTypeOf(RPCClientRoundTripLatencyView, view.DistributionAggregation{})
+	assertTypeOf(RPCClientRequestBytesView, view.DistributionAggregation{})
+	assertTypeOf(RPCClientResponseBytesView, view.DistributionAggregation{})
+	assertTypeOf(RPCClientRequestCountView, view.DistributionAggregation{})
+	assertTypeOf(RPCClientResponseCountView, view.DistributionAggregation{})
 }
 
 func TestStrictViewNames(t *testing.T) {
 	alreadySeen := make(map[string]int)
-	assertName := func(v *stats.View, want string) {
+	assertName := func(v *view.View, want string) {
 		_, _, line, _ := runtime.Caller(1)
 		if prevLine, ok := alreadySeen[v.Name()]; ok {
 			t.Errorf("Item's Name on line %d was already used on line %d", line, prevLine)
@@ -84,20 +68,4 @@ func TestStrictViewNames(t *testing.T) {
 	assertName(RPCClientResponseBytesView, "grpc.io/client/response_bytes/cumulative")
 	assertName(RPCClientRequestCountView, "grpc.io/client/request_count/cumulative")
 	assertName(RPCClientResponseCountView, "grpc.io/client/response_count/cumulative")
-	assertName(RPCClientRoundTripLatencyMinuteView, "grpc.io/client/roundtrip_latency/minute")
-	assertName(RPCClientRequestBytesMinuteView, "grpc.io/client/request_bytes/minute")
-	assertName(RPCClientResponseBytesMinuteView, "grpc.io/client/response_bytes/minute")
-	assertName(RPCClientErrorCountMinuteView, "grpc.io/client/error_count/minute")
-	assertName(RPCClientStartedCountMinuteView, "grpc.io/client/started_count/minute")
-	assertName(RPCClientFinishedCountMinuteView, "grpc.io/client/finished_count/minute")
-	assertName(RPCClientRequestCountMinuteView, "grpc.io/client/request_count/minute")
-	assertName(RPCClientResponseCountMinuteView, "grpc.io/client/response_count/minute")
-	assertName(RPCClientRoundTripLatencyHourView, "grpc.io/client/roundtrip_latency/hour")
-	assertName(RPCClientRequestBytesHourView, "grpc.io/client/request_bytes/hour")
-	assertName(RPCClientResponseBytesHourView, "grpc.io/client/response_bytes/hour")
-	assertName(RPCClientErrorCountHourView, "grpc.io/client/error_count/hour")
-	assertName(RPCClientStartedCountHourView, "grpc.io/client/started_count/hour")
-	assertName(RPCClientFinishedCountHourView, "grpc.io/client/finished_count/hour")
-	assertName(RPCClientRequestCountHourView, "grpc.io/client/request_count/hour")
-	assertName(RPCClientResponseCountHourView, "grpc.io/client/response_count/hour")
 }
diff --git a/plugin/grpc/grpcstats/server_handler_test.go b/plugin/grpc/grpcstats/server_handler_test.go
index d77f7ff..0aae1bb 100644
--- a/plugin/grpc/grpcstats/server_handler_test.go
+++ b/plugin/grpc/grpcstats/server_handler_test.go
@@ -20,7 +20,7 @@ import (
 
 	"golang.org/x/net/context"
 
-	istats "go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
 
 	"google.golang.org/grpc/codes"
@@ -38,8 +38,8 @@ func TestServerDefaultCollections(t *testing.T) {
 	}
 
 	type wantData struct {
-		v    func() *istats.View
-		rows []*istats.Row
+		v    func() *view.View
+		rows []*view.Row
 	}
 	type rpc struct {
 		tags        []tagPair
@@ -72,44 +72,44 @@ func TestServerDefaultCollections(t *testing.T) {
 			},
 			[]*wantData{
 				{
-					func() *istats.View { return RPCServerRequestCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerRequestCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 1, 1, 1, 0),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerResponseCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerResponseCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 1, 1, 1, 0),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerRequestBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerRequestBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 10, 10, 10, 0),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerResponseBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerResponseBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, 10, 10, 10, 0),
 						},
@@ -149,34 +149,34 @@ func TestServerDefaultCollections(t *testing.T) {
 			},
 			[]*wantData{
 				{
-					func() *istats.View { return RPCServerErrorCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerErrorCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyStatus, Value: "Canceled"},
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyStatus, Value: "Canceled"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newCountData(1),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerRequestCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerRequestCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 2, 1, 2, 1.5, 0.5),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerResponseCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerResponseCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 2, 2, 3, 2.5, 0.5),
 						},
@@ -229,63 +229,63 @@ func TestServerDefaultCollections(t *testing.T) {
 			},
 			[]*wantData{
 				{
-					func() *istats.View { return RPCServerErrorCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerErrorCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyStatus, Value: "Canceled"},
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyStatus, Value: "Canceled"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newCountData(1),
 						},
 						{
 							[]tag.Tag{
-								{Key: keyStatus, Value: "Aborted"},
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyStatus, Value: "Aborted"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newCountData(1),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerRequestCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerRequestCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 3, 1, 2, 1.333333333, 0.333333333*2),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerResponseCountView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerResponseCountView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcCountBucketBoundaries, []int64{0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 3, 2, 3, 2.666666666, 0.333333333*2),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerRequestBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerRequestBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 4, 1, 16384, 4864.25, 59678208.25*3),
 						},
 					},
 				},
 				{
-					func() *istats.View { return RPCServerResponseBytesView },
-					[]*istats.Row{
+					func() *view.View { return RPCServerResponseBytesView },
+					[]*view.Row{
 						{
 							[]tag.Tag{
-								{Key: keyMethod, Value: "package.service/method"},
+								{Key: KeyMethod, Value: "package.service/method"},
 							},
 							newDistributionData(rpcBytesBucketBoundaries, []int64{0, 1, 1, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 8, 1, 65536, 13696.125, 481423542.982143*7),
 						},
@@ -356,18 +356,18 @@ func TestServerDefaultCollections(t *testing.T) {
 	}
 }
 
-func newCountData(v int) *istats.CountData {
-	cav := istats.CountData(v)
+func newCountData(v int) *view.CountData {
+	cav := view.CountData(v)
 	return &cav
 }
 
-func newMeanData(count, mean float64) *istats.MeanData {
-	mav := istats.MeanData{Count: count, Mean: mean}
+func newMeanData(count, mean float64) *view.MeanData {
+	mav := view.MeanData{Count: count, Mean: mean}
 	return &mav
 }
 
-func newDistributionData(bounds []float64, countPerBucket []int64, count int64, min, max, mean, sumOfSquaredDev float64) *istats.DistributionData {
-	return &istats.DistributionData{
+func newDistributionData(bounds []float64, countPerBucket []int64, count int64, min, max, mean, sumOfSquaredDev float64) *view.DistributionData {
+	return &view.DistributionData{
 		Count:           count,
 		Min:             min,
 		Max:             max,
diff --git a/plugin/ochttp/client_test.go b/plugin/ochttp/client_test.go
index 8818d75..f0e392b 100644
--- a/plugin/ochttp/client_test.go
+++ b/plugin/ochttp/client_test.go
@@ -23,7 +23,7 @@ import (
 	"testing"
 
 	"go.opencensus.io/plugin/ochttp"
-	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 const reqCount = 5
@@ -41,7 +41,7 @@ func TestClient(t *testing.T) {
 		"opencensus.io/http/client/response_size",
 	}
 	for _, name := range views {
-		v := stats.FindView(name)
+		v := view.Find(name)
 		if v == nil {
 			t.Errorf("view not found %q", name)
 			continue
@@ -87,7 +87,7 @@ func TestClient(t *testing.T) {
 	}
 
 	for _, viewName := range views {
-		v := stats.FindView(viewName)
+		v := view.Find(viewName)
 		if v == nil {
 			t.Errorf("view not found %q", viewName)
 			continue
@@ -104,9 +104,9 @@ func TestClient(t *testing.T) {
 		data := rows[0].Data
 		var count int64
 		switch data := data.(type) {
-		case *stats.CountData:
+		case *view.CountData:
 			count = *(*int64)(data)
-		case *stats.DistributionData:
+		case *view.DistributionData:
 			count = data.Count
 		default:
 			t.Errorf("don't know how to handle data type: %v", data)
diff --git a/plugin/ochttp/propagation/propagation_test.go b/plugin/ochttp/propagation_test.go
similarity index 98%
rename from plugin/ochttp/propagation/propagation_test.go
rename to plugin/ochttp/propagation_test.go
index ae6ec80..aa7dba2 100644
--- a/plugin/ochttp/propagation/propagation_test.go
+++ b/plugin/ochttp/propagation_test.go
@@ -1,4 +1,4 @@
-package propagation_test
+package ochttp
 
 import (
 	"context"
diff --git a/plugin/ochttp/trace_test.go b/plugin/ochttp/trace_test.go
index 0069f36..f437671 100644
--- a/plugin/ochttp/trace_test.go
+++ b/plugin/ochttp/trace_test.go
@@ -117,6 +117,9 @@ func TestTransport_RoundTrip(t *testing.T) {
 }
 
 func TestHandler(t *testing.T) {
+	// TODO(#431): remove SetDefaultSampler
+	trace.SetDefaultSampler(trace.ProbabilitySampler(0.0))
+
 	traceID := [16]byte{16, 84, 69, 170, 120, 67, 188, 139, 242, 6, 177, 32, 0, 16, 0, 0}
 	tests := []struct {
 		header           string
diff --git a/stats/benchmark_test.go b/stats/benchmark_test.go
index 89ee924..9d8d98c 100644
--- a/stats/benchmark_test.go
+++ b/stats/benchmark_test.go
@@ -1,39 +1,25 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package stats
+package stats_test
 
 import (
 	"context"
 	"log"
 	"testing"
+
+	"go.opencensus.io/stats"
+	_ "go.opencensus.io/stats/view"
 )
 
+var m = makeMeasure()
+
 func BenchmarkRecord(b *testing.B) {
-	restart()
-	var m = makeMeasure()
 	var ctx = context.Background()
-	b.ResetTimer()
-
 	for i := 0; i < b.N; i++ {
-		Record(ctx, m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1))
+		stats.Record(ctx, m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1), m.M(1))
 	}
 }
 
-func makeMeasure() *MeasureInt64 {
-	m, err := NewMeasureInt64("m", "test measure", "")
+func makeMeasure() *stats.Int64Measure {
+	m, err := stats.Int64("m", "test measure", "")
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/stats/example_test.go b/stats/example_test.go
index f403389..4d2a250 100644
--- a/stats/example_test.go
+++ b/stats/example_test.go
@@ -17,13 +17,13 @@ package stats_test
 import (
 	"context"
 	"log"
-	"time"
 
 	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
 )
 
 func Example_record() {
-	m, err := stats.NewMeasureInt64("my.org/measure/openconns", "open connections", "")
+	m, err := stats.Int64("my.org/measure/openconns", "open connections", "")
 	if err != nil {
 		log.Fatal(err)
 	}
@@ -32,18 +32,17 @@ func Example_record() {
 }
 
 func Example_view() {
-	m, err := stats.NewMeasureInt64("my.org/measure/openconns", "open connections", "")
+	m, err := stats.Int64("my.org/measure/openconns", "open connections", "")
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	view, err := stats.NewView(
+	view, err := view.New(
 		"my.org/views/openconns",
-		"open connections distribution over one second time window",
+		"open connections",
 		nil,
 		m,
-		stats.DistributionAggregation([]float64{0, 1000, 2000}),
-		stats.Interval{Duration: time.Second},
+		view.DistributionAggregation([]float64{0, 1000, 2000}),
 	)
 	if err != nil {
 		log.Fatal(err)
diff --git a/stats/measure_test.go b/stats/measure_test.go
new file mode 100644
index 0000000..af1a004
--- /dev/null
+++ b/stats/measure_test.go
@@ -0,0 +1,103 @@
+package stats
+
+import (
+	"strings"
+	"testing"
+)
+
+func TestCheckMeasureName(t *testing.T) {
+	tests := []struct {
+		name    string
+		view    string
+		wantErr bool
+	}{
+		{
+			name:    "valid measure name",
+			view:    "my.org/measures/response_size",
+			wantErr: false,
+		},
+		{
+			name:    "long name",
+			view:    strings.Repeat("a", 256),
+			wantErr: true,
+		},
+		{
+			name:    "name with non-ASCII",
+			view:    "my.org/measures/\007",
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if err := checkName(tt.view); (err != nil) != tt.wantErr {
+				t.Errorf("checkName() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func Test_FindMeasure(t *testing.T) {
+	mf1, err := Float64("MF1", "desc MF1", "unit")
+	if err != nil {
+		t.Errorf("stats.Float64(\"MF1\", \"desc MF1\") got error %v, want no error", err)
+	}
+	mf2, err := Float64("MF2", "desc MF2", "unit")
+	if err != nil {
+		t.Errorf("stats.Float64(\"MF2\", \"desc MF2\") got error %v, want no error", err)
+	}
+	mi1, err := Int64("MI1", "desc MI1", "unit")
+	if err != nil {
+		t.Errorf("stats.Int64(\"MI1\", \"desc MI1\") got error %v, want no error", err)
+	}
+
+	type testCase struct {
+		label string
+		name  string
+		m     Measure
+	}
+
+	tcs := []testCase{
+		{
+			"0",
+			mf1.Name(),
+			mf1,
+		},
+		{
+			"1",
+			"MF1",
+			mf1,
+		},
+		{
+			"2",
+			mf2.Name(),
+			mf2,
+		},
+		{
+			"3",
+			"MF2",
+			mf2,
+		},
+		{
+			"4",
+			mi1.Name(),
+			mi1,
+		},
+		{
+			"5",
+			"MI1",
+			mi1,
+		},
+		{
+			"6",
+			"other",
+			nil,
+		},
+	}
+
+	for _, tc := range tcs {
+		m := FindMeasure(tc.name)
+		if m != tc.m {
+			t.Errorf("FindMeasure(%q) got measure %v; want %v", tc.label, m, tc.m)
+		}
+	}
+}
diff --git a/stats/validation_test.go b/stats/validation_test.go
deleted file mode 100644
index 8e68fe9..0000000
--- a/stats/validation_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package stats
-
-import (
-	"strings"
-	"testing"
-)
-
-func Test_checkViewName(t *testing.T) {
-	tests := []struct {
-		name    string
-		view    string
-		wantErr bool
-	}{
-		{
-			name:    "valid view name",
-			view:    "my.org/views/response_size",
-			wantErr: false,
-		},
-		{
-			name:    "long name",
-			view:    strings.Repeat("a", 256),
-			wantErr: true,
-		},
-		{
-			name:    "name with non-ASCII",
-			view:    "my.org/views/\007",
-			wantErr: true,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if err := checkViewName(tt.view); (err != nil) != tt.wantErr {
-				t.Errorf("checkViewName() error = %v, wantErr %v", err, tt.wantErr)
-			}
-		})
-	}
-}
-
-func TestCheckMeasureName(t *testing.T) {
-	tests := []struct {
-		name    string
-		view    string
-		wantErr bool
-	}{
-		{
-			name:    "valid measure name",
-			view:    "my.org/measures/response_size",
-			wantErr: false,
-		},
-		{
-			name:    "long name",
-			view:    strings.Repeat("a", 256),
-			wantErr: true,
-		},
-		{
-			name:    "name with non-ASCII",
-			view:    "my.org/measures/\007",
-			wantErr: true,
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if err := checkMeasureName(tt.view); (err != nil) != tt.wantErr {
-				t.Errorf("checkMeasureName() error = %v, wantErr %v", err, tt.wantErr)
-			}
-		})
-	}
-}
diff --git a/stats/aggregation_data_test.go b/stats/view/aggregation_data_test.go
similarity index 99%
rename from stats/aggregation_data_test.go
rename to stats/view/aggregation_data_test.go
index 1f95d6f..6e9d0e5 100644
--- a/stats/aggregation_data_test.go
+++ b/stats/view/aggregation_data_test.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 //
 
-package stats
+package view
 
 import (
 	"reflect"
diff --git a/stats/collector_test.go b/stats/view/collector_test.go
similarity index 99%
rename from stats/collector_test.go
rename to stats/view/collector_test.go
index 3b5a539..57720c1 100644
--- a/stats/collector_test.go
+++ b/stats/view/collector_test.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package stats
+package view
 
 import (
 	"context"
diff --git a/stats/view/example_test.go b/stats/view/example_test.go
new file mode 100644
index 0000000..9422adb
--- /dev/null
+++ b/stats/view/example_test.go
@@ -0,0 +1,45 @@
+// Copyright 2018, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package view_test
+
+import (
+	"log"
+
+	"go.opencensus.io/stats"
+	"go.opencensus.io/stats/view"
+)
+
+func Example_view() {
+	m, err := stats.Int64("my.org/measure/openconns", "open connections", "")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	v, err := view.New(
+		"my.org/views/openconns",
+		"open connections distribution over one second time window",
+		nil,
+		m,
+		view.DistributionAggregation([]float64{0, 1000, 2000}),
+	)
+	if err != nil {
+		log.Fatal(err)
+	}
+	if err := v.Subscribe(); err != nil {
+		log.Fatal(err)
+	}
+
+	// Use stats.RegisterExporter to export collected data.
+}
diff --git a/stats/view/view_test.go b/stats/view/view_test.go
new file mode 100644
index 0000000..3993dec
--- /dev/null
+++ b/stats/view/view_test.go
@@ -0,0 +1,454 @@
+// Copyright 2017, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package view
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"go.opencensus.io/tag"
+)
+
+func Test_View_MeasureFloat64_AggregationDistribution_WindowCumulative(t *testing.T) {
+	k1, _ := tag.NewKey("k1")
+	k2, _ := tag.NewKey("k2")
+	k3, _ := tag.NewKey("k3")
+	agg1 := DistributionAggregation([]float64{2})
+	view, err := New("VF1", "desc VF1", []tag.Key{k1, k2}, nil, agg1)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	type tagString struct {
+		k tag.Key
+		v string
+	}
+	type record struct {
+		f    float64
+		tags []tagString
+	}
+
+	type testCase struct {
+		label    string
+		records  []record
+		wantRows []*Row
+	}
+
+	tcs := []testCase{
+		{
+			"1",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k1, "v1"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					&DistributionData{
+						2, 1, 5, 3, 8, []int64{1, 1}, agg1,
+					},
+				},
+			},
+		},
+		{
+			"2",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k2, "v2"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					&DistributionData{
+						1, 1, 1, 1, 0, []int64{1, 0}, agg1,
+					},
+				},
+				{
+					[]tag.Tag{{Key: k2, Value: "v2"}},
+					&DistributionData{
+						1, 5, 5, 5, 0, []int64{0, 1}, agg1,
+					},
+				},
+			},
+		},
+		{
+			"3",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k1, "v1"}, {k3, "v3"}}},
+				{1, []tagString{{k1, "v1 other"}}},
+				{5, []tagString{{k2, "v2"}}},
+				{5, []tagString{{k1, "v1"}, {k2, "v2"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					&DistributionData{
+						2, 1, 5, 3, 8, []int64{1, 1}, agg1,
+					},
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1 other"}},
+					&DistributionData{
+						1, 1, 1, 1, 0, []int64{1, 0}, agg1,
+					},
+				},
+				{
+					[]tag.Tag{{Key: k2, Value: "v2"}},
+					&DistributionData{
+						1, 5, 5, 5, 0, []int64{0, 1}, agg1,
+					},
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
+					&DistributionData{
+						1, 5, 5, 5, 0, []int64{0, 1}, agg1,
+					},
+				},
+			},
+		},
+		{
+			"4",
+			[]record{
+				{1, []tagString{{k1, "v1 is a very long value key"}}},
+				{5, []tagString{{k1, "v1 is a very long value key"}, {k3, "v3"}}},
+				{1, []tagString{{k1, "v1 is another very long value key"}}},
+				{1, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
+				{5, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
+				{3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
+				{3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1 is a very long value key"}},
+					&DistributionData{
+						2, 1, 5, 3, 8, []int64{1, 1}, agg1,
+					},
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1 is another very long value key"}},
+					&DistributionData{
+						1, 1, 1, 1, 0, []int64{1, 0}, agg1,
+					},
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1 is a very long value key"}, {Key: k2, Value: "v2 is a very long value key"}},
+					&DistributionData{
+						4, 1, 5, 3, 2.66666666666667 * 3, []int64{1, 3}, agg1,
+					},
+				},
+			},
+		},
+	}
+
+	for _, tc := range tcs {
+		view.clearRows()
+		view.subscribe()
+		for _, r := range tc.records {
+			mods := []tag.Mutator{}
+			for _, t := range r.tags {
+				mods = append(mods, tag.Insert(t.k, t.v))
+			}
+			ctx, err := tag.New(context.Background(), mods...)
+			if err != nil {
+				t.Errorf("%v: NewMap = %v", tc.label, err)
+			}
+			view.addSample(tag.FromContext(ctx), r.f, time.Now())
+		}
+
+		gotRows := view.collectedRows(time.Now())
+		for i, got := range gotRows {
+			if !containsRow(tc.wantRows, got) {
+				t.Errorf("%v-%d: got row %v; want none", tc.label, i, got)
+				break
+			}
+		}
+
+		for i, want := range tc.wantRows {
+			if !containsRow(gotRows, want) {
+				t.Errorf("%v-%d: got none; want row %v", tc.label, i, want)
+				break
+			}
+		}
+	}
+}
+
+func Test_View_MeasureFloat64_AggregationSum(t *testing.T) {
+	k1, _ := tag.NewKey("k1")
+	k2, _ := tag.NewKey("k2")
+	k3, _ := tag.NewKey("k3")
+	view, err := New("VF1", "desc VF1", []tag.Key{k1, k2}, nil, SumAggregation{})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	type tagString struct {
+		k tag.Key
+		v string
+	}
+	type record struct {
+		f    float64
+		tags []tagString
+	}
+
+	tcs := []struct {
+		label    string
+		records  []record
+		wantRows []*Row
+	}{
+		{
+			"1",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k1, "v1"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					newSumData(6),
+				},
+			},
+		},
+		{
+			"2",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k2, "v2"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					newSumData(1),
+				},
+				{
+					[]tag.Tag{{Key: k2, Value: "v2"}},
+					newSumData(5),
+				},
+			},
+		},
+		{
+			"3",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k1, "v1"}, {k3, "v3"}}},
+				{1, []tagString{{k1, "v1 other"}}},
+				{5, []tagString{{k2, "v2"}}},
+				{5, []tagString{{k1, "v1"}, {k2, "v2"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					newSumData(6),
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1 other"}},
+					newSumData(1),
+				},
+				{
+					[]tag.Tag{{Key: k2, Value: "v2"}},
+					newSumData(5),
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
+					newSumData(5),
+				},
+			},
+		},
+	}
+
+	for _, tt := range tcs {
+		view.clearRows()
+		view.subscribe()
+		for _, r := range tt.records {
+			mods := []tag.Mutator{}
+			for _, t := range r.tags {
+				mods = append(mods, tag.Insert(t.k, t.v))
+			}
+			ctx, err := tag.New(context.Background(), mods...)
+			if err != nil {
+				t.Errorf("%v: New = %v", tt.label, err)
+			}
+			view.addSample(tag.FromContext(ctx), r.f, time.Now())
+		}
+
+		gotRows := view.collectedRows(time.Now())
+		for i, got := range gotRows {
+			if !containsRow(tt.wantRows, got) {
+				t.Errorf("%v-%d: got row %v; want none", tt.label, i, got)
+				break
+			}
+		}
+
+		for i, want := range tt.wantRows {
+			if !containsRow(gotRows, want) {
+				t.Errorf("%v-%d: got none; want row %v", tt.label, i, want)
+				break
+			}
+		}
+	}
+}
+
+func Test_View_MeasureFloat64_AggregationMean_WindowCumulative(t *testing.T) {
+	k1, _ := tag.NewKey("k1")
+	k2, _ := tag.NewKey("k2")
+	k3, _ := tag.NewKey("k3")
+	view, err := New("VF1", "desc VF1", []tag.Key{k1, k2}, nil, MeanAggregation{})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	type tagString struct {
+		k tag.Key
+		v string
+	}
+	type record struct {
+		f    float64
+		tags []tagString
+	}
+
+	tcs := []struct {
+		label    string
+		records  []record
+		wantRows []*Row
+	}{
+		{
+			"1",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k1, "v1"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					newMeanData(3, 2),
+				},
+			},
+		},
+		{
+			"2",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k2, "v2"}}},
+				{-0.5, []tagString{{k2, "v2"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					newMeanData(1, 1),
+				},
+				{
+					[]tag.Tag{{Key: k2, Value: "v2"}},
+					newMeanData(2.25, 2),
+				},
+			},
+		},
+		{
+			"3",
+			[]record{
+				{1, []tagString{{k1, "v1"}}},
+				{5, []tagString{{k1, "v1"}, {k3, "v3"}}},
+				{1, []tagString{{k1, "v1 other"}}},
+				{5, []tagString{{k2, "v2"}}},
+				{5, []tagString{{k1, "v1"}, {k2, "v2"}}},
+				{-4, []tagString{{k1, "v1"}, {k2, "v2"}}},
+			},
+			[]*Row{
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}},
+					newMeanData(3, 2),
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1 other"}},
+					newMeanData(1, 1),
+				},
+				{
+					[]tag.Tag{{Key: k2, Value: "v2"}},
+					newMeanData(5, 1),
+				},
+				{
+					[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
+					newMeanData(0.5, 2),
+				},
+			},
+		},
+	}
+
+	for _, tt := range tcs {
+		view.clearRows()
+		view.subscribe()
+		for _, r := range tt.records {
+			mods := []tag.Mutator{}
+			for _, t := range r.tags {
+				mods = append(mods, tag.Insert(t.k, t.v))
+			}
+			ctx, err := tag.New(context.Background(), mods...)
+			if err != nil {
+				t.Errorf("%v: New = %v", tt.label, err)
+			}
+			view.addSample(tag.FromContext(ctx), r.f, time.Now())
+		}
+
+		gotRows := view.collectedRows(time.Now())
+		for i, got := range gotRows {
+			if !containsRow(tt.wantRows, got) {
+				t.Errorf("%v-%d: got row %v; want none", tt.label, i, got)
+				break
+			}
+		}
+
+		for i, want := range tt.wantRows {
+			if !containsRow(gotRows, want) {
+				t.Errorf("%v-%d: got none; want row %v", tt.label, i, want)
+				break
+			}
+		}
+	}
+}
+
+func TestViewSortedKeys(t *testing.T) {
+	k1, _ := tag.NewKey("a")
+	k2, _ := tag.NewKey("b")
+	k3, _ := tag.NewKey("c")
+	ks := []tag.Key{k1, k3, k2}
+
+	v, err := New("sort_keys", "desc sort_keys", ks, nil, MeanAggregation{})
+	if err != nil {
+		t.Fatalf("NewView() = %v", err)
+	}
+
+	want := []string{"a", "b", "c"}
+	vks := v.TagKeys()
+	if len(vks) != len(want) {
+		t.Errorf("Keys = %+v; want %+v", vks, want)
+	}
+
+	for i, v := range want {
+		if got, want := v, vks[i].Name(); got != want {
+			t.Errorf("View name = %q; want %q", got, want)
+		}
+	}
+}
+
+// containsRow returns true if rows contain r.
+func containsRow(rows []*Row, r *Row) bool {
+	for _, x := range rows {
+		if r.Equal(x) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/stats/view/worker_test.go b/stats/view/worker_test.go
new file mode 100644
index 0000000..fd87de4
--- /dev/null
+++ b/stats/view/worker_test.go
@@ -0,0 +1,583 @@
+// Copyright 2017, OpenCensus Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package view
+
+import (
+	"context"
+	"errors"
+	"sync"
+	"testing"
+	"time"
+
+	"go.opencensus.io/stats"
+	"go.opencensus.io/tag"
+)
+
+func Test_Worker_MeasureCreation(t *testing.T) {
+	restart()
+
+	if _, err := stats.Float64("MF1", "desc MF1", "unit"); err != nil {
+		t.Errorf("stats.Float64(\"MF1\", \"desc MF1\") got error %v, want no error", err)
+	}
+
+	if _, err := stats.Float64("MF1", "Duplicate measure with same name as MF1.", "unit"); err == nil {
+		t.Error("stats.Float64(\"MF1\", \"Duplicate Float64Measure with same name as MF1.\") got no error, want no error")
+	}
+
+	if _, err := stats.Int64("MF1", "Duplicate measure with same name as MF1.", "unit"); err == nil {
+		t.Error("stats.Int64(\"MF1\", \"Duplicate Int64Measure with same name as MF1.\") got no error, want no error")
+	}
+
+	if _, err := stats.Float64("MF2", "desc MF2", "unit"); err != nil {
+		t.Errorf("stats.Float64(\"MF2\", \"desc MF2\") got error %v, want no error", err)
+	}
+
+	if _, err := stats.Int64("MI1", "desc MI1", "unit"); err != nil {
+		t.Errorf("stats.Int64(\"MI1\", \"desc MI1\") got error %v, want no error", err)
+	}
+
+	if _, err := stats.Int64("MI1", "Duplicate measure with same name as MI1.", "unit"); err == nil {
+		t.Error("stats.Int64(\"MI1\", \"Duplicate Int64 with same name as MI1.\") got no error, want no error")
+	}
+
+	if _, err := stats.Float64("MI1", "Duplicate measure with same name as MI1.", "unit"); err == nil {
+		t.Error("stats.Float64(\"MI1\", \"Duplicate Float64 with same name as MI1.\") got no error, want no error")
+	}
+}
+
+func Test_Worker_ViewRegistration(t *testing.T) {
+	someError := errors.New("some error")
+
+	sc1 := make(chan *Data)
+
+	type registerWant struct {
+		vID string
+		err error
+	}
+	type unregisterWant struct {
+		vID string
+		err error
+	}
+	type byNameWant struct {
+		name string
+		vID  string
+		ok   bool
+	}
+
+	type subscription struct {
+		c   chan *Data
+		vID string
+		err error
+	}
+	type testCase struct {
+		label         string
+		regs          []registerWant
+		subscriptions []subscription
+		unregs        []unregisterWant
+		bynames       []byNameWant
+	}
+	tcs := []testCase{
+		{
+			"register and subscribe to v1ID",
+			[]registerWant{
+				{
+					"v1ID",
+					nil,
+				},
+			},
+			[]subscription{
+				{
+					sc1,
+					"v1ID",
+					nil,
+				},
+			},
+			[]unregisterWant{
+				{
+					"v1ID",
+					someError,
+				},
+			},
+			[]byNameWant{
+				{
+					"VF1",
+					"v1ID",
+					true,
+				},
+				{
+					"VF2",
+					"vNilID",
+					false,
+				},
+			},
+		},
+		{
+			"register v1ID+v2ID, susbsribe to v1ID",
+			[]registerWant{
+				{
+					"v1ID",
+					nil,
+				},
+				{
+					"v2ID",
+					nil,
+				},
+			},
+			[]subscription{
+				{
+					sc1,
+					"v1ID",
+					nil,
+				},
+			},
+			[]unregisterWant{
+				{
+					"v1ID",
+					someError,
+				},
+				{
+					"v2ID",
+					someError,
+				},
+			},
+			[]byNameWant{
+				{
+					"VF1",
+					"v1ID",
+					true,
+				},
+				{
+					"VF2",
+					"v2ID",
+					true,
+				},
+			},
+		},
+		{
+			"register to v1ID; subscribe to v1ID and view with same ID",
+			[]registerWant{
+				{
+					"v1ID",
+					nil,
+				},
+			},
+			[]subscription{
+				{
+					sc1,
+					"v1ID",
+					nil,
+				},
+				{
+					sc1,
+					"v1SameNameID",
+					someError,
+				},
+			},
+			[]unregisterWant{
+				{
+					"v1ID",
+					someError,
+				},
+				{
+					"v1SameNameID",
+					nil,
+				},
+			},
+			[]byNameWant{
+				{
+					"VF1",
+					"v1ID",
+					true,
+				},
+			},
+		},
+	}
+
+	mf1, _ := stats.Float64("MF1/Test_Worker_ViewRegistration", "desc MF1", "unit")
+	mf2, _ := stats.Float64("MF2/Test_Worker_ViewRegistration", "desc MF2", "unit")
+
+	for _, tc := range tcs {
+		t.Run(tc.label, func(t *testing.T) {
+			restart()
+
+			v1, _ := New("VF1", "desc VF1", nil, mf1, nil)
+			v11, _ := New("VF1", "desc duplicate name VF1", nil, mf1, nil)
+			v2, _ := New("VF2", "desc VF2", nil, mf2, nil)
+
+			views := map[string]*View{
+				"v1ID":         v1,
+				"v1SameNameID": v11,
+				"v2ID":         v2,
+				"vNilID":       nil,
+			}
+
+			for _, reg := range tc.regs {
+				v := views[reg.vID]
+				err := Register(v)
+				if (err != nil) != (reg.err != nil) {
+					t.Errorf("%v: Register() = %v, want %v", tc.label, err, reg.err)
+				}
+				v.subscribe()
+			}
+
+			for _, s := range tc.subscriptions {
+				v := views[s.vID]
+				err := v.Subscribe()
+				if (err != nil) != (s.err != nil) {
+					t.Errorf("%v: Subscribe() = %v, want %v", tc.label, err, s.err)
+				}
+			}
+
+			for _, unreg := range tc.unregs {
+				v := views[unreg.vID]
+				err := Unregister(v)
+				if (err != nil) != (unreg.err != nil) {
+					t.Errorf("%v: Unregister() = %v; want %v", tc.label, err, unreg.err)
+				}
+			}
+
+			for _, byname := range tc.bynames {
+				v := Find(byname.name)
+				if v == nil && byname.ok {
+					t.Errorf("%v: ViewByName(%q) = nil, want non-nil view", tc.label, byname.name)
+				}
+
+				wantV := views[byname.vID]
+				if v != wantV {
+					t.Errorf("%v: ViewByName(%q) = %v; want %v", tc.label, byname.name, v, wantV)
+				}
+			}
+		})
+	}
+}
+
+func Test_Worker_RecordFloat64(t *testing.T) {
+	restart()
+
+	someError := errors.New("some error")
+	m, err := stats.Float64("Test_Worker_RecordFloat64/MF1", "desc MF1", "unit")
+	if err != nil {
+		t.Errorf("stats.Float64(\"MF1\", \"desc MF1\") got error '%v', want no error", err)
+	}
+
+	k1, _ := tag.NewKey("k1")
+	k2, _ := tag.NewKey("k2")
+	ctx, err := tag.New(context.Background(),
+		tag.Insert(k1, "v1"),
+		tag.Insert(k2, "v2"),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	v1, err := New("VF1", "desc VF1", []tag.Key{k1, k2}, m, CountAggregation{})
+	if err != nil {
+		t.Fatal(err)
+	}
+	v2, err := New("VF2", "desc VF2", []tag.Key{k1, k2}, m, CountAggregation{})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	type want struct {
+		v    *View
+		rows []*Row
+		err  error
+	}
+	type testCase struct {
+		label         string
+		registrations []*View
+		subscriptions []*View
+		records       []float64
+		wants         []want
+	}
+
+	tcs := []testCase{
+		{
+			"0",
+			[]*View{v1, v2},
+			[]*View{},
+			[]float64{1, 1},
+			[]want{{v1, nil, someError}, {v2, nil, someError}},
+		},
+		{
+			"1",
+			[]*View{v1, v2},
+			[]*View{v1},
+			[]float64{1, 1},
+			[]want{
+				{
+					v1,
+					[]*Row{
+						{
+							[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
+							newCountData(2),
+						},
+					},
+					nil,
+				},
+				{v2, nil, someError},
+			},
+		},
+		{
+			"2",
+			[]*View{v1, v2},
+			[]*View{v1, v2},
+			[]float64{1, 1},
+			[]want{
+				{
+					v1,
+					[]*Row{
+						{
+							[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
+							newCountData(2),
+						},
+					},
+					nil,
+				},
+				{
+					v2,
+					[]*Row{
+						{
+							[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
+							newCountData(2),
+						},
+					},
+					nil,
+				},
+			},
+		},
+	}
+
+	for _, tc := range tcs {
+		for _, v := range tc.registrations {
+			if err := Register(v); err != nil {
+				t.Fatalf("%v: Register(%v) = %v; want no errors", tc.label, v.Name(), err)
+			}
+		}
+
+		for _, v := range tc.subscriptions {
+			if err := v.Subscribe(); err != nil {
+				t.Fatalf("%v: Subscribe(%v) = %v; want no errors", tc.label, v.Name(), err)
+			}
+		}
+
+		for _, value := range tc.records {
+			stats.Record(ctx, m.M(value))
+		}
+
+		for _, w := range tc.wants {
+			gotRows, err := w.v.RetrieveData()
+			if (err != nil) != (w.err != nil) {
+				t.Fatalf("%v: RetrieveData(%v) = %v; want no errors", tc.label, w.v.Name(), err)
+			}
+			for _, got := range gotRows {
+				if !containsRow(w.rows, got) {
+					t.Errorf("%v: got row %v; want none", tc.label, got)
+					break
+				}
+			}
+			for _, want := range w.rows {
+				if !containsRow(gotRows, want) {
+					t.Errorf("%v: got none; want %v'", tc.label, want)
+					break
+				}
+			}
+		}
+
+		// cleaning up
+		for _, v := range tc.subscriptions {
+			if err := v.Unsubscribe(); err != nil {
+				t.Fatalf("%v: Unsubscribing from view %v errored with %v; want no error", tc.label, v.Name(), err)
+			}
+		}
+
+		for _, v := range tc.registrations {
+			if err := Unregister(v); err != nil {
+				t.Fatalf("%v: Unregistering view %v errrored with %v; want no error", tc.label, v.Name(), err)
+			}
+		}
+	}
+}
+
+func TestReportUsage(t *testing.T) {
+	ctx := context.Background()
+
+	m, err := stats.Int64("measure", "desc", "unit")
+	if err != nil {
+		t.Fatalf("stats.Int64() = %v", err)
+	}
+
+	cum1, _ := New("cum1", "", nil, m, CountAggregation{})
+	cum2, _ := New("cum1", "", nil, m, CountAggregation{})
+
+	tests := []struct {
+		name         string
+		view         *View
+		wantMaxCount int64
+	}{
+		{
+			name:         "cum",
+			view:         cum1,
+			wantMaxCount: 8,
+		},
+		{
+			name:         "cum2",
+			view:         cum2,
+			wantMaxCount: 8,
+		},
+	}
+
+	for _, tt := range tests {
+		restart()
+		SetReportingPeriod(25 * time.Millisecond)
+
+		if err := tt.view.Subscribe(); err != nil {
+			t.Fatalf("%v: cannot subscribe: %v", tt.name, err)
+		}
+
+		e := &countExporter{}
+		RegisterExporter(e)
+
+		stats.Record(ctx, m.M(1))
+		stats.Record(ctx, m.M(1))
+		stats.Record(ctx, m.M(1))
+		stats.Record(ctx, m.M(1))
+
+		time.Sleep(50 * time.Millisecond)
+
+		stats.Record(ctx, m.M(1))
+		stats.Record(ctx, m.M(1))
+		stats.Record(ctx, m.M(1))
+		stats.Record(ctx, m.M(1))
+
+		time.Sleep(50 * time.Millisecond)
+
+		e.Lock()
+		count := e.count
+		e.Unlock()
+		if got, want := count, tt.wantMaxCount; got > want {
+			t.Errorf("%v: got count data = %v; want at most %v", tt.name, got, want)
+		}
+	}
+
+}
+
+func Test_SetReportingPeriodReqNeverBlocks(t *testing.T) {
+	t.Parallel()
+
+	worker := newWorker()
+	durations := []time.Duration{-1, 0, 10, 100 * time.Millisecond}
+	for i, duration := range durations {
+		ackChan := make(chan bool, 1)
+		cmd := &setReportingPeriodReq{c: ackChan, d: duration}
+		cmd.handleCommand(worker)
+
+		select {
+		case <-ackChan:
+		case <-time.After(500 * time.Millisecond): // Arbitrarily using 500ms as the timeout duration.
+			t.Errorf("#%d: duration %v blocks", i, duration)
+		}
+	}
+}
+
+func TestWorkerStarttime(t *testing.T) {
+	restart()
+
+	ctx := context.Background()
+	m, err := stats.Int64("measure/TestWorkerStarttime", "desc", "unit")
+	if err != nil {
+		t.Fatalf("stats.Int64() = %v", err)
+	}
+	v, err := New("testview", "", nil, m, CountAggregation{})
+	if err != nil {
+		t.Fatalf("New() = %v", err)
+	}
+
+	SetReportingPeriod(25 * time.Millisecond)
+	if err := v.Subscribe(); err != nil {
+		t.Fatalf("cannot subscribe to %v: %v", v.Name(), err)
+	}
+
+	e := &vdExporter{}
+	RegisterExporter(e)
+	defer UnregisterExporter(e)
+
+	stats.Record(ctx, m.M(1))
+	stats.Record(ctx, m.M(1))
+	stats.Record(ctx, m.M(1))
+	stats.Record(ctx, m.M(1))
+
+	time.Sleep(50 * time.Millisecond)
+
+	stats.Record(ctx, m.M(1))
+	stats.Record(ctx, m.M(1))
+	stats.Record(ctx, m.M(1))
+	stats.Record(ctx, m.M(1))
+
+	time.Sleep(50 * time.Millisecond)
+
+	e.Lock()
+	if len(e.vds) == 0 {
+		t.Fatal("Got no view data; want at least one")
+	}
+
+	var start time.Time
+	for _, vd := range e.vds {
+		if start.IsZero() {
+			start = vd.Start
+		}
+		if !vd.Start.Equal(start) {
+			t.Errorf("Cumulative view data start time = %v; want %v", vd.Start, start)
+		}
+	}
+	e.Unlock()
+}
+
+type countExporter struct {
+	sync.Mutex
+	count int64
+}
+
+func (e *countExporter) ExportView(vd *Data) {
+	if len(vd.Rows) == 0 {
+		return
+	}
+	d := vd.Rows[0].Data.(*CountData)
+
+	e.Lock()
+	defer e.Unlock()
+	e.count = int64(*d)
+}
+
+type vdExporter struct {
+	sync.Mutex
+	vds []*Data
+}
+
+func (e *vdExporter) ExportView(vd *Data) {
+	e.Lock()
+	defer e.Unlock()
+
+	e.vds = append(e.vds, vd)
+}
+
+// restart stops the current processors and creates a new one.
+func restart() {
+	defaultWorker.stop()
+	defaultWorker = newWorker()
+	go defaultWorker.start()
+}
diff --git a/stats/view_test.go b/stats/view_test.go
deleted file mode 100644
index 892ee8a..0000000
--- a/stats/view_test.go
+++ /dev/null
@@ -1,852 +0,0 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package stats
-
-import (
-	"context"
-	"testing"
-	"time"
-
-	"go.opencensus.io/tag"
-)
-
-func Test_View_MeasureFloat64_AggregationDistribution_WindowCumulative(t *testing.T) {
-	k1, _ := tag.NewKey("k1")
-	k2, _ := tag.NewKey("k2")
-	k3, _ := tag.NewKey("k3")
-	agg1 := DistributionAggregation([]float64{2})
-	view, err := NewView("VF1", "desc VF1", []tag.Key{k1, k2}, nil, agg1, Cumulative{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	type tagString struct {
-		k tag.Key
-		v string
-	}
-	type record struct {
-		f    float64
-		tags []tagString
-	}
-
-	type testCase struct {
-		label    string
-		records  []record
-		wantRows []*Row
-	}
-
-	tcs := []testCase{
-		{
-			"1",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k1, "v1"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					&DistributionData{
-						2, 1, 5, 3, 8, []int64{1, 1}, agg1,
-					},
-				},
-			},
-		},
-		{
-			"2",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k2, "v2"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					&DistributionData{
-						1, 1, 1, 1, 0, []int64{1, 0}, agg1,
-					},
-				},
-				{
-					[]tag.Tag{{Key: k2, Value: "v2"}},
-					&DistributionData{
-						1, 5, 5, 5, 0, []int64{0, 1}, agg1,
-					},
-				},
-			},
-		},
-		{
-			"3",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k1, "v1"}, {k3, "v3"}}},
-				{1, []tagString{{k1, "v1 other"}}},
-				{5, []tagString{{k2, "v2"}}},
-				{5, []tagString{{k1, "v1"}, {k2, "v2"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					&DistributionData{
-						2, 1, 5, 3, 8, []int64{1, 1}, agg1,
-					},
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1 other"}},
-					&DistributionData{
-						1, 1, 1, 1, 0, []int64{1, 0}, agg1,
-					},
-				},
-				{
-					[]tag.Tag{{Key: k2, Value: "v2"}},
-					&DistributionData{
-						1, 5, 5, 5, 0, []int64{0, 1}, agg1,
-					},
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
-					&DistributionData{
-						1, 5, 5, 5, 0, []int64{0, 1}, agg1,
-					},
-				},
-			},
-		},
-		{
-			"4",
-			[]record{
-				{1, []tagString{{k1, "v1 is a very long value key"}}},
-				{5, []tagString{{k1, "v1 is a very long value key"}, {k3, "v3"}}},
-				{1, []tagString{{k1, "v1 is another very long value key"}}},
-				{1, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
-				{5, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
-				{3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
-				{3, []tagString{{k1, "v1 is a very long value key"}, {k2, "v2 is a very long value key"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1 is a very long value key"}},
-					&DistributionData{
-						2, 1, 5, 3, 8, []int64{1, 1}, agg1,
-					},
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1 is another very long value key"}},
-					&DistributionData{
-						1, 1, 1, 1, 0, []int64{1, 0}, agg1,
-					},
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1 is a very long value key"}, {Key: k2, Value: "v2 is a very long value key"}},
-					&DistributionData{
-						4, 1, 5, 3, 2.66666666666667 * 3, []int64{1, 3}, agg1,
-					},
-				},
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		view.clearRows()
-		view.subscribe()
-		for _, r := range tc.records {
-			mods := []tag.Mutator{}
-			for _, t := range r.tags {
-				mods = append(mods, tag.Insert(t.k, t.v))
-			}
-			ctx, err := tag.New(context.Background(), mods...)
-			if err != nil {
-				t.Errorf("%v: NewMap = %v", tc.label, err)
-			}
-			view.addSample(tag.FromContext(ctx), r.f, time.Now())
-		}
-
-		gotRows := view.collectedRows(time.Now())
-		for i, got := range gotRows {
-			if !containsRow(tc.wantRows, got) {
-				t.Errorf("%v-%d: got row %v; want none", tc.label, i, got)
-				break
-			}
-		}
-
-		for i, want := range tc.wantRows {
-			if !containsRow(gotRows, want) {
-				t.Errorf("%v-%d: got none; want row %v", tc.label, i, want)
-				break
-			}
-		}
-	}
-}
-
-func Test_View_MeasureFloat64_AggregationDistribution_WindowSlidingTime(t *testing.T) {
-	startTime := time.Date(2010, 1, 1, 0, 0, 0, 0, time.UTC)
-
-	k1, _ := tag.NewKey("k1")
-	k2, _ := tag.NewKey("k2")
-	agg1 := DistributionAggregation([]float64{2})
-	view, err := NewView("VF1", "desc VF1", []tag.Key{k1, k2}, nil, agg1, Interval{10 * time.Second, 5})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	type tagString struct {
-		k tag.Key
-		v string
-	}
-	type record struct {
-		f    float64
-		tags []tagString
-		now  time.Time
-	}
-
-	type wantRows struct {
-		label        string
-		retrieveTime time.Time
-		rows         []*Row
-	}
-
-	type testCase struct {
-		label    string
-		records  []record
-		wantRows []wantRows
-	}
-
-	tcs := []testCase{
-		{
-			"1",
-			[]record{
-				{1, []tagString{{k1, "v1"}}, startTime.Add(1 * time.Second)},
-				{2, []tagString{{k1, "v1"}}, startTime.Add(6 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(6 * time.Second)},
-				{4, []tagString{{k1, "v1"}}, startTime.Add(10 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(10 * time.Second)},
-				{4, []tagString{{k1, "v1"}}, startTime.Add(14 * time.Second)},
-				{3, []tagString{{k1, "v1"}}, startTime.Add(14 * time.Second)},
-			},
-			[]wantRows{
-				{
-					"last 6 recorded",
-					startTime.Add(14 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								6, 2, 5, 3.8333333333, 1.3666666667 * 5, []int64{0, 6}, agg1,
-							},
-						},
-					},
-				},
-				{
-					"last 4 recorded",
-					startTime.Add(18 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								4, 3, 5, 4, 0.6666666667 * 3, []int64{0, 4}, agg1,
-							},
-						},
-					},
-				},
-				{
-					"last 2 recorded",
-					startTime.Add(22 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								2, 3, 4, 3.5, 0.5, []int64{0, 2}, agg1,
-							},
-						},
-					},
-				},
-			},
-		},
-		{
-			"2",
-			[]record{
-				{1, []tagString{{k1, "v1"}}, startTime.Add(3 * time.Second)},
-				{2, []tagString{{k1, "v1"}}, startTime.Add(5 * time.Second)},
-				{3, []tagString{{k1, "v1"}}, startTime.Add(5 * time.Second)},
-				{4, []tagString{{k1, "v1"}}, startTime.Add(8 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(8 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(8 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(9 * time.Second)},
-			},
-			[]wantRows{
-				{
-					"no partial bucket",
-					startTime.Add(10 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								7, 1, 5, 3.57142857142857, 2.61904761904762 * 6, []int64{1, 6}, agg1,
-							},
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 50%)",
-					startTime.Add(12 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								7, 1, 5, 3.57142857142857, 2.61904761904762 * 6, []int64{1, 6}, agg1,
-							},
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 99.99%)",
-					startTime.Add(15 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								6, 2, 5, 4, 1.6 * 5, []int64{0, 6}, agg1,
-							},
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 0.001%)",
-					startTime.Add(17*time.Second - 1*time.Millisecond),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								6, 2, 5, 4, 1.6 * 5, []int64{0, 6}, agg1,
-							},
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 50%)",
-					startTime.Add(18 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							&DistributionData{
-								4, 4, 5, 4.75, 0.25 * 3, []int64{0, 4}, agg1,
-							},
-						},
-					},
-				},
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		view.clearRows()
-		view.subscribe()
-		for _, r := range tc.records {
-			mods := []tag.Mutator{}
-			for _, t := range r.tags {
-				mods = append(mods, tag.Insert(t.k, t.v))
-			}
-			ctx, err := tag.New(context.Background(), mods...)
-			if err != nil {
-				t.Errorf("%v: New = %v", tc.label, err)
-			}
-			view.addSample(tag.FromContext(ctx), r.f, r.now)
-		}
-
-		for _, wantRows := range tc.wantRows {
-			gotRows := view.collectedRows(wantRows.retrieveTime)
-
-			for _, gotRow := range gotRows {
-				if !containsRow(wantRows.rows, gotRow) {
-					t.Errorf("got unexpected row '%v' for test case: '%v' with label '%v'", gotRow, tc.label, wantRows.label)
-					break
-				}
-			}
-
-			for _, wantRow := range wantRows.rows {
-				if !containsRow(gotRows, wantRow) {
-					t.Errorf("want row '%v' for test case: '%v' with label '%v'. Not received", wantRow, tc.label, wantRows.label)
-					break
-				}
-			}
-		}
-
-	}
-}
-
-func Test_View_MeasureFloat64_AggregationCount_WindowSlidingTime(t *testing.T) {
-	startTime := time.Date(2010, 1, 1, 0, 0, 0, 0, time.UTC)
-
-	k1, _ := tag.NewKey("k1")
-	k2, _ := tag.NewKey("k2")
-	agg1 := CountAggregation{}
-	view, err := NewView("VF1", "desc VF1", []tag.Key{k1, k2}, nil, agg1, Interval{10 * time.Second, 5})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	type tagString struct {
-		k tag.Key
-		v string
-	}
-	type record struct {
-		f    float64
-		tags []tagString
-		now  time.Time
-	}
-
-	type wantRows struct {
-		label        string
-		retrieveTime time.Time
-		rows         []*Row
-	}
-
-	type testCase struct {
-		label    string
-		records  []record
-		wantRows []wantRows
-	}
-
-	tcs := []testCase{
-		{
-			"1",
-			[]record{
-				{1, []tagString{{k1, "v1"}}, startTime.Add(1 * time.Second)},
-				{2, []tagString{{k1, "v1"}}, startTime.Add(6 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(6 * time.Second)},
-				{4, []tagString{{k1, "v1"}}, startTime.Add(10 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(10 * time.Second)},
-				{4, []tagString{{k1, "v1"}}, startTime.Add(14 * time.Second)},
-				{3, []tagString{{k1, "v1"}}, startTime.Add(14 * time.Second)},
-			},
-			[]wantRows{
-				{
-					"last 6 recorded",
-					startTime.Add(14 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(6),
-						},
-					},
-				},
-				{
-					"last 4 recorded",
-					startTime.Add(18 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(4),
-						},
-					},
-				},
-				{
-					"last 2 recorded",
-					startTime.Add(22 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(2),
-						},
-					},
-				},
-			},
-		},
-		{
-			"2",
-			[]record{
-				{1, []tagString{{k1, "v1"}}, startTime.Add(3 * time.Second)},
-				{2, []tagString{{k1, "v1"}}, startTime.Add(5 * time.Second)},
-				{3, []tagString{{k1, "v1"}}, startTime.Add(5 * time.Second)},
-				{4, []tagString{{k1, "v1"}}, startTime.Add(8 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(8 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(8 * time.Second)},
-				{5, []tagString{{k1, "v1"}}, startTime.Add(9 * time.Second)},
-			},
-			[]wantRows{
-				{
-					"no partial bucket",
-					startTime.Add(10 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(7),
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 50%) (count: 1)",
-					startTime.Add(12 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(7),
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 80%) (count: 2)",
-					startTime.Add(15*time.Second + 400*time.Millisecond),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(6),
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 50%) (count: 2)",
-					startTime.Add(16 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(5),
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 90%) (count: 3)",
-					startTime.Add(17*time.Second + 200*time.Millisecond),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(4),
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 50%) (count: 3)",
-					startTime.Add(18 * time.Second),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(3),
-						},
-					},
-				},
-				{
-					"oldest partial bucket: (remaining time: 20%) (count: 3)",
-					startTime.Add(18*time.Second + 600*time.Millisecond),
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}},
-							newCountData(2),
-						},
-					},
-				},
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		view.clearRows()
-		view.subscribe()
-		for _, r := range tc.records {
-			mods := []tag.Mutator{}
-			for _, t := range r.tags {
-				mods = append(mods, tag.Insert(t.k, t.v))
-			}
-			ctx, err := tag.New(context.Background(), mods...)
-			if err != nil {
-				t.Errorf("%v: New = %v", tc.label, err)
-			}
-			view.addSample(tag.FromContext(ctx), r.f, r.now)
-		}
-
-		for _, wantRows := range tc.wantRows {
-			gotRows := view.collectedRows(wantRows.retrieveTime)
-
-			for _, gotRow := range gotRows {
-				if !containsRow(wantRows.rows, gotRow) {
-					t.Errorf("got unexpected row '%v' for test case: '%v' with label '%v'", gotRow, tc.label, wantRows.label)
-					break
-				}
-			}
-
-			for _, wantRow := range wantRows.rows {
-				if !containsRow(gotRows, wantRow) {
-					t.Errorf("want row '%v' for test case: '%v' with label '%v'. Not received", wantRow, tc.label, wantRows.label)
-					break
-				}
-			}
-		}
-
-	}
-}
-
-func Test_View_MeasureFloat64_AggregationSum_WindowCumulative(t *testing.T) {
-	k1, _ := tag.NewKey("k1")
-	k2, _ := tag.NewKey("k2")
-	k3, _ := tag.NewKey("k3")
-	view, err := NewView("VF1", "desc VF1", []tag.Key{k1, k2}, nil, SumAggregation{}, Cumulative{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	type tagString struct {
-		k tag.Key
-		v string
-	}
-	type record struct {
-		f    float64
-		tags []tagString
-	}
-
-	tcs := []struct {
-		label    string
-		records  []record
-		wantRows []*Row
-	}{
-		{
-			"1",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k1, "v1"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					newSumData(6),
-				},
-			},
-		},
-		{
-			"2",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k2, "v2"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					newSumData(1),
-				},
-				{
-					[]tag.Tag{{Key: k2, Value: "v2"}},
-					newSumData(5),
-				},
-			},
-		},
-		{
-			"3",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k1, "v1"}, {k3, "v3"}}},
-				{1, []tagString{{k1, "v1 other"}}},
-				{5, []tagString{{k2, "v2"}}},
-				{5, []tagString{{k1, "v1"}, {k2, "v2"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					newSumData(6),
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1 other"}},
-					newSumData(1),
-				},
-				{
-					[]tag.Tag{{Key: k2, Value: "v2"}},
-					newSumData(5),
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
-					newSumData(5),
-				},
-			},
-		},
-	}
-
-	for _, tt := range tcs {
-		view.clearRows()
-		view.subscribe()
-		for _, r := range tt.records {
-			mods := []tag.Mutator{}
-			for _, t := range r.tags {
-				mods = append(mods, tag.Insert(t.k, t.v))
-			}
-			ctx, err := tag.New(context.Background(), mods...)
-			if err != nil {
-				t.Errorf("%v: New = %v", tt.label, err)
-			}
-			view.addSample(tag.FromContext(ctx), r.f, time.Now())
-		}
-
-		gotRows := view.collectedRows(time.Now())
-		for i, got := range gotRows {
-			if !containsRow(tt.wantRows, got) {
-				t.Errorf("%v-%d: got row %v; want none", tt.label, i, got)
-				break
-			}
-		}
-
-		for i, want := range tt.wantRows {
-			if !containsRow(gotRows, want) {
-				t.Errorf("%v-%d: got none; want row %v", tt.label, i, want)
-				break
-			}
-		}
-	}
-}
-
-func Test_View_MeasureFloat64_AggregationMean_WindowCumulative(t *testing.T) {
-	k1, _ := tag.NewKey("k1")
-	k2, _ := tag.NewKey("k2")
-	k3, _ := tag.NewKey("k3")
-	view, err := NewView("VF1", "desc VF1", []tag.Key{k1, k2}, nil, MeanAggregation{}, Cumulative{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	type tagString struct {
-		k tag.Key
-		v string
-	}
-	type record struct {
-		f    float64
-		tags []tagString
-	}
-
-	tcs := []struct {
-		label    string
-		records  []record
-		wantRows []*Row
-	}{
-		{
-			"1",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k1, "v1"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					newMeanData(3, 2),
-				},
-			},
-		},
-		{
-			"2",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k2, "v2"}}},
-				{-0.5, []tagString{{k2, "v2"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					newMeanData(1, 1),
-				},
-				{
-					[]tag.Tag{{Key: k2, Value: "v2"}},
-					newMeanData(2.25, 2),
-				},
-			},
-		},
-		{
-			"3",
-			[]record{
-				{1, []tagString{{k1, "v1"}}},
-				{5, []tagString{{k1, "v1"}, {k3, "v3"}}},
-				{1, []tagString{{k1, "v1 other"}}},
-				{5, []tagString{{k2, "v2"}}},
-				{5, []tagString{{k1, "v1"}, {k2, "v2"}}},
-				{-4, []tagString{{k1, "v1"}, {k2, "v2"}}},
-			},
-			[]*Row{
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}},
-					newMeanData(3, 2),
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1 other"}},
-					newMeanData(1, 1),
-				},
-				{
-					[]tag.Tag{{Key: k2, Value: "v2"}},
-					newMeanData(5, 1),
-				},
-				{
-					[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
-					newMeanData(0.5, 2),
-				},
-			},
-		},
-	}
-
-	for _, tt := range tcs {
-		view.clearRows()
-		view.subscribe()
-		for _, r := range tt.records {
-			mods := []tag.Mutator{}
-			for _, t := range r.tags {
-				mods = append(mods, tag.Insert(t.k, t.v))
-			}
-			ctx, err := tag.New(context.Background(), mods...)
-			if err != nil {
-				t.Errorf("%v: New = %v", tt.label, err)
-			}
-			view.addSample(tag.FromContext(ctx), r.f, time.Now())
-		}
-
-		gotRows := view.collectedRows(time.Now())
-		for i, got := range gotRows {
-			if !containsRow(tt.wantRows, got) {
-				t.Errorf("%v-%d: got row %v; want none", tt.label, i, got)
-				break
-			}
-		}
-
-		for i, want := range tt.wantRows {
-			if !containsRow(gotRows, want) {
-				t.Errorf("%v-%d: got none; want row %v", tt.label, i, want)
-				break
-			}
-		}
-	}
-}
-
-func TestViewSortedKeys(t *testing.T) {
-	k1, _ := tag.NewKey("a")
-	k2, _ := tag.NewKey("b")
-	k3, _ := tag.NewKey("c")
-	ks := []tag.Key{k1, k3, k2}
-
-	v, err := NewView("sort_keys", "desc sort_keys", ks, nil, MeanAggregation{}, Cumulative{})
-	if err != nil {
-		t.Fatalf("NewView() = %v", err)
-	}
-
-	want := []string{"a", "b", "c"}
-	vks := v.TagKeys()
-	if len(vks) != len(want) {
-		t.Errorf("Keys = %+v; want %+v", vks, want)
-	}
-
-	for i, v := range want {
-		if got, want := v, vks[i].Name(); got != want {
-			t.Errorf("View name = %q; want %q", got, want)
-		}
-	}
-}
-
-// TODO(songya): add tests for AggregationSum and AggregationMean with Interval Window
-
-// containsRow returns true if rows contain r.
-func containsRow(rows []*Row, r *Row) bool {
-	for _, x := range rows {
-		if r.Equal(x) {
-			return true
-		}
-	}
-	return false
-}
diff --git a/stats/worker_test.go b/stats/worker_test.go
deleted file mode 100644
index 3ed49c2..0000000
--- a/stats/worker_test.go
+++ /dev/null
@@ -1,787 +0,0 @@
-// Copyright 2017, OpenCensus Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package stats
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"sync"
-	"testing"
-	"time"
-
-	"go.opencensus.io/tag"
-)
-
-func Test_Worker_MeasureCreation(t *testing.T) {
-	restart()
-
-	if _, err := NewMeasureFloat64("MF1", "desc MF1", "unit"); err != nil {
-		t.Errorf("NewMeasureFloat64(\"MF1\", \"desc MF1\") got error %v, want no error", err)
-	}
-
-	if _, err := NewMeasureFloat64("MF1", "Duplicate measure with same name as MF1.", "unit"); err == nil {
-		t.Error("NewMeasureFloat64(\"MF1\", \"Duplicate MeasureFloat64 with same name as MF1.\") got no error, want no error")
-	}
-
-	if _, err := NewMeasureInt64("MF1", "Duplicate measure with same name as MF1.", "unit"); err == nil {
-		t.Error("NewMeasureInt64(\"MF1\", \"Duplicate MeasureInt64 with same name as MF1.\") got no error, want no error")
-	}
-
-	if _, err := NewMeasureFloat64("MF2", "desc MF2", "unit"); err != nil {
-		t.Errorf("NewMeasureFloat64(\"MF2\", \"desc MF2\") got error %v, want no error", err)
-	}
-
-	if _, err := NewMeasureInt64("MI1", "desc MI1", "unit"); err != nil {
-		t.Errorf("NewMeasureInt64(\"MI1\", \"desc MI1\") got error %v, want no error", err)
-	}
-
-	if _, err := NewMeasureInt64("MI1", "Duplicate measure with same name as MI1.", "unit"); err == nil {
-		t.Error("NewMeasureInt64(\"MI1\", \"Duplicate NewMeasureInt64 with same name as MI1.\") got no error, want no error")
-	}
-
-	if _, err := NewMeasureFloat64("MI1", "Duplicate measure with same name as MI1.", "unit"); err == nil {
-		t.Error("NewMeasureFloat64(\"MI1\", \"Duplicate NewMeasureFloat64 with same name as MI1.\") got no error, want no error")
-	}
-}
-
-func Test_Worker_FindMeasure(t *testing.T) {
-	restart()
-
-	mf1, err := NewMeasureFloat64("MF1", "desc MF1", "unit")
-	if err != nil {
-		t.Errorf("NewMeasureFloat64(\"MF1\", \"desc MF1\") got error %v, want no error", err)
-	}
-	mf2, err := NewMeasureFloat64("MF2", "desc MF2", "unit")
-	if err != nil {
-		t.Errorf("NewMeasureFloat64(\"MF2\", \"desc MF2\") got error %v, want no error", err)
-	}
-	mi1, err := NewMeasureInt64("MI1", "desc MI1", "unit")
-	if err != nil {
-		t.Errorf("NewMeasureInt64(\"MI1\", \"desc MI1\") got error %v, want no error", err)
-	}
-
-	type testCase struct {
-		label string
-		name  string
-		m     Measure
-	}
-
-	tcs := []testCase{
-		{
-			"0",
-			mf1.Name(),
-			mf1,
-		},
-		{
-			"1",
-			"MF1",
-			mf1,
-		},
-		{
-			"2",
-			mf2.Name(),
-			mf2,
-		},
-		{
-			"3",
-			"MF2",
-			mf2,
-		},
-		{
-			"4",
-			mi1.Name(),
-			mi1,
-		},
-		{
-			"5",
-			"MI1",
-			mi1,
-		},
-		{
-			"6",
-			"other",
-			nil,
-		},
-	}
-
-	for _, tc := range tcs {
-		m := FindMeasure(tc.name)
-		if m != tc.m {
-			t.Errorf("FindMeasure(%q) got measure %v; want %v", tc.label, m, tc.m)
-		}
-	}
-}
-
-func Test_Worker_MeasureDelete(t *testing.T) {
-	someError := errors.New("some error")
-
-	registerViewFunc := func(viewName string) func(m Measure) error {
-		return func(m Measure) error {
-			switch x := m.(type) {
-			case *MeasureInt64:
-				v, err := NewView(viewName, "", nil, x, nil, nil)
-				if err != nil {
-					return err
-				}
-				return RegisterView(v)
-			case *MeasureFloat64:
-				v, err := NewView(viewName, "", nil, x, nil, nil)
-				if err != nil {
-					return err
-				}
-				return RegisterView(v)
-			default:
-				return fmt.Errorf("cannot create view '%v' with measure '%v'", viewName, m.Name())
-			}
-		}
-	}
-
-	type vRegistrations struct {
-		measureName string
-		regFunc     func(m Measure) error
-	}
-
-	type deletion struct {
-		name      string
-		findOk    bool
-		deleteErr error
-	}
-
-	type testCase struct {
-		label         string
-		measureNames  []string
-		registrations []vRegistrations
-		deletions     []deletion
-	}
-
-	tcs := []testCase{
-		{
-			"0",
-			[]string{"mi1"},
-			[]vRegistrations{},
-			[]deletion{
-				{"mi1", true, nil},
-			},
-		},
-		{
-			"1",
-			[]string{"mi1"},
-			[]vRegistrations{
-				{
-					"mi1",
-					registerViewFunc("vw1"),
-				},
-			},
-			[]deletion{
-				{"mi1", true, someError},
-				{"mi2", false, nil},
-			},
-		},
-		{
-			"2",
-			[]string{"mi1", "mi2"},
-			[]vRegistrations{
-				{
-					"mi1",
-					registerViewFunc("vw1"),
-				},
-			},
-			[]deletion{
-				{"mi1", true, someError},
-				{"mi2", true, nil},
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		restart()
-
-		for _, n := range tc.measureNames {
-			if _, err := NewMeasureInt64(n, "some desc", "unit"); err != nil {
-				t.Errorf("%v: Cannot create measure: %v'", tc.label, err)
-			}
-		}
-
-		for _, r := range tc.registrations {
-			m := FindMeasure(r.measureName)
-			if m == nil {
-				t.Errorf("%v: FindMeasure(%q) = nil; want non-nil measure", tc.label, r.measureName)
-				continue
-			}
-			if err := r.regFunc(m); err != nil {
-				t.Errorf("%v: Cannot register view: %v", tc.label, err)
-				continue
-			}
-		}
-
-		for _, d := range tc.deletions {
-			m := FindMeasure(d.name)
-			if m == nil && d.findOk {
-				t.Errorf("%v: FindMeasure = nil; want non-nil measure", tc.label)
-				continue
-			}
-
-			if m == nil {
-				// ok was expected to be true
-				continue
-			}
-
-			err := DeleteMeasure(m)
-			if (err != nil) != (d.deleteErr != nil) {
-				t.Errorf("%v: Cannot delete measure: got %v as error; want %v", tc.label, err, d.deleteErr)
-			}
-
-			var deleted bool
-			if err == nil {
-				deleted = true
-			}
-
-			if m := FindMeasure(d.name); deleted && m != nil {
-				t.Errorf("%v: Measure %q shouldn't exist after deletion but exists", tc.label, d.name)
-				continue
-			}
-		}
-	}
-}
-
-func Test_Worker_ViewRegistration(t *testing.T) {
-	someError := errors.New("some error")
-
-	sc1 := make(chan *ViewData)
-
-	type registerWant struct {
-		vID string
-		err error
-	}
-	type unregisterWant struct {
-		vID string
-		err error
-	}
-	type byNameWant struct {
-		name string
-		vID  string
-		ok   bool
-	}
-
-	type subscription struct {
-		c   chan *ViewData
-		vID string
-		err error
-	}
-	type testCase struct {
-		label         string
-		regs          []registerWant
-		subscriptions []subscription
-		unregs        []unregisterWant
-		bynames       []byNameWant
-	}
-	tcs := []testCase{
-		{
-			"register and subscribe to v1ID",
-			[]registerWant{
-				{
-					"v1ID",
-					nil,
-				},
-			},
-			[]subscription{
-				{
-					sc1,
-					"v1ID",
-					nil,
-				},
-			},
-			[]unregisterWant{
-				{
-					"v1ID",
-					someError,
-				},
-			},
-			[]byNameWant{
-				{
-					"VF1",
-					"v1ID",
-					true,
-				},
-				{
-					"VF2",
-					"vNilID",
-					false,
-				},
-			},
-		},
-		{
-			"register v1ID+v2ID, susbsribe to v1ID",
-			[]registerWant{
-				{
-					"v1ID",
-					nil,
-				},
-				{
-					"v2ID",
-					nil,
-				},
-			},
-			[]subscription{
-				{
-					sc1,
-					"v1ID",
-					nil,
-				},
-			},
-			[]unregisterWant{
-				{
-					"v1ID",
-					someError,
-				},
-				{
-					"v2ID",
-					someError,
-				},
-			},
-			[]byNameWant{
-				{
-					"VF1",
-					"v1ID",
-					true,
-				},
-				{
-					"VF2",
-					"v2ID",
-					true,
-				},
-			},
-		},
-		{
-			"register to v1ID; subscribe to v1ID and view with same ID",
-			[]registerWant{
-				{
-					"v1ID",
-					nil,
-				},
-			},
-			[]subscription{
-				{
-					sc1,
-					"v1ID",
-					nil,
-				},
-				{
-					sc1,
-					"v1SameNameID",
-					someError,
-				},
-			},
-			[]unregisterWant{
-				{
-					"v1ID",
-					someError,
-				},
-				{
-					"v1SameNameID",
-					nil,
-				},
-			},
-			[]byNameWant{
-				{
-					"VF1",
-					"v1ID",
-					true,
-				},
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		restart()
-
-		mf1, _ := NewMeasureFloat64("MF1", "desc MF1", "unit")
-		mf2, _ := NewMeasureFloat64("MF2", "desc MF2", "unit")
-
-		v1, _ := NewView("VF1", "desc VF1", nil, mf1, nil, nil)
-		v11, _ := NewView("VF1", "desc duplicate name VF1", nil, mf1, nil, nil)
-		v2, _ := NewView("VF2", "desc VF2", nil, mf2, nil, nil)
-
-		views := map[string]*View{
-			"v1ID":         v1,
-			"v1SameNameID": v11,
-			"v2ID":         v2,
-			"vNilID":       nil,
-		}
-
-		for _, reg := range tc.regs {
-			v := views[reg.vID]
-			err := RegisterView(v)
-			if (err != nil) != (reg.err != nil) {
-				t.Errorf("%v: RegisterView() = %v, want %v", tc.label, err, reg.err)
-			}
-			v.subscribe()
-		}
-
-		for _, s := range tc.subscriptions {
-			v := views[s.vID]
-			err := v.Subscribe()
-			if (err != nil) != (s.err != nil) {
-				t.Errorf("%v: Subscribe() = %v, want %v", tc.label, err, s.err)
-			}
-		}
-
-		for _, unreg := range tc.unregs {
-			v := views[unreg.vID]
-			err := UnregisterView(v)
-			if (err != nil) != (unreg.err != nil) {
-				t.Errorf("%v: UnregisterView() = %v; want %v", tc.label, err, unreg.err)
-			}
-		}
-
-		for _, byname := range tc.bynames {
-			v := FindView(byname.name)
-			if v == nil && byname.ok {
-				t.Errorf("%v: ViewByName(%q) = nil, want non-nil view", tc.label, byname.name)
-			}
-
-			wantV := views[byname.vID]
-			if v != wantV {
-				t.Errorf("%v: ViewByName(%q) = %v; want %v", tc.label, byname.name, v, wantV)
-			}
-		}
-	}
-}
-
-func Test_Worker_RecordFloat64(t *testing.T) {
-	restart()
-
-	someError := errors.New("some error")
-	m, err := NewMeasureFloat64("MF1", "desc MF1", "unit")
-	if err != nil {
-		t.Errorf("NewMeasureFloat64(\"MF1\", \"desc MF1\") got error '%v', want no error", err)
-	}
-
-	k1, _ := tag.NewKey("k1")
-	k2, _ := tag.NewKey("k2")
-	ctx, err := tag.New(context.Background(),
-		tag.Insert(k1, "v1"),
-		tag.Insert(k2, "v2"),
-	)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	v1, err := NewView("VF1", "desc VF1", []tag.Key{k1, k2}, m, CountAggregation{}, Cumulative{})
-	if err != nil {
-		t.Fatal(err)
-	}
-	v2, err := NewView("VF2", "desc VF2", []tag.Key{k1, k2}, m, CountAggregation{}, Cumulative{})
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	type want struct {
-		v    *View
-		rows []*Row
-		err  error
-	}
-	type testCase struct {
-		label         string
-		registrations []*View
-		subscriptions []*View
-		records       []float64
-		wants         []want
-	}
-
-	tcs := []testCase{
-		{
-			"0",
-			[]*View{v1, v2},
-			[]*View{},
-			[]float64{1, 1},
-			[]want{{v1, nil, someError}, {v2, nil, someError}},
-		},
-		{
-			"1",
-			[]*View{v1, v2},
-			[]*View{v1},
-			[]float64{1, 1},
-			[]want{
-				{
-					v1,
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
-							newCountData(2),
-						},
-					},
-					nil,
-				},
-				{v2, nil, someError},
-			},
-		},
-		{
-			"2",
-			[]*View{v1, v2},
-			[]*View{v1, v2},
-			[]float64{1, 1},
-			[]want{
-				{
-					v1,
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
-							newCountData(2),
-						},
-					},
-					nil,
-				},
-				{
-					v2,
-					[]*Row{
-						{
-							[]tag.Tag{{Key: k1, Value: "v1"}, {Key: k2, Value: "v2"}},
-							newCountData(2),
-						},
-					},
-					nil,
-				},
-			},
-		},
-	}
-
-	for _, tc := range tcs {
-		for _, v := range tc.registrations {
-			if err := RegisterView(v); err != nil {
-				t.Fatalf("%v: RegisterView(%v) = %v; want no errors", tc.label, v.Name(), err)
-			}
-		}
-
-		for _, v := range tc.subscriptions {
-			if err := v.Subscribe(); err != nil {
-				t.Fatalf("%v: Subscribe(%v) = %v; want no errors", tc.label, v.Name(), err)
-			}
-		}
-
-		for _, value := range tc.records {
-			Record(ctx, m.M(value))
-		}
-
-		for _, w := range tc.wants {
-			gotRows, err := w.v.RetrieveData()
-			if (err != nil) != (w.err != nil) {
-				t.Fatalf("%v: RetrieveData(%v) = %v; want no errors", tc.label, w.v.Name(), err)
-			}
-			for _, got := range gotRows {
-				if !containsRow(w.rows, got) {
-					t.Errorf("%v: got row %v; want none", tc.label, got)
-					break
-				}
-			}
-			for _, want := range w.rows {
-				if !containsRow(gotRows, want) {
-					t.Errorf("%v: got none; want %v'", tc.label, want)
-					break
-				}
-			}
-		}
-
-		// cleaning up
-		for _, v := range tc.subscriptions {
-			if err := v.Unsubscribe(); err != nil {
-				t.Fatalf("%v: Unsubscribing from view %v errored with %v; want no error", tc.label, v.Name(), err)
-			}
-		}
-
-		for _, v := range tc.registrations {
-			if err := UnregisterView(v); err != nil {
-				t.Fatalf("%v: Unregistering view %v errrored with %v; want no error", tc.label, v.Name(), err)
-			}
-		}
-	}
-}
-
-func TestReportUsage(t *testing.T) {
-	ctx := context.Background()
-
-	m, err := NewMeasureInt64("measure", "desc", "unit")
-	if err != nil {
-		t.Fatalf("NewMeasureInt64() = %v", err)
-	}
-
-	cum1, _ := NewView("cum1", "", nil, m, CountAggregation{}, Cumulative{})
-	cum2, _ := NewView("cum1", "", nil, m, CountAggregation{}, &Cumulative{})
-	interval, _ := NewView("cum1", "", nil, m, CountAggregation{}, Interval{Duration: 5 * time.Millisecond, SubIntervals: 1})
-
-	tests := []struct {
-		name         string
-		view         *View
-		wantMaxCount int64
-	}{
-		{
-			name:         "cum",
-			view:         cum1,
-			wantMaxCount: 8,
-		},
-		{
-			name:         "cum2",
-			view:         cum2,
-			wantMaxCount: 8,
-		},
-		{
-			name:         "interval",
-			view:         interval,
-			wantMaxCount: 2,
-		},
-	}
-
-	for _, tt := range tests {
-		restart()
-		SetReportingPeriod(25 * time.Millisecond)
-
-		if err := tt.view.Subscribe(); err != nil {
-			t.Fatalf("%v: cannot subscribe: %v", tt.name, err)
-		}
-
-		e := &countExporter{}
-		RegisterExporter(e)
-
-		Record(ctx, m.M(1))
-		Record(ctx, m.M(1))
-		Record(ctx, m.M(1))
-		Record(ctx, m.M(1))
-
-		time.Sleep(50 * time.Millisecond)
-
-		Record(ctx, m.M(1))
-		Record(ctx, m.M(1))
-		Record(ctx, m.M(1))
-		Record(ctx, m.M(1))
-
-		time.Sleep(50 * time.Millisecond)
-
-		e.Lock()
-		count := e.count
-		e.Unlock()
-		if got, want := count, tt.wantMaxCount; got > want {
-			t.Errorf("%v: got count data = %v; want at most %v", tt.name, got, want)
-		}
-	}
-
-}
-
-func Test_SetReportingPeriodReqNeverBlocks(t *testing.T) {
-	t.Parallel()
-
-	worker := newWorker()
-	durations := []time.Duration{-1, 0, 10, 100 * time.Millisecond}
-	for i, duration := range durations {
-		ackChan := make(chan bool, 1)
-		cmd := &setReportingPeriodReq{c: ackChan, d: duration}
-		cmd.handleCommand(worker)
-
-		select {
-		case <-ackChan:
-		case <-time.After(500 * time.Millisecond): // Arbitrarily using 500ms as the timeout duration.
-			t.Errorf("#%d: duration %v blocks", i, duration)
-		}
-	}
-}
-
-func TestWorkerCumStarttime(t *testing.T) {
-	restart()
-
-	ctx := context.Background()
-	m, err := NewMeasureInt64("measure", "desc", "unit")
-	if err != nil {
-		t.Fatalf("NewMeasureInt64() = %v", err)
-	}
-	view, err := NewView("cum", "", nil, m, CountAggregation{}, Cumulative{})
-	if err != nil {
-		t.Fatalf("NewView() = %v", err)
-	}
-
-	SetReportingPeriod(25 * time.Millisecond)
-	if err := view.Subscribe(); err != nil {
-		t.Fatalf("cannot subscribe to %v: %v", view.Name(), err)
-	}
-
-	e := &vdExporter{}
-	RegisterExporter(e)
-	defer UnregisterExporter(e)
-
-	Record(ctx, m.M(1))
-	Record(ctx, m.M(1))
-	Record(ctx, m.M(1))
-	Record(ctx, m.M(1))
-
-	time.Sleep(50 * time.Millisecond)
-
-	Record(ctx, m.M(1))
-	Record(ctx, m.M(1))
-	Record(ctx, m.M(1))
-	Record(ctx, m.M(1))
-
-	time.Sleep(50 * time.Millisecond)
-
-	e.Lock()
-	if len(e.vds) == 0 {
-		t.Fatal("Got no view data; want at least one")
-	}
-
-	var start time.Time
-	for _, vd := range e.vds {
-		if start.IsZero() {
-			start = vd.Start
-		}
-		if !vd.Start.Equal(start) {
-			t.Errorf("Cumulative view data start time = %v; want %v", vd.Start, start)
-		}
-	}
-	e.Unlock()
-}
-
-type countExporter struct {
-	sync.Mutex
-	count int64
-}
-
-func (e *countExporter) ExportView(vd *ViewData) {
-	if len(vd.Rows) == 0 {
-		return
-	}
-	d := vd.Rows[0].Data.(*CountData)
-
-	e.Lock()
-	defer e.Unlock()
-	e.count = int64(*d)
-}
-
-type vdExporter struct {
-	sync.Mutex
-	vds []*ViewData
-}
-
-func (e *vdExporter) ExportView(vd *ViewData) {
-	e.Lock()
-	defer e.Unlock()
-
-	e.vds = append(e.vds, vd)
-}
-
-// restart stops the current processors and creates a new one.
-func restart() {
-	defaultWorker.stop()
-	defaultWorker = newWorker()
-	go defaultWorker.start()
-}