绑定服务没有正确绑定

时间:2016-02-15 18:03:27

标签: java android android-service android-service-binding

我已经浏览了整个互联网,无法找到解决方案。

我找到了一些教程,我的代码似乎很好用,包括android开发示例。

该应用程序的目的是同时播放多个音轨,我已成功完成并且没有任何问题。当我尝试实现服务时出现了问题。我认为绑定服务是最好的方法,因为引用服务来使用其公共媒体播放器控件似乎是最好的路径。

我所做的是,在onCreate()中,服务以意图启动。然后我使用调用onCreate()的{​​{1}}方法从我的演示者类调用绑定服务api(也在setup()中)。我收到错误,服务似乎无法运行,我的应用程序崩溃了。

当我尝试调用我的第一个公共服务方法时,我的服务引用(sessionPresenter.setupSounds())为空。调试显示

serviceReference

返回true。但是,

context.bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);

未被调用。这是我的服务参考实例化。

我在StackOverflow和其他地方发现了一些帖子,其中人们遇到了@Override public void onServiceConnected(ComponentName name, IBinder service){...}未被调用的问题,但每个解决方案都与正确返回IBinder或未声明服务有关在onServiceConnected。我非常确定我正确地做了这些事情。

好像整整两天试图解决这个问题,请帮帮忙。

上次我在这里发布一个很长的问题时,结果却是一个令人尴尬的简单剪切和粘贴错误。我担心这可能是相似的,对不起,如果是,但我别无选择。

感谢任何能提供帮助的人。

这里是代码和堆栈跟踪:

SoundSessionActivity.java

AndroidManifest.xml

SoundSessionPresenter.java

public class SoundSessionActivity extends AppCompatActivity implements ISoundSession {

private static final String TAG = "SoundSessionActivity";

private int numberOfSounds;
private RecyclerView recList;
private String sessionTitle;
private SoundSessionPresenter sessionPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sound_session);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true); //ToDo: try to fix?

    //get the intent and retrieve the session name from it
    Intent intent = getIntent();
    Bundle bundle = intent.getExtras();
    sessionTitle = bundle.getString(MainActivity.EXTRA_MESSAGE + "NAME_OF_SESSION");
    setTitle(sessionTitle);

    //begin service
    Intent serviceIntent = new Intent(this, SoundSessionService.class);
    startService(serviceIntent);

    sessionPresenter = new SoundSessionPresenter(this,sessionTitle);
    sessionPresenter.bindSoundSessionService();

    //initializes/sets up the saved session card list
    recList = (RecyclerView) findViewById(R.id.cardList);
    recList.setHasFixedSize(true);
    LinearLayoutManager layoutManager = new LinearLayoutManager(this);
    layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    recList.setLayoutManager(layoutManager);

    setup(); //this is were setupSounds() is invoked

}//onCreate


@Override
public void playPauseSwitch(final View v){
    /*sessionPresenter.playPause(new Runnable() {
        @Override
        public void run() {
            sessionPresenter.playPauseSwitch(v);
        }
    });*/


}

@Override
public void setup() {
    sessionPresenter.setupSounds(); //This is where the service referenced is called
    recList.setAdapter(sessionPresenter.getCardAdapter());

}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_sound_session, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();

    switch (item.getItemId()){
        case R.id.action_settings:
            return true;
        case R.id.action_edit:
            Intent intent = new Intent(this,EditSoundsActivity.class);
            intent.putExtra(MainActivity.EXTRA_MESSAGE + "NAME_OF_SESSION",sessionTitle);
            startActivity(intent);
            return true;
    }

    return super.onOptionsItemSelected(item);
}


//ToDO: may need to clean stuff up here. Might not.
@Override
protected void onDestroy() {
    super.onDestroy();
    //ToDo: may need to unbind from service in here
    if(isFinishing()){
        Intent stopIntent = new Intent(this,SoundSessionService.class);
        stopService(stopIntent);
    }

}


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

@Override
protected void onResume(){
    super.onResume();
}

@Override
protected void onStart(){
    super.onStart();
}

@Override
protected void onStop() {
    super.onStop();
}
}

SoundSessionService.java

public class SoundSessionPresenter {

private Context context;
private String sessionTitle;
private CardAdapter cAdapter;
private List<Integer> soundIconList;

private boolean isBound;
private SoundSessionService serviceReference;

private ServiceConnection mConnection = new ServiceConnection() {
    //these methods never get called
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        SoundSessionService.LocalBinder mBinder = (SoundSessionService.LocalBinder) service;
        serviceReference = mBinder.getService();//serviceReference remains null
        isBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        serviceReference = null;
        isBound = false;
    }
};

//constructors
public SoundSessionPresenter(){} 

public SoundSessionPresenter(Context context,String sessionTitle){
    this.context = context;
    this.sessionTitle = sessionTitle;
}

//service api
public void bindSoundSessionService(){
    doBindToService();
}

public void unbindSoundSessionService(){
    doUnbindService();
}

private void doBindToService() {
    Toast.makeText(context, "Binding...", Toast.LENGTH_SHORT).show();
    if (!isBound) {
        Intent bindIntent = new Intent(context, SoundSessionService.class);
        isBound = context.bindService(bindIntent, mConnection,
                Context.BIND_AUTO_CREATE);
    }
}

private void doUnbindService() {
    Toast.makeText(context, "Unbinding...", Toast.LENGTH_SHORT).show();
    clearMediaPlayers();
    context.unbindService(mConnection);
    isBound = false;
}



public void setupSounds(){
    //this is where the error is happening
    //serviceReference is null
    serviceReference.setupSounds(sessionTitle);
}

public List<Integer> getSoundList(){
    return soundIconList;
}


public void playPauseSwitch(View v){
    Activity activity = (Activity) context;
    final ImageView button = (ImageView) v;
    if(serviceReference.playPause((String) button.getTag())){
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                button.setImageResource(R.drawable.ic_pause);
            }
        });
    }else{
        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                button.setImageResource(R.drawable.ic_play_arrow);
            }
        });
    }
}

public void changeVolume(String soundName, Float newVolume){
    serviceReference.changeVolume(soundName, newVolume);
}

public void updatePlayerVolume(String soundName, float newVolume){
    serviceReference.updatePlayerVolume(soundName, newVolume);
}



public void clearMediaPlayers(){
    serviceReference.clearMediaPlayers();
}


public CardAdapter getCardAdapter(){
    cAdapter = new CardAdapter(sessionTitle);
    return cAdapter;
}
}

的AndroidManifest.xml

public class SoundSessionService extends Service implements MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener{


private final String TAG = "SoundSessionService";

private final String PATH_PREFIX =
        "android.resource://com.firsttread.anthony.soundscope//" ;

private int REQUEST_CODE = 101;
private int NOTIFICATION_ID = 102;

private SoundThread sThread;
private final IBinder mBinder = new LocalBinder();

private Context context; //may just pass context to each methods that needs it
private static int mpCount; //may need to keep track of the number of MediaPlayers
//private String sessionTitle;//may just pass
private HashMap<String,Integer> currentSounds;// holds raw int
private List<String> soundList;// holds string name of sounds

private MyMediaPlayerPool mPool; //holds all mediaplayers used in current session


// service methods
@Override
public void onCreate() {
    super.onCreate();
    soundList = new ArrayList<>();
    currentSounds = new HashMap<>();
    sThread = new SoundThread("SoundThread");
    sThread.start();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    startForeground(NOTIFICATION_ID,getNotification());

    return START_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    clearMediaPlayers();
    Thread dummy = sThread;
    sThread = null;
    dummy.interrupt();
    Toast.makeText(this,"Service being destroyed...",Toast.LENGTH_LONG).show();
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    Log.i(TAG, "Cancelling notification");
    notificationManager.cancel(NOTIFICATION_ID);

}

@Override
public IBinder onBind(Intent intent) {
    Log.i(TAG, "onBind called");
    return mBinder;
}

//media player setup
public void setupSounds(String sessionTitle){

    //ToDo: do in other thread or asynctask

    //querying the data from the specific session
    //and use key=(String name) item=(Integer raw)

    DatabaseHelper DBHelper = DatabaseHelper.getInstance();
    SQLiteDatabase db = DBHelper.getReadableDatabase();

    /*
    *
    *
    *  The following string resembles this query:
    *
    *  SELECT
    *  sound, raw, volume
    *  FROM
    *  sound_info INNER JOIN sounds
    *  ON
    *  sound_info.session = ?
    *  AND
    *  sound_info.sound
    *  =
    *  sounds.name
    *
    *  The result will be a table in the form of:
    *  sound|raw|volume
    *
    *  for all sounds in the current session defined by whereArgs = sessionTitle
    *
    */

    //ToDo: use volume
    String soundsInSessionQuery = DBContract.DBInfo.SELECT +
            DBContract.DBInfo.COLUMN_SOUND + DBContract.DBInfo.COMMA + DBContract.DBInfo.COLUMN_RAW + DBContract.DBInfo.COMMA +
            DBContract.DBInfo.COLUMN_VOLUME   +
            DBContract.DBInfo.FROM +
            DBContract.DBInfo.TABLE_SOUND_INFO + DBContract.DBInfo.INNER_JOIN + DBContract.DBInfo.TABLE_SOUNDS +
            DBContract.DBInfo.ON +
            DBContract.DBInfo.TABLE_SOUND_INFO + DBContract.DBInfo.DOT + DBContract.DBInfo.COLUMN_SESSION +
            DBContract.DBInfo.EQUALS + DBContract.DBInfo.Q_MARK +
            DBContract.DBInfo.AND +
            DBContract.DBInfo.TABLE_SOUND_INFO + DBContract.DBInfo.DOT + DBContract.DBInfo.COLUMN_SOUND +
            DBContract.DBInfo.EQUALS +
            DBContract.DBInfo.TABLE_SOUNDS + DBContract.DBInfo.DOT + DBContract.DBInfo.COLUMN_SOUND_NAME;

    String[] whereArgs = {sessionTitle};

    //fetches all sounds in current session
    Cursor c = db.rawQuery(soundsInSessionQuery,whereArgs);

    while(c.moveToNext()){
        currentSounds.put(
                c.getString(c.getColumnIndex(DBContract.DBInfo.COLUMN_SOUND)),
                c.getInt(c.getColumnIndex(DBContract.DBInfo.COLUMN_RAW)) );

        soundList.add(c.getString(c.getColumnIndex(DBContract.DBInfo.COLUMN_SOUND)));
    }

    db.close();
    c.close();

    createPool();

}

public void createPool(){

    sThread.doRunnable(new Runnable() {
        @Override
        public void run() {
            mPool = new MyMediaPlayerPool();
            for(String soundName:soundList){
                try{
                    String path = PATH_PREFIX + currentSounds.get(soundName);
                    MyMediaPlayer mp = new MyMediaPlayer();
                    mp.setDataSource(context, Uri.parse(path));
                    mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
                    mp.setOnPreparedListener(SoundSessionService.this);
                    mp.setOnErrorListener(SoundSessionService.this);
                    mp.setState(State.IDLE);
                    mp.setLooping(true);
                    mPool.addMyMediaPlayer(soundName,mp);
                }catch(IOException e){
                    Toast.makeText(context, "ERROR: io exception!", Toast.LENGTH_SHORT ).show();
                }catch(IllegalStateException e){
                    Toast.makeText(context, "ERROR: state exception!", Toast.LENGTH_SHORT ).show();
                }catch(IllegalArgumentException e){
                    Toast.makeText(context, "ERROR: illegal argument", Toast.LENGTH_SHORT ).show();
                }
            }
        }
    });

}

//media player control
public boolean playPause(String soundName){
    if(mPool.getMyMediaPlayer(soundName) == null ||
            !(mPool.getMyMediaPlayer(soundName).isPlaying()) ){
        play(soundName);
        mPool.getMyMediaPlayer(soundName).setState(State.PLAYING);
        return true;
    }else{
        pause(soundName);
        return false;
    }
}

public void play(String soundName){
    if(mPool.getMyMediaPlayer(soundName).getCurrentState() == State.PAUSED){
        mPool.getMyMediaPlayer(soundName).start();
    }else if(mPool.getMyMediaPlayer(soundName).getCurrentState() == State.IDLE){
        mPool.getMyMediaPlayer(soundName).prepareAsync();
    }
}

public void pause(String soundName){
    mPool.getMyMediaPlayer(soundName).pause();
    mPool.getMyMediaPlayer(soundName).setState(State.PAUSED);
}

public boolean isPlaying(String soundName){
    return mPool.getMyMediaPlayer(soundName).isPlaying();
}

//media player volume
public void changeVolume(String soundName, Float newVolume){
    MyMediaPlayer mp = mPool.getMyMediaPlayer(soundName);
    //sets both L and R volume to the same value
    mp.setVolume(newVolume, newVolume);
}

public float getVolume(String soundName){
    return mPool.getMyMediaPlayer(soundName).getCurrentVolume();
}

public void updatePlayerVolume(String soundName, float newVolume){
    mPool.getMyMediaPlayer(soundName).setCurrentVolume(newVolume);
}

//cleanup
public void clearMediaPlayers(){
    for(String soundName:soundList){
        mPool.getMyMediaPlayer(soundName).release();
    }
}


//notification
private Notification getNotification() {

    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle("Service Running")
            .setTicker("Music Playing")
            .setWhen(System.currentTimeMillis())
            .setOngoing(true);
    Intent startIntent = new Intent(this, SoundSessionActivity.class);
    PendingIntent contentIntent = PendingIntent.getActivity(this,
            REQUEST_CODE, startIntent, 0);
    builder.setContentIntent(contentIntent);
    Notification notification = builder.build();
    NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(NOTIFICATION_ID, notification);

    return notification;

}

@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
    return false;
}

@Override
public void onPrepared(MediaPlayer mp) {
    mp.start();
}

public class LocalBinder extends Binder {
    public SoundSessionService getService() {
        return SoundSessionService.this;
    }
}
}

堆栈跟踪

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.firsttread.anthony.soundscope">

<application
    android:name="com.firsttread.anthony.soundscope.application.App"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".application.view.MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity
        android:name=".application.view.SoundSessionActivity"
        android:parentActivityName=".application.view.MainActivity"
        android:theme="@style/AppTheme.NoActionBar">
        <meta-data
            android:name="android.support.PARENT_ACTIVITY"
            android:value=".application.view.MainActivity" />
    </activity>

    <activity android:name=".application.view.EditSoundsActivity"> </activity>

    <service android:name=".application.presenter.mediaplayback.SoundSessionService" />

</application>

1 个答案:

答案 0 :(得分:0)

我正在等待某人回答并让它发挥作用时进行了一些实验。

显然,问题在于我没有在实际的活动类中设置绑定/连接。我正在使用我的演示者类来托管代码并在活动中调用它。即使我使用的是Activity上下文,我认为实际的ServiceConnection实例必须存在于绑定到服务的实际活动类中。

如果有人对这个问题有任何输入或解释,这将有助于我理解更多。