简体   繁体   中英

Not getting location updates when App is removed from background also

"I am trying to get the location in the background but it is showing this error. I am unable to get the location anyway. Notification not working. How should my doMystuff() function look like in the main activity? Can someone please provide how can I register the broadcast receiver in manifest and how to implement the code with onButton Click." UPDATE[2]: "Here I have just written the run-time permission to be accessed. Now on button click "startButton" in the doStuff function should I write startService or startForegroundService?" "MAIN ACTIVITY"

package com.example.locationrunandall;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private TextView textView;
    private Button butt1, butt2;
    public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textview);
        butt1 = findViewById(R.id.butt1);
        butt2 = findViewById(R.id.butt2);


    }



    public void startButton(View view) {
        fetchCode();
    }

    private void fetchCode() {
        if (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.ACCESS_FINE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {

            // Permission is not granted
            if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                    Manifest.permission.ACCESS_FINE_LOCATION)) {
                // Show an explanation to the user *asynchronously* -- don't block
                new AlertDialog.Builder(this)
                        .setTitle("Required Location")
                        .setMessage("You have to get this permission to access the feature.")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                ActivityCompat.requestPermissions(MainActivity.this,
                                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                        MY_PERMISSIONS_REQUEST_LOCATION);
                            }
                        })
                        .setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.dismiss();
                            }
                        }).create().show();
            } else {
                // No explanation needed; request the permission
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        MY_PERMISSIONS_REQUEST_LOCATION);

            }
        } else {
            // Permission has already been granted
            doMyStuff();
        }
    }

    private void doMyStuff() {
        Toast.makeText(this, "Working", Toast.LENGTH_SHORT).show();
        Intent intent = new Intent(this,ForeService.class);
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
            startForegroundService(intent);
        }else{
            startService(intent);
        }
    }   
    public void stopButton(View view) {
        Intent intent = new Intent(this,ForeService.class);
        stopService(intent);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            if(requestCode==MY_PERMISSIONS_REQUEST_LOCATION){
                if(grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    Intent gpsOptionsIntent = new Intent(
                            android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                    startActivity(gpsOptionsIntent);
                }else
                {
                }
            }
    }
}

You should use ForegroundService instead of Service. Call startForegroundService() when you want to start it.

Next in this new ForegroundService in onCreate method call startForeground(). With startForeground you have to pass a notification to inform the user that service is running.

Moreover I've found that some devices like HUAWEI have a feature called “power-intensive app monitor“. It kills every app that runs in the background for a long time unless user gives special permissions to it. The path to do this: Settings -> Security & privacy -> Location services -> recent location requests: YOUR APP NAME -> Battery -> uncheck Power-intensive prompt, App launch: Manage manually: check all three positions: Auto-launch, secondary launch, run in background.

I don't know is there a way to do this programmatically. I think the best way is to create a sort of help activity and explain the user what to do if application won't work.

Service code to get Location update every 10 seconds

public class LocationService extends Service implements com.google.android.gms.location.LocationListener,
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{

    private GoogleApiClient mGoogleApiClient;
    private LocationRequest mLocationRequest;

    private String mChannelId = "my_service";
    private int mNotificationId = 1;
    private NotificationCompat.Builder mNotifBuilder = new NotificationCompat.Builder(this, mChannelId);
    private Notification mNotif;
    private NotificationManager mNotificationManager;
    private boolean mRestartService = true;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void initGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initGoogleApiClient();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null && intent.getAction() != null) {
            String mAction = intent.getAction();
            if(mAction.equals("start_service")){
                startForegroundService();
            }else if(mAction.equals("stop_service")){
                stopForegroundService();
            }
        }
        return START_NOT_STICKY;
    }

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10000);
        mLocationRequest.setFastestInterval(5000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        try {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {

    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {

    }

    @Override
    public void onLocationChanged(Location location) {
        Toast.makeText(this, "Location Updated", Toast.LENGTH_SHORT).show();
        //Broadcast Location
    }

    private void createNotificationChannel(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String description = "Foreground Service Channel";
            String name = "My Background Channel";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(mChannelId, name, importance);
            channel.setDescription(description);
            mNotificationManager.createNotificationChannel(channel);
        }
    }

    private void startForegroundService() {
        //Initializing Notifications and intents
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

        createNotificationChannel();

        mNotifBuilder.setContentText("Notification Content")
                .setContentTitle("Notification Title")
                .setSmallIcon(R.drawable.ic_notify)
                .setContentIntent(pendingIntent)
                .setOngoing(true)
                .setAutoCancel(true);

        mNotif = mNotifBuilder.build();
        startForeground(mNotificationId, mNotif);
    }

    private void stopForegroundService() {
        mRestartService = false;
        if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        }
        stopForeground(true);
    }

    /*@Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Toast.makeText(this, "Task Removed", Toast.LENGTH_SHORT).show();
        if (mRestartService){
            Intent intent = new Intent(this, RestartService.class);
            intent.setAction("restart_service");
            sendBroadcast(intent);
        }
    }*/
}

Then in your Activity add a button or a toggle button to start and stop the service. You can also do it with a button in the Notification.

private void startService() {
        Intent intent = new Intent(MainActivity.this, LocationService.class);
        intent.setAction("start_service");
        startService(intent);
    }

private void stopService() {
    Intent intent = new Intent(MainActivity.this, LocationService.class);
    intent.setAction("stop_service");
    startService(intent);
}

The service should run even the app is killed. In some devices like xiaomi, OPPO, VIVO...etc it might get killed regardless of what you do in the service. In those you can try to restart the service again with a broadcast to start the service when it is killed.

public class RestartService extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(action.equals("restart_service")){
            Intent intent1 = new Intent(context, LocationService.class);
            intent.setAction("start_service");
            context.startService(intent1);
        }
    }
}

You can add it in onTaskRemoved() if the service is killed when the activity is removed from the background. You can add it in onDestroy() if it is killed sometime after the activity is removed from the background

public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        Toast.makeText(this, "Task Removed", Toast.LENGTH_SHORT).show();
        if (mRestartService){
            Intent intent = new Intent(this, RestartService.class);
            intent.setAction("restart_service");
            sendBroadcast(intent);
        }
    }

Even with this the service might be killed in some mobiles. In those cases you need to ask the user to exclude the service from battery optimizations and also allow other exceptions for it to run freely.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM