3

В игровом приложении у меня есть следующий сценарий:ServiceConnection.onServiceConnected() никогда не вызывается после связывания с начатой ​​службой

  • Из основной игры Activity, игрок начинает несколько игровых задач, которые выполняются в фоновом режиме с различными продолжительность.
  • Игрок должен иметь возможность просматривать ход выполнения заданий игры в отдельном View.

Чтобы сделать это, я создал два Activity с и Service, определяются следующим образом:

  • Service ProgressService обрабатывает несколько ProgressBar s работает одновременно на параллельных потоков.

  • Activity WorkScreen2 создает задачу игры, начинает Service с startService() с параметрами задачи, передаваемых в Bundle.

  • Activity ProgressScreen связывается с Service, чтобы получить и показать ProgressBar с выполняемыми задачами.

  • Оба вида деятельности находятся под отдельными TabHost s одного TabActivity.


Проблема у меня в том, что метод ServiceConnection.onServiceConnected() никогда не вызывается. Я получаю Java.lang.NullPointerException, потому что пытаюсь вызвать метод объекта Service, который должен быть назначен этим методом. См. Код ниже.

Я использую getApplicationContext().bindService(), чтобы связать Activity с Service, потому что TabSpec cannot bind to Services. Этот метод возвращает true. Следовательно, привязка успешна.


Вот Service:

public class ProgressService extends Service implements GameConstants { 
    public static final String BROADCAST_PROGRESS = "com.mycompany.android.mygame.progressbroadcast"; 
    private static final long UPDATE_INTERVAL = 500; 
    private IBinder mBinder; 
    private List<ProgressBar> mProgressBarList; 
    private List<String> mStaffNameList; 

    private final class ServiceHandler extends Handler { 

     public ServiceHandler(Looper looper) { 
      super(looper); 
     } 

     @Override 
     public void handleMessage(Message msg) { 
      ProgressBar progressBar = new ProgressBar(ProgressService.this); 
      mProgressBarList.add(progressBar); 

      Bundle bundle = msg.getData(); 
      String staffName = bundle.getString(WorkScreen2.STAFF_NAME); 
      mStaffNameList.add(staffName); 

      int taskDurationMillis = bundle.getInt(WorkScreen2.TASK_DURATION) * 1000; 
      progressBar.setMax(taskDurationMillis/1000); 

      long startTimeMillis = SystemClock.uptimeMillis(); 
      long elapsedTimeMillis = SystemClock.uptimeMillis() 
        - startTimeMillis; 

      Intent intent = new Intent(); 
      intent.setAction(BROADCAST_PROGRESS); 

      while (elapsedTimeMillis < taskDurationMillis) { 
       try { 
        Thread.sleep(UPDATE_INTERVAL); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       elapsedTimeMillis = SystemClock.uptimeMillis() 
         - startTimeMillis; 
       int elapsedTimeSeconds = (int) elapsedTimeMillis/1000; 
       progressBar.setProgress(elapsedTimeSeconds); 
       sendBroadcast(intent); 
      } 

      progressBar.setVisibility(View.GONE); 
      mProgressBarList.remove(progressBar); 
      mStaffNameList.remove(staffName); 
      sendBroadcast(intent); 

      if (mProgressBarList.isEmpty()) { 
       stopSelf(msg.arg1); 
      } 
     } 
    } 

    @Override 
    public void onCreate() { 
     super.onCreate(); 
     mBinder = new ProgressServiceBinder(); 
     mProgressBarList = Collections 
       .synchronizedList(new ArrayList<ProgressBar>()); 
     mStaffNameList = Collections.synchronizedList(new ArrayList<String>()); 
    } 

    /* 
    * Creates a thread for each game task with parameters passed in 
    * <code>intent</code> 
    */ 
    @Override 
    public int onStartCommand(Intent intent, int flags, int startId) { 
     Toast.makeText(this, "starting service", Toast.LENGTH_LONG).show(); 

     HandlerThread thread = new HandlerThread("ServiceStartArguments", 
       Process.THREAD_PRIORITY_BACKGROUND); 
     thread.start(); 

     Handler serviceHandler = new ServiceHandler(thread.getLooper()); 

     Message msg = serviceHandler.obtainMessage(); 
     msg.arg1 = startId; 
     msg.setData(intent.getExtras()); 
     serviceHandler.sendMessage(msg); 

     return START_STICKY; 
    } 

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

    public class ProgressServiceBinder extends Binder { 
     ProgressService getService() { 
      return ProgressService.this; 
     } 
    } 

    public List<ProgressBar> getProgressBarList() { 
     return mProgressBarList; 
    } 

    public List<String> getStaffNameList() { 
     return mStaffNameList; 
    } 

    @Override 
    public void onDestroy() { 
     Toast.makeText(this, "Service done", Toast.LENGTH_SHORT).show(); 
    } 
} 

И это Activity, который связывается с ним:

public class ProgressScreen extends ListActivity { 
    private final String TAG = "ProgressScreen"; 

    private ProgressScreenAdapter mAdapter; 
    private ProgressService mProgressService; 
    private List<ProgressBar> mProgressBarList; 
    private List<String> mStaffNameList; 

    @Override 
    public void onCreate(Bundle bundle) { 
     Log.i(TAG, "ProgressScreen oncreate"); 

     super.onCreate(bundle); 
     setContentView(R.layout.progress_screen_layout); 
     IntentFilter filter = new IntentFilter(); 
     filter.addAction(ProgressService.BROADCAST_PROGRESS); 
     registerReceiver(receiver, filter); 
     doBindService(); 

     mAdapter = new ProgressScreenAdapter(this, mStaffNameList, mProgressBarList); 
     setListAdapter(mAdapter); // Returns true 

     /* 
     * This is where I get the NullPointerException 
     * mProgressService is null here 
     */ 
     mProgressBarList = mProgressService.getProgressBarList(); 
     mStaffNameList = mProgressService.getStaffNameList(); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 
     IntentFilter filter = new IntentFilter(); 
     filter.addAction(ProgressService.BROADCAST_PROGRESS); 
     registerReceiver(receiver, filter); 
    } 

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

    boolean doBindService() { 
     return getApplicationContext().bindService(new Intent(this, ProgressService.class), mConnection, Context.BIND_AUTO_CREATE); 
    } 

    void doUnbindService() { 
     getApplicationContext().unbindService(mConnection); 
    } 

    ServiceConnection mConnection = new ServiceConnection() { 

     public void onServiceConnected(ComponentName className, IBinder binder) { 
      mProgressService = ((ProgressService.ProgressServiceBinder) binder).getService(); 
      Toast.makeText(ProgressScreen.this, "Connected to ProgressService", Toast.LENGTH_SHORT).show(); 
     } 

     public void onServiceDisconnected(ComponentName name) { 
      mProgressService = null; 
     } 
    }; 

    private BroadcastReceiver receiver = new BroadcastReceiver() { 
     @Override 
     public void onReceive(Context context, Intent intent) { 
      mAdapter.notifyDataSetChanged(); 
     } 
    }; 
} 

И в Service запускается из основного Activity следующим образом:

Intent intent = new Intent(WorkScreen2.this, ProgressService.class); 
intent.putExtra(TASK_DURATION, task.getDuration()); 
intent.putExtra(STAFF_NAME, staff.getName()); 
startService(intent); 

AndroidManifest.xml содержит

<service 
    android:name=".ProgressService" 
    android:label="@string/progress_service"> 
</service> 

ответ

14

ServiceConnection в onServiceConnected() называется, но никто не гарантирует, что она будет называться перед тем OnCreate продолжает выполнение.Итак, что происходит здесь - вы успешно связываетесь с сервисом (поэтому onBind возвращает true), но вы не полностью подключены - onServiceConnected() еще не вызван, поэтому ваш локальный объект mProgressService еще не инициализирован, и поэтому вы получаете исключение NullPointerException. Функция

mProgressBarList = mProgressService.getProgressBarList(); 
mStaffNameList = mProgressService.getStaffNameList(); 

из OnCreate() в onServiceConnected() (используйте объект службы после он инициализируется в onServiceConnected()):

Решение:

Переместить эти две строки.

+1

Я знаю, что это старо, но вы сэр, абсолютный спасатель ... Я люблю вас. <3 – LokiSinclair

+0

Во-вторых, что. Я всю ночь обманывал это, и это, наконец, указывало на очевидное. Благодаря! –

9

Проверить AndroidManifest.xml твоего и добавить услуги, которые вы пытались связать.

+0

Я забыл упомянуть, что у меня есть '' обслуживания элемент «AndroidManifest.xml' для' 'обслуживания Я пытаюсь связать с. Связывание выполняется успешно, так как 'bindService()' возвращает 'true'. – Wesam

+0

Я не мог поблагодарить вас больше Mr Oldtimer:) –

+0

@oldtimer: Спасибо :) –

2

Вы должны вернуть Binder внутренний класс от

private final IBinder mBinder = new ServiceBinder(); 

public class ServiceBinder extends Binder { 
    public PlayerActivity getService() { 
    return PlayerActivity.this; 
    } 
    } 

@Nullable 
@Override 
      public IBinder onBind(Intent intent) { 
      return mBinder; 
      } 
Смежные вопросы