Streaming Measurement in Android
This chapter describes the basic use of the sensor for the measurement of streaming content.
Properties of the Library
Property | Default | Description |
---|---|---|
tracking | true | If this property is set to |
offlineMode | false | If this property is set to |
debug | false | If the value is set to true , the debug messages are issued by the library. |
timeout | 10 | Timeout Setting for the HTTP-Request. |
Lifecycle of the Measurement
This chapter explains step by step the use of the sensor and the measurement of streaming content . The following steps are necessary in order to measure a content.
- Generating the sensor
- Implementation of the adapter
- Beginning of the measurement
- End of the measurement
Generating the Sensor
When you start the app the sensor must be instantiated once. It is not possible to change the site name or the application name after this call.
String site = ...; // will delivered by the measurement provider Spring appName = ...; // will delivered by the measurement provider SpringStreams sensor = new SpringStreams(site, appName, getApplicationContext());
The site name and application name is specified by the operator of the measurement system.
Implementation of the Adapter
In principle it is possible to measure any media library with an adapter, that is available within an app to use streaming content .
Therefore the protocol Meta
and the interface StreamAdapter
must be implemented. The meta object supplies the information regarding the used player, the player version and the screen size.
The library must be able to read continuously the current position on a stream in seconds or the current position of player.
package de.spring.mobile; /** * This adapter must be used to support some players by the library. * * @see Meta * @see SpringStreams * @see SpringStreams#track(StreamAdapter, java.util.Map) * * @author <a href="mailto:support@spring.de">spring GmbH & Co.KG</a> */ public interface StreamAdapter { public interface Meta { public String getPlayerName(); public String getPlayerVersion(); public int getScreenWidth(); public int getScreenHeight(); } public Meta getMeta(); public int getPosition(); public int getDuration(); public int getWidth(); public int getHeight(); }
Beginning of the Measurement
This chapter explains step by step how a streaming content is transferred to the library for the measurement.
In the library an adapter for class android.widget.VideoView
The source code for this implementation can be found in Appendix A and in the library.
The following code block shows an example for the use of the library.
// Create a sensor once in the application SpringStreams sensor = new SpringStreams(site, appName, getApplicationContext()); ... public MyVideoActivity extends Activity { private static SpringStreams sensor; private Stream stream; public MyVideoActivity(SpringStreams sensor) { if(MyVideoActivity.sensor == null) MyVideoActivity.sensor = sensor; } @Override protected void onStart() { Map<String, Object> atts = new HashMap<String, Object>(); atts.put("stream", "android/teststream"); // mandatory // atts.put("cq", "4711"); // optional see implementation guideline // atts.put("ct", "mobile"); // optional see implementation guideline stream = sensor.track(new VideoViewAdapter(this.videoView), atts); super.onStart(); } @Override protected void onStop() { stream.stop(); super.onStop(); } ... }
First, the player and the object needs to be instantiated, that is able to deliver the current position on a stream in seconds. In the second step the adapters must be produced, which implements this requirement accurately.
Then an NSDictionary is generated in order to formulate more detailed information about the stream. Therefore the attribute stream must always be specified
The attribute stream
is always required and must be specified for each measurement
Next, the method track
is called with the adapter and the description property of the stream. From this point on, the stream is measured by the library as long as the application remains in foreground and the measured data are cyclically transmitted to the measuring system.
When the application goes into the background , all measurements are stopped and closed, i.e. when the application comes to the foreground again, the method track
must be called again.
A stream is measured as long as the Activity is in the foreground. When the Activity goes into the background, the current status is transmitted to the measurement system and the measurement stops. If the stream should be measured again, when the application will come back to the foreground, the method track
must be called again.
End of the Measurement
After the measurement of a stream has been started, this stream is measured by the sensor. The measurement can be stopped by calling the method stop
on the stream object. All measurements will be automatically stopped by the library, when the application goes into the background.
// start streaming measurement Stream *stream = sensor.track(adapter, atts); ... // stop measurement programmatically stream.stop();
If the stream should be measured again, when the Activity comes to the foreground or after the method stop
has been called, the method track
must be called again.
Ending the application
If the application is closed, the method unload
can be called. This call sends the current state of the measurements to the measuring system and then terminates all measurements. This method is automatically called by the library, when the application goes into the background.
... sensor.unload();
Using the SpringStreamsActivity
In the library is a SpringStreamsActivity included, that can be used to largely automate the measurement. The source code of this class can be found in Appendix B and in the library.
public class VideoPlayer extends SpringStreamsActivity { VideoView videoView; /** * */ public VideoPlayer() { super("test", "Streaming Test App"); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); videoView = ...; } @Override protected void onStart() { Map<String, Object> atts = new HashMap<String, Object>(); atts.put("stream", "android/teststream"); super.track(new VideoViewAdapter(this.videoView), atts); super.onStart(); } }
ProGuard
Please Note: If you are using ProGuard, our library could be compromised.
Therefore it is necessary to add the following lines into the ProGuard configuration file if using version spring-appstreaming-android-1.2.5 and lower:
# Keep spring files
-keep class de.spring.** { *; }
-keep class org.apache.** { *; }
Appendix A
In the following example, the adapter has been implemented for the VideoView
from the standard API.
/** * Implementation of an adapter for the {@link VideoView} in standard * android api. * * @author <a href="mailto:support@spring.de">spring GmbH & Co.KG</a> */ public class VideoViewAdapter implements StreamAdapter { private VideoView videoView; public VideoViewAdapter(VideoView videoView) { if(videoView == null) throw new NullPointerException("videoView may not be null"); this.videoView = videoView; } @Override public Meta getMeta() { return new Meta() { public String getPlayerName() { return "android.widget.VideoView"; } public String getPlayerVersion() { return Build.VERSION.RELEASE; } public int getScreenHeight() { return (((WindowManager)videoView.getContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay()).getHeight(); } public int getScreenWidth() { return (((WindowManager)videoView.getContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay()).getWidth(); } }; } @Override public int getPosition() { return (int)Math.round(videoView.getCurrentPosition()/1000.0); } @Override public int getDuration() { return (int)Math.round(videoView.getDuration()/1000.0); } @Override public int getWidth() { return videoView.getWidth(); } @Override public int getHeight() { return videoView.getHeight(); } }
Appendix B
The source code of class SpringStreamsActivity
package de.spring.mobile; import java.util.Map; import android.app.Activity; import android.os.Bundle; /** * * @author <a href="mailto:support@spring.de">spring GmbH & Co.KG</a> */ public abstract class SpringStreamsActivity extends Activity { private static SpringStreams sensor; private static int instances = 0; private Stream stream; public SpringStreamsActivity(String site, String appName) { this(site, appName, 10); } public SpringStreamsActivity(String site, String appName, int timeout) { if(site == null) throw new NullPointerException("parameter site may not be null"); if(appName == null) throw new NullPointerException("parameter appName may not be null"); if(sensor == null) { sensor = new SpringStreams(site, appName, getApplicationContext()); sensor.setTimeout(timeout); } } public Stream getStream() { return stream; } public void setSensorDebug(boolean debug) { sensor.setDebug(debug); } public boolean isSensorDebug() { return sensor.isDebug(); } public void setTracking(boolean tracking) { sensor.setTracking(tracking); } public boolean isTracking() { return sensor.isTracking(); } public void track(StreamAdapter adapter, Map<String, Object> atts) { if(this.stream != null) { stream.stop(); stream = null; } stream = sensor.track(adapter, atts); } /** * @see android.app.Activity#onCreate(android.os.Bundle) */ @Override protected void onCreate(Bundle savedInstanceState) { instances++; super.onCreate(savedInstanceState); } /** * @see android.app.Activity#onStop() */ @Override protected void onStop() { if(stream != null) { stream.stop(); stream = null; } super.onStop(); } /** * @see android.app.Activity#onDestroy() */ @Override protected void onDestroy() { instances--; if(instances <= 0) { sensor.unload(); sensor = null; stream = null; } super.onDestroy(); } }