我已经浏览了整个互联网,无法找到解决方案。
我找到了一些教程,我的代码似乎很好用,包括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>
答案 0 :(得分:0)
我正在等待某人回答并让它发挥作用时进行了一些实验。
显然,问题在于我没有在实际的活动类中设置绑定/连接。我正在使用我的演示者类来托管代码并在活动中调用它。即使我使用的是Activity上下文,我认为实际的ServiceConnection实例必须存在于绑定到服务的实际活动类中。
如果有人对这个问题有任何输入或解释,这将有助于我理解更多。