1

我是android平台的新手。目前正在开发流媒体广播应用程序。

我想制作应用程序,开始音频播放并继续播放,即使应用程序进入后台。

我已经使用startService()创建了一个服务,因此即使应用程序进入后台,播放也会继续。我还使用了 bindService()来获取 serviceConnection 引用来执行带有服务的 RPC 调用。

应用程序启动服务并正确启动播放,并且 bindService 能够获取引用并能够进行 RPC 调用,一切正常,直到应用程序进入后台。

我在主要活动的onResume()onPause()中调用bindService()unBindService()调用。

当应用程序进入后台时,播放继续没有任何问题。unBindservice()调用分离服务连接,并且在 Service 类中调用了onUnbind(Intent)方法。到目前为止没有问题。

当应用程序进入前台时(当服务正在运行并且播放继续时),我没有得到服务连接引用(onServiceConnected()没有被调用),即使在活动onResume()方法中调用了bindService() 。我还验证了服务类onBind(Intent)方法也没有被调用。

在过去的几天里,我一直在努力解决这个问题。

将startService()bindService()与用户定义的 Android 服务一起使用的推荐方法是什么。

任何帮助解决此问题,高度赞赏。

活动 onResume/onPause 代码。

@Override
public void onCreate(Bundle savedInstanceState) {
    Log.d(TAG, "onCreate => " + getIntent());
    super.onCreate(savedInstanceState);

    // set the default network preference, if it is not already set
    setDefaultNetwork();

    // initialize the navigation tab bars
    initializeTabBar();

    mPlayerController = AudioPlayerController.getInstance(this);

    mActionBar.setDisplayShowHomeEnabled(true);
    mActionBar.setDisplayShowTitleEnabled(true);

    mRemoteControlReceiver = new RemoteControlReceiver();
    IntentFilter filter = new IntentFilter(
            "android.intent.action.MEDIA_BUTTON");
    registerReceiver(mRemoteControlReceiver, filter);

    setVolumeControlStream(AudioManager.STREAM_MUSIC);
}

@Override
public void onResume() {
    super.onResume();
    Log.d(TAG,"onResume");
    mPlayerController.bindService();
    restoreAppState();
}

@Override
public void onPause() {
    super.onPause();
    Log.d(TAG,"onPause");
    mPlayerController.unBindService();
    storeAppState();
}

控制器类具有服务相关的启动/停止/绑定/取消绑定方法

    public void bindService() {
    Intent intent = new Intent();
    intent.setClassName("com.vikkrithik.radio.indradio",
            "com.vikkrithik.radio.indradio.AudioPlayerService");
    Log.d(TAG, "bindService Context " + mPlayerContext
            + " serviceConnection => " + audioServiceConnection);
    mPlayerContext.bindService(intent, audioServiceConnection, 0);
}

public void unBindService() {
    mPlayerContext.unbindService(audioServiceConnection);
    Log.d(TAG, "unBindService done context => " + mPlayerContext
            + " serviceConnection => " + audioServiceConnection);
}

private ServiceConnection audioServiceConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.d(TAG, "onServiceConnected " + name);
        mAudioService = AudioPlayerServiceInterface.Stub
                .asInterface(service);
    }

    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "onServiceDisconnected " + name);
        mAudioService = null;
    }
};
/**
 * Start the AudioService
 * 
 */
public void startAudioService(Station station) {
    Intent intent = new Intent();
    intent.setClassName("com.vikkrithik.radio.indradio",
            "com.vikkrithik.radio.indradio.AudioPlayerService");

    Bundle extras = new Bundle();
    extras.putString("station_url", station.getUrl());
    extras.putString("station_name", station.getStationName());
    intent.putExtras(extras);
    mCurrentStation = station;
    mPlayerContext.startService(intent);
    bindService();
    Log.d(TAG, "startService called");
}

public void stopAudioService() {
    //
    // Check if AudioService is already created
    //
    if (null == mAudioService)
        return;

    Intent intent = new Intent();
    intent.setClassName("com.vikkrithik.radio.indradio",
            "com.vikkrithik.radio.indradio.AudioPlayerService");
    mPlayerContext.stopService(intent);
    Log.d(TAG, "stopAudioService done");
}

服务类方法

@Override
public IBinder onBind(Intent arg0) {
    Log.d(TAG, "onBind invoked");
    return audioServiceStub;
}

@Override
public boolean onUnbind(Intent intent) {
    Log.d(TAG,"onUnbind invoked");
    return super.onUnbind(intent);
}

@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG, "onCreate");
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

    mNetworkStatusReceiver = new NetworkStatusReceiver();
    IntentFilter filter = new IntentFilter(
            ConnectivityManager.CONNECTIVITY_ACTION);
    registerReceiver(mNetworkStatusReceiver, filter);

    PreferenceManager.getDefaultSharedPreferences(this)
            .registerOnSharedPreferenceChangeListener(
                    mNetworkStatusReceiver);
}

@Override
public void onDestroy() {
    Log.d(TAG, "onDestory");
    stopPlayback();

    PreferenceManager.getDefaultSharedPreferences(this)
            .unregisterOnSharedPreferenceChangeListener(
                    mNetworkStatusReceiver);
    unregisterReceiver(mNetworkStatusReceiver);
    cancelNotification();
    audioServiceStub = null;
    super.onDestroy();
}

日志输出

应用启动时

TabbarMainActivity(15432): onResume AudioPlayerController(15432): bindService 上下文 com.vikkrithik.radio.indradio.TabbarMainActivity@4150fdb8 serviceConnection => com.vikkrithik.radio.indradio.AudioPlayerController$1@418a2d30

按下播放按钮时

AudioPlayerController(15432): bindService 上下文 com.vikkrithik.radio.indradio.TabbarMainActivity@4150fdb8 serviceConnection => com.vikkrithik.radio.indradio.AudioPlayerController$1@418a2d30

AudioPlayerController(15432): 调用 startService

AudioPlayerService(15432):onCreate

AudioPlayerService(15432):onBind 调用onBind() 调用

AudioPlayerService(15432): onStartCommand with IndetntIntent { cmp=xxxx (has extras) }

AudioPlayerController(15432):onServiceConnected ComponentInfo{com.vikkrithik.radio.indradio/com.vikkrithik.radio.indradio.AudioPlayerService} onServiceConnected 调用

按下后退按钮时,应用程序进入后台

TabbarMainActivity(15432):完成调用

TabbarMainActivity(15432):onPause

AudioPlayerController(15432): unBindService done context => com.vikkrithik.radio.indradio.TabbarMainActivity@4150fdb8 serviceConnection => com.vikkrithik.radio.indradio.AudioPlayerController$1@418a2d30 onPause() 调用 unBindService()

AudioPlayerService(15432): onUnbind 调用 unBind 在服务中调用

TabbarMainActivity(15432):onDestroy

应用程序进入前台

TabbarMainActivity(15432): onCreate => Intent { cmp=xxxx }

TabbarMainActivity(15432): 从 onResume 调用 onResume bindService

AudioPlayerController(15432): bindService 上下文 com.vikkrithik.radio.indradio.TabbarMainActivity@4150fdb8 serviceConnection => com.vikkrithik.radio.indradio.AudioPlayerController$1@418a2d30

播放停止后

音频播放器服务(15432):停止

AudioPlayerService(15432):重置

AudioPlayerController(15432): stopAudioService 完成

AudioPlayerFragment(15432): 移除可视化器

AudioPlayerService(15432): onDestory

4

3 回答 3

4

我无法仅仅“看到”问题,所以我创建了一个应用程序,它可以完成您需要的大部分事情,作为解决方案的基础。它不做网络或使用真正的 MediaPlayer,但该服务有一个后台线程来输出音频。您可以从两个按钮开始和停止音频。我能够点击后退按钮,并继续听到服务,然后从图标启动应用程序并停止并重新开始音频播放。我认为您可能可以按照我的步骤进行操作,如果它适用于您的设备,则开始将 MediaPlayer 和网络代码添加到功能服务中。

我使用以下教程作为服务代码的基础,它使用消息而不是服务的存根/接口。

它基于 Eclipse 中的一个新 Android 项目,然后我只添加了两个按钮并为它们提供了新的文本和 Id。其余的在这里:

首先是我在 Eclipse 创建的 MainActivity 中放入的内容:

private final static String TAG = "MainActivity";
// Service stuff
Messenger myService = null;
boolean isBound;
private Button startBut;
private Button stopBut;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    startBut = (Button) findViewById(R.id.startAudio);
    startBut.setOnClickListener(new Button.OnClickListener(){
        public void onClick(View v){
            try
            {
                if (myService != null && isBound)
                {
                    Log.d(TAG, "Service Connected! sending start request");
                    Message msg = Message.obtain();
                    Bundle bundle = new Bundle();
                    bundle.putString("MyString", "start");

                    msg.setData(bundle);
                    myService.send(msg);
                }
            }
            catch (Throwable th)
            {
                Log.d(TAG, "error sending start", th);
            }
        }
    });

    stopBut = (Button) findViewById(R.id.stopAudio);
    stopBut.setOnClickListener(new Button.OnClickListener(){
        public void onClick(View v){
            try
            {
                Log.d(TAG, "Service Connected! sending stop request");

                Message msg = Message.obtain();
                Bundle bundle = new Bundle();
                bundle.putString("MyString", "stop");

                msg.setData(bundle);
                myService.send(msg);
            }
            catch (Throwable th)
            {
                Log.d(TAG, "error sending start", th);
            }
        }
    });
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

@Override
protected void onPause()
{
    super.onPause();

    unBindService();
}

@Override
protected void onResume()
{
    super.onResume();
    Log.d(TAG, "Resuming: binding to service");
    Intent intent = new Intent("com.example.audioservice.AudioPlayerService");

    // Start the service ourselves because we don't want it to stop when the activity/application does.
    startService(intent);
    bindService(intent, myConnection, 0);
}

private ServiceConnection myConnection = new ServiceConnection() 
{
    public void onServiceConnected(ComponentName className, IBinder service) {
        myService = new Messenger(service);
        isBound = true;
        Log.d(TAG, "Service Connected!");
    }

    public void onServiceDisconnected(ComponentName className) {
        myService = null;
        isBound = false;
    }
};

public void unBindService() 
{
    this.unbindService(myConnection);
    Log.d(TAG, "unBindService done context => " 
            + " serviceConnection => " + myConnection);
}

接下来,在 Manifest 文件中插入一小段内容:

<service android:enabled="true"  android:name="com.example.audioservice.AudioPlayerService"
         android:process=":my_process" >
         <intent-filter>
            <action android:name="com.example.audioservice.AudioPlayerService" >
         </action>
      </intent-filter>
    </service>

最后是AudioPlayerService类的内容:

public class AudioPlayerService extends Service
{
private static final String TAG = "AudioPlayerService";
AudioManager audioManager;
private boolean playing = false;
private Player player;

class IncomingHandler extends Handler
{
    @Override
    public void handleMessage(Message msg)
    {
        Bundle data = msg.getData();
        String dataString = data.getString("MyString");
        if ("start".equalsIgnoreCase(dataString))
        {
            Log.d(TAG, "Got a start request");
            // Start playing
            playing = true;
        }
        else if ("stop".equalsIgnoreCase(dataString))
        {
            Log.d(TAG, "Got a stop request");
            // Stop playing
            playing = false;
        }
    }
}

@SuppressLint("HandlerLeak")
final Messenger myMessenger = new Messenger(new IncomingHandler());

@Override
public IBinder onBind(Intent arg0)
{
    Log.d(TAG, "onBind invoked");
    return myMessenger.getBinder();
}

@Override
public boolean onUnbind(Intent intent)
{
    Log.d(TAG, "onUnbind invoked");
    return super.onUnbind(intent);
}

@Override
public void onCreate()
{
    super.onCreate();
    Log.d(TAG, "onCreate");
    audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    audioManager.loadSoundEffects();
    player = new Player();
    player.start();
}

@Override
public void onDestroy()
{
    Log.d(TAG, "onDestory");
    stopPlayback();

    super.onDestroy();
}

private void stopPlayback()
{
    playing = false;
}

// Pointless Thread to let us know the service is still doing its thing
class Player extends Thread
{
    public void run()
    {
        int x = 7;
        while (1 < x)
        {
            try
            {
                if (playing)
                {
                    Thread.sleep(10000);
                    Log.d(TAG, ".");
                    audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
                }
            }
            catch (Throwable th)
            {
                Log.d(TAG, "error in Player", th);
            }
        }
    }
}
}
于 2013-02-28T12:30:16.380 回答
0

为了能够重新绑定,请onUnbind确保您从.onRebindServicebindServiceActivity

Service.java

@Override
public boolean onUnbind(Intent intent) {
        super.onUnbind(intent);
        /* logic */
        return true; // future binds from activity will call onRebind
}

@Override
public void onRebind(Intent intent) {
    // Is called when activity issues a `bindService` after an `undbindService`
    super.onRebind(intent);
    /*logic*/
}
于 2015-03-31T15:52:37.280 回答
0

我看到这些问题与运行使用 GPS 记录车辆行程的服务的应用程序有关。只需按一下按钮,服务就会启动并继续录制,如果需要,可以持续数小时。按下停止按钮,服务终止。在录制过程中,有一个界面可以传达显示在 UI 上的某些实时数据,因此绑定/取消绑定处理由于旋转、后台/恢复应用程序等引起的中断/重新连接周期。

只要服务运行就没有问题。但是当我停止录制意味着我停止服务但继续保持与服务类的绑定/解除绑定解决方案时,会出现服务泄漏消息。我可能会尝试做的是重新设计一些东西,以便我(1)只在服务运行时关心绑定取消绑定,或者(2)在应用程序生命周期的任何时候运行服务并添加方法来告诉它开始或停止录制并让它在其他时间“空闲”。也许(1)是要走的路,而维持与非运行服务的服务连接的方法可能不是这样做的方法。

于 2013-07-30T01:24:33.997 回答