我的应用程序的工作原理如下:我有一个带有广播接收器的活动作为内部类(实际上可以在自己的类中移动)和远程服务(在自己的线程中工作)。活动设置所有变量并启动所有过程。该服务使用proximityAlerts跟踪我的移动,并在我在特定地点内时触发广播接收者。现在的问题是,在完成广播记录器之后,我需要回调该服务来清理所有内容并调用stopSelf()。鉴于活动可能不再是前台,我需要一种方法来使用来自后台广播接收者的处理程序来调用服务。我会告诉你代码然后我告诉你我得到的错误:
MainActivity:
public class MainActivity extends Activity {
public static MainActivity instance;
static Messenger messenger;
static boolean serviceBind;
private static ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
messenger = new Messenger(binder);
serviceBind = true;
}
public void onServiceDisconnected(ComponentName className) {
messenger = null;
serviceBind = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
instance = this;
MyVariable mv = new MyVariable();
addProximityAlert(mv);
}
protected static class ResponseHandler extends Handler {
Boolean result;
@Override
public void handleMessage(Message msg) {
int respCode = msg.what;
switch(respCode) {
case ProximityService.ADD_PROXIMITY_ALERT_RESPONSE: {
result = msg.getData().getBoolean("respData");
}
case ProximityService.REMOVE_PROXIMITY_ALERT_RESPONSE: {
result = msg.getData().getBoolean("respData");
}
}
}
}
public void startService() {
Intent i = new Intent (this, ProximityService.class);
startService(i);
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
}
public void addNewProximityAlert(MyVariable mv) {
Message msg = Message.obtain(null, ProximityService.ADD_PROXIMITY_ALERT);
msg.replyTo = new Messenger(new ResponseHandler());
Bundle b = new Bundle();
b.putParcelable("data", mv);
msg.setData(b);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void removeProximityAlert(MyVariable mv) {
Message msg = Message.obtain(null, ProximityService.REMOVE_PROXIMITY_ALERT);
msg.replyTo = new Messenger(new ResponseHandler());
Bundle b = new Bundle();
b.putParcelable("data", mv);
msg.setData(b);
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public static class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String key = LocationManager.KEY_PROXIMITY_ENTERING;
Boolean entering = intent.getBooleanExtra(key, true);
Bundle bundle = intent.getExtras();
bundle.setClassLoader(context.getClassLoader());
MyVariable mv = bundle.getParcelable("com.example.MyBroadcastReceiver.mv");
if (entering) {
// do stuff
} else {
// do stuff
};
MainActivity ssm = MainActivity.instance;
ssm.removeProximityAlert(mv);
}
protected void onResume() {
super.onResume();
Intent intent= new Intent(this, ProximityService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
serviceBind=true;
}
protected void onPause() {
super.onPause();
if(serviceBind) {
serviceBind = false;
unbindService(mConnection);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(serviceBind) {
serviceBind = false;
unbindService(mConnection);
}
}
ProximityService:
public class ProximityService extends Service {
static final long POINT_RADIUS = 250; // in Meters
static final long PROX_ALERT_EXPIRATION = -1;
static final int ADD_PROXIMITY_ALERT = 2;
static final int ADD_PROXIMITY_ALERT_RESPONSE = 3;
static final int REMOVE_PROXIMITY_ALERT = 4;
static final int REMOVE_PROXIMITY_ALERT_RESPONSE = 5;
public static final String RESULT = "result";
int minDistance = 50; // 50 metri
int minTime = 1000*5*1; // 5 Secondi
private MyLocationListener myLocationListener;
private Messenger msg = new Messenger(new MyHandler());
private static boolean isRunning = false;
@Override
public void onCreate() {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
isRunning = true;
}
@Override
public IBinder onBind(Intent arg0) {
return msg.getBinder();
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
int msgType = msg.what;
switch(msgType) {
case ADD_PROXIMITY_ALERT: {
try {
final Bundle bundle = msg.getData();
bundle.setClassLoader(getClassLoader());
MyVariable mv = bundle.getParcelable("data");
addProximityAlert(getApplicationContext(), mv);
Message resp = Message.obtain(null, ADD_PROXIMITY_ALERT_RESPONSE);
Bundle bResp = new Bundle();
bResp.putBoolean("respData", true);
resp.setData(bResp);
msg.replyTo.send(resp);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
case REMOVE_PROXIMITY_ALERT: {
try {
final Bundle bundle = msg.getData();
bundle.setClassLoader(getClassLoader());
MyVariable mv = bundle.getParcelable("data");
removeProximityAlert(mv);
Message resp = Message.obtain(null, REMOVE_PROXIMITY_ALERT_RESPONSE);
Bundle bResp = new Bundle();
bResp.putBoolean("respData", true);
resp.setData(bResp);
msg.replyTo.send(resp);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
default:
super.handleMessage(msg);
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
myLocationListener = new MyLocationListener();
return(START_REDELIVER_INTENT);
}
public static boolean isRunning() {
return isRunning;
}
public void addProximityAlert(Context context, MyVariable mv) {
locationManager.requestLocationUpdates(getProvider(), minTime, minDistance, myLocationListener);
Intent intent = new Intent(this, MainActivity.MyBroadcastReceiver.class);
intent.putExtra("com.example.MyBroadcastReceiver.mv", mv);
PendingIntent proximityIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
locationManager.addProximityAlert(mv.getLocation().getLatitude(), mv.getLocation().getLongitude(), POINT_RADIUS, PROX_ALERT_EXPIRATION, proximityIntent);
}
public void removeProximityAlert(MyVariable mv) {
Intent intent = new Intent(this, MainActivity.MyBroadcastReceiver.class);
PendingIntent proximityIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
locationManager.removeProximityAlert(proximityIntent);
locationManager.removeUpdates(myLocationListener);
myLocationListener = null;
locationManager = null;
stopSelf();
}
public class MyLocationListener implements LocationListener {
@Override
public void onLocationChanged(Location location) {
//do stuff
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
}
@Override
public void onDestroy() {
super.onDestroy();
isRunning = false;
}
}
所以一切正常但是当它必须执行ssm.removeProximityAlert(mv)时,我得到一个NullPointerException错误,因为removeProximityAlert末尾的messenger为null。我知道为什么我会收到这个错误,但我不知道如何修复它。我该怎么办?当然,如果你发现其他不起作用或做得不好的事情,我很高兴听到意见和建议。 这里显示的代码实际上是我的代码的简化,只是为了缩小它并删除无用的东西。
答案 0 :(得分:0)
问题在于,当您创建收到回复的邮件时,您必须始终填充replyTo。
这会掩盖服务中的错误。
if (msg.replyTo !=null) {
msg.replyTo.send(Message.obtain(null, REMOVE_ALERT));
}
这将修复错误,使用例程来创建使用客户端信使填充replyto字段的新消息。
private static Message getMsg(int msgWhat) {
Message msg = Message.obtain(null, msgWhat);
// only set reply to on the register command
msg.replyTo = mMessenger;
msg.what = msgWhat;
return msg;
}
您的代码的其他注意事项。
考虑使用arg1和arg2传递简单数据。 arg1 = 1表示true arg1 = 0表示false这比填充bundle传递布尔值更有效。
在ANR触发之前,请考虑应用程序在BroadcastReceiver中有10秒钟,因此您不应该// doStuff。广播接收器用于执行简单的即时操作,例如切换可见性,启动服务,启动异步任务。
http://developer.android.com/training/articles/perf-anr.html
表示活动或片段。您的实施可能会有所不同。
以下示例仅预期对寄存器的回复或取消注册发送给服务的消息。该服务保存处理程序(replyTo)并在记录位置时发送消息。这是我在myapp BestRides中使用的代码。这是客户端类的一部分,片段和活动用于连接到记录位置的服务。我建立的代码是在developer.android.com上,但我今天找不到它。
public ClientServiceLocationRecorder(WeakReference<Context> weakContext,
WeakReference<LocationRecorderClientCallback> weakCallback) {
ClientServiceLocationRecorder.weakContext = weakContext;
ClientServiceLocationRecorder.weakCallbacks.add(weakCallback);
//start of the service its import to start and bind so the service
//stays up when the activity finishes.
new Thread(new Runnable() {
@Override
public void run() {
Context context = ClientServiceLocationRecorder.weakContext.get();
context.startService(new Intent(context, ServiceLocationRecorder.class));
// bind to the service
if (!mIsBound) {
// Bind to the service
context.bindService(new Intent(context,
ServiceLocationRecorder.class), mConnection,
Context.BIND_AUTO_CREATE);
mIsBound = true;
}
sndMessageToLocationRecorder(ServiceLocationRecorder.MSG_GET_DISTANCE);
runRecordStatus();
}
}).start();
}
private static ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
// We want to monitor the service for as long as we are
// connected to it.
sendMsg(getMsg(ServiceLocationRecorder.MSG_REGISTER_CLIENT));
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
private static void sendMsg(Message msg) {
if (isRegistered || msg.replyTo != null) {
try {
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
} else {
// queue the messages until we are registered
// msgQueue is arrayList<Message>
msgQueue.add(msg);
}
}
private static Message getMsg(int locationRecorderMsg) {
Message msg = Message.obtain(null, locationRecorderMsg);
// only set reply to on the register command
msg.replyTo = ((locationRecorderMsg == ServiceLocationRecorder.MSG_REGISTER_CLIENT
|| locationRecorderMsg == ServiceLocationRecorder.MSG_UNREGISTER_CLIENT)
? mMessenger
: null);
msg.what = locationRecorderMsg;
return msg;
}
final static Messenger mMessenger = new Messenger(new IncomingHandler());
private static class IncomingHandler extends Handler {
Bundle data;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//always have most used switch at the top for efficiency
case ServiceLocationRecorder.MSG_LOCATION:
data = msg.getData();
lat = data.getDouble(ServiceLocationRecorder.BUNDLE_LATITUDE);
lon = data.getDouble(ServiceLocationRecorder.BUNDLE_LONGITUDE);
distance = data
.getDouble(ServiceLocationRecorder.BUNDLE_TRACKED_DISTANCE);
startMillis = data
.getLong(ServiceLocationRecorder.BUNDLE_START_MILLIS);
endMillis = data
.getLong(ServiceLocationRecorder.BUNDLE_END_MILLIS);
numberOfLocations = msg.arg1;
sendLocationMessageToClient();
break;
// THESE ARE JUST SIMPLE MESSAGES
case ServiceLocationRecorder.MSG_REGISTER_CLIENT:
// send queued messages once registered.
if (!isRegistered) {
isRegistered = true;
for (Message qMsg : msgQueue) {
sendMsg(qMsg);
}
msgQueue.clear();
}
break;
case ServiceLocationRecorder.MSG_PICTURE:
// TODO NOT IMPLEMENTED
// call the client function
// not implemented.
break;
case ServiceLocationRecorder.MSG_GET_DISTANCE:
data = msg.getData();
distance = data
.getDouble(ServiceLocationRecorder.BUNDLE_TRACKED_DISTANCE);
startMillis = data
.getLong(ServiceLocationRecorder.BUNDLE_START_MILLIS);
endMillis = data
.getLong(ServiceLocationRecorder.BUNDLE_END_MILLIS);
numberOfLocations = msg.arg1;
sendDistanceMessageToClient();
break;
default:
super.handleMessage(msg);
}
}
}
// interact with the above code send a message to the service.
public void sndMessageToLocationRecorder(int locationRecorderMsg) {
switch (locationRecorderMsg) {
case ServiceLocationRecorder.MSG_RECORD_START:
runStartRecording();
break;
case ServiceLocationRecorder.MSG_RECORD_STOP:
runStopRecording();
break;
case ServiceLocationRecorder.MSG_CLEAR:
ClientServiceLocationRecorder.distance = 0;
ClientServiceLocationRecorder.startMillis = 0;
sendMsg(getMsg(ServiceLocationRecorder.MSG_CLEAR));
break;
case ServiceLocationRecorder.MSG_RECORD_STATUS:
runRecordStatus();
break;
case ServiceLocationRecorder.MSG_GET_DISTANCE:
sendMsg(getMsg(ServiceLocationRecorder.MSG_GET_DISTANCE));
break;
}
}