安卓无法绑定到服务。

3

我是新手,正在尝试理解Android中的Services。我正在尝试将一个服务绑定到一个活动上,我正在按照文档中的示例进行操作,但是我一直在下面标记的行(appService.playSong(title))上遇到NullPointerException异常。在调试器中检查发现appService确实是null。

public class Song extends Activity implements OnClickListener,Runnable {
protected static int currentPosition;
private ProgressBar progress;
private TextView songTitle;
private MPService appService;

private ServiceConnection onService = new ServiceConnection() {
    public void onServiceConnected(ComponentName className,
            IBinder rawBinder) {
        appService = ((MPService.LocalBinder)rawBinder).getService();
    }

    public void onServiceDisconnected(ComponentName classname) {
        appService = null;
    }
};


public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.song);

    Intent bindIntent = new Intent(Song.this,MPService.class);
    bindService(bindIntent,onService,
            Context.BIND_AUTO_CREATE);

    Bundle b = getIntent().getBundleExtra("songdata");
    String title = b.getString("song title");

    // ... 

    appService.playSong(title); // nullpointerexception

    // ...

}

以下是相关服务的内容:
package org.example.music;

// imports

public class MPService extends Service {
private MediaPlayer mp;
public static int currentPosition = 0;
public List<String> songs = new ArrayList<String>();
public static String songTitle;
private static final String MEDIA_PATH = new String("/mnt/sdcard/");

@Override
public void onCreate() {
    super.onCreate();

    mp = new MediaPlayer();
    songs = Music.songs;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    return Service.START_STICKY;
}

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

private final IBinder binder = new LocalBinder();

@Override
public IBinder onBind(Intent intent) {
    return binder;
}

public void playSong(String songPath) {
try {
    mp.reset();
    mp.setDataSource(songPath);
    mp.prepare();
    mp.start();

    mp.setOnCompletionListener(new OnCompletionListener() {
        public void onCompletion(MediaPlayer arg0) {
            nextSong();
        }
    });

    songTitle = songPath.substring(12,songPath.length()-4);

} catch (IOException e) {
    Log.v(getString(R.string.app_name),e.getMessage());
}
}

public void nextSong() {
if (++currentPosition >= songs.size()) {
    currentPosition = 0;
}
String song = MEDIA_PATH+songs.get(currentPosition);
playSong(song);
} 

public void prevSong() {
if (--currentPosition<0) {
    currentPosition=songs.size()-1;
}
String song = Music.MEDIA_PATH+songs.get(currentPosition);
playSong(song);
}

public int getSongPosition() {
return mp.getCurrentPosition();
}

public MediaPlayer getMP() {
return mp;
}
}

我已在AndroidManifest.xml中注册了服务并设置android:enabled="true"。你看到这里有什么明显的错误吗?
3个回答

1

你可以创建本地绑定和远程绑定两种类型的绑定。本地绑定仅供应用程序使用,而远程绑定则可供实现特定接口的任何应用程序使用。 你应该从本地绑定开始。

本地绑定教程
远程绑定教程

我的解决方案没有绑定:

public class MyActivity extends Activity{

  @Override
  public void onCreate(Bundle savedInstanceState){
   ...
   Intent it = new Intent(MyService.ACTIVITY_START_APP);
   it.setClass(getApplicationContext(), MyService.class);
   startService(it);
}

  ...

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

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

  ...

private BroadcastReceiver receiver = new BroadcastReceiver(){

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(MyService.BROADCAST_INIT)) {
                //do your stuff here after init
            }
        }
    };

private void registerBroadcastReceiver(){
         IntentFilter filter = new IntentFilter();
         filter.addAction(HMyService.BROADCAST_INIT);
         this.registerReceiver(receiver, filter);
    }
}

您的服务:

public class MyService extends Service{

public static final String BROADCAST_INITIAL_DATA = "org.myapp.BROADCAST_INIT";
public static final String ACTIVITY_START_APP = "org.myapp.ACTIVITY_START_APP";

  @Override
  public int onStartCommand (Intent intent, int flags, int startId){
    super.onStartCommand(intent, flags, startId);
    if(intent.getAction().equals(ACTIVITY_START_APP)){
      //do your initialization
      //inform the client/GUI
      Intent i = new Intent();
      i.setAction(BROADCAST_INIT);
      sendBroadcast(i);
    }else{
      //some other stuff like handle buttons
    }              
  }
}

祝你好运。


嗯,你能解释一下onServiceConnected的内容吗? - herpderp
实际上我想要展示给你的是绑定到远程,这需要一些AIDL接口来实现,而且比较复杂。我会给你提供教程链接。 - danizmax

1

你假设bindService()会同步连接到服务,但实际上连接只有在onCreate()完成后才可用。

框架在UI线程上运行onCreate(),而bindService()只是记下稍后连接到服务的注释。连接到服务将始终在UI线程上执行,因此这只能在onCreate()执行后发生。你甚至不能指望连接在onCreate()之后立即建立。它会在那之后的某个时候发生:)。此外,框架可能会在低内存条件下自行断开服务,尽管应该只在低内存条件下发生。

因此,将与appService一起工作的代码从onCreate()移动到onServiceConnected()中,它就可以正常工作了。


问题在于 onServiceConnected 没有被触发。 - herpderp

0

从快速浏览中看来,您似乎正在尝试在绑定完成之前访问您的服务。您必须确保在尝试调用服务上的任何方法之前已经触发了onServiceConnected

示例:

Intent bindIntent = new Intent(Song.this,MPService.class);
bindService(bindIntent,onService, Context.BIND_AUTO_CREATE);

//Wait until service has bound
while(appService == null){
     Thread.sleep(100);
}
appService.playSong(title);

这个例子并不是最好的,但它证明了在尝试访问服务之前必须等待绑定完成。


是的,我已经尝试过了,进行了空值检查。如果appService为空,则执行bindService(),然后进行playSong调用。但似乎没有帮助。 - herpderp
现在发生的一切就是应用程序挂起了;显然Thread.sleep(100)一遍又一遍地运行。最终,我会收到一个对话框,上面写着应用程序已停止响应,并带有“强制退出”和“等待”按钮。如果您能从中获得任何有用的信息,请查看挂起期间的日志输出:http://pastebin.com/REVuZ1YQ - herpderp
尝试调试您的应用程序,查看它是否会触发onServiceConnected方法,如果会,请检查它分配给appService的内容。 - CeejeeB
从您的堆栈跟踪来看,您的服务存在错误: 超时执行服务:ServiceRecord{4064b528 org.example.music/.MPService} 这将导致您的绑定始终失败。 - CeejeeB
我有完全相同的问题,从来没有找到解决办法。 - noloman
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接