web-dev-qa-db-ja.com

サービスを使用してバックグラウンドを実行し、通知を作成する

ボタンがクリックされたときにアプリがサービスを開始し、サービスがバックグラウンドで実行されて特定の時刻に通知が表示されるようにします。これを行うには、次のコードがあります。しかし、それは私が理解できないエラーを示しています。私はAndroidが初めてで、これがサービスを使用する最初のアプリです。任意の助けをいただければ幸いです。前もって感謝します。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
    package="com.example.newtrial"
    Android:versionCode="1"
    Android:versionName="1.0" >

    <uses-sdk
        Android:minSdkVersion="8"
        Android:targetSdkVersion="18" />

    <application
        Android:allowBackup="true"
        Android:icon="@drawable/ic_launcher"
        Android:label="@string/app_name"
        Android:theme="@style/AppTheme" >
        <activity
            Android:name="com.example.newtrial.CreateNotificationActiviy"
            Android:label="@string/app_name" >
            <intent-filter>
                <action Android:name="Android.intent.action.MAIN" />

                <category Android:name="Android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            Android:name="com.example.newtrial.ResultActivity"
            Android:label="@string/title_activity_result" >

        </activity>

        <service Android:enabled="true" Android:name=".UpdaterServiceManager" />

    </application>

</manifest>

CreateNotificationActiviy.Java

package com.example.newtrial;

import Android.os.Bundle;
import Android.app.Activity;
import Android.app.Notification;
import Android.app.NotificationManager;
import Android.app.PendingIntent;
import Android.content.Intent;
import Android.view.Menu;
import Android.view.View;
import Android.view.View.OnClickListener;
import Android.widget.Button;

public class CreateNotificationActiviy extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.create_notification_activiy);

        Button b=(Button)findViewById(R.id.button1);
        b.setOnClickListener(new OnClickListener(){

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub      
                startService(new Intent(CreateNotificationActiviy.this, UpdaterServiceManager.class));
            }

        });

    }

    public void createNotification(View view) {
        // Prepare intent which is triggered if the
        // notification is selected
        Intent intent = new Intent(this, ResultActivity.class);
        PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

        // Build notification
        // Actions are just fake
        Notification noti = new Notification.Builder(this)
            .setContentTitle("Notification Title")
            .setContentText("Click here to read").setSmallIcon(R.drawable.ic_launcher)
            .setContentIntent(pIntent)
            .build();
        NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        // hide the notification after its selected
        noti.flags |= Notification.FLAG_AUTO_CANCEL;

        notificationManager.notify(0, noti);

      } 

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.create_notification_activiy, menu);
        return true;
    }

}

UpdaterServiceManager.Java

package com.example.newtrial;

import Java.util.Calendar;
import Java.util.Timer;
import Java.util.TimerTask;

import Android.app.AlertDialog;
import Android.app.Notification;
import Android.app.NotificationManager;
import Android.app.PendingIntent;
import Android.app.Service;
import Android.content.Context;
import Android.content.Intent;
import Android.os.IBinder;
import Android.util.Log;
import Android.view.View;

public class UpdaterServiceManager extends Service {

    private final int UPDATE_INTERVAL = 60 * 1000;
    private Timer timer = new Timer();
    private static final int NOTIFICATION_EX = 1;
    private NotificationManager notificationManager;
    CreateNotificationActiviy not;

    public UpdaterServiceManager() {
        not=new CreateNotificationActiviy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onCreate() {
        // code to execute when the service is first created
        super.onCreate();
        Log.i("MyService", "Service Started.");
        showNotification();
    }

    public void showNotification()
    {
        final Calendar cld = Calendar.getInstance();

        int time = cld.get(Calendar.HOUR_OF_DAY);
        if(time>12)
        {
                  not.createNotification(null); 

        }
        else
        {
            AlertDialog.Builder alert=new AlertDialog.Builder(this);
            alert.setMessage("Not yet");
            alert.setTitle("Error");
            alert.setPositiveButton("OK", null);
            alert.create().show();
        }
    }

    @Override
    public void onDestroy() {
        if (timer != null) {
            timer.cancel();
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startid) 
    {
        return START_STICKY;
    }

    private void stopService() {
        if (timer != null) timer.cancel();
    }

}

ResultActivity.Java

package com.example.newtrial;

import Android.os.Bundle;
import Android.app.Activity;
import Android.view.Menu;
import Android.widget.TextView;

public class ResultActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_result);
        TextView tv=(TextView)findViewById(R.id.textView1);
        tv.setText("After notification is clicked" );
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.result, menu);
        return true;
    }

}

Logcat

12-10 12:14:04.286: I/Process(872): Sending signal. PID: 872 SIG: 9
12-10 12:14:11.774: I/MyService(893): Service Started.
12-10 12:14:12.094: D/AndroidRuntime(893): Shutting down VM
12-10 12:14:12.094: W/dalvikvm(893): threadid=1: thread exiting with uncaught exception (group=0x414c4700)
12-10 12:14:12.124: E/AndroidRuntime(893): FATAL EXCEPTION: main
12-10 12:14:12.124: E/AndroidRuntime(893): Java.lang.RuntimeException: Unable to create service com.example.newtrial.UpdaterServiceManager: Android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.app.ActivityThread.handleCreateService(ActivityThread.Java:2587)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.app.ActivityThread.access$1600(ActivityThread.Java:141)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1338)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.os.Handler.dispatchMessage(Handler.Java:99)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.os.Looper.loop(Looper.Java:137)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.app.ActivityThread.main(ActivityThread.Java:5103)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Java.lang.reflect.Method.invokeNative(Native Method)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Java.lang.reflect.Method.invoke(Method.Java:525)
12-10 12:14:12.124: E/AndroidRuntime(893):  at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:737)
12-10 12:14:12.124: E/AndroidRuntime(893):  at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:553)
12-10 12:14:12.124: E/AndroidRuntime(893):  at dalvik.system.NativeStart.main(Native Method)
12-10 12:14:12.124: E/AndroidRuntime(893): Caused by: Android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.view.ViewRootImpl.setView(ViewRootImpl.Java:563)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.view.WindowManagerGlobal.addView(WindowManagerGlobal.Java:269)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.view.WindowManagerImpl.addView(WindowManagerImpl.Java:69)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.app.Dialog.show(Dialog.Java:281)
12-10 12:14:12.124: E/AndroidRuntime(893):  at com.example.newtrial.UpdaterServiceManager.showNotification(UpdaterServiceManager.Java:65)
12-10 12:14:12.124: E/AndroidRuntime(893):  at com.example.newtrial.UpdaterServiceManager.onCreate(UpdaterServiceManager.Java:41)
12-10 12:14:12.124: E/AndroidRuntime(893):  at Android.app.ActivityThread.handleCreateService(ActivityThread.Java:2577)
12-10 12:14:12.124: E/AndroidRuntime(893):  ... 10 more
44
user2648852

質問は比較的古いですが、この投稿が他の人にも関連することを願っています。

TL; DR:AlarmManagerを使用してタスクをスケジュールし、IntentServiceを使用します。サンプルコードを参照してください here ;

このテストアプリケーション(および指示)の目的:

2時間ごとに通知を送信するシンプルなhelloworldアプリ。通知をクリックすると、アプリでセカンダリアクティビティが開きます。通知トラックを削除します。

いつ使うべきか:

スケジュールに基づいていくつかのタスクを実行する必要がある場合。私の場合:1日に1回、サーバーから新しいコンテンツを取得し、取得したコンテンツに基づいて通知を作成し、ユーザーに表示したい。

対処方法:

  1. 最初に、notification-serviceを開始するMainActivityと、notificationをクリックして開始されるNotificationActivityの2つのアクティビティを作成します。

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:padding="16dp">
        <Button
            Android:id="@+id/sendNotifications"
            Android:onClick="onSendNotificationsButtonClick"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:text="Start Sending Notifications Every 2 Hours!" />
    </RelativeLayout>
    

    MainActivity.Java

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void onSendNotificationsButtonClick(View view) {
            NotificationEventReceiver.setupAlarm(getApplicationContext());
        }   
    }
    

    notificationActivityは、思いつくランダムなアクティビティです。 NB!両方のアクティビティをAndroidManifestに追加することを忘れないでください。

  2. 次に、 WakefulBroadcastReceiver ブロードキャストレシーバーを作成しましょう。上記のコードでNotificationEventReceiverを呼び出しました。

    ここでは、AlarmManagerを2時間ごとに(または他の頻度で)起動するようにPendingIntentを設定し、onReceive()メソッドでこのインテントの処理アクションを指定します。この例では-wakefullyIntentServiceを開始します。これは後の手順で指定します。このIntentServiceは、通知を生成します。

    また、このレシーバーには、後で使用するPendintIntentsの作成などのヘルパーメソッドが含まれます。

    NB1!WakefulBroadcastReceiver を使用しているため、追加の許可をマニフェストに追加する必要があります:<uses-permission Android:name="Android.permission.WAKE_LOCK" />

    NB2!確実にしたいので、IntentServiceの操作中にデバイスがスリープ状態に戻らないように、ウェイクフルバージョンのブロードキャストレシーバーを使用します。ハローワールドでは、それはそれほど重要ではありません(このサービスで長時間実行される操作はありませんが、この操作中にサーバーから比較的大きなファイルを取得する必要がある場合を想像してください)。 Device Awake こちら の詳細をご覧ください。

    NotificationEventReceiver.Java

    public class NotificationEventReceiver extends WakefulBroadcastReceiver {
    
        private static final String ACTION_START_NOTIFICATION_SERVICE = "ACTION_START_NOTIFICATION_SERVICE";
        private static final String ACTION_DELETE_NOTIFICATION = "ACTION_DELETE_NOTIFICATION";
        private static final int NOTIFICATIONS_INTERVAL_IN_HOURS = 2;
    
        public static void setupAlarm(Context context) {
            AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            PendingIntent alarmIntent = getStartPendingIntent(context);
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                    getTriggerAt(new Date()),
                    NOTIFICATIONS_INTERVAL_IN_HOURS * AlarmManager.INTERVAL_HOUR,
                    alarmIntent);
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            Intent serviceIntent = null;
            if (ACTION_START_NOTIFICATION_SERVICE.equals(action)) {
                Log.i(getClass().getSimpleName(), "onReceive from alarm, starting notification service");
                serviceIntent = NotificationIntentService.createIntentStartNotificationService(context);
            } else if (ACTION_DELETE_NOTIFICATION.equals(action)) {
                Log.i(getClass().getSimpleName(), "onReceive delete notification action, starting notification service to handle delete");
                serviceIntent = NotificationIntentService.createIntentDeleteNotification(context);
            }
    
            if (serviceIntent != null) {
                startWakefulService(context, serviceIntent);
            }
        }
    
        private static long getTriggerAt(Date now) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(now);
            //calendar.add(Calendar.HOUR, NOTIFICATIONS_INTERVAL_IN_HOURS);
            return calendar.getTimeInMillis();
        }
    
        private static PendingIntent getStartPendingIntent(Context context) {
            Intent intent = new Intent(context, NotificationEventReceiver.class);
            intent.setAction(ACTION_START_NOTIFICATION_SERVICE);
            return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        }
    
        public static PendingIntent getDeleteIntent(Context context) {
            Intent intent = new Intent(context, NotificationEventReceiver.class);
            intent.setAction(ACTION_DELETE_NOTIFICATION);
            return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        }
    }
    
  3. それでは、IntentServiceを作成して、実際に通知を作成しましょう。

    そこで、startWakefulServiceメソッドで渡したNotificationEventReceiverの意図に対する応答であるonHandleIntent()を指定します。

    削除アクションの場合-たとえば、分析に記録できます。開始通知インテントの場合-NotificationCompat.Builderを使用して、新しい通知を作成し、NotificationManager.notifyで表示します。通知の作成中に、クリックおよび削除アクションの保留インテントも設定しています。かなり簡単。

    NotificationIntentService.Java

    public class NotificationIntentService extends IntentService {
    
        private static final int NOTIFICATION_ID = 1;
        private static final String ACTION_START = "ACTION_START";
        private static final String ACTION_DELETE = "ACTION_DELETE";
    
        public NotificationIntentService() {
            super(NotificationIntentService.class.getSimpleName());
        }
    
        public static Intent createIntentStartNotificationService(Context context) {
            Intent intent = new Intent(context, NotificationIntentService.class);
            intent.setAction(ACTION_START);
            return intent;
        }
    
        public static Intent createIntentDeleteNotification(Context context) {
            Intent intent = new Intent(context, NotificationIntentService.class);
            intent.setAction(ACTION_DELETE);
            return intent;
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            Log.d(getClass().getSimpleName(), "onHandleIntent, started handling a notification event");
            try {
                String action = intent.getAction();
                if (ACTION_START.equals(action)) {
                    processStartNotification();
                }
                if (ACTION_DELETE.equals(action)) {
                    processDeleteNotification(intent);
                }
            } finally {
                WakefulBroadcastReceiver.completeWakefulIntent(intent);
            }
        }
    
        private void processDeleteNotification(Intent intent) {
            // Log something?
        }
    
        private void processStartNotification() {
            // Do something. For example, fetch fresh data from backend to create a rich notification?
    
            final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
            builder.setContentTitle("Scheduled Notification")
                    .setAutoCancel(true)
                    .setColor(getResources().getColor(R.color.colorAccent))
                    .setContentText("This notification has been triggered by Notification Service")
                    .setSmallIcon(R.drawable.notification_icon);
    
            PendingIntent pendingIntent = PendingIntent.getActivity(this,
                    NOTIFICATION_ID,
                    new Intent(this, NotificationActivity.class),
                    PendingIntent.FLAG_UPDATE_CURRENT);
            builder.setContentIntent(pendingIntent);
            builder.setDeleteIntent(NotificationEventReceiver.getDeleteIntent(this));
    
            final NotificationManager manager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
            manager.notify(NOTIFICATION_ID, builder.build());
        }
    }
    
  4. ほぼ完了しました。デバイスの再起動後またはタイムゾーンの変更後(たとえば、ユーザーがアメリカからヨーロッパに飛んでいて、通知を表示したくない場合)、BOOT_COMPLETED、TIMEZONE_CHANGED、およびTIME_SETイベントのブロードキャストレシーバーを追加してAlarmManagerを再セットアップします真夜中ですが、現地時間にこだわりました:-))。

    NotificationServiceStarterReceiver.Java

    public final class NotificationServiceStarterReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            NotificationEventReceiver.setupAlarm(context);
        }
    }
    
  5. AndroidManifestのすべてのサービス、ブロードキャストレシーバーも登録する必要があります。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:Android="http://schemas.Android.com/apk/res/Android"
        package="klogi.com.notificationbyschedule">
    
        <uses-permission Android:name="Android.permission.INTERNET" />
        <uses-permission Android:name="Android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission Android:name="Android.permission.RECEIVE_BOOT_COMPLETED" />
        <uses-permission Android:name="Android.permission.WAKE_LOCK" />
    
        <application
            Android:allowBackup="true"
            Android:icon="@mipmap/ic_launcher"
            Android:label="@string/app_name"
            Android:supportsRtl="true"
            Android:theme="@style/AppTheme">
            <activity Android:name=".MainActivity">
                <intent-filter>
                    <action Android:name="Android.intent.action.MAIN" />
    
                    <category Android:name="Android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
            <service
                Android:name=".notifications.NotificationIntentService"
                Android:enabled="true"
                Android:exported="false" />
    
            <receiver Android:name=".broadcast_receivers.NotificationEventReceiver" />
            <receiver Android:name=".broadcast_receivers.NotificationServiceStarterReceiver">
                <intent-filter>
                    <action Android:name="Android.intent.action.BOOT_COMPLETED" />
                    <action Android:name="Android.intent.action.TIMEZONE_CHANGED" />
                    <action Android:name="Android.intent.action.TIME_SET" />
                </intent-filter>
            </receiver>
    
            <activity
                Android:name=".NotificationActivity"
                Android:label="@string/title_activity_notification"
                Android:theme="@style/AppTheme.NoActionBar"/>
        </application>
    
    </manifest>
    

それでおしまい!

このプロジェクトのソースコードは here です。この投稿がお役に立てば幸いです。

93

エラーはonCreateおよびshowNotificationメソッドのUpdaterServiceManagerにあります。

Service using Activity Contextからnotificationを表示しようとしています。 Every Service has its own Context,はそれを使用します。特定のpass a Service an Activity's Context.が必要な理由がわからないActivity's Context to show Notification.は必要ありません

pdateServiceManager.classのcreateNotificationメソッドを入力します。 CreateNotificationActivityを削除サービスからではありません。

アクティビティではないコンテキストを介してアプリケーションウィンドウ/ダイアログを表示することはできません。有効なアクティビティ参照を渡してみてください

2
Xar E Ahmer