Create service to detect any action from the user

2019-07-15 07:27发布

I'm trying to create a service where I want to detect something about user, let's say when user lays the device on a table, the thing is that I have that action detected but I have it on a MainActivty and I want it to put on Service. The thing is that on my MainActivity() I had my registerAction() and on my onResume() were called and in onPause() I call the unregisterListener() from my sensor, as well I have a HandlerThread where I start it on my onCreate() how do I change it to Service? Would be a problem? I see that there aren't the same methods...

I've created my Service and I've got :

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }



    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("CREATE","ONCREATE");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("DESTROY","ONDESTROY");
    }
}

Also my MainActivity I have put implements SensorEventListener.

A skeleton of my class is :

    public class MainActivity extends Activity implements SensorEventListener {

    private HandlerThread mSensorThread;
    private SensorManager mSensorManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mSensorThread = new HandlerThread("sensor_thread");
        mSensorThread.start();
    }

    private void registerSensorListener() {
        mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_FASTEST, new Handler(mSensorThread.getLooper()));
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        //DO stuff
        if (isLayed()) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                   Log.d("LAY","LAYLAY");
                }
            });
            mSensorManager.unregisterListener(this);
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

    private boolean isLayed() {


        return stuff;
    }


    @Override
    protected void onResume() {
        super.onResume();

        registerSensorListener();
    }

    @Override
    protected void onPause() {
        super.onPause();

        mSensorManager.unregisterListener(this);
    }
}

EDIT

I'm using szamani20 code, but I'm having problems with runOnUiThread because I can not call from my Service also, I'm having this issue

java.lang.RuntimeException: Unable to start service com.example.developer.qwe.MyService@d8c613b with null: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getAction()' on a null object reference

2条回答
Juvenile、少年°
2楼-- · 2019-07-15 07:55

You can register SensorManager inside service in OnStartCommand.Also try using startForeground as android os will kill your service when app is killed

查看更多
劳资没心,怎么记你
3楼-- · 2019-07-15 08:15

First of all you need to decide whether you want the user to be aware of your running service or not. Take a review on Background Execution Limits in android Oreo:

To improve the user experience, Android 8.0 (API level 26) imposes limitations on what apps can do while running in the background.

So considering your case where it seems there are lots of work to do in many situations, it would be a better approach to use a foreground service. As android document says about foreground services:

A foreground service is a service that the user is actively aware of and is not a candidate for the system to kill when low on memory. A foreground service must provide a notification for the status bar, which is placed under the Ongoing heading. This means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.

Since you mentioned that you have the action detected I won't enter that part of your code. So you need to create a subclass of Service as you did and use the startService method to get it's onCreate called. One thing you need to notice is that the onCreate method of service is called once you call startService on that service for the first time, no matter how many times you call startService again the onCreate method won't get called and only the onStartCommand get called. We use that fact alongside that you could provide a string action within your intent to properly register and unregister your listener.

In MainActivity.java:

String action = "start";  // Or to unregister listener "stop"!
final Intent intent = new Intent(this, MyService.class);
intent.setAction(action);
startService(intent);

and then in MyService.java:

@Override
public void onCreate() {
    super.onCreate();
    // Do initialization or whatever here (executed once per service lifecycle)
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent.getAction().equals("start")) {
        // Register your listener or whatever
        showForegroundNotification();
    }
    if (intent.getAction().equals("stop")) {
        // Unregister your listener or whatever
        stopForeground(true);
        stopSelf();
    }

    return START_STICKY;
}

private void showForegroundNotification() {
    Intent myServiceNotificationIntent = new Intent(this, MainActivity.class);
    myServiceNotificationIntent.setFlags(
            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    PendingIntent pendingIntent = PendingIntent
            .getActivity(this, MY_SERVICE_REQUEST_CODE,
                    myServiceNotificationIntent, MY_SERVICE_FLAG);

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle(MY_SERVICE_NOTIFICATION_CONTENT_TITLE)
            .setTicker(MY_SERVICE_NOTIFICATION_TICKER)
            .setContentText(MY_SERVICE_NOTIFICATION_CONTENT_TEXT)
            .setSmallIcon(R.drawable.ic_whatever)
            .setContentIntent(pendingIntent)
            .setOngoing(true)
            .build();
    startForeground(MY_SERVICE_NOTIFICATION_ID, notification);
}

Finally don't forget to unregister your listener in onDestroy in case of android kill your service (which is very rare):

@Override
public void onDestroy() {
    super.onDestroy();
    // Unregister your listener
}
查看更多
登录 后发表回答