简体   繁体   中英

Connection from Activity to Service takes too long

Im currently developing a Music Player and due to the fact that each time the orientation changes on the Phone and the Activity is re-created, I wanted the music to be played by a service. This way, the user is able to leave the activity without the music stopping.. Now.. I have this weird issue I been unable to solve... Each time I created the Activity and Inflate the GUI, the service is started. But the Service always gets Bounded after the Activity has send the data... So the music never starts... I know this happens because if I add a Button to resend the data, the Music starts playing... Here is my code for the activity:

public class Player extends Activity{

private Cursor audioCursor;
public static int position=0;
private int count;
private boolean pause = false,
                play= false,
                stop= false,
                next= false,
                back= false,
                playerActive= true,
                dataChanged= false,
                finished= false,
                playing= true;
private String action;
Messenger mService = null;
boolean mIsBound;
final Messenger mMessenger = new Messenger(new IncomingHandler());
private ServiceConnection mConnection=null;
static final int MSG_SET_BOOLEAN_VALUE = 5;
static final int MSG_SET_STRING_VALUE = 4;
static final int MSG_SET_INT_VALUE = 3;

@Override
protected void onCreate(Bundle savedInstanceState) {
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    super.onCreate(savedInstanceState);

    setContentView(R.layout.player);
    Bundle extras = getIntent().getExtras();
    action=extras.getString("action");

            if(!(Background.isRunning()))
                startService(new Intent(Player.this, Background.class));
        doBindService();

    if(action.equals("play")){
        position=extras.getInt("position");
        String[] proj = {
                MediaStore.Audio.Media.ARTIST,
                MediaStore.Audio.Media.TITLE,
                MediaStore.Audio.Media.ALBUM,
                MediaStore.Audio.Media.DURATION,
                MediaStore.Audio.Media.IS_MUSIC,
                MediaStore.Audio.Media.ALBUM_ID};

        audioCursor = getContentResolver().query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, proj,
                MediaStore.Audio.Media.IS_MUSIC, null,
                MediaStore.Audio.Media.TITLE + " ASC");
        startManagingCursor(audioCursor);
        count = audioCursor.getCount();

        inflatePlayer();
                    /////////////////////THIS IS THE CODE THAT ACTS BEFORE THE SERVICE CONNECTION
        sendBoolToService(playerActive, "playerActive");
        sendIntToService(position);
        sendStringToService(action);
    }
}

    //THIS CODE MUST BE FASTER, BUT THE CONNECTION TAKES TOO LONG

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        mService = new Messenger(service);
        Toast.makeText(getApplicationContext(), "ATTACHED!", Toast.LENGTH_LONG).show();
        try {
            Message msg = Message.obtain(null, Background.MSG_REGISTER_CLIENT);
            msg.replyTo = mMessenger;
            mService.send(msg);
        } catch (RemoteException e) {
            Toast.makeText(getApplicationContext(), "Connection failed!", Toast.LENGTH_LONG).show();
        }
    }
    public void onServiceDisconnected(ComponentName className) {
        mService = null;
        Toast.makeText(getApplicationContext(), "UNATTACHED!", Toast.LENGTH_LONG).show();
    }
};

private void inflatePlayer(){
    //LOTS OF CODE FOR THE GUI, NOTHING TO DO WITH THE SERVICE... SO I OMITTED IT
}

@Override
protected void onStop(){
    playerActive=false;
    try {
        doUnbindService();
    } catch (Throwable t) {
    }
    if(!playing)
        stopService(new Intent(Player.this, Background.class));
    super.onStop();
}

@Override
protected void onDestroy(){
    playerActive=false;
    audioCursor.close();
    try {
        doUnbindService();
    } catch (Throwable t) {
    }
    if(!playing)
        stopService(new Intent(Player.this, Background.class));
    super.onDestroy();
}

class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case MSG_SET_INT_VALUE:
            String str = Integer.toString(msg.getData().getInt("int1"));
            Toast.makeText(getApplicationContext(), "Int Message: " + str, Toast.LENGTH_LONG).show();
            break;
        case MSG_SET_STRING_VALUE:
            String str1 = msg.getData().getString("str1");
            break;
        case MSG_SET_BOOLEAN_VALUE:
            dataChanged=msg.getData().getBoolean("dataChanged");
            finished=msg.getData().getBoolean("finished");
            playing=msg.getData().getBoolean("playing");
            if(!playing){
                if(finished){
                    finished=false;
                    finish();
                }
            }
        default:
            super.handleMessage(msg);
        }
    }
}

private void sendIntToService(int intvaluetosend) {
    if (mService != null) {
        try {
            Bundle b = new Bundle();
            b.putInt("int1", intvaluetosend);
            Message msg = Message.obtain(null, MSG_SET_INT_VALUE);
            msg.setData(b);
            mService.send(msg);
        } catch (RemoteException e) {
        }
    }
}

private void sendStringToService(String stringtosend) {
    if (mService != null) {
        try {
            Bundle b = new Bundle();
            b.putString("str1", stringtosend);
            Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
            msg.setData(b);
            mService.send(msg);
        } catch (RemoteException e) {
        }
    }
}

private void sendBoolToService(boolean booltosend, String name) {
    if (mService != null) {
        try {
            Bundle b = new Bundle();
            b.putBoolean(name, booltosend);
            Message msg = Message.obtain(null, MSG_SET_BOOLEAN_VALUE);
            msg.setData(b);
            mService.send(msg);
        } catch (RemoteException e) {
        }
    }
}


void doBindService() {
    bindService(new Intent(this, Background.class), mConnection, Context.BIND_AUTO_CREATE);
    mIsBound = true;
    Toast.makeText(getApplicationContext(), "BOUND!", Toast.LENGTH_LONG).show();
}

void doUnbindService() {
    if (mIsBound) {
        if (mService != null) {
            try {
                Message msg = Message.obtain(null, Background.MSG_UNREGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);
            } catch (RemoteException e) {
            }
        }
        unbindService(mConnection);
        mIsBound = false;
        Toast.makeText(getApplicationContext(), "UNBOUND!", Toast.LENGTH_LONG).show();
    }
}
}

The Service:

public class Background extends Service {

private NotificationManager nm;
private Cursor audioCursor;
MediaPlayer mp = new MediaPlayer();
private int count;
private boolean pause = false,
                play= false,
                stop= false,
                next= false,
                back= false,
                playerActive= true,
                dataChanged= false,
                finished= false,
                playing= false;
private int position;
private String action;

ArrayList<Messenger> mClients = new ArrayList<Messenger>();
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_INT_VALUE = 3;
static final int MSG_SET_STRING_VALUE = 4;
static final int MSG_SET_BOOLEAN_VALUE = 5;
final Messenger mMessenger = new Messenger(new IncomingHandler());
private static boolean isRunning = false;
private static final String TAG = "Background";

@Override
public IBinder onBind(Intent intent) {
    return mMessenger.getBinder();
}

class IncomingHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case MSG_REGISTER_CLIENT:
            mClients.add(msg.replyTo);
            break;
        case MSG_UNREGISTER_CLIENT:
            mClients.remove(msg.replyTo);
            break;
        case MSG_SET_INT_VALUE:
            position=msg.getData().getInt("int1");
            break;
        case MSG_SET_STRING_VALUE:
            action=msg.getData().getString("str1");
            if(action.equals("play")){
                String[] proj = { MediaStore.Audio.Media.DATA,
                        MediaStore.Audio.Media.DURATION,
                        MediaStore.Audio.Media.IS_MUSIC,
                        MediaStore.Audio.Media.TITLE};

                audioCursor = getContentResolver().query(
                        MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, proj,
                        MediaStore.Audio.Media.IS_MUSIC, null,
                        MediaStore.Audio.Media.TITLE + " ASC");
                count = audioCursor.getCount();

                audioCursor.moveToPosition(position);
                int column_index = audioCursor.getColumnIndex(MediaStore.Audio.Media.DATA);
                String path = audioCursor.getString(column_index);
                startAudioPlayer(path);
                playing=true;
                if(playerActive)
                    sendBool(playing, "playing");
            }else{
                startAudioPlayer(action);
                playing=true;
                if(playerActive)
                    sendBool(playing, "playing");
            }
            action=null;
            break;
        case MSG_SET_BOOLEAN_VALUE:
            pause=msg.getData().getBoolean("pause");
            play=msg.getData().getBoolean("play");
            stop=msg.getData().getBoolean("stop");
            next=msg.getData().getBoolean("next");
            back=msg.getData().getBoolean("back");
            playerActive=msg.getData().getBoolean("playerActive");
            if(pause){
                mp.pause();
                play=false;
                playing=false;
                sendBool(playing, "playing");
                pause=false;
            }
            if(play){
                pause=false;
                mp.start();
                playing=true;
                sendBool(playing, "playing");
                play=false;
            }
        default:
            super.handleMessage(msg);
        }
    }
}

private void sendInt(int intvaluetosend) {
    for (int i=mClients.size()-1; i>=0; i--) {
        try {
            Bundle b = new Bundle();
            b.putInt("int1", intvaluetosend);
            Message msg = Message.obtain(null, MSG_SET_INT_VALUE);
            msg.setData(b);
            mClients.get(i).send(msg);
        } catch (RemoteException e) {
            mClients.remove(i);
            Log.d(TAG, "Int not send..."+e.getMessage());
        }
    }
}

private void sendString(String stringtosend) {
    for (int i=mClients.size()-1; i>=0; i--) {
        try {
            Bundle b = new Bundle();
            b.putString("str1", stringtosend);
            Message msg = Message.obtain(null, MSG_SET_STRING_VALUE);
            msg.setData(b);
            mClients.get(i).send(msg);
        } catch (RemoteException e) {
            mClients.remove(i);
            Log.d(TAG, "String not send..." +e.getMessage());
        }
    }
}

private void sendBool(boolean booltosend, String name) {
    for (int i=mClients.size()-1; i>=0; i--) {
        try {
            Bundle b = new Bundle();
            b.putBoolean(name, booltosend);
            Message msg = Message.obtain(null, MSG_SET_BOOLEAN_VALUE);
            msg.setData(b);
            mClients.get(i).send(msg);
        } catch (RemoteException e) {
            mClients.remove(i);
            Log.d(TAG, "Bool not send..." +e.getMessage());
        }
    }
}

@Override
public void onCreate() {
    super.onCreate();
    showNotification();
    isRunning=true;
}

private void showNotification() {
    nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
    CharSequence text = getText(R.string.maintit);
    Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, Player.class), 0);
    notification.setLatestEventInfo(this, getText(R.string.app_name), text, contentIntent);
    nm.notify(R.string.app_name, notification);
}

@Override
public void onDestroy() {

    //REMEMBER TO SAVE DATA!
    if(mp.isPlaying())
        mp.stop();
    mp.release();
    isRunning=false;
    audioCursor.close();
    nm.cancel(R.string.app_name);
    super.onDestroy();
}

public static boolean isRunning()
{
    return isRunning;
}

public void startAudioPlayer(String path){    
    try {
        if(mp.isPlaying())
            mp.reset();
        mp.setDataSource(path);
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
        Log.d(TAG,e.getMessage());
    } catch (IllegalStateException e) {
        e.printStackTrace();
        Log.d(TAG,e.getMessage());
    } catch (IOException e) {
        e.printStackTrace();
        Log.d(TAG,e.getMessage());
    }
    try {
        mp.prepare();
    } catch (IllegalStateException e) {
        e.printStackTrace();
        Log.d(TAG,e.getMessage());
    } catch (IOException e) {
        e.printStackTrace();
        Log.d(TAG,e.getMessage());
    }
    mp.start();
}
}

I hope someone can help, im getting very frustrated with this, Also, Im pretty sure there is no problem with the media player. I tested it before without the service... the cursors also work properly... Thing is..? Do I need to necessarily call the service from the GUI for it to play the music?? What am I doing wrong?

EDIT: The website wont allow me to answer my own question so I post the solution here:

Ok, finally found a solution. I read that the interaction with the service is only available once the onCreate method has finished.., So: I added a Timer and filled it with the methods I needed to run:

new Timer().schedule(new TimerTask(){
                public void run(){
                    sendBoolToService(playerActive, "playerActive");
                    sendIntToService(position);
                    sendStringToService(action);    
            }
        }, 1000);

AND VOILA: It works! :D Hope its useful to someone!

What you need to do is to move the code in onCreate() which is dependent on the service being available to your onServiceConnected() method in your ServiceConnection implementation:

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        mService = new Messenger(service);
        Toast.makeText(getApplicationContext(), "ATTACHED!", Toast.LENGTH_LONG).show();
        try {
            Message msg = Message.obtain(null, Background.MSG_REGISTER_CLIENT);
            msg.replyTo = mMessenger;
            mService.send(msg);
            sendBoolToService(playerActive, "playerActive");
            sendIntToService(position);
            sendStringToService(action);    
        } catch (RemoteException e) {
            Toast.makeText(getApplicationContext(), "Connection failed!", Toast.LENGTH_LONG).show();
        }
    }
    public void onServiceDisconnected(ComponentName className) {
        mService = null;
        Toast.makeText(getApplicationContext(), "UNATTACHED!", Toast.LENGTH_LONG).show();
    }
};

I would also look at your service implementation as I cannot understand why you are calling mService = new Messenger(service). Your IBinder instance should provide you with a mechanism for obtaining a reference to your service instance.

In my case, my issue was using android:process attribute for <service> element within Android Manifest, which is supposed to improve performance, but in reallity, maybe it does once the service is running, but it takes a very long while to reach onCreate() (and so also to reach onBind() ). For me it was taking minutes. Now Apps and services run smooth and as expected.

I now this a very old question, but showing your Manifest file here makes sense.

More info: https://developer.android.com/guide/topics/manifest/service-element

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM