2012-10-27 124 views
4

我有一個Music類中的媒體播放器,這個類是從另一箇中學Activity調用的。它工作正常。如何防止mediaplayer在屏幕熄滅時停止?

但是當屏幕熄滅(通過超時或按鈕)時,音樂停止播放,當回來並嘗試關閉活動時,程序進入「應用程序未響應」狀態,因爲在查詢mediaplayer.isPlaying()處查詢IllegalStateException

如何防止mediaplayer在屏幕熄滅時停止?

是否需要通過服務?

假設答案是肯定的,我試圖將Music類轉換成服務(見下文)。我還添加<service android:enabled="true" android:name=".Music" />Manifest.xml,和我打電話的Music類是這樣的:

startService(new Intent(getBaseContext(), Music.class)); 
Music track = Music(fileDescriptor); 

只有2中的主要活動新線startService(new Intent(getBaseContext(), Music.class));stopService(new Intent(getBaseContext(), Music.class));,與相應的進口在一起。

但是現在我得到InstantiationException錯誤,因爲can't instantiate class嘗試啓動服務時。我錯過了什麼?

這是例外:

E/AndroidRuntime(16642): FATAL EXCEPTION: main 
E/AndroidRuntime(16642): java.lang.RuntimeException: Unable to instantiate service com.floritfoto.apps.ave.Music:                    java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor                 
E/AndroidRuntime(16642): at android.app.ActivityThread.handleCreateService(ActivityThread.java:2249) 
E/AndroidRuntime(16642): at android.app.ActivityThread.access$1600(ActivityThread.java:127) 
E/AndroidRuntime(16642): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1213) 
E/AndroidRuntime(16642): at android.os.Handler.dispatchMessage(Handler.java:99) 
E/AndroidRuntime(16642): at android.os.Looper.loop(Looper.java:137) 
E/AndroidRuntime(16642): at android.app.ActivityThread.main(ActivityThread.java:4507) 
E/AndroidRuntime(16642): at java.lang.reflect.Method.invokeNative(Native Method) 
E/AndroidRuntime(16642): at java.lang.reflect.Method.invoke(Method.java:511) 
E/AndroidRuntime(16642): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980) 
E/AndroidRuntime(16642): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747) 
E/AndroidRuntime(16642): at dalvik.system.NativeStart.main(Native Method) 
E/AndroidRuntime(16642): Caused by: java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor 
E/AndroidRuntime(16642): at java.lang.Class.newInstanceImpl(Native Method) 
E/AndroidRuntime(16642): at java.lang.Class.newInstance(Class.java:1319) 
E/AndroidRuntime(16642): at android.app.ActivityThread.handleCreateService(ActivityThread.java:2246) 
E/AndroidRuntime(16642): ... 10 more 

,這是Music.class:

package com.floritfoto.apps.ave; 

import java.io.FileDescriptor; 
import java.io.IOException; 

import android.app.Service; 
import android.content.Intent; 
import android.media.MediaPlayer; 
import android.media.MediaPlayer.OnCompletionListener; 
import android.os.IBinder; 
import android.widget.Toast; 

public class Music extends Service implements OnCompletionListener{ 
    MediaPlayer mediaPlayer; 
    boolean isPrepared = false; 

    //// TEstes de servico 
    @Override 
    public void onCreate() { 
     super.onCreate(); 
     info("Servico criado!"); 
    } 
    @Override 
    public void onDestroy() { 
     info("Servico fudeu!"); 
    } 
    @Override 
    public void onStart(Intent intent, int startid) { 
     info("Servico started!"); 
    } 
    @Override 
    public IBinder onBind(Intent intent) { 
     return null; 
    } 
    public void info(String txt) { 
     Toast toast = Toast.makeText(getApplicationContext(), txt, Toast.LENGTH_LONG); 
     toast.show(); 
    } 
    //// Fim testes de servico 

    public Music(FileDescriptor fileDescriptor){ 
     mediaPlayer = new MediaPlayer(); 
     try{ 
      mediaPlayer.setDataSource(fileDescriptor); 
      mediaPlayer.prepare(); 
      isPrepared = true; 
      mediaPlayer.setOnCompletionListener(this); 
     } catch(Exception ex){ 
      throw new RuntimeException("Couldn't load music, uh oh!"); 
     } 
    } 

    public void onCompletion(MediaPlayer mediaPlayer) { 
     synchronized(this){ 
      isPrepared = false; 
     } 
    } 

    public void play() { 
     if(mediaPlayer.isPlaying()) return; 
     try{ 
      synchronized(this){ 
       if(!isPrepared){ 
        mediaPlayer.prepare(); 
       } 
       mediaPlayer.seekTo(0); 
       mediaPlayer.start(); 
      } 
     } catch(IllegalStateException ex){ 
      ex.printStackTrace(); 
     } catch(IOException ex){ 
      ex.printStackTrace(); 
     } 
    } 

    public void stop() { 
     mediaPlayer.stop(); 
     synchronized(this){ 
      isPrepared = false; 
     } 
    } 

    public void switchTracks(){ 
     mediaPlayer.seekTo(0); 
     mediaPlayer.pause(); 
    } 

    public void pause() { 
     mediaPlayer.pause(); 
    } 

    public boolean isPlaying() { 
     return mediaPlayer.isPlaying(); 
    } 

    public boolean isLooping() { 
     return mediaPlayer.isLooping(); 
    } 

    public void setLooping(boolean isLooping) { 
     mediaPlayer.setLooping(isLooping); 
    } 

    public void setVolume(float volumeLeft, float volumeRight) { 
     mediaPlayer.setVolume(volumeLeft, volumeRight); 
    } 

    public String getDuration() { 
     return String.valueOf((int)(mediaPlayer.getDuration()/1000)); 
    } 
    public void dispose() { 
     if(mediaPlayer.isPlaying()){ 
      stop(); 
     } 
     mediaPlayer.release(); 
    } 
} 
+0

你讀過關於如何使用服務的文檔? http://developer.android.com/reference/android/app/Service.html – deefactorial

+0

@deefactorial當然不是!我只是想實現有史以來最基本的媒體播放器!我不希望(也不期望不需要)知道所有的細節。我只是想在這裏http://marakana.com/forums/android/examples/60.html去關注(着名的?)marakana例子。我做錯了什麼... –

回答

5

這從logcat的線是重要的一項:

Caused by: java.lang.InstantiationException: can't instantiate class com.floritfoto.apps.ave.Music; no empty constructor 

您的服務需求另一個不帶參數的構造函數:

public Music() { 
    super("Music"); 
} 

編輯

使用的服務是正確的做法,如果你想保持音樂播放時,屏幕是關閉的。但是,屏幕關閉時,手機會嘗試睡眠,這可能會中斷您的MediaPlayer

最可靠的解決方案是使用partial WakeLock來防止設備在播放音樂時進入睡眠狀態。當您不在主動播放音樂時,請務必正確地發佈WakeLock;否則電池會流失。

您可能還想使用startForeground(),這樣可以降低存在內存壓力時服務被終止的風險。它還將通過在服務運行時顯示持久通知來創建出色的用戶體驗。

實例化Music類與Music track = Music(fileDescriptor);可能會造成一些傷害。更好的方法是通過文件描述符作爲IntentExtra你傳遞給startService()

Intent serviceIntent = new Intent(this, Music.class); 
serviceIntent.putExtra("ServiceFileDescriptor", fileDescriptor); 
startService(serviceIntent); 

然後,檢索來自同一Intent文件描述符,當它傳遞給你的服務的onStartCommand()方法:

public int onStartCommand(Intent intent, int flags, int startId) { 
    super.onStart(); 

    Bundle bundle = intent.getExtras(); 
    // NOTE: The next line will vary depending on the data type for the file 
    // descriptor. I'm assuming that it's an int. 
    int fileDescriptor = bundle.getIntExtra("ServiceFileDescriptor"); 
    mediaPlayer = new MediaPlayer(); 
    try { 
     mediaPlayer.setDataSource(fileDescriptor); 
     ... 
    ... 
    return START_STICKY; 
} 

這裏有幾點需要注意。我已將原始構造函數中的代碼(應該刪除)移動到onStartCommand()。您也可以刪除onStart()方法,因爲它只會在2.0之前的設備上調用。如果您想支持現代Android版本,則需要使用onStartCommand()。最後,START_STICKY返回值將確保服務保持運行狀態,直到您從您的活動中調用stopService()

編輯2

使用服務允許用戶在不中斷MediaPlayer活動之間移動。你不能很好地控制Activity將保留在內存中的時間,但是除非存在很強的內存壓力,否則活動的Service(特別是如果你調用startForeground())不會被殺死。

要在服務啓動後與MediaPlayer進行交互,您有幾個選項。您可以通過創建Intent s並使用動作字符串(和/或一些附加信息)來告訴服務您希望它執行的操作,從而將其他命令傳遞給該服務。只需再次調用startActivity(),新的IntentonStartCommand()將在服務中調用,此時您可以操作MediaPlayer。第二種選擇是使用bound service(示例here),並在每次進入/離開需要與服務通信的活動時綁定/解除綁定。使用綁定服務「感覺」好像直接操作服務一樣,但它也更復雜,因爲您需要管理綁定和解除綁定。

+0

你的意思是跟隨'公共音樂(FileDescriptor fileDescriptor){'?我做到了,並得到「構造函數Service(String)未定義」,並要求我刪除「音樂」。我做到了,並得到了相同的'InstantiationException'。 :( –

+0

現在我明白了你的意思(但是我必須刪除括號內的''Music''),但是我仍然遇到同樣的問題:我在logcat中看到mediaplayer在屏幕關閉時斷開連接,我在查詢'mediaPlayer.isPlaying'時得到'IllegalStateException',就好像沒有執行任何服務一樣。:( –

+0

也許服務不是我需要解決的問題,還有一些其他更簡單的解決方案?請幫忙... –

0

你能夠保持激活保持的MediaPlayer再現媒體屏幕的選項:

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);