package com.oyly.gpsdemo
import android.content.Context
import android.media.MediaPlayer
class SoundGenerator(context: Context) : MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener {
private var mediaPlayer: MediaPlayer = MediaPlayer()
private var nextSound: Int = R.raw.coast
init {
try { mediaPlayer = MediaPlayer.create(context, R.raw.coast) }
catch (ex : Exception) { println("lyseoy11: $ex") }
mediaPlayer.setOnPreparedListener(this)
mediaPlayer.setOnCompletionListener (this)
}
override fun onPrepared(p0: MediaPlayer) {mediaPlayer.start()}
override fun onCompletion(p0: MediaPlayer?) { playSound() }
fun queueSound(resid: Int) {
nextSound = resid
if (!mediaPlayer.isPlaying) playSound()
}
fun playSound() {
mediaPlayer.reset()
mediaPlayer.setDataSource(resources.openRawResourceFd(nextSound))
mediaPlayer.prepareAsync()
}
}
The "mediaPlayer = MediaPlayer.create(context, R.raw.coast" line throws the following exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
I have moved this code from a standalone app, where it worked, into this class of another app. Now it doesn't work and I can't understand why. The MediaPlayer.Create() method takes as arguments the context (which I pass on from the MainActivity class that calls this class), and an uri to the audio file to be played. Neither of them are null.
Why do I get this exception?
Edit:
The only place SoundGenerator is created is in the MainActivity Initializer:
class MainActivity : AppCompatActivity() {
private var soundGenerator : SoundGenerator = SoundGenerator(this)
...
}
Often it is best to wait to use the activity as a context until later in the lifecycle when things have been set up (e.g. in onCreate) to avoid initialization order issues. For exampe:
class MainActivity : AppCompatActivity() {
private lateinit var soundGenerator : SoundGenerator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
soundGenerator = SoundGenerator(this)
}
}
Alternately, sometimes you can use applicationContext instead of the current activity as an appropriate context.
Related
I was wondering how to make the music play automatically when starting the app and how to make it stop playing in the background when pressing the home button. Right now, it starts and stops by pressing the toggle button. I was also wondering if its possible to automatically switch to other music when going to another activity?
MainActivity.kt
private lateinit var player: MediaPlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val secondActivity = findViewById<Button>(R.id.secondActivity)
secondActivity.setOnClickListener {
val intent = Intent(this, MainActivity2::class.java)
startActivity(intent)
}
val toggle: ToggleButton = findViewById(R.id.toggleButton)
toggle.setOnCheckedChangeListener { _, isChecked ->
val svc = Intent(this, MusicService::class.java)
if (isChecked) {
startService(svc)
} else {
stopService(svc)
}
}
}
MusicService.kt
class MusicService : Service() {
private lateinit var player: MediaPlayer
override fun onBind(intent: Intent?): IBinder? {
TODO("Return the communication channel to the service.")
return null
}
override fun onCreate() {
super.onCreate()
player = MediaPlayer.create(this, R.raw.music)
player.setLooping(true)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
player.start()
return START_STICKY
}
override fun onDestroy() {
super.onDestroy()
player.stop()
}
}
Depending on your specific requirements, you should or shouldn't use a MediaService as part of your solution.
To be more clear, a Service should only be used if you want the audio to keep going whenever you're outside the app. This solution will usually be accompanied by a media Notification which you should populate with controls, image assets, etc. (Think of Spotify or SoundCloud) If this is the solution you're looking for, take a look at this doc page from Google and follow it through. Beware that this is a longer and tougher process to maintain.
On the other hand, if all you want to do is play music/sounds while your user is inside your app, then a simple
private lateinit var localMedia: MediaPlayer
override fun onCreate() {
...
localMedia = MediaPlayer.create(context, R.raw.your_audio_file)
}
override fun onResume() {
...
localMedia.start()
}
override fun onPause() {
...
localMedia.release()
}
Furthermore, if you want different audio files to be played on different Activities/Fragments, you might want to abstract the code I provided above into it's own Manager class or so and access it the same but changing the specific .mp3 file (or whatever format) as you see fit.
EDIT:
For a Manager class, you'll have to create your own functions and handle the MediaPlayer inside of it
private class MediaPlayerManager(private val context: Context) {
private lateinit var mediaPlayer: MediaPlayer
fun setupPlayer() {
mediaPlayer = MediaPlayer.create(context, R.raw.your_audio_file)
}
fun play() {
mediaPlayer.start()
}
fun stop() {
mediaPlayer.stop()
}
}
And call these functions from their respective lifecycle method inside your Activity/Fragment, depending on your specific needs
class YourActivity {
val mediaPlayerManager = MediaPlayerManager(context)
override onCreate() {
...
mediaPlayerManager.setupPlayer()
}
override fun onResume() {
...
mediaPlayerManager.play()
}
override fun onPause() {
...
mediaPlayerManager.stop()
}
}
I should add that I'm not necessarily providing a fully-fledged answer here, but a starting point for you to massage to your own needs. The Manager class is nothing but an abstraction of the concept I'm trying to communicate. Lastly, if you want to use a different audio resource file in another Activity/Fragment, you would have to create a method to re-assign the MediaPlayer object inside it with the appropriate file.
E.g.
fun setupPlayer(audioRes: Int) {
mediaPlayer = MediaPlayer.create(context, audioRes)
}
I have done this before
create a Class and named it (for example I named it C) and extend that from Application like following (don't forget put android:name=".C" in <application> tag in manifest.xml):
class C:Application() {
fun onCreate() {
super.onCreate()
context = getApplicationContext()
app = this
}
companion object {
private val context:Context
var currentActivity:Activity
var currentActivities:ArrayList<Activity> = ArrayList()
var handler:Handler
var app:C
fun get():C {
return app
}
fun getContext():Context {
if (currentActivity != null)
{
return currentActivity
}
return context
}
}
}
I created a class for parent of AppCompatActivity and (named UAppCompactActivity)extend it form AppCompatActivity then extend all activities from UAppCompactActivity:
abstract class UAppCompatActivity:AppCompatActivity() {
fun onCreate(#Nullable savedInstanceState:Bundle, #Nullable persistentState:PersistableBundle) {
super.onCreate(savedInstanceState, persistentState)
}
protected fun onResume() {
super.onResume()
C.setCurrentActivity(this)
C.currentActivities.add(this)
/* you can play your music here or do any action you desired */
}
protected fun onPause() {
super.onPause()
C.currentActivities.remove(this)
/*
you can stop your music here or do any action you desired
if (UBase.currentActivities.size() === 0)
G.backgroundMusics.get(G.app.musicNumberBackAndNowPlay).pause()
else
play music or switch to new music
*/
}
}
Im working on a flutter plugin that handles the generation of a service.
However recently i found an issue with the GeneratedPluginRegistrant that Flutter-Framework uses.
When i execute this command:
flutter run
The result is a Build Failured with the following message.
GeneratedPluginRegistrant.java:14: error: non-static method
registerWith(Registrar) cannot be referenced from a static context
FloatyHeadPlugin.registerWith(registry.registrarFor("ni.devotion.floaty_head.FloatyHeadPlugin"));
However as far i know on the Plugin Kotlin Class that function isn't related inside a Companion to be handled as a static function, and it doesn't matter if i change that the error won't go away...
class FloatyHeadPlugin : Activity(), MethodCallHandler {
companion object {
lateinit var instance: FloatyHeadPlugin
var activity: Activity? = null
var context: Context? = null
var sBackgroundFlutterView: FlutterNativeView? = null
var sIsIsolateRunning = AtomicBoolean(false)
private var channel: MethodChannel? = null
private var backgroundChannel: MethodChannel? = null
}
fun registerWith(pluginRegistrar: Registrar) {
context = pluginRegistrar.context()
channel = MethodChannel(pluginRegistrar.messenger(), METHOD_CHANNEL)
channel?.setMethodCallHandler(FloatyHeadPlugin())
}
if you wanna see more detail about it,you can also check the github repo is in the branch
feature/code_enhancement
GITHUB REPOSITORY
Someone knows how to solve this issue? thanks.
Well... for anyone having this problem, the solution is to implement in your FlutterPlugin Class, the extension ActivityAware.
And implement the following functions:
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel?.setMethodCallHandler(null)
//release()
}
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, METHOD_CHANNEL)
channel?.setMethodCallHandler(this)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
instance = this#FloatyHeadPlugin
}
override fun onDetachedFromActivity() {
//release()
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {
//release()
}
I have a ViewModel that inherits from a base class and I would like to have a corresponding Activity also inherit from a base class. The activity would call the same method of the derived ViewModel each time. So it would be something like this:
BaseViewModel:
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
protected val context = getApplication<Application>().applicationContext
protected var speechManager: SpeechRecognizerManager? = null
var _actionToTake : MutableLiveData<AnalyseVoiceResults.Actions> = MutableLiveData()
var actionToTake : LiveData<AnalyseVoiceResults.Actions> = _actionToTake
open fun stopListening() {
if (speechManager != null) {
speechManager?.destroy()
speechManager = null
}
open fun startListening() {
val isListening = speechManager?.ismIsListening() ?: false
if (speechManager == null) {
SetSpeechListener()
} else if (!isListening) {
speechManager?.destroy()
SetSpeechListener()
}
}
}
BaseActivity
class BaseActivity : AppCompatActivity() {
private lateinit var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
Derived Activity:
class DerivedActivity : BaseActivity() {
private val nextActivityViewModel: NextActivityViewModel by inject()
///^^inherits from BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*** pass reference ***/
baseViewModel = nexActivityViewModel
nextActivityViewModel.actionToTake.observe(this, object : Observer<AnalyseVoiceResults.Actions?> {
override fun onChanged(t: AnalyseVoiceResults.Actions?) {
if (t?.equals(AnalyseVoiceResults.Actions.GO_BACK) ?: false) {
goback()
}
}
})
startListening()
}
}
Will this cause memory leaks to have two instances of a view model for this activity? Is there a better way to do this? I don't want to keep repeating the same code for all my activities. (I would also have the same question if I was doing this with one base fragment).
make this var baseViewModel: BaseViewModel an abstract variable where all the children class must override it. So, when you call the startListening and stopListening, these methods will be called from children implementation.
Edit:
Make the BaseActivity an abstract class and the baseViewModel as an abstract variable
abstract class BaseActivity : AppCompatActivity() {
private abstract var baseViewModel: BaseViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun goback() {
super.onBackPressed()
baseViewModel.stopListening()
finish()
}
fun startListening() {
baseViewModel.startListening()
}
override fun onDestroy() {
super.onDestroy()
baseViewModel.stopListening()
}
}
So, your DerivedActivity must override the baseViewModel, and every call on father's class will trigger the child
class DerivedActivity : BaseActivity() {
override val baseViewModel: NextActivityViewModel by inject()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Will this cause memory leaks to have two instances of a view model for
this activity?
No, there are no memory leaks with this approach. Nor do you have 2 instances of the ViewModel for the same activity. It's a single instance of ViewModel which is being referenced with different variables in BaseActivity and BaseViewModel.
Is there a better way to do this?
The first issue I see is that you have Android specific code in the ViewModels, which is not considered a good practice. You should move the speech manager code to the base activity itself, and ViewModel should only hold the "state" data that you want to retain on orientation changes. This will ensure all the Speech Management methods (create, resume, destroy) will be in the base activity. Concrete activity will only have observers if the state changes.
If you are following any architecture pattern (like MVP), once you move the Speech Manager code out to activity, it would become obvious to move this further out to the Presenter.
EDIT: I have not used the MVVM pattern in production, but this is a light variant of what you may want:
The basic idea is to move Speech management code in a lifecycle-aware component. All UI code in view/activity and business logic / non-android state in viewmodel. I don't see a point in having base activity or viewmodel based on the requirements you have shared so far.
/**
* All the speech related code is encapsulated here, so any new activity/fragment can use it by registering it's lifecycle
*/
class SpeechManager(private val context: Context): LifecycleObserver {
val TAG = "SpeechManager"
private var speechRecognizer: SpeechRecognizer? = null
fun registerWithLifecycle(lifecycle: Lifecycle) {
Log.e(TAG, "registerWithLifecycle")
lifecycle.addObserver(this)
}
#OnLifecycleEvent(Lifecycle.Event.ON_START)
fun start() {
Log.e(TAG, "start")
speechRecognizer = (speechRecognizer ?: SpeechRecognizer.createSpeechRecognizer(context)).apply {
// setRecognitionListener(object : RecognitionListener {
// //implement methods
// })
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stop() {
Log.e(TAG, "stop")
speechRecognizer?.run {
stopListening()
destroy()
}
}
}
ViewModel:
class SpeechViewModel: ViewModel() {
val TAG = "SpeechViewModel"
//List all your "data/state" that needs to be restores across activity restarts
private val actions: MutableLiveData<Actions> = MutableLiveData<Actions>().apply { value = Actions.ActionA }
//Public API for getting observables and all use-cases
fun getActions() = actions
fun doActionA(){
//validations, biz logic
Log.e(TAG, "doActionA")
actions.value = Actions.ActionA
}
fun doActionB(){
Log.e(TAG, "doActionB")
actions.value = Actions.ActionB
}
}
sealed class Actions{
object ActionA: Actions()
object ActionB: Actions()
}
Activity/View:
class SpeechActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_speech)
setSupportActionBar(toolbar)
initSpeechManager()
}
private lateinit var speechManager: SpeechManager
private lateinit var speechViewModel: SpeechViewModel
/**
* Register lifecycle aware components and start observing state changes from ViewModel.
* All UI related code should ideally be here (or your view equivalent in MVVM)
*/
private fun initSpeechManager() {
speechManager = SpeechManager(this).apply {registerWithLifecycle(lifecycle)}
speechViewModel = ViewModelProviders.of(this).get(SpeechViewModel::class.java).apply {
getActions().observe(this#SpeechActivity, Observer<Actions>{
when(it){
is Actions.ActionA -> {
Log.e(TAG, "Perform ActionA")
speechManager.start()
}
is Actions.ActionB -> {
Log.e(TAG, "Perform ActionB")
speechManager.stop()
super.onBackPressed()
}
}
})
}
}
}
I have ExoPlayerWrapper class which is wrapper for ExoPlayer and is singleton injected by Dagger. Init block in ExoPlayerWrapper class looks in the following way:
#Singleton
class ExoPlayerWrapper #Inject constructor(
context: Context,
userAgentInfo: UserAgentInfo
) {
private val exoPlayer: ExoPlayer
private val httpDataSource: HttpDataSource
private val mediaSourceFactory: ExtractorMediaSource.Factory
override val exoPlayerInstance: ExoPlayer
get() = exoPlayer
init {
...
exoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector)
httpDataSource = CustomHttpDataSource(userAgentInfo.userAgent, null)
mediaSourceFactory = ExtractorMediaSource.Factory { httpDataSource }
state = Player.STATE_IDLE
}
override fun playFromUrl(uri: Uri, headers: Map<String, String>) {
reset()
...
val mediaSource = mediaSourceFactory.createMediaSource(uri)
exoPlayer.prepare(mediaSource)
exoPlayer.playWhenReady = true
}
override fun pause() {
exoPlayer.playWhenReady = false
}
override fun reset() {
stop()
state = Player.STATE_IDLE
}
override fun stop() {
exoPlayer.stop()
exoPlayer.seekTo(0)
}
override fun seekTo(position: Long) {
exoPlayer.seekTo(position)
}
...
override fun release() {
exoPlayer.release()
state = Player.STATE_RELEASED
}
And in onCreate() method of Activity I have the following code:
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_player)
...
if (savedInstanceState != null) {
initialized = savedInstanceState.getBoolean(INITIALIZED)
}
if (!initialized) {
val uri = Uri.parse(attachedVideo?.uri)
player.playFromUrl(uri, sessionStore.sessionHeaders)
videoView.requestFocus()
initialized = true
}
videoView.player = player.exoPlayerInstance
backBtn.setOnClickListener{ _ ->
player.release()
finish()
}
}
But in this case I can play only one video after click on backBtn button, other videos after that aren't played until I close app and reopen app again. And if I change backBtn's OnClickListener in the following way:
backBtn.setOnClickListener{ _ ->
player.pause()
finish()
}
all works fine, other videos are played after click on backBtn button. So it seems to be problem with reinitializing ExoPlayer after release() method invocation. And how correctly to reinitialize ExoPlayer after releasing?
UPD
And after release() invocation and after attempt to open video again I see the following error in Logcat:
java.lang.IllegalStateException: Handler (android.os.Handler) {81fa8ef} sending message to a Handler on a dead thread
at android.os.MessageQueue.enqueueMessage(MessageQueue.java:545)
at android.os.Handler.enqueueMessage(Handler.java:661)
at android.os.Handler.sendMessageAtTime(Handler.java:630)
at android.os.Handler.sendMessageDelayed(Handler.java:600)
at android.os.Handler.sendMessage(Handler.java:537)
at android.os.Message.sendToTarget(Message.java:418)
at com.google.android.exoplayer2.ExoPlayerImplInternal.stop(ExoPlayerImplInternal.java:207)
at com.google.android.exoplayer2.ExoPlayerImpl.stop(ExoPlayerImpl.java:357)
at com.google.android.exoplayer2.SimpleExoPlayer.stop(SimpleExoPlayer.java:777)
at com.google.android.exoplayer2.SimpleExoPlayer.stop(SimpleExoPlayer.java:772)
at package.ExoPlayerWrapper.stop(ExoPlayerWrapper.kt:xx)
at package.ExoPlayerWrapper.reset(ExoPlayerWrapper.kt:xx)
at package.ExoPlayerWrapper.playFromUrl(ExoPlayerWrapper.kt:xx)
at package.VideoPlayerActivity.onCreate(VideoPlayerActivity.kt:xx)
The issue arises from trying to use a player whose resources have already been released. Because:
exoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector)
only gets called once, during the initialization of ExoPlayerWrapper. If your activity is not restarted, then that instance of ExoPlayerWrapper sticks around with the released player.
Either create a new instance of ExoPlayerWrapper whenever you go to play your video, or create a player initialization method inside ExoPlayerWrapper, which you can call just before playing the video.
For your requirement no need to use release method. If you are using this method you should reinitialize exoplayer again.
You can use stop() method to stop the exoplayer.
I have this function in kotlin extension file to pass method but it doesn't work. Please explain me how it make correctly, I try this:
fun showErrorClientScreen(context: Context, action : () -> Unit) {
val intent = Intent(context, RestClientErrorActivity::class.java)
val bundle = Bundle()
bundle.putSerializable(UPDATE_CLIENT_ERROR, ErrorClientListener { action })
intent.putExtra(UPDATE_CLIENT_ERROR_BUNDLE, bundle)
context.startActivity(intent)
}
use java interface
public interface ErrorClientListener extends Serializable {
void tryAgainFunction();
}
and my activity where i need listen click button and try again send request:
class RestClientErrorActivity: BaseActivity(), View.OnClickListener {
private lateinit var errorClientListener: ErrorClientListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_rest_client_error)
try {
val bundle = intent.getBundleExtra(UPDATE_CLIENT_ERROR_BUNDLE)
errorClientListener = bundle?.getSerializable(UPDATE_CLIENT_ERROR) as ErrorClientListener
} catch (e: Exception) {
e.message
}
}
override fun onClick(v: View?) {
when (v?.id) {
R.id.ib_update -> errorClientListener.tryAgainFunction()
}
}
}
It is quite strange to package interfaces between activities and it is definitely not advisable. One reason why it is maybe not serializing between Activity A and Activity B is because the object was created in Activity A, it is treated as anonymous class creation and Activity A holds the reference to this object, hence preventing it from being serialised. This is good, because you can create references to objects within the interface callback whose reference in turn would be held by class instantiating it. Therefore, garbage collector won't be able to run collections on these objects and free up the space; causing a massive memory leak.
The alternative approach to your problem could be using clean architectures and a Singleton class pattern that is accessible by both activities and instantiated only once by say Activity A:
class SingletonErrorHandler private constructor(){
var isError = false
fun doOnError() {
// do non view related stuff
// like a network call or something
}
companion object {
val instance by lazy { SingletonErrorHandler() }
}
}
in the activity you can define
class ActivityA : AppCompatActivity() {
fun onError() {
SingletonErrorHandler.instance.isError = true
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.a_activity)
}
}
in activity B
class ActivityB : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.b_activity)
val errorHandler = SingletonErrorHandler.instance
if(errorHandler.isError)
errorHandler.doOnError()
}
}
You can write factory method to start the activity like android studio generates factory method for fragment creation.
class RestClientErrorActivity : AppCompatActivity() {
companion object {
private var completion: (() -> Unit)? = null
fun start(context: Context, completion: (() -> Unit)?) {
RestClientErrorActivity.completion = completion
val bundle = Bundle()
intent.putExtra(UPDATE_CLIENT_ERROR_BUNDLE, bundle)
context.startActivity(intent)
}
}
private lateinit var retryButton: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retryButton = findViewById(R.id.btn_retry)
}
fun onRetryClick(view: View) {
finish()
completion?.invoke()
}
}
Note: completion is not mandatory. so i made that as nullable. if you start activity without using factory method app will not crash.
I had the same problem. As mentioned in HawkPriest's Answer, your object is not serializable, because its an anonymous class. Another way to fix this is to simply implement a non-anonymous class that implements your interface. Here is my code:
Interface
interface MyInterface : Serializable {
fun instruction()
}
Class
class MyClass : MyInterface {
override fun instruction() {
// does something
}
}
Calling Activity
val myObject = MyClass()
val intent = Intent(context, MyActivity::class.java).putExtra("Tag", myObject)
context.startActivity(intent)
Activity
override fun onCreate(savedInstanceState: Bundle?) {
val myObject = intent.getSerializableExtra("Tag") as MyInterface
myObject.instruction()
}
Regarding the "native resources" as mentioned in your comment, you can make your instruction take parameters or pass them to your MyObject.
P.S. The problems I have with the Singleton solution:
Singleton is not eligable for garbage collection, which means it lives on after its not needed anymore. (not 100% sure about that, but that's what I get from this answer)
Using singleton would mean you cant have "multiple different uses" for your activity. If an interface is used, it is to be able to use multiple different implementations of that interface. A singleton wouldn't provide that, without using an interface architecture within your singleton, which would then again render it unnecessary, considering my proposed solution.