问题描述
我创建后台服务的过程是在用户登录后启动的,即使活动被“扔进垃圾箱”也应该可以工作。服务节目正在运行的服务通知,注册两个广播接收器以监视wifi和电话状态,并且应保持运行直到令牌未过期或用户注销。
一切正常,但服务被android杀死。 REALLY WORKS 的唯一解决方案是本文的说明 http://nine-faq.9folders.com/articles/37422-stop-your-huawei-smartphone-from-closing-apps-when-you-lock-the-screen 不幸的是,该解决方案不可接受,因为它依赖于用户。
C#使用Xamarin创建的代码,但是如果有人知道如何以编程方式实现解决方案,即使对于其他语言(java,kotlin),我也很乐意提供有用的建议
清单
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="com.companyname.com.bgapptest">
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="28" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
<service
android:name="com.BGAppTest.BackgroundService"
android:enabled="true"
android:exported="false"/>
</application>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGnorE_BATTERY_OPTIMIZATIONS" />
</manifest>
从活动开始服务
活动
using System;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using AlertDialog = Android.Support.V7.App.AlertDialog;
namespace com.BGAppTest
{
[Activity(Label = "@string/app_name",Theme = "@style/AppTheme.NoActionBar",MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
public static string HOPE = "nothing";
string[] perms = new string[] { "android.permission.ACCESS_FINE_LOCATION","android.permission.ACCESS_COARSE_LOCATION","android.permission.ACCESS_NETWORK_STATE","android.permission.READ_PHONE_STATE","android.permission.REQUEST_IGnorE_BATTERY_OPTIMIZATIONS"};
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;
HOPE = DateTime.Now.ToString();
StartService();
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main,menu);
return true;
}
public override bool OnoptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_settings)
{
return true;
}
return base.OnoptionsItemSelected(item);
}
private void FabOnClick(object sender,EventArgs eventArgs)
{
View view = (View)sender;
Intent service = new Intent(this,typeof(BackgroundService));
StopService(service);
}
protected void IGnoreBatteryActivity()
{
PowerManager m = GetSystemService(PowerService) as PowerManager;
Intent intent = new Intent();
if (m.IsIgnoringBatteryOptimizations(this.PackageName))
{
//intent.SetAction(Settings.ActionIgnoreBatteryOptimizationSettings);
}
else
{
intent.SetAction(Settings.ActionRequestIgnoreBatteryOptimizations);
intent.SetData(Android.Net.Uri.Parse("package:" + PackageName));
StartActivity(intent);
}
}
void StartService()
{
foreach (var p in perms)
{
if (CheckSelfPermission(p) == Permission.Denied)
{
RequestPermissions(perms,2);
return;
}
}
IGnoreBatteryActivity();
Intent service = new Intent(this,typeof(BackgroundService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
StartForegroundService(service);
else
StartService(service);
}
public override void OnRequestPermissionsResult(int requestCode,string[] permissions,[GeneratedEnum] Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode,permissions,grantResults);
if (grantResults.Any(x => x == Permission.Denied))
{
RunOnUiThread(() =>
{
new AlertDialog.Builder(this)
.SetMessage("Uprawnienia są wymagane.Chcesz nadać uprawnienia?")
.SetNegativeButton("Nie",delegate
{
this.FinishAffinity();
})
.SetPositiveButton("Tak",delegate
{
RequestPermissions(perms,requestCode);
})
.SetCancelable(false)
.Create()
.Show();
});
}
else
StartService();
}
}
}
服务
[Service(Name = "com.BGAppTest.BackgroundService")]
public class BackgroundService : Service
{
NetworkChangeReceiver networkReceiver;
PhoneCallsReceiver receiver;
const int Service_Running_Notification_ID = 937;
public bool isstarted = false;
PowerManager.WakeLock wakeLock;
public BackgroundService()
{
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent,[GeneratedEnum] StartCommandFlags flags,int startId)
{
if (isstarted)
return StartCommandResult.Sticky;
isstarted = true;
PowerManager m = GetSystemService(Context.PowerService) as PowerManager;
wakeLock = m.NewWakeLock(WakeLockFlags.Partial,"MYWeakLock");
wakeLock.Acquire();
return StartCommandResult.Sticky;
}
public override void OnCreate()
{
base.OnCreate();
RegisterForegroundService();
RegisterWifiReceiver();
RegisterPhoneReceiver();
}
public void RegisterForegroundService()
{
Notification notification = BuildNotification("Title","Text");
StartForeground(Service_Running_Notification_ID,notification);
}
Notification BuildNotification(string title,string message)
{
NotificationCompat.Builder notificationBuilder = null;
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
String NOTIFICATION_CHANNEL_ID = "com.BGAppTest";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID,"MY_Foreground",NotificationImportance.High);
notificationmanager manager = (notificationmanager)GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(chan);
notificationBuilder = new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL_ID);
}
else
notificationBuilder = new NotificationCompat.Builder(this);
return notificationBuilder
.SetSmallIcon(Resource.Drawable.ic_mtrl_chip_checked_black)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentIntent(BuildIntentToShowMainActivity())
.SetContentTitle(title)
.SetStyle(new NotificationCompat.BigTextStyle().BigText(message))
.Setongoing(true)
.Build();
}
private PendingIntent BuildIntentToShowMainActivity()
{
var intent = this.PackageManager.GetLaunchIntentForPackage(this.PackageName);
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this,intent,PendingIntentFlags.UpdateCurrent);
return pendingIntent;
}
public void RegisterWifiReceiver()
{
var networkReceiver = new NetworkChangeReceiver();
IntentFilter intentFilters = new IntentFilter();
intentFilters.AddAction("android.net.conn.CONNECTIVITY_CHANGE");
RegisterReceiver(networkReceiver,intentFilters);
}
public void RegisterPhoneReceiver()
{
var receiver = new PhoneCallsReceiver();
IntentFilter intentFilters = new IntentFilter();
intentFilters.AddAction("android.intent.action.PHONE_STATE");
RegisterReceiver(receiver,intentFilters);
}
public override IBinder OnBind(Intent intent)
{
return null;
}
#region Session checker
//Todo:przenieść tą fukcionalność do odzielnego serwisu
System.Timers.Timer timers = new System.Timers.Timer();
private void StartTokenExpiredTimer()
{
}
#endregion
public override void OnDestroy()
{
base.OnDestroy();
try
{
UnregisterReceiver(receiver);
UnregisterReceiver(networkReceiver);
}
catch { }
}
}
接收者
using Android.Content;
using Android.Widget;
namespace com.BGAppTestReceivers
{
public class NetworkChangeReceiver: broadcastReceiver
{
static NetworkChangeReceiver()
{
}
public NetworkChangeReceiver()
{
}
public override async void OnReceive(Context context,Intent intent)
{
Toast.MakeText(context,"BGNetworkChange",ToastLength.Short).Show();
}
}
public class PhoneCallsReceiver : broadcastReceiver
{
static PhoneCallsReceiver()
{
}
public PhoneCallsReceiver()
{
}
public override async void OnReceive(Context context,"BGPhoneChange",ToastLength.Short).Show();
}
}
}
更新26.09.2020
更新30.09.2020
添加android.permission.REQUEST_IGnorE_BATTERY_OPTIMIZATIONS
和ActionRequestIgnoreBatteryOptimizations
解决方法
您必须请求权限才能使您的应用免于省电和应用待机模式。
您可以首先从设置>应用>特殊应用访问>电池优化>为您的应用关闭优化。
如果它对您有用,那么您可以要求用户使用以下权限将您的应用列入白名单
android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
,
我是Floating Apps的作者,经过7年的发展,我已经看到它可以在将近11.000种不同的设备上运行。我也有posted a series of articles on background services and floating windows。
首先要说的是,没有用户交互就不可能在所有设备上实现这一目标。一些供应商对Android系统进行了太多更改,并添加了激进的内存或流程管理,有时需要手动操作。另外,有些设备根本无法解决这个问题。
有用的是在不同的进程中运行服务(在multiprocess
中使用AndroidManifest.xml
),您应该为服务添加android:stopWithTask
,以免被活动杀死-帮助一些设备。
但是,基本上,在Floating Apps中,我尝试检测具有已知问题的手机,并要求用户设置手机以使该应用程序正常工作-禁用电池优化功能,将其添加到Huawei上受保护的应用程序中,等等。问题由我们的客户支持解决(我的妻子实际上正在休产假:-))。
最后一个实例是将用户发送到:https://dontkillmyapp.com
坦率地说,对于大多数用户而言,这实际上并不是什么大问题,并且在禁用电池优化的情况下,该应用程序对于大多数用户而言效果很好。但是,对于其中的一部分,根本没有解决方案-既不是程序化的也不是手动的。通常不要尝试在所有手机上都解决此问题。