首页 文章

Android服务 - 与startService一起使用时与BindService一起出现问题

提问于
浏览
1

我是android平台的新手 . 目前正致力于流媒体广播应用 .

我想制作应用程序,启动音频播放和连续播放甚至应用程序转到后台 .

我使用 startService() 创建了一个服务,以便即使应用程序转到后台也可以继续播放 . 我还使用 bindService() 获取serviceConnection引用以使用service执行RPC调用 .

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

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

当应用程序转到后台时,播放将继续,不会出现任何问题 . unBindservice()调用分离服务连接和在Service类中调用的onUnbind(Intent)方法 . 到目前为止没有问题 . 当应用程序到达前台时(当服务正在运行并且继续播放时),我没有获得服务连接引用(onServiceConnected()没有被调用),即使在activity onResume()方法中调用了bindService() . 我还验证了Service类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();
}

Controller类具有与服务相关的启动/停止/绑定/解除绑定方法

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();
}

Logcat output

当应用程序启动时

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

按下播放按钮时

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

AudioPlayerController(15432):调用startService

AudioPlayerService(15432):onCreate

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

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

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

当按下后退按钮时,应用程序转到后台

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() calls unBindService()

AudioPlayerService(15432):onUnbind被调用 unBind Got invoked in Service

TabbarMainActivity(15432):onDestroy

App来到forground

TabbarMainActivity(15432):onCreate => Intent

TabbarMainActivity(15432):onResume bindService is called from onResume

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

播放停止后

AudioPlayerService(15432):停止

AudioPlayerService(15432):重置

AudioPlayerController(15432):stopAudioService完成

AudioPlayerFragment(15432):removeVisualizer

AudioPlayerService(15432):onDestory

3 回答

  • 4

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

    我使用以下tutorial作为服务代码的基础,该服务代码使用消息而不是服务的Stub /接口 .

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

    首先是我在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);
                }
            }
        }
    }
    }
    
  • 0

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

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

  • 0

    为了能够重新绑定,请确保从 onUnbind 返回true,在这种情况下,当从 Activity 调用 bindService 时,将在 Service 中调用 onRebind .

    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*/
    }
    

相关问题