始终在通知栏中显示服务

11

我想将我的应用程序添加到通知栏中,以便它始终像Google Play商店中的某些应用程序一样显示。

我希望它是这个屏幕截图:

enter image description here

我希望我的通知不被清除,并且在单击通知时打开我的应用程序。

这是我的服务类代码:

package com.demo;

import java.util.Random;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.widget.Toast;

public class ServiceExample extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Toast.makeText(this,"Service Created",300).show();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(this,"Service Destroy",300).show();
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        Toast.makeText(this,"Service LowMemory",300).show();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        Toast.makeText(this,"Service start",300).show();
        Notification notification = new Notification(R.drawable.ic_launcher,
                "Rolling text on statusbar", System.currentTimeMillis());

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, ServiceDemoActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

        notification.setLatestEventInfo(this,
                "Notification title", "Notification description", contentIntent);

        startForeground(1, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        Toast.makeText(this,"task perform in service",300).show();
        /*ThreadDemo td=new ThreadDemo();
        td.start();*/
        Notification notification = new Notification(R.drawable.ic_launcher,
                "Rolling text on statusbar", System.currentTimeMillis());

        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, ServiceDemoActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

        notification.setLatestEventInfo(this,
                "Notification title", "Notification description", contentIntent);

        startForeground(1, notification);

        return super.onStartCommand(intent, flags, startId);
    }

    private class ThreadDemo extends Thread{
        @Override
        public void run() {
            super.run();
            try{
            sleep(70*1000); 
            handler.sendEmptyMessage(0);
            }catch(Exception e){
                e.getMessage();
            }
        }
    }
   private Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        showAppNotification();
    }
   };

   void showAppNotification() {
       try{
        NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
        // The PendingIntent to launch our activity if the user selects this
        // notification.  Note the use of FLAG_CANCEL_CURRENT so that, if there
        // is already an active matching pending intent, cancel it and replace
        // it with the new array of Intents.
//      PendingIntent contentIntent = PendingIntent.getActivities(this, 0,
//             "My service completed", PendingIntent.FLAG_CANCEL_CURRENT);

        // The ticker text, this uses a formatted string so our message could be localized
        String tickerText ="djdjsdjkd";

        // construct the Notification object.
        Notification notif = new Notification(R.drawable.ic_launcher, tickerText,
                System.currentTimeMillis());

        // Set the info for the views that show in the notification panel.
//      notif.setLatestEventInfo(this, from, message, contentIntent);

        // We'll have this notification do the default sound, vibration, and led.
        // Note that if you want any of these behaviors, you should always have
        // a preference for the user to turn them off.
        notif.defaults = Notification.DEFAULT_ALL;

        // Note that we use R.layout.incoming_message_panel as the ID for
        // the notification.  It could be any integer you want, but we use
        // the convention of using a resource id for a string related to
        // the notification.  It will always be a unique number within your
        // application.
        nm.notify(0, notif);
       }catch(Exception e){
           e.getMessage();
       }
    }
}

我在项目清单文件中声明了我的服务:

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

    <uses-sdk android:minSdkVersion="8" />
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".ServiceDemoActivity"
            android:label="@string/app_name"  >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service android:name=".ServiceExample"></service>
    </application>

</manifest>

这是我用于启动和停止服务的类:

package com.demo;

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ReceiverCallNotAllowedException;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

public class ServiceDemoActivity extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        findViewById(R.id.start).setOnClickListener(this);
        findViewById(R.id.stop).setOnClickListener(this);
    }

    private Intent inetnt;
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.start:

            inetnt=new Intent(this,ServiceExample.class);
            startService(inetnt);
            break;
        case R.id.stop:

            inetnt=new Intent(this,ServiceExample.class);
            stopService(inetnt);
            break;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
//      
    }
}

这是我的布局代码:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="StartService" 
        android:id="@+id/start"/>

        <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="StopService"
        android:id="@+id/stop" />

</LinearLayout>
4个回答

20

为了让你的通知始终存在,你需要设置这两个标志:

notification.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
请注意,将您的服务设置为前台将使您获得持续的事件,但除非您确实需要您的服务在前台运行,否则这是一件非常不恰当的事情。音乐播放器是应该这样做的一个好例子 - 用户期望他们的音乐在进行许多其他设备操作时仍能不间断地播放。

然而,大多数服务在内存不足时可以被系统暂停,并在内存再次可用时自动重新启动。因此,正确的思考方式是将这两个概念分开。

  1. 如果您希望始终显示通知,请使用我提到的两个标志。
  2. 如果您确实需要您的服务在前台运行,您可以调用Service.startForeground(),但不要把它视为获取持续通知的方法。

2
@HeroVsZero 非常感谢。您可能考虑将此选为被接受的答案,因为这是正确的答案。尽管我很希望paradx能因他真诚和周到的回答而获得声誉,但他所建议的实际上是错误的建议,正如我所解释的那样。我认为引导未来的访问者找到最正确的答案是决定选择哪个答案最重要的因素。 - Darshan Rivka Whittle

11

如果你想让你的应用程序始终出现在状态栏上,你需要编写一个服务,并在服务的onStart(...)onStartCommand(...)方法中调用startForeground(id, notification)方法,在服务的onDestroy()方法中分别调用stopForeground()方法。

id是你可以分配给通知的整数,notification是一个Notification对象(你可以在这里阅读更多关于它的信息:http://developer.android.com/guide/topics/ui/notifiers/notifications.html)。

这样只要你的服务在运行,状态栏通知就会显示。

Notification notification = new Notification(R.drawable.statusbar_icon,
        "Rolling text on statusbar", System.currentTimeMillis());

PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
        new Intent(this, YourActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

notification.setLatestEventInfo(this,
        "Notification title", "Notification description", contentIntent);

startForeground(1, notification);
你可以将这段代码放在服务的 onStart(...)onStartCommand(...) 方法中。
此外,你可以在这里阅读更多关于服务的信息:http://developer.android.com/reference/android/app/Service.html

@paradx 报错:方法 startForeground(int, Notification) 未定义。 - Dinesh
startForeground(...) 方法是 Service 类的成员。如果你想在状态栏上保留通知,你必须实现一个服务,不能从活动中调用它。 - Adam Monos
@paradx,你能发布服务类代码以及如何注册服务的方法吗? - Dinesh
1
@HeroVsZero 这不是一个坏答案,但我想澄清的是,只有当您确实需要您的服务在前台时(例如音乐播放器)才调用startForeground()。 @paradx错误地认为这与具有持续通知有关。 (相反,关系是相反的-必须有通知才能使您的服务在前台运行。) - Darshan Rivka Whittle
我同意,但是如果您不做任何后台工作,我认为您不需要持续的通知,这就是我的答案。如果您确实没有在后台执行任何操作,但仍希望获得通知,您可以使用相同的代码创建通知,但是不要使用startForeground()将其添加到前台,而应该在onCreate()方法中使用NotificationManager将其放置在状态栏上,并在onDestroy()方法中将其删除。 - Adam Monos
@paradx “进行后台工作”并不需要您的服务在前台运行,这只是一个优先级的问题。当系统内存不足时,前台服务较不容易被杀死。无论是前台还是后台,服务都可能随时被终止,并且无论哪种方式,资源再次可用时都会重新启动。唯一需要调用startForeground()的情况是如果用户对服务的暂时中断非常烦恼。我认为你搞反了——大多数服务不属于那个类别。 - Darshan Rivka Whittle

2

只需使用以下代码始终显示通知栏。

    Notification.Builder builder = new Notification.Builder(MainActivity.this);
    builder.setSmallIcon(R.mipmap.ic_launcher)
            .setContentText("Call Recorder")
            .setAutoCancel(false);
    Notification notification = builder.getNotification();

    notification.flags |= Notification.FLAG_NO_CLEAR
            | Notification.FLAG_ONGOING_EVENT;

    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    notificationManager.notify(1, notification);

1
这是使用NotificationCompact.Builder类构建通知的示例,它是最近版本的通知构建器。
private void startNotification() {

     //Sets an ID for the notification

  int mNotificationId = 001;

    // Build Notification , setOngoing keeps the notification always in status bar
    NotificationCompat.Builder mBuilder =
            new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.ldb)
                    .setContentTitle("Stop LDB")
                    .setContentText("Click to stop LDB")
                    .setOngoing(true);




    // Gets an instance of the NotificationManager service
    NotificationManager mNotifyMgr =
            (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

    // Build the notification and issues it.
    mNotifyMgr.notify(mNotificationId, mBuilder.build());


}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接