Я смотрел по всему Интернету и не могу найти решение.Связанный сервис не обязательно привязывается
Я нашел несколько руководств, и мой код, похоже, хорошо подходит, в том числе примеры Android-разработчика.
Пункт приложения состоит в том, чтобы воспроизводить сразу несколько звуковых дорожек, что я сделал успешно и не имею проблем. Проблема возникла, когда я попытался реализовать службу. Я решил, что связанный сервис будет лучшим подходом, потому что ссылка на услугу, чтобы использовать средства управления медиа-плейерами, казалась лучшим маршрутом.
У меня есть это, в onCreate()
услуга начинается с намерения. Затем я вызываю службу связывания api (также в onCreate()
) из моего класса презентатора, используя метод setup()
, который вызывает sessionPresenter.setupSounds()
. Я получаю сообщение об ошибке, и служба, похоже, вообще не запускается, и мое приложение падает.
Моя служебная ссылка (serviceReference
) имеет значение null, когда я пытаюсь вызвать мой первый метод публичной службы. Debugging показал, что
context.bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
возвращается правда. Но, то
@Override public void onServiceConnected(ComponentName name, IBinder service){...}
не вызывается. Какова была ссылка на мою службу.
Я нашел несколько постов на StackOverflow и других местах, где люди имели проблемы с onServiceConnected
не называется, но каждое решение должно было делать либо не возвращенная IBinder правильно или не объявляя службу в AndroidManifest.xml
. Я уверен, что правильно делаю это.
Это было как два целых дня, пытаясь решить эту проблему, пожалуйста, помогите.
Последний раз, когда я разместил здесь длинный вопрос, это оказалось неловкой простой ошибкой вырезания и вставки. Я боюсь, что это может быть похоже, извините, если это так, но у меня нет выбора.
Спасибо всем, кто может помочь.
Вот код и трассировки стека:
SoundSessionActivity.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();
}
}
SoundSessionPresenter.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;
}
}
SoundSessionService.java
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;
}
}
}
И roidManifest.XML
<?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>
трассировки стека
02-15 09:51:25.571 16112-16112/? E/Zygote: MountEmulatedStorage()
02-15 09:51:25.571 16112-16112/? E/Zygote: v2
02-15 09:51:25.571 16112-16112/? I/libpersona: KNOX_SDCARD checking this for 10375
02-15 09:51:25.571 16112-16112/? I/libpersona: KNOX_SDCARD not a persona
02-15 09:51:25.571 16112-16112/? I/SELinux: Function: selinux_compare_spd_ram, SPD-policy is existed. and_ver=SEPF_SAMSUNG-SM-N910A_5.1.1 ver=38
02-15 09:51:25.581 16112-16112/? I/SELinux: Function: selinux_compare_spd_ram , priority [1] , priority version is VE=SEPF_SAMSUNG-SM-N910A_5.1.1_0038
02-15 09:51:25.581 16112-16112/? E/Zygote: accessInfo : 0
02-15 09:51:25.581 16112-16112/? E/SELinux: [DEBUG] get_category: variable seinfo: default sensitivity: NULL, cateogry: NULL
02-15 09:51:25.581 16112-16112/? I/art: Late-enabling -Xcheck:jni
02-15 09:51:25.611 16112-16112/com.firsttread.anthony.soundscope D/TimaKeyStoreProvider: TimaSignature is unavailable
02-15 09:51:25.611 16112-16112/com.firsttread.anthony.soundscope D/ActivityThread: Added TimaKeyStore provider
02-15 09:51:25.691 16112-16112/com.firsttread.anthony.soundscope D/SecWifiDisplayUtil: Metadata value : none
02-15 09:51:25.761 16112-16112/com.firsttread.anthony.soundscope D/PhoneWindow: *FMB* installDecor mIsFloating : false
02-15 09:51:25.761 16112-16112/com.firsttread.anthony.soundscope D/PhoneWindow: *FMB* installDecor flags : -2139029248
02-15 09:51:25.851 16112-16112/com.firsttread.anthony.soundscope D/DatabaseHelper: DatabaseHelper instance successful
02-15 09:51:26.041 16112-16197/com.firsttread.anthony.soundscope D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
02-15 09:51:26.061 16112-16112/com.firsttread.anthony.soundscope D/PhoneWindow: *FMB* isFloatingMenuEnabled mFloatingMenuBtn : null
02-15 09:51:26.061 16112-16112/com.firsttread.anthony.soundscope D/PhoneWindow: *FMB* isFloatingMenuEnabled return false
02-15 09:51:26.081 16112-16112/com.firsttread.anthony.soundscope D/SRIB_DCS: log_dcs ThreadedRenderer::initialize entered!
02-15 09:51:26.081 16112-16197/com.firsttread.anthony.soundscope I/Adreno: EGLInit: QTI Build: 07/16/15, 126f54a, If3804f16ae
02-15 09:51:26.091 16112-16197/com.firsttread.anthony.soundscope I/OpenGLRenderer: Initialized EGL, version 1.4
02-15 09:51:26.101 16112-16197/com.firsttread.anthony.soundscope D/OpenGLRenderer: Get maximum texture size. GL_MAX_TEXTURE_SIZE is 16384
02-15 09:51:26.101 16112-16197/com.firsttread.anthony.soundscope D/OpenGLRenderer: Enabling debug mode 0
02-15 09:51:26.251 16112-16112/com.firsttread.anthony.soundscope I/Timeline: Timeline: Activity_idle id: [email protected] time:217638745
02-15 09:51:27.451 16112-16112/com.firsttread.anthony.soundscope D/ViewRootImpl: ViewPostImeInputStage ACTION_DOWN
02-15 09:51:27.521 16112-16112/com.firsttread.anthony.soundscope I/Timeline: Timeline: Activity_launch_request id:com.firsttread.anthony.soundscope time:217640012
02-15 09:51:27.551 16112-16112/com.firsttread.anthony.soundscope D/PhoneWindow: *FMB* installDecor mIsFloating : false
02-15 09:51:27.551 16112-16112/com.firsttread.anthony.soundscope D/PhoneWindow: *FMB* installDecor flags : -2139029248
02-15 09:51:27.591 16112-16112/com.firsttread.anthony.soundscope D/AndroidRuntime: Shutting down VM
02-15 09:51:27.591 16112-16112/com.firsttread.anthony.soundscope E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.firsttread.anthony.soundscope, PID: 16112
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.firsttread.anthony.soundscope/com.firsttread.anthony.soundscope.application.view.SoundSessionActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.firsttread.anthony.soundscope.application.presenter.mediaplayback.SoundSessionService.setupSounds(java.lang.String)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3149)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3248)
at android.app.ActivityThread.access$1000(ActivityThread.java:197)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1681)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6872)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.firsttread.anthony.soundscope.application.presenter.mediaplayback.SoundSessionService.setupSounds(java.lang.String)' on a null object reference
at com.firsttread.anthony.soundscope.application.presenter.SoundSessionPresenter.setupSounds(SoundSessionPresenter.java:83)
at com.firsttread.anthony.soundscope.application.view.SoundSessionActivity.setup(SoundSessionActivity.java:86)
at com.firsttread.anthony.soundscope.application.view.SoundSessionActivity.onCreate(SoundSessionActivity.java:67)
at android.app.Activity.performCreate(Activity.java:6550)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1120)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3102)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3248)
at android.app.ActivityThread.access$1000(ActivityThread.java:197)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1681)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6872)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)
'android.content.ServiceConnection' - это просто интерфейс, он может быть реализован в любом месте, поэтому, скорее всего, ваша проблема находится где-то в другом месте – pskink