如何创建持久的Android服务

时间:2015-12-26 17:15:06

标签: java android service android-notifications

在forground中运行服务的Android documentation中,提供了以下示例代码:

Notification notification = new Notification(R.drawable.icon, getText(R.string.ticker_text),
        System.currentTimeMillis());
Intent notificationIntent = new Intent(this, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);

但是,此代码不起作用。首先,不推荐使用用于通知的constructor。其次,方法setLatestEventInfo(Context, String, String, PendingIntent)不再包含在Notification类中。当我消除这些并以正确的方式创建通知时,会出现如下错误:

Caused by: java.lang.NullPointerException: class name is null
  at android.content.ComponentName.<init>(ComponentName.java:114)
  at android.app.Service.startForeground(Service.java:654)
  at com.vcapra1.motionsensors.MainActivity.startService(MainActivity.java:53)

编辑:以下是我使用的代码,来自@提供的链接 RusheelJain:

// prepare intent which is triggered if the
// notification is selected
Intent intent = new Intent(ctx, MotionMonitorService.class);
// use System.currentTimeMillis() to have a unique ID for the pending intent
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, (int) System.currentTimeMillis(), intent, 0);

// build notification
// the addAction re-use the same intent to keep the example short
Notification notification = new Notification.Builder(ctx)
  .setContentTitle("Monitoring Motion")
  .setContentText("No events yet...")
  .setSmallIcon(R.mipmap.ic_launcher)
  .setContentIntent(pendingIntent).build();

NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(NOTIFICATION_SERVICE);

// notificationManager.notify(1, notification);
startForeground(1, notification);

当我使用notificationManager.notify(1, notification);行时,会创建一个通知,但它不是持久性的,也不会启动服务(当然,这不会发生)。但是,当我使用startForeground(1, notification);行时,应用程序会崩溃上面的堆栈跟踪。

所以我的最后一个问题是:启动即使应用程序关闭也会继续运行的服务的正确方法是什么?我检查了几个来源,它们都包括我找到的方法在Android文档上。

2 个答案:

答案 0 :(得分:3)

使用有效compileAspect { additionalAjcArgs = ['encoding' : 'UTF-8'] } 从服务生命周期方法(例如startForeground()Notification)或其中一种生命周期方法调用的方法调用onCreate()(例如, onStartCommand()的{​​{1}}。

例如,在onHandleIntent()进行下载时,此服务使用前景IntentService

Notification

(来自this sample project

您的堆栈跟踪表明您尝试从onHandleIntent()拨打/*** Copyright (c) 2008-2012 CommonsWare, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. From _The Busy Coder's Guide to Android Development_ https://commonsware.com/Android */ package com.commonsware.android.foredown; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Intent; import android.net.Uri; import android.os.Environment; import android.support.v4.app.NotificationCompat; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class Downloader extends IntentService { public static final String ACTION_COMPLETE= "com.commonsware.android.downloader.action.COMPLETE"; private static int NOTIFY_ID=1337; private static int FOREGROUND_ID=1338; public Downloader() { super("Downloader"); } @Override public void onHandleIntent(Intent i) { try { String filename=i.getData().getLastPathSegment(); startForeground(FOREGROUND_ID, buildForegroundNotification(filename)); File root= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); root.mkdirs(); File output=new File(root, filename); if (output.exists()) { output.delete(); } URL url=new URL(i.getData().toString()); HttpURLConnection c=(HttpURLConnection)url.openConnection(); FileOutputStream fos=new FileOutputStream(output.getPath()); BufferedOutputStream out=new BufferedOutputStream(fos); try { InputStream in=c.getInputStream(); byte[] buffer=new byte[8192]; int len=0; while ((len=in.read(buffer)) >= 0) { out.write(buffer, 0, len); } out.flush(); } finally { fos.getFD().sync(); out.close(); c.disconnect(); } stopForeground(true); raiseNotification(i, output, null); } catch (IOException e2) { stopForeground(true); raiseNotification(i, null, e2); } } private void raiseNotification(Intent inbound, File output, Exception e) { NotificationCompat.Builder b=new NotificationCompat.Builder(this); b.setAutoCancel(true).setDefaults(Notification.DEFAULT_ALL) .setWhen(System.currentTimeMillis()); if (e == null) { b.setContentTitle(getString(R.string.download_complete)) .setContentText(getString(R.string.fun)) .setSmallIcon(android.R.drawable.stat_sys_download_done) .setTicker(getString(R.string.download_complete)); Intent outbound=new Intent(Intent.ACTION_VIEW); outbound.setDataAndType(Uri.fromFile(output), inbound.getType()); b.setContentIntent(PendingIntent.getActivity(this, 0, outbound, 0)); } else { b.setContentTitle(getString(R.string.exception)) .setContentText(e.getMessage()) .setSmallIcon(android.R.drawable.stat_notify_error) .setTicker(getString(R.string.exception)); } NotificationManager mgr= (NotificationManager)getSystemService(NOTIFICATION_SERVICE); mgr.notify(NOTIFY_ID, b.build()); } private Notification buildForegroundNotification(String filename) { NotificationCompat.Builder b=new NotificationCompat.Builder(this); b.setOngoing(true); b.setContentTitle(getString(R.string.downloading)) .setContentText(filename) .setSmallIcon(android.R.drawable.stat_sys_download) .setTicker(getString(R.string.downloading)); return(b.build()); } } ,这是不可能的,当然也不是合适的模式。

答案 1 :(得分:2)

您必须使用Notifiation.Builder而不是Notification的类构造函数。

以下是使用操作启动前台服务的示例:

private Notification getForegroundNotification() {      
    Intent showTaskIntent = MyActivity.createStartIntent(this, mContext);

    PendingIntent contentIntent = PendingIntent.getActivity(
            getApplicationContext(),
            0,
            showTaskIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);


    Notification.Builder builder = new Notification.Builder(this);

    builder.setContentTitle(getString(R.string.title))
        //.setContentText(getString(R.string.notification_text))
        .setSmallIcon(R.drawable.notification_white)
        .setContentIntent(contentIntent);



    Intent shutdownActionIntent = new Intent(this, MyService.class);
    shutdownActionIntent.setAction(SHUTDOWN_NOTIFICATION_ACTION);

    PendingIntent shutdownPendingIntent = PendingIntent.getService(
            getApplicationContext(),
            1,
            shutdownActionIntent,
            PendingIntent.FLAG_UPDATE_CURRENT);

    builder.addAction(android.R.drawable.ic_lock_power_off, getString(R.string.shutdown_action), shutdownPendingIntent);

    if(isRunningJellybeanOrLater()) {
        builder.setPriority(Notification.PRIORITY_MAX);
        return builder.build();
    } else {
        return builder.getNotification();
    }
} 

这是你如何开始的:

void handleStartAction() {      
        Notification notification = getForegroundNotification();
        startForeground(FOREGROUND_NOTIFICATION_ID, notification);
    }