I have only done minor experiments with Android so this answer will be platform agnostic. Ignore it if you want an Android specific solution.
For specific case of the "Get ready" screen my laziest and favorite way is to load an image into the game window and outside the game main loop. Enter the resources loading code, the image will remain visible as nothing is refreshing the graphics. When resource loading finished and entering the main loop, the typical renderGraphics() call, or whatever you named it, at the end of the loop will get rid of the "Get ready" image during its first execution, as you probably are clearing all pixels to black before draw anything each frame.
For sounds, the audio api calls probably do not block while playing. OpenSL? Your game only needs to be fast enough when queuing buffers to avoid artifacts. Then you only need a class with a play method that let you specify the sound to play by resource name or file name. Something like:
SoundService.play(string name, bool loop, bool stream)
When stream is true, the file should be big and you want it to be read in chunks and not all at once, or another option is to let the method decide it by examining the file size, and not having a stream parameter at all.
Number of buffers are probably limited like in OpenAL, when streaming the code that checks if new buffers can be queued and read, and probably decodes to PCM, a new chunk of the file to a buffer, can be part of the main loop, something like SoundService.update() at the end of the main loop, or it can reside in a separate thread. Having a thread per audio resource playing is chaotic, better to have a single thread for all audio streaming.
For example, during collision detection and response you will do something like this:
If (player.health == 0) SoundService.play("boom", false, false)
You can have play calls everywhere but probably you will only need them during collision response, or, in some cases, during input handling.
For other things not audio related. The idea of messaging and event handling was already proposed by another answer. To give an alternative, you can translate the concept of the play method to code that should run like tasks, for example:
Task is a class, that have a run virtual method. Then during main loop you iterate through the task list and call it run method. You enqueue tasks following the same logic that sounds, I mean that you can do it everywhere in you code. Something like this:
If (victory condition reached) taskQueue.add(new ScenarioLoaderTask("level2"))
ScenarioLoaderTask is a class derived from Task that implements run.
Despite its name, tasks are not mean to spawn threads (while the AudioService may). Also they not must contains loops, because that may block the game for a long time. If you need a task to run in more than one game frame, then implement a way of tell the code calling the run method that you don't want it to be removed yet from the queue. Something like the run method returning a bool.
I called it tasks, but don't get confused to that another pattern that exists, that is not what I proposing here.
The disadvantage of this recommendation again a message system is that with messages, when you post a message you don't know how the game will react to it, you are only informing the game that something happened but how the game will react will depend of what objects are alive at that time listening to that particular message. So messages may be more versatile.