programing tip

알림을 표시하지 않고 Foreground ()를 시작하는 방법은 무엇입니까?

itbloger 2020. 10. 31. 09:25
반응형

알림을 표시하지 않고 Foreground ()를 시작하는 방법은 무엇입니까?


서비스를 만들고 포 그라운드에서 실행하고 싶습니다.

대부분의 예제 코드에는 알림이 있습니다. 하지만 알림을 표시하고 싶지 않습니다. 가능합니까?

몇 가지 예를 들어 주시겠습니까? 대안이 있습니까?

내 앱 서비스가 미디어 플레이어를 수행하고 있습니다. 앱을 제외하고 시스템이 내 서비스를 죽이지 않게 만드는 방법 (예 : 버튼으로 음악 일시 중지 또는 중지).


안드로이드 플랫폼의 보안 기능으로, 당신은 할 수없는 , 아래에 어떤 상황도 통지를하지 않고 foregrounded 서비스가있다. 이는 포 그라운드 서비스가 더 많은 양의 리소스를 소비하고 백그라운드 서비스와 다른 스케줄링 제약 (즉, 빠르게 종료되지 않음)이 적용되고 사용자가 배터리를 먹는 원인을 알아야하기 때문입니다. 그러니 이러지 마세요 .

그러나, 그것은 이다 , 즉는 "가짜"알림을 가질 수 있습니다, 당신은 투명 알림 아이콘 (IIRC) 할 수 있습니다. 이것은 사용자에게 극도로 불명예하며 배터리를 죽여 악성 코드를 생성하는 것 외에는 할 이유가 없습니다.


업데이트 : 이것은 Android 7.1에서 "수정"되었습니다. https://code.google.com/p/android/issues/detail?id=213309

4.3 업데이트 이후, 그것은 기본적으로의 불가능 과 함께 서비스를 시작 startForeground()알림을 표시하지 않고.

그러나 공식 API를 사용하여 아이콘을 숨길 수 있습니다. 투명 아이콘이 필요하지 않습니다. ( NotificationCompat이전 버전을 지원하는 데 사용 )

NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setPriority(Notification.PRIORITY_MIN);

알림 자체가 여전히 있어야한다는 사실과 평화를 이루었지만 여전히 알림을 숨기고 싶은 사람을 위해 해결 방법을 찾았을 수도 있습니다.

  1. startForeground()알림과 모든 것으로 가짜 서비스를 시작하십시오 .
  2. startForeground()(동일한 알림 ID)를 사용하여 실행하려는 실제 서비스를 시작하십시오.
  3. 첫 번째 (가짜) 서비스를 중지합니다 ( stopSelf()및 onDestroy call에서 호출 할 수 있음 stopForeground(true)).

Voilà! 알림이 전혀없고 두 번째 서비스가 계속 실행됩니다.


이는 Android 7.1에서 더 이상 작동하지 않으며 Google Play의 개발자 정책을 위반할 수 있습니다 .

대신 사용자가 서비스 알림을 차단하도록하는 것이 좋습니다 .


Lior Iluz 의 답변대한 기술 구현은 다음과 같습니다 .

암호

ForegroundService.java

public class ForegroundService extends Service {

    static ForegroundService instance;

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

        instance = this;

        if (startService(new Intent(this, ForegroundEnablingService.class)) == null)
            throw new RuntimeException("Couldn't find " + ForegroundEnablingService.class.getSimpleName());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        instance = null;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

ForegroundEnablingService.java

public class ForegroundEnablingService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (ForegroundService.instance == null)
            throw new RuntimeException(ForegroundService.class.getSimpleName() + " not running");

        //Set both services to foreground using the same notification id, resulting in just one notification
        startForeground(ForegroundService.instance);
        startForeground(this);

        //Cancel this service's notification, resulting in zero notifications
        stopForeground(true);

        //Stop this service so we don't waste RAM.
        //Must only be called *after* doing the work or the notification won't be hidden.
        stopSelf();

        return START_NOT_STICKY;
    }

    private static final int NOTIFICATION_ID = 10;

    private static void startForeground(Service service) {
        Notification notification = new Notification.Builder(service).getNotification();
        service.startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

AndroidManifest.xml

<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />

적합성

테스트 및 작업 :

  • 공식 에뮬레이터
    • 4.0.2
    • 4.1.2
    • 4.2.2
    • 4.3.1
    • 4.4.2
    • 5.0.2
    • 5.1.1
    • 6.0
    • 7.0
  • Sony Xperia M
    • 4.1.2
    • 4.3
  • 삼성 갤럭시 ?
    • 4.4.2
    • 5.X
  • Genymotion
    • 5.0
    • 6.0
  • CyanogenMod
    • 5.1.1

Android 7.1에서 더 이상 작동하지 않습니다.


이것을 사용할 수 있습니다 (@Kristopher Micinski가 제안한대로) :

Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );

최신 정보:

Android KitKat + 릴리스에서는 더 이상 허용되지 않습니다. 그리고 이것은 @Kristopher Micinski가 언급 한대로 백그라운드 작업을 사용자에게 표시하는 Android의 디자인 원칙을 다소 위반한다는 점을 명심하십시오.


알림 ID를 0으로 설정하기 만하면됩니다.

// field for notification ID
private static final int NOTIF_ID = 0;

    ...
    startForeground(NOTIF_ID, mBuilder.build());
    NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    mNotificationManager.cancel(NOTIF_ID);
    ...

얻을 수있는 이점은 Service높은 메모리 압력이없는 한 Android 시스템에 의해 파괴되지 않고 높은 우선 순위로 실행될 수 있다는 것입니다.

편집하다

Pre-Honeycomb 및 Android 4.4 이상에서 작동하려면 NotificationCompat.Builder.NET Framework 대신 지원 라이브러리 v7에서 제공 하는 것을 사용해야 Notification.Builder합니다.


Notification 생성자에 대한 icon 매개 변수를 0으로 설정 한 다음 결과 알림을 startForeground ()에 전달했습니다. 로그에 오류가없고 알림이 표시되지 않습니다. 하지만 서비스가 성공적으로 포 그라운드되었는지 여부는 모르겠습니다. 확인할 방법이 있습니까?

편집 됨 : dumpsys로 확인했으며 실제로 서비스는 내 2.3 시스템에서 포 그라운드됩니다. 아직 다른 OS 버전으로 확인하지 않았습니다.


한 가지 해결 방법이 있습니다. 아이콘을 설정하지 않고 알림을 생성하면 알림이 표시되지 않습니다. 어떻게 작동하는지 모르지만 작동합니다. :)

    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("Title")
            .setTicker("Title")
            .setContentText("App running")
            //.setSmallIcon(R.drawable.picture)
            .build();
    startForeground(101,  notification);

버전 4.3 (18) 이상은 서비스 알림을 숨길 수 없지만 아이콘을 비활성화 할 수 있으며 버전 4.3 (18) 이하는 알림을 숨길 수 있습니다.

Notification noti = new Notification();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
    noti.priority = Notification.PRIORITY_MIN;
}
startForeground(R.string.app_name, noti);

Android 8.0에서 알림 채널을 사용하지 않아도 가능하다는 것을 알았습니다.

public class BootCompletedIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                Intent notificationIntent = new Intent(context, BluetoothService.class);    
                context.startForegroundService(notificationIntent);

            } else {
                //...
            }

        }
    }
}

And in BluetoothService.class:

 @Override
    public void onCreate(){    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            Intent notificationIntent = new Intent(this, BluetoothService.class);

            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);

            Notification notification = new Notification.Builder(this)
                    .setContentTitle("Title")
                    .setContentText("App is running")
                    .setSmallIcon(R.drawable.notif)
                    .setContentIntent(pendingIntent)
                    .setTicker("Title")
                    .setPriority(Notification.PRIORITY_DEFAULT)
                    .build();

            startForeground(15, notification);

        }

    }

A persistent notification is not shown, however you will see the Android 'x apps are running in the background' notification.


Block the foreground service notification

Android 7.1+ can't be exploited to hide the notification. Instead, get the user to block it.

Android 4.1 - 7.1

The only way is to block all notifications from your app:

  1. Send user to app's details screen:

    Uri uri = Uri.fromParts("package", getPackageName(), null);
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
    startActivity(intent);
    
  2. Have user block app's notifications

Note this also blocks your app's toasts.

Android 8

It's not worth blocking the notification on Android O because the OS will just replace it with a "running in the background" or "using battery" notification.

Android 9

Use a Notification Channel to block just the service notification.

  1. Assign service notification to notification channel
  2. Send user to notification channel's settings

    Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
        .putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
        .putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
    startActivity(intent);
    
  3. Have user block channel's notifications


You can hide notification on Android 9 by using custom layout with layout_height = "0dp"

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.custom_notif);
builder.setContent(remoteViews);
builder.setPriority(NotificationCompat.PRIORITY_LOW);
builder.setVisibility(Notification.VISIBILITY_SECRET);

custom_notif.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="0dp">
</LinearLayout>

Tested on Pixel 1, android 9. This solution doesn't work on Android 8 or less


Here is a way to make your app 's oom_adj to 1 (Tested in ANDROID 6.0 SDK emulator). Add a temporary service, In your main service call startForgroundService(NOTIFICATION_ID, notificion). And then start the temporary service call startForgroundService(NOTIFICATION_ID, notificion) with same notification id again, after a while in the temporary service call stopForgroundService(true) to dismiss the onging ontification.


You can also declare your application as persistent.

<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
    android:theme="@style/Theme"
    *android:persistent="true"* >
</application>

This essentially sets your app at a higher memory priority, decreasing the probability of it being killed.


I developed a simple media player couple of months ago. So what I believe is if you are doing something like:

Intent i = new Intent(this, someServiceclass.class);

startService(i);

Then then system shouldn't be able to kill your service.

reference: read the paragraph which discuss when system stops the service

참고URL : https://stackoverflow.com/questions/10962418/how-to-startforeground-without-showing-notification

반응형