サービスを作成してフォアグラウンドで実行したいと思います。
ほとんどのサンプルコードには通知があります。しかし、私は通知を表示したくありません。それは可能ですか?
いくつか例を挙げていただけますか?代替手段はありますか?
私のアプリサービスはメディアプレーヤーをやっています。アプリがサービス自体を強制終了する以外に、システムがサービスを強制終了しないようにする方法(ボタンで音楽を一時停止または停止するなど)。
サービスを作成してフォアグラウンドで実行したいと思います。
ほとんどのサンプルコードには通知があります。しかし、私は通知を表示したくありません。それは可能ですか?
いくつか例を挙げていただけますか?代替手段はありますか?
私のアプリサービスはメディアプレーヤーをやっています。アプリがサービス自体を強制終了する以外に、システムがサービスを強制終了しないようにする方法(ボタンで音楽を一時停止または停止するなど)。
回答:
Androidプラットフォームのセキュリティ機能として、あなたがすることはできません、下のいずれかの状況、また通知をせずに前景化サービスを提供しています。これは、フォアグラウンドサービスが大量のリソースを消費し、バックグラウンドサービスとは異なるスケジューリング制約(つまり、すぐに強制終了されない)の影響を受け、ユーザーはバッテリーを消費している可能性があるものを知る必要があるためです。だから、これをしないでください。
ただし、「偽の」通知を受け取ることは可能です。つまり、透明な通知アイコン(iirc)を作成できます。これはユーザーにとって非常に不誠実であり、ユーザーのバッテリーを殺してマルウェアを作成する以外に、それを行う理由はありません。
更新:これはAndroid7.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);
私は通知自体がまだそこにある必要があるという事実で和解しましたが、それでもそれを隠したい人のために、私はそのための回避策も見つけたかもしれません:
startForeground()
通知とすべてで偽のサービスを開始します。startForeground()
(同じ通知ID)を使用して、実行する実際のサービスを開始しますstopSelf()
onDestroy呼び出しで呼び出すことができますstopForeground(true)
)。Voilà!通知はまったくなく、2番目のサービスは実行を続けます。
これはAndroid7.1以降では機能しなくなり、GooglePlayのデベロッパーポリシーに違反する可能性があります。
代わりに、ユーザーにサービス通知をブロックしてもらいます。
これが、LiorIluzによる回答のテクニックの私の実装です。
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;
}
}
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;
}
}
<service android:name=".ForegroundEnablingService" />
<service android:name=".ForegroundService" />
テストおよび作業中:
Android7.1以降は機能しなくなりました。
ForegroundService
あなたはこれを使うことができます(@KristopherMicinskiによって提案されたように):
Notification note = new Notification( 0, null, System.currentTimeMillis() );
note.flags |= Notification.FLAG_NO_CLEAR;
startForeground( 42, note );
更新:
これは、Android KitKat +リリースでは許可されなくなったことに注意してください。また、@ Kristopher Micinskiが述べたように、これはAndroidの設計原則に多かれ少なかれ違反しており、バックグラウンド操作がユーザーに表示されることに注意してください。
警告:この回答は機能しているように見えますが、実際には、サービスがフォアグラウンドサービスになるのを黙って妨げています。
元の答え:
通知のIDをゼロに設定するだけです。
// 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およびAndroid4.4以降で動作させるNotificationCompat.Builder
には、の代わりにSupport Libraryv7が提供するものを使用してくださいNotification.Builder
。
Notification.Builder
Support Libraryv4NotificationCompat.Builder
とSupportLibraryv7を試してみましたNotificationCompat.Builder
。通知ドロワーまたはステータスバーに通知が表示されませんでしたが、とを使用getRunningServices()
して確認したところ、サービスがフォアグラウンドモードで実行されていませんでしたdumpsys
。
adb shell dumpsys activity services
確認に使用します。
あなたは上の通知非表示にすることができますアンドロイド9+を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>
Pixel 1、Android 9でテスト済み。このソリューションは、Android8以下では機能しません。
更新:これはAndroid4.3以降では機能しなくなりました
1つの回避策があります。アイコンを設定せずに通知を作成してみてください。通知が表示されません。それがどのように機能するかはわかりませんが、それはします:)
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Title")
.setTicker("Title")
.setContentText("App running")
//.setSmallIcon(R.drawable.picture)
.build();
startForeground(101, notification);
(YourServiceName) is running
アイコンを指定しない場合、Androidは独自の通知ではなく通知を表示します。CommonsWareによると、これは、Android 4.3以降の場合されています:commonsware.com/blog/2013/07/30/...
更新:これはAndroid4.3以降では機能しなくなりました
Notificationのコンストラクターのiconパラメーターをゼロに設定し、結果の通知をstartForeground()に渡しました。ログにエラーはなく、通知も表示されません。ただし、サービスが正常にフォアグラウンドされたかどうかはわかりません。確認する方法はありますか?
編集:dumpsysで確認しました。実際、サービスは私の2.3システムでフォアグラウンドになっています。他のOSバージョンではまだ確認していません。
Notification()
コンストラクターを呼び出すだけです。
ここでのほとんどの回答は、機能しないか、フォアグラウンドサービスを中断するか、GooglePlayポリシーに違反しています。
通知を確実かつ安全に非表示にする唯一の方法は、ユーザーに通知をブロックさせることです。
唯一の方法は、アプリからのすべての通知をブロックすることです。
ユーザーをアプリの詳細画面に送信します。
Uri uri = Uri.fromParts("package", getPackageName(), null);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData(uri);
startActivity(intent);
ユーザーにアプリの通知をブロックさせる
これにより、アプリのトーストもブロックされることに注意してください。
OSは通知を「バックグラウンドで実行中」または「バッテリー使用中」の通知に置き換えるだけなので、AndroidOで通知をブロックする価値はありません。
通知チャネルを使用して、他の通知に影響を与えることなくサービス通知をブロックします。
ユーザーを通知チャネルの設定に送信します
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName())
.putExtra(Settings.EXTRA_CHANNEL_ID, myNotificationChannel.getId());
startActivity(intent);
ユーザーにチャンネルの通知をブロックさせる
バージョン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 {
//...
}
}
}
}
そして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);
}
}
永続的な通知は表示されませんが、Androidの「xアプリはバックグラウンドで実行されています」という通知が表示されます。
apps are running in the background
通知はアプリ固有の通知よりもさらに悪いと思うので、このアプローチが価値があるかどうかはわかりません。
更新:これはAndroid7.1以降では機能しなくなりました
アプリのoom_adjを1にする方法は次のとおりです(ANDROID 6.0 SDKエミュレーターでテスト済み)。一時的なサービスを追加しますstartForgroundService(NOTIFICATION_ID, notificion)
。メインのサービスコールで。次にstartForgroundService(NOTIFICATION_ID, notificion)
、同じ通知IDで一時サービス呼び出しを再開します。しばらくすると、一時サービス呼び出しstopForgroundService(true)を実行して、オンになっているオンティフィケーションを閉じます。
アプリケーションを永続として宣言することもできます。
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/Theme"
*android:persistent="true"* >
</application>
これにより、基本的にアプリのメモリ優先度が高くなり、アプリが強制終了される可能性が低くなります。
私は数ヶ月前にシンプルなメディアプレーヤーを開発しました。だから私が信じているのは、あなたが次のようなことをしている場合です:
Intent i = new Intent(this, someServiceclass.class);
startService(i);
そうすれば、システムはあなたのサービスを殺すことができないはずです。