【问题标题】:ServiceConnection.onServiceConnected() never called after binding to started serviceServiceConnection.onServiceConnected() 绑定到启动服务后从未调用
【发布时间】:2012-03-04 00:52:39
【问题描述】:

在游戏应用程序中,我有以下场景:

  • 在主游戏Activity 中,玩家启动了几个在后台运行且持续时间不同的游戏任务。
  • 玩家应该能够在单独的View 中查看正在运行的游戏任务的进度。

为此,我创建了两个Activitys 和一个Service,定义如下:

  • Service ProgressService 处理多个在并行线程上同时运行的ProgressBar

  • Activity WorkScreen2 创建一个游戏任务,以startService() 启动Service,并在Bundle 中传递任务参数。

  • Activity ProgressScreen绑定Service获取并显示正在运行的任务的ProgressBars。

  • 两个活动都在单独的TabHosts 和一个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>

【问题讨论】:

    标签: android multithreading android-service android-tabhost android-progressbar


    【解决方案1】:

    ServiceConnection 的 onServiceConnected() 被调用,但没有人保证它会在 onCreate 继续执行之前被调用。那么,这里会发生什么 - 你成功绑定到服务(这就是 onBind 返回 true 的原因),但你还没有完全连接 - onServiceConnected() 还没有被调用,所以你的本地 mProgressService 对象是尚未初始化,因此您会收到 NullPointerException。

    解决方案:

    移动这两行:

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

    从 onCreate() 到 onServiceConnected() 函数(在 onServiceConnected() 中初始化之后使用服务对象)。

    【讨论】:

    • 我知道这已经过时了,但先生,您是绝对的救星……我爱您。
    • 第二个。我整晚都在搞这个,这终于指出了显而易见的事情。谢谢!
    【解决方案2】:

    检查您的 AndroidManifest.xml 并添加您尝试绑定的服务。

    【讨论】:

    • 我忘了提到我在“AndroidManifest.xml”中有一个Service 元素,用于我要绑定的Service。绑定成功,因为bindService()返回true
    【解决方案3】:

    你必须从

    返回你的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;
               }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多