The application structure
A key part of the development is the application structure. In this section we take you through the steps in building it out with code examples.
- Declare the UI of the application by modifying
activity_main.xml
, which is underapp/src/main/res/layout.
- As shown in the following code, use one linear layout to align the controls vertically. The additional linear layout is used here to position the Truncate and Convolution buttons next to each other. The
ScrollView
wraps all controls. This means that the user can scroll down if the screen is too small to display everything at once.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/buttonGenerateSignal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="buttonGenerateSignalClicked"
android:text="Generate Signal"
android:layout_margin="5dp"/>
<CheckBox
android:id="@+id/checkboxUseNeon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Use Neon?" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal">
<Button
android:id="@+id/buttonTruncate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="buttonTruncateClicked"
android:text="Truncate"
android:layout_margin="5dp"/>
<Button
android:id="@+id/buttonConvolution"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:onClick="buttonConvolutionClicked"
android:text="Convolution"
android:layout_margin="5dp"/>
</LinearLayout>
<com.jjoe64.graphview.GraphView
android:id="@+id/graph"
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_margin="10dp"/>
<TextView
android:id="@+id/textViewProcessingTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="18sp" />
</LinearLayout>
</ScrollView> -
To plot the signal before and after processing, use the GraphView library. Install this library by including the following line under the dependency node of your
build.gradle
:
implementation 'com.jjoe64:graphview:4.2.2'
-
Then, include the
GraphView
in the UI by using the <com.jjoe64.graphview.GraphView> attribute. WhenGraphView
is included, you get a reference to the GraphView instance like you would get with any other Android control.
In the configureGraph
method that we invoke under the onCreate
activity method in the following code. This can then use a reference to the GraphView
to format the plot and add data series. We use a three-line series with instances of the LineGraphSeries
from the GraphView
library. One displays the input signal, one is for the truncated signal, and the last one is for convolution.
GraphView graph; private LineGraphSeriessignalSeries = new LineGraphSeries<>(); private void configureGraph(){ // Initialize graph graph = (GraphView) findViewById(R.id.graph); // Set bounds graph.getViewport().setXAxisBoundsManual(true); graph.getViewport().setMaxX(getSignalLength()); graph.getViewport().setYAxisBoundsManual(true); graph.getViewport().setMinY(-150); graph.getViewport().setMaxY(150); // Configure series int thickness = 4; signalSeries.setTitle("Signal"); signalSeries.setThickness(thickness); signalSeries.setColor(Color.BLUE); // Truncate, and convolution series are configured in the same way // Add series graph.addSeries(signalSeries); // Add legend graph.getLegendRenderer().setVisible(true); graph.getLegendRenderer().setAlign(LegendRenderer.LegendAlign.TOP); }
After setting up the graph, implement event handlers for buttons. The structure of each event handler is similar. The event handlers use the resetData
method of the appropriate LineGraphSeries
instance to display the data in the chart. For example, the following code displays input signal and data after convolution:
private LineGraphSeriessignalSeries = new LineGraphSeries<>(); private LineGraphSeries signalAfterConvolutionSeries = new LineGraphSeries<>(); public void buttonGenerateSignalClicked(View v) { signalSeries.resetData(getDataPoints(generateSignal())); } public void buttonConvolutionClicked(View v) { signalAfterConvolutionSeries.resetData(getDataPoints(convolution(useNeon()))); displayProcessingTime(); }
Event handlers that are related to truncation and convolution also take the information, whether the processing is done using Neon intrinsics or native C++. Check the state of the Use neon checkbox that is provided in the application interface, as you can see here:
private boolean useNeon() { return checkBoxUseNeon.isChecked(); }
getDataPoints
is the helper that converts the byte array, from native-lib
, to a collection of DataPoints
that the GraphView
requires for plotting. Refer to the code repository in the related information tab for more information.
Another helper, displayProcessingTime
, is used to depict the code execution time in the label that is located below the chart. Use this to compare the processing performance between Neon and non-Neon code.