There are several things you can do to improve the performance of your app, including designing better maps and scenes, using the appropriate rendering mode, and using graphics more efficiently.
Performance may be defined differently depending on the type of app you create. Mobile apps might require battery conservation, underpowered devices might require more efficient use of CPU and memory, apps with heavy analysis or geoprocessing may need to keep the UI thread responsive. To appropriately gauge performance for your app, you should test on devices that are representative of those on which your app will run, including the lower-end of the range.
You can define a
GraphicsOverlay.RenderingMode for each
GraphicsOverlay in your geoview and a
FeatureLayer.RenderingMode for each
FeatureLayer in the map. Setting the appropriate rendering mode for each layer and graphics overlay can help fine tune display performance for your app. The available rendering modes are described below.
- Dynamic: Creates representations of the data on the device's graphics processing unit (GPU) and those representations essentially live on the GPU for the lifetime of the layer. A GPU based representation is created for every symbol (and geometry) and is drawn with every frame.
Rendering with the GPU is extremely fast and doesn't use much battery. Dynamic rendering mode is good for moving objects and for maintaining graphical fidelity during extent changes, since individual graphic changes can be efficiently applied directly to the GPU state. This provides a seamless look and feel when interacting with the display. The number of features or graphics has a direct impact on GPU resources and can therefore affect the responsiveness of user interaction. Ultimately, the number and complexity of objects that can be rendered in dynamic rendering mode is dependent on the power and memory of the GPU.
- Static: Uses the device's central processing unit (CPU) to draw an image and then that image is pushed to the GPU to render. This is like creating a new picture every time the view needs to be redrawn (when the extent changes, for example). Optimizations in ArcGIS Runtime rendering prevent this from occurring with each view interaction, but may result in the map becoming blurry or blank before it eventually redraws.
Static mode only renders features and graphics when needed (for example, after an extent change) and offloads a significant portion of the graphical processing onto the CPU. As a result, less work is required by the GPU to draw, and the GPU can spend its resources on keeping the UI interactive. This mode is well-suited for stationary features or graphics, complex geometries, and very large numbers of features or graphics. The number of features and graphics has little impact on frame render time, meaning it scales well, and pushes a constant GPU payload. However, rendering updates is CPU and system memory intensive, which can have an impact on device battery life.
- Graphics overlays render in dynamic rendering mode
- Point feature layers render in dynamic rendering mode
- Polyline and polygon feature layers render in static rendering mode
You can define
LoadSettings to set the preferred rendering mode for feature layers of each geometry type in a map or scene.
As a general rule, it's typically better to use static rendering mode on complex geometries, such as polygon graphics with a large number of vertices.
A map or scene is often the heart of your ArcGIS Runtime app, and there is a lot you can do to improve app performance without touching your code. The maps and scenes displayed by your apps should be designed for visual appeal and usability, as well as for performance.
Features on the map should only be displayed at scales at which they are meaningful. This generally means not showing features at a small scale that can only be distinguished at a larger scale. For instance, displaying building footprints when the map is zoomed to the scale of a large city would likely draw what appears to be a single large polygon on the map. Setting a scale range for that layer would ensure that the buildings are only drawn when the user zooms the map to a scale at which individual features can be distinguished. Each layer in the map can have its own scale range defined to toggle layer display on and off as appropriate for the data.
Designing a map with meaningful scale ranges for layers not only conserves drawing resources, it makes the information in the map more digestible for your user.
Keeping layers with the same geometry type and rendering mode next to each other can improve performance by optimizing data transfer to the device's graphics processing unit (GPU). Layers in static rendering mode are rendered to an image on the central processing unit (CPU). ArcGIS Runtime combines static rendering mode layers which are adjacent (in the stack) and push the stack as a single set of textures to the GPU. In contrast, when static layers are not adjacent to one another (if interleaved with dynamic layers), ArcGIS Runtime must generate multiple images to preserve visibility ordering. In this case, multiple sets of textures must be pushed for each extent change, thereby negatively impacting performance.
Layer order does not affect layers in dynamic rendering mode, because all resources for the layers reside on the GPU, and the GPU can easily arrange these items when drawing.
Some symbols perform better than others, especially when displaying a lot of features or graphics. In general, you should limit the use of composite symbols in favor of a
DictionaryRenderer, limit the use of
TextSymbol objects in favor of the labeling API (see the Add labels topic for details), and use a
Renderer whenever possible to apply symbols to geoelements.
Additional tips for improving performance for graphics symbols are described in the graphics section below.
When possible, you should use the same spatial reference for your map and all the layers and data it consumes. This also applies to large result sets returned from operations like geoprocessing, geocoding, or routing. While ArcGIS Runtime provides powerful functionality for reprojecting data as needed, it comes with a computational cost. Keeping your basemap and all operational layers in the same spatial reference can therefore greatly improve performance.
Graphics are in-memory representations of geographic elements for display on a map view or scene view. They have a shape (point, line, or polygon geometry), a set of attributes, and a symbol. Graphics are created while the app is running and are not persisted between app sessions. For more information about graphics, see the Features and graphics topic.
There are several use cases for working with graphics. Some apps may only use a small number of graphics to show a few points of interest, text labels, and perhaps a user-defined sketch on the display. Others may have graphics-intensive workflows such as updating the symbols and locations for thousands of moving graphics in real time. For apps that only use a few graphics and that don't require frequent graphic updates, performance is unlikely to be affected by the things described in this section. For apps that rely on thousands of frequently updated graphics, however, significant performance improvements can be made by following some of the recommendations below.
A graphics overlay can contain graphics of different geometry types and with different sets of attributes. For apps that only need to display a few graphics, maintaining all graphics in a single graphics overlay may provide an acceptable option. Graphics maintained this way typically have their own symbol to define their display: marker symbols for points, line symbols for polylines, and so on.
For apps that use a large number of graphics, there are performance benefits for organizing graphics according to their geometry type. As with the map layer order, keeping graphics overlays with the same geometry type and rendering mode next to each other can improve performance by optimizing data transfer to the GPU.
Organizing your graphics into the appropriate graphics overlays is a good first step to further improve performance by:
Graphics performance can be impacted not only by the number of graphics an app uses, but also by the number of symbols used to display them. An app with hundreds of graphics that each define their own symbol also uses hundreds of symbols. If designed correctly, a graphics overlay with thousands of graphics can be symbolized with just a handful of symbols.
Organizing graphics with the same geometry type into the same graphics overlay allows you to define graphic symbology by applying a renderer to the graphics overlay. A renderer uses one or several symbols, but those symbols are shared by all graphics in the graphics overlay, which can greatly reduce the number of symbols used in your app. If the graphics in your graphics overlay also have a consistent attribute schema, you can use those attributes to create a
For more information about creating and applying a renderer, see Symbols, renderers, and styles.
Some graphics operations, such as adding graphics to a graphics overlay or adding attributes to a graphic, provide an option of working in batches rather than one object at a time. It's more efficient to pass a batch of objects into the ArcGIS Runtime object since it prevents events from being triggered for each individual addition, including drawing. Whenever you have code that works with individual graphics within a loop, you should explore alternatives that allow doing the same operations in a batch.
GraphicsOverlay exposes a collection of graphics to which you can add and remove graphics. The graphics collection, like most collections in ArcGIS Runtime, also provides the ability to add several objects by passing in a collection of things to add.
When constructing a
Graphic, you have the option of passing in an object that defines a collection of attribute names and values. When creating a lot of graphics (that perhaps also have a lot of attributes), it's more efficient to use this constructor than adding attributes one at a time.
When working with a lot of graphics, it's best to keep them as lean as possible. You should only maintain attributes that are required for your app to: uniquely identify each graphic in the overlay, display a symbol for the graphic (with a unique value or class breaks renderer, for example), or to display information in a popup or label.
If additional attribute information is needed to support functionality in your app, consider keeping that information in another data structure (such as a
Hash) that uses the graphic's identifier as a unique key. If you created graphics by reading them from a data source, for example, you should be able to query the original data source using an ID that ties the graphic to a row in the table. This is similar to the technique described in this topic for maintaining a graphics lookup.
For an app that tracks objects in real time and updates their graphic representations on a map view or scene view, graphics updates need to be as fast and efficient as possible. Think of a vehicle control center that receives thousands of updates each second to update the location and status for a large fleet of vehicles, for example.
The first step when updating a graphic, of course, is to find the correct one to update. Your initial thought may be to store a unique ID as an attribute with each graphic and stream through all graphics in the overlay to find graphics by ID. This is a viable approach and would likely work well for apps that don't have too many graphics or that don't require updates too frequently.
A much faster approach, however, and one that works better for frequent graphic updates is to use a
Hash (a collection of key-value pairs) to maintain a lookup of IDs and graphics. With this structure in place, updating graphics (perhaps by processing a stream of update messages) is simple. When an update comes in (a message is received, for example), a check is made against the lookup to see if a graphic already exists for the object (vehicle, for example). If it exists, the graphic update is made (its location and/or attribute values) and if it doesn't, a new graphic is created and added to both the graphics overlay and to the lookup.
For an example of this technique, see the blog article: Creating and updating 1000s of graphics in an ArcGIS Runtime app.
On mobile devices, where resources are limited and network speed is variable, it is advisable to not perform long running tasks on the main thread.
You should avoid running tasks on the main thread because your app may stop responding to user interaction and become unresponsive. While ArcGIS Runtime provides a number of asynchronous methods, which place work directly in the background, some methods are synchronous and you should always consider whether they could block the main thread. For example, finding the number of edits in a local geodatabase could block the main thread as the call is made to an external file and contains an unknown number of results. To ensure a good user experience and keep the solution scalable, you should avoid placing expensive tasks on the main thread.
See the Processes and threads overview in the Android developer guide to learn how processes and threads work in an Android application.
Mobile devices often use 3G or sometimes lower speed radio communication networks to obtain and transfer data. The speed of these networks vary but are much slower (in terms of data per second) than wired or wireless networks. Due to this network latency, even small requests can take time to return, which makes your application seem sluggish. Therefore, you need to carefully manage the total amount of data and the number of network requests submitted by your mobile application. As an example, it may be more efficient to send a single large request for data rather than multiple small requests. Changing the layer types, application functions, or the flow of your application can also affect network speed.
If your mobile application users are always in the range of a wireless network, your application can retrieve and submit larger amounts of data. However, even in this scenario, it's always good practice to remember the amount of data and number of requests your application uses, whatever the bandwidth.
By understanding the characteristics of the different types of map layers in the API, you can determine the best layers for your needs and ensure that your application performs for your users.
Some application users may only have intermittent network access, perhaps due to working in remote areas or scheduled daily access to a wireless network for synchronizing data. If this is the case, local storage usage is important. The application can be designed to connect to the server to retrieve data the user needs, then store this data on the local device. Applications need to be developed robustly with this in mind, because the network connection can be dropped anytime. Functions need to fail gracefully, and any long running application transactions may need to be rolled back.
- Creating and updating 1000s of graphics in an ArcGIS Runtime app (Blog)
- Tips to improve graphics performance (Dev Summit 2022 presentation: ArcGIS Runtime .NET - Building Apps - part 2, starts around 22:30) ArcGIS Online Login required