处理程序在服务和广播接收器之间的通信

时间:2014-12-03 18:55:36

标签: android service background broadcastreceiver handler

我的应用程序的工作原理如下:我有一个带有广播接收器的活动作为内部类(实际上可以在自己的类中移动)和远程服务(在自己的线程中工作)。活动设置所有变量并启动所有过程。该服务使用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。我知道为什么我会收到这个错误,但我不知道如何修复它。我该怎么办?当然,如果你发现其他不起作用或做得不好的事情,我很高兴听到意见和建议。 这里显示的代码实际上是我的代码的简化,只是为了缩小它并删除无用的东西。

1 个答案:

答案 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;
    }
}