The reason why startActivity in the background of Service does not respond

The secret of why the page in the application does not respond when startActivity is enabled in the background of Service

    • question
    • analyze
    • Solution

Question

When I start a service in the A application and open a thread in the service, when we manually slide to kill the APP, the service is still alive. We want to restart the APP application. We may try the following code, and sometimes it can be pulled up, Sometimes nothing happens, but judging from the logs, the service is running normally and the heartbeat logs are printed normally.

public class StepService extends Service {
    private static final String TAG = "StepService";

    public static boolean isRemoveTask = false;

    private Handler handler = new Handler(Looper.getMainLooper());

    private SvrBinder mBinder;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG, "<Stub> StepService Connected.");
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
// Toast.makeText(RemoteService.this,"Link is broken, restart LocalService",Toast.LENGTH_LONG).show();
            Log.e(TAG, "<Stub> StepService Disconnected.");
 // startService(new Intent(StepService.this, GuardService.class));
// bindService(new Intent(StepService.this, GuardService.class), this, Context.BIND_IMPORTANT);
            Log.e(TAG, "<Stub> StepService Disconnected end.");
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "<Stub> StepService onCreate.");

        String CHANNEL_ONE_ID = "StepServiceId";
        String CHANNEL_ONE_NAME = "StepServiceChannel";
        String CHANNEL_ID = "StepServiceChannelId";

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            if (manager == null)
                return;
            manager.createNotificationChannel(channel);
            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setAutoCancel(true)
                    .setCategory(Notification.CATEGORY_SERVICE)
                    .setOngoing(true)
                    .setPriority(NotificationManager.IMPORTANCE_LOW)
                    .build();
        }

        isHeartBeatThread = true;
        if (heartBeatThread == null || !heartBeatThread.isAlive()){
            heartBeatThread = new HeartBeatThread();
            heartBeatThread.start();
        }

// startService(new Intent(StepService.this, GuardService.class));
// bindService(new Intent(StepService.this, GuardService.class),connection,Context.BIND_IMPORTANT);

        Log.e(TAG, "<Stub> StepService isRemoveTask:" + isRemoveTask);

        if (isRemoveTask){
/* Intent intent = new Intent(getBaseContext(), MainActivity.class);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            getApplicationContext().startActivity(intent);*/
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
// Toast.makeText(this,"RemoteService Start",Toast.LENGTH_LONG).show();
        Log.e(TAG, "<Stub> StepService onStartCommand.");
        return START_STICKY;
    }



    @Override
    public IBinder onBind(Intent intent) {
        mBinder = new SvrBinder();
        return mBinder;
    }

    private HeartBeatThread heartBeatThread;
    private boolean isHeartBeatThread;
    class HeartBeatThread extends Thread{

        @Override
        public void run() {
            super.run();
            while (isHeartBeatThread){
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e(TAG, "HeartBeatThread on on on");
            }
        }
    }

    /**
     * Class for client binders. Since we know that this service always runs in the same process as its client,
     * So we don't need to deal with IPC.
     */
    public class SvrBinder extends IStepServiceAidlInterface.Stub {
        @Override
        public void keepAlive() throws RemoteException {
            Log.e(TAG, "<Stub> StepService keepAlive.");
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isHeartBeatThread = false;
        unbindService(connection);
        Log.e(TAG, "<Stub> StepService onDestroy.");
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);

        isRemoveTask = true;
        Log.e(TAG, "<Stub> StepService onTaskRemoved.");

/* Intent intent = new Intent(getBaseContext(), MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getApplicationContext().startActivity(intent);*/


/* Intent intent = getBaseContext().getPackageManager()
                .getLaunchIntentForPackage(getBaseContext().getPackageName());
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent restartIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
        AlarmManager mgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, restartIntent); // Restart the application after 1 second*/


        Intent k = getBaseContext().getPackageManager()
                .getLaunchIntentForPackage(getBaseContext().getPackageName());
        k.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        k.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(k);

  // stopForeground(true);
 // unbindService(connection);
 // stopSelf();
    }

The code in the service is simple.

Analysis

Later, I found out that the reason was because Android 10 and later did not allow startActivity in the background. As shown below

Solution

Add permissions

 <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

This permission requires the user to actively enable it.

 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this)) {
                startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())));
            }
        } else {
            Toast.makeText(getApplicationContext(), "API < " + android.os.Build.VERSION_CODES.M, Toast.LENGTH_SHORT).show();
        }

Try again. After killing the program, the APP main page starts up immediately, which is a perfect solution.