Android Mp3Player Service
Tjenare.
Jag håller på att implementera en mediaspelare för android och har stött på ett problem som jag inte lyckats lösa.
Jag har lyckats med att "binda" min service till min main activity. Men sedan har jag tänkt att man ska kunna visa en playlist
om man trycker på en meny knapp. Detta är inga problem utan playlist activity startas utan problem. Playlist består av en Listactivity
och tanken är att när man trycker på ett item så skickar man positionen tillbaka till huvud aktiviteten men här kraschar programmet.
Felmeddelande när applikationen kraschar:
01-08 21:06:07.385: E/AndroidRuntime(638): java.lang.RuntimeException: Unable to resume activity {com.lab2.assignment2/com.lab2.Mp3Player.Mp3Main}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=0, result=-1, data=Intent { cmp=com.lab2.assignment2/com.lab2.Mp3Player.Mp3Main (has extras) }} to activity {com.lab2.assignment2/com.lab2.Mp3Player.Mp3Main}: java.lang.NullPointerException
Det jag funderar på är om jag ska skicka informationen direkt till min service, eller om jag behöver använda en broadcast receiver i Mp3Main
där jag tar emot trackIndex i onResume().
Ett annat problem som ibland uppstår är: "Has leaked service connection that was originally bound here".
Någon som vet vad detta kan beror på? uppstår när man försöker öppna applikationen efter att ha stängt den även fast musiken spelar vidare.
Tacksam för hjälp.
TracksList.java:
package com.lab2.Mp3Player;
import com.lab2.assignment2.R;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.ActionBar;
import android.app.Activity;
import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class TracksList extends ListActivity{
public ArrayList<HashMap<String, String>> tracks = new ArrayList<HashMap<String, String>>();
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.trackslist);
ActionBar ab = getActionBar();
ab.setDisplayHomeAsUpEnabled(true);
ArrayList<HashMap<String, String>> tracksInfo = new ArrayList<HashMap<String, String>>();
FetchMedia ftm = new FetchMedia();
this.tracks = ftm.getTracks(); // Fetching media from SD-card
for(int i=0; i < tracks.size(); i++){
HashMap<String, String> track = tracks.get(i);
tracksInfo.add(track);
}
ListAdapter adapter = new SimpleAdapter(this, tracksInfo, R.layout.trackslist_item,
new String [] {"title"}, new int [] {R.id.trackTitle});
setListAdapter(adapter);
ListView lv = getListView();
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
int trackIndex = position;
Intent it = new Intent(getApplicationContext(), Mp3Main.class);
it.putExtra("trackIndex", trackIndex);
setResult(Activity.RESULT_OK, it);
finish();
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
//return super.onOptionsItemSelected(item);
switch(item.getItemId()){
case android.R.id.home:
Intent it = new Intent(this, Mp3Main.class);
it.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(it);
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Delar av Mp3Main.java:
private Mp3Service srv = null;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.mp3main);
Intent it = new Intent(main, Mp3Service.class);
main.bindService(it, Mp3Connection, Context.BIND_AUTO_CREATE);
....
....
....
}
@Override
public boolean onCreateOptionsMenu(Menu menu){
// TODO Auto-generated method stub
getActionBar().setDisplayHomeAsUpEnabled(true);
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mp3_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
//return super.onOptionsItemSelected(item);
Intent it;
switch(item.getItemId()){
case R.id.playlist:
it = new Intent(this, TracksList.class);
startActivityForResult(it, 0);
playlist = true;
return true;
case android.R.id.home:
it = new Intent(this, com.lab2.MainList.MainList.class);
it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(it);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void playTrack(int index) {
// TODO Auto-generated method stub
try {
srv.playTrack(index);
String track_title = srv.tracks.get(index).get("title");
title.setText(track_title);
play.setImageResource(R.drawable.img_btn_pause);
progressbar.setProgress(0);
progressbar.setMax(100);
updateProgressBar();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e){
e.printStackTrace();
}
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
// Push and Pop an empty notification
ShowDummyNotification();
nf.cancel(ID);
}
@Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
onClose();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
if(!close)
showInfoNotification();
if(bound){
unbindService(Mp3Connection);
bound = false;
}
}
private void onClose() {
// TODO Auto-generated method stub
Mp3Service.shuffleTrack = false;
Mp3Service.repeatTrack = false;
Mp3Service.trackIndex = 0;
close = true;
srv.Stop();
finish();
}
private ServiceConnection Mp3Connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
srv = null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
srv = ((Mp3Service.myBinder)service).getService();
bound = true;
}
};
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
Mp3Service.trackIndex = data.getIntExtra("trackIndex", 0);
playTrack(Mp3Service.trackIndex);
playlist = false;
}
}
Mp3Service:
package com.lab2.Mp3Player;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Binder;
import android.os.IBinder;
public class Mp3Service extends Service{
private final IBinder myBinder = new myBinder();
public ArrayList<HashMap<String, String>> tracks = new ArrayList<HashMap<String, String>>();
public static int trackIndex = 0;
public static boolean shuffleTrack = false;
public static boolean repeatTrack = false;
private FetchMedia ftm;
public static MediaPlayer mp;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return myBinder;
}
public class myBinder extends Binder{
Mp3Service getService(){
return Mp3Service.this;
}
}
@Override
public void onCreate(){
// TODO Auto-generated method stub
ftm = new FetchMedia();
tracks = ftm.getTracks();
Thread tr = new Thread(null, run, "Mp3Service");
tr.start();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
// super.onDestroy();
// Nothing to do here..
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
return Service.START_NOT_STICKY;
}
Runnable run = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
mp = new MediaPlayer();
try{
mp.setOnCompletionListener(new OnCompletionListener() {
/* Method called when track ends
* to play next track.
*/
@Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
if(repeatTrack)
playTrack(trackIndex);
else if(shuffleTrack){
Random rand = new Random();
trackIndex = rand.nextInt(tracks.size() - 1);
playTrack(trackIndex);
}
else{
// no repeat or shuffle ON - play next song
if(trackIndex < (tracks.size() - 1)){
playTrack(trackIndex + 1);
trackIndex += 1;
}else{
// play first song
playTrack(0);
trackIndex = 0;
}
}
}
});
}
catch(Exception e){
}
}
};
public boolean isPlaying(){
return mp.isPlaying();
}
public boolean shuffle(){
return shuffleTrack;
}
public boolean repeat(){
return repeatTrack;
}
public void pause(){
mp.pause();
}
public void start(){
mp.start();
}
public void seekTo(int progress){
mp.seekTo(progress);
}
public int getDuration(){
return mp.getDuration();
}
public int getPosition(){
return mp.getCurrentPosition();
}
public void Stop(){
mp.stop();
this.stopSelf();
}
public void playTrack(int index){
try{
mp.reset();
mp.setDataSource(tracks.get(index).get("path"));
mp.prepare();
mp.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
}
}
}
"This is VAR, spelled A-U-T-O"