fc2ブログ

記事一覧

Androidでスリープ中でもバックグラウンド処理を定期的に実行する方法 | Xamarin.Forms


以前の記事で、Android端末においてOS起動時に自動実行するバックグラウンドサービスの作成方法をご紹介していました。今回はその続きで定期的なバックグラウンド処理を行う方法をご紹介いたします。
OS 起動時に一度実行されたバックグラウンドサービスをそのまま待機させて、ループすることにより繰り返し処理を実行すればよいと思っておりましたが、調べてみると Android にもありました。Dozeというバッテリーの消耗を防ぐためにスリープ時にはバックグラウンドサービスはKillされる機能が。
そこで定期的に実行するアプリといえばアラームアプリですが、アラームアプリで使用されている AlarmManager を使用して実行していきたいと思います。



前提条件
・Windows10 Pro 64Bit
・Visual Studio 2015 Community Update3
・Xamarin 4.3.0.795 (NuGet Xamarin.Forms 2.3.4.247)
・macOS Sierra 10.12.4 / Xcode8.3.1 / Xamarin.iOS 10.6.0.10



1.パーミッション追加

AndroidManifest.xml に以下のパーミッションを追加します。

AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />



2.ホワイトリストに登録

Android6.0 以降の端末で動作せる場合は次の記事「Dozeのホワイトリストにアプリを登録する方法」でご紹介しています内容を実装する必要があります。



3.BroadcastReceiverを修正する

以前の記事の BroadcastReceiver を継承したクラスに PowerManager(画面ロックを解除する機能)と AlarmManager によるリマインダー機能を追加します。

BootReceiver.cs
[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted,
                        "android.intent.action.QUICKBOOT_POWERON",
                        "com.htc.intent.action.QUICKBOOT_POWERON",
                        "android.intent.action.PACKAGE_INSTALL",
                        "android.intent.action.PACKAGE_ADDED",
                        Intent.ActionMyPackageReplaced })]
public class BootReceiver : BroadcastReceiver
{
    private static PowerManager.WakeLock _wakeLock = null;

    public override void OnReceive(Context context, Intent intent)
    {
        //画面ロックの取得
        PowerManager pm = (PowerManager)context.GetSystemService(Context.PowerService);
        _wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "BootReceiver");
        _wakeLock.Acquire();

//リマインダーをセット
this.SetReminder(context);

        //サービスの起動
        //以前の記事でご紹介済みなので割愛

    }

    //Android用のリマインダー
    public void SetReminder(Context context)
    {
        Intent alarmIntent = new Intent(context, typeof(BootReceiver));
        PendingIntent pendingIntent = PendingIntent.GetBroadcast(context, 0, alarmIntent, PendingIntentFlags.UpdateCurrent);
        AlarmManager alarmManager = (AlarmManager)context.GetSystemService(Context.AlarmService);

        //AlarmManagerで1時間に1回の実行を予約する
long fireTime = SystemClock.ElapsedRealtime() + 60 * 1000 * 60; //60s*1000ms*60minutes=1hour
        alarmManager.SetInexactRepeating(AlarmType.ElapsedRealtimeWakeup, fireTime, AlarmManager.IntervalHour, pendingIntent);
    }

    public static void ReleaseWakeLock()
    {
        if (_wakeLock != null)
        {
            _wakeLock.Release();
        }
    }
}


Android.App.AlarmType:
項目説明
RtcWakeup
(値0)
UTC時刻を指定する
(スリープ状態のときは電源をONにしてくれる)
Rtc
(値1)
UTC時刻を指定する
ElapsedRealtimeWakeup
(値2)
端末が起動してからの経過時間で指定する
(スリープ状態のときは電源をONにしてくれる)
ElapsedRealtime
(値3)
端末が起動してからの経過時間で指定する


※AlarmManager は端末を再起動すると予約されているスケジュールは無効となってしましいますが、Intent.ActionBootCompleted により再起動後に再度スケジューリングされるようにしてあります。
※SetInexactRepeating メソッドは Doze モードの影響を受け、発火しない場合もあります。ほかのメソッドについては次の記事「AlarmManagerで指定した時間に実行する方法」をご参考ください。

また、Android バージョン毎に AlarmManager で有効な設定が異なりますので、以下のURL(外部サイト)をご参考頂いた方が良いかもしれません。
http://blog.integrityworks.co.jp/2016/06/21/post-1003/
https://www.gaprot.jp/pickup/tips/android-m-summary



4.バックグラウンドサービスの修正

以前の記事の BackgroundService.cs では Service クラスを継承していましたが、それを変更して IntentService を継承させます。

BackgroundService.cs
[Service(Name = "com.CompanyName.BootReceiverTest.BackgroundService", Exported = false, Process = ":TestProcess")]
public class BackgroundService : IntentService
{
    public override IBinder OnBind(Intent intent)
    {
        return null;
    }
//ServicのOnStartCommandはコメントアウトする(バックグラウンドサービスが再起動しないように)
    //public override StartCommandResult OnStartCommand(Android.Content.Intent intent, StartCommandFlags flags, int startId)
//{
// Thread t = new Thread(() =>
// {
// //Xamarinを有効化
// var bundle = new Bundle();
// global::Xamarin.Forms.Forms.Init(this, bundle);

// while (true)
// {
// System.Threading.Thread.Sleep(10000);
// NotificationService nService = new NotificationService(this);
// nService.On("BackgroundService", "BackgroundService.OnStartCommand");
// }
// });
// t.Start();
// return StartCommandResult.Sticky;
//}

//OnHandleIntentを追加する
private bool _endThread = false;
protected override void OnHandleIntent(Intent intent)
{
//Xamarinを有効化
var bundle = new Bundle();
global::Xamarin.Forms.Forms.Init(this, bundle);

_endThread = false;

while (true)
{
System.Threading.Thread.Sleep(10000);
NotificationService nService = new NotificationService(this);
nService.On("BackgroundService", "BackgroundService.OnStartCommand");
break;
}
BootReceiver.ReleaseWakeLock();

_endThread = true;
}

public void StartBackgroundService()
{
System.Diagnostics.Debug.WriteLine("●StartBackgroundService");

//サービスを起動する
base.StartService(this.GetServiceIntent());
}

public void StopBackgroundService()
{
System.Diagnostics.Debug.WriteLine("●StopBackgroundService");

//サービスを停止する
base.StopService(this.GetServiceIntent());
}

private Intent GetServiceIntent()
{
System.Diagnostics.Debug.WriteLine("●BackgroundService.GetServiceIntent");

//サービスを起動する
Intent serviceIntent = new Intent(this, typeof(BackgroundService));
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop &&
Build.VERSION.SdkInt <= BuildVersionCodes.LollipopMr1)
{
//Android5 Lollipop対応
string packageName = this.PackageManager.GetPackageInfo(this.PackageName, 0).PackageName;
serviceIntent.SetPackage(packageName);
serviceIntent.SetClassName(this, packageName + ".BackgroundService");
}
else
{
serviceIntent.AddFlags(ActivityFlags.NewTask);
}
return serviceIntent;
}

public override void OnDestroy()
{
base.OnDestroy();

//処理が中断された場合
if (!_endThread)
{
//Killされてもサービスを再起動する。
this.StartBackgroundService();
}
}
}


※Android8.0 Oreo 以降では5秒以内に StartForeground をコールする必要があります。以前の記事「AndroidでOS起動時に自動実行するバックグラウンドサービスの作成方法」でご紹介しておりますのでご参考ください。



5.トラブルシューティング

AlarmManager が正しく動作しない場合は以下を確認してください。

(1)正しく予約されているかどうかを確認する方法は adb コマンドプロンプトから以下のコマンドを入力します。
adb shell dumpsys alarm

(2)AndroidManifest.xml の minSdkVersion を確認してください。
ターゲット Android バージョンにより動作するかどうかも変化するようです。




最後までお読みいただきありがとうございます。
当ブログの内容をまとめた Xamarin逆引きメニュー は以下のURLからご覧になれます。
http://itblogdsi.blog.fc2.com/blog-entry-81.html


関連記事

コメント

コメントの投稿

※名前とタイトルが入力されていないコメントでは他のコメントとの区別ができません。

 入力されていないコメントには返信しませんのであらかじめご了承くださいませ。

※ニックネームでも良いので必ずご入力ください。

    

※必ずご入力ください。

    
    

※必ずご入力ください。

※技術的な質問には環境やエラーについて正確かつ詳細にお教えください。

・正確なエラーの内容

・Windowsのバージョン番号

・Visual Studioのバージョン

・機器の型番

・アプリやソフトのバージョン

    

カテゴリ別記事一覧

広告

プロフィール

石河 純


著者名 :石河 純
自己紹介:素人上がりのIT技術者。趣味は卓球・車・ボウリング

IT関連の知識はざっくりとこんな感じです。
【OS関連】
WindowsServer: 2012/2008R2/2003/2000/NT4
Windows: 10/8/7/XP/2000/me/NT4/98
Linux: CentOS RedHatLinux9
Mac: macOS Catalina 10.15 / Mojave 10.14 / High Sierra 10.13 / Sierra 10.12 / OSX Lion 10.7.5 / OSX Snow Leopard 10.6.8
【言語】
VB.net ASP.NET C#.net Java VBA
Xamarin.Forms
【データベース】
Oracle 10g/9i
SQLServer 2016/2008R2/2005/2000
SQLAnywhere 16/11/8
【BI/レポートツール】
Cognos ReportNet (IBM)
Microsoft PowerBI
ActiveReport (GrapeCity)
CrystalReport
【OCX関連】
GrapeCity InputMan SPREAD MultiRow GridView
【ネットワーク関連】
CCNP シスコ技術者認定
Cisco Catalyst シリーズ
Yamaha RTXシリーズ
FireWall関連
【WEB関連】
SEO SEM CSS jQuery IIS6/7 apache2

休みの日は卓球をやっています。
現在、卓球用品通販ショップは休業中です。