1

私はアンドロイドプラットフォームの初心者です。現在、ストリーミング ラジオ アプリケーションに取り組んでいます。

アプリケーションを作成したいのですが、オーディオの再生を開始し、アプリケーションがバックグラウンドになっても再生を続けます。

アプリケーションがバックグラウンドに移行しても再生が継続されるように、startService()を使用してサービスを作成しました。また、bindService()を使用して serviceConnection 参照を取得し、サービスで RPC 呼び出しを実行しました。

アプリケーションはサービスを開始し、再生は正しく開始され、bindService は参照を取得して RPC 呼び出しを行うことができます。アプリがバックグラウンドに移行するまで、すべて正常に動作します。

メインアクティビティの onResume ()onPause()で bindService( )unBindService()呼び出しを呼び出しています。

アプリがバックグラウンドに移行すると、問題なく再生が続行されます。unBindservice()呼び出しでサービス接続を切り離し、 Service クラスでonUnbind(Intent)メソッドが呼び出されます。今のところ問題ありません。

アプリケーションがフォアグラウンドになると (サービスが実行され、再生が継続している間)、bindService() がアクティビティの onResume() メソッドで呼び出されていても、サービス接続の参照が取得されません ( onServiceConnected ( )呼び出されません) 。サービス クラスのonBind(Intent)メソッドも呼び出されないことも確認しました。

過去数日間、この問題に苦しんでいます。

ユーザー定義の Android サービスでstartService()およびbindService()を使用するための推奨されるアプローチは何ですか。

この問題を解決するための助けをいただければ幸いです。

アクティビティ 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();
}

Logcat 出力

アプリ起動時

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 (エクストラあり) }

AudioPlayerController (15432): onServiceConnected ComponentInfo{com.vikkrithik.radio.indradio/com.vikkrithik.radio.indradio.AudioPlayerService} onServiceConnected が呼び出されました

戻るボタンを押すと、アプリはバックグラウンドに移行します

TabbarMainActivity(15432): 終了の呼び出し

TabbarMainActivity(15432): onPause

AudioPlayerController(15432): unBindService 完了コンテキスト => com.vikkrithik.radio.indradio.TabbarMainActivity@4150fdb8 serviceConnection => com.vikkrithik.radio.indradio.AudioPlayerController$1@418a2d30 onPause() が unBindService() を呼び出します

AudioPlayerService(15432): onUnbind が unBind を呼び出しました Service で呼び出されました

TabbarMainActivity(15432): onDestroy

アプリがフォアグラウンドになる

TabbarMainActivity(15432): onCreate => インテント { cmp=xxxx }

TabbarMainActivity(15432): onResume bindService が onResume から呼び出されます

AudioPlayerController (15432): bindService コンテキスト 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

4

3 に答える 3

4

問題を「見る」ことができなかったので、ソリューションの基礎として必要なことのほとんどを実行するアプリを作成しました。ネットワークを実行したり、実際の MediaPlayer を使用したりしませんが、サービスにはオーディオを出力するバックグラウンド スレッドがあります。2つのボタンからオーディオの開始と停止ができます。戻るボタンを押してサービスを聞き続け、アイコンからアプリを起動し、オーディオの再生を停止して再開することができました. 私の手順に従うことができると思います。デバイスで動作する場合は、MediaPlayer とネットワーク コードを機能するサービスに追加してください。

サービスへのスタブ/インターフェイスではなくメッセージを使用するサービス コードの基礎として、次のチュートリアルを使用しました。

これは Eclipse の新しい Android プロジェクトに基づいており、2 つのボタンを追加して新しいテキストと 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);
}

次に、マニフェスト ファイルへの小さな挿入:

<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

GPS で車両の移動を記録するサービスを実行するアプリでこれらの問題が発生します。ボタンを押すだけでサービスが開始され、必要に応じて何時間も継続できる録画が続行されます。停止ボタンを押すと、サービスが終了します。記録中に、UI に表示される特定のライブ データを通信するためのインターフェイスがあるため、バインド/バインド解除は、回転、アプリのバックグラウンド化/再開などによる中断/再接続サイクルを処理します。

サービスが実行されている限り、問題はありません。しかし、記録を停止すると、サービスは停止しますが、サービス クラスでバインド/バインド解除ソリューションを維持し続けると、サービス リーク メッセージが表示されます。私がやろうとしているのは、(1) サービスの実行中にのみバインドのバインド解除を行うか、(2) アプリの有効期間中は常にサービスを実行し、開始するように指示する方法を追加するように、物事を再設計することです。または、記録を停止して、それ以外の時間は「アイドル」にします。おそらく (1) が進むべき道であり、実行されていないサービスへのサービス接続も維持するアプローチは、それを行う方法ではないかもしれません。

于 2013-07-30T01:24:33.997 に答える
0

再バインドできるようにするには、 から true を返すようにしてください。onUnbindその場合、から呼び出しonRebindServiceときに で呼び出されます。bindServiceActivity

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 に答える