在游戏应用程序中,我有以下场景:
Activity
中,玩家会启动几个在后台运行且持续时间不同的游戏任务。View
中查看正在运行的游戏任务的进度。为此,我创建了两个Activity
和一个Service
,定义如下:
Service ProgressService
处理在并行线程上同时运行的多个ProgressBar
。
Activity WorkScreen2
创建游戏任务,使用Service
启动startService()
,Bundle
传递任务参数。
Activity ProgressScreen
绑定到Service
以获取并显示正在运行的任务的ProgressBar
。
这两项活动都在一个TabHost
的单独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>
答案 0 :(得分:14)
ServiceConnection的onServiceConnected()被称为,但没有人保证在 onCreate继续执行之前它将被称为。那么,这里发生了什么 - 你成功地绑定到服务(这就是为什么onBind返回true),但你没有完全连接 - onServiceConnected()尚未被调用,所以你的本地mProgressService对象是还没有初始化,因此你得到NullPointerException。
<强>解决方案:强>
移动这两行:
mProgressBarList = mProgressService.getProgressBarList();
mStaffNameList = mProgressService.getStaffNameList();
从onCreate()到onServiceConnected()函数(在之后使用服务对象在onServiceConnected()中初始化)。
答案 1 :(得分:9)
检查您的AndroidManifest.xml并添加您尝试绑定的服务。
答案 2 :(得分: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;
}