Android Notification 史上最全面的解析

关于Notification那些事,最近一直在弄这块,抽了点时间总结一下,做了一个Demo,废话不多说,先上图看效果


Android Notification 史上最全面的解析_第1张图片
通知.png

Android Notification 史上最全面的解析_第2张图片
通知2.png

在Android 3.0 (API level 11)之前,使用new Notification()方式创建通知:

String tickerText = "tikcker内容";
String title ="标题";
String content ="内容";

NotificationManager mNotifyMgr = 
      (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, 
new Intent(this, SecondActivity.class), 0);

Notification notification = new Notification(icon, tickerText, when);
notification.setLatestEventInfo(this, title, content, contentIntent);

mNotifyMgr.notify(0, notification);

Android 3.0 以后以及高版本改用Notification.Builder()来创建通知,原来的new Notification() 的方式被启弃用了

NotificationManager mNotifyMgr=(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
PendingIntent contentIntent=PendingIntent.getActivity(
      this, 0, new Intent(this, SecondActivity.class), 0);

Notification notification = new Notification.Builder(this)
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle("标题")
            .setContentText("需要展示的内容")
            .setContentIntent(contentIntent)
            .build();

mNotifyMgr.notify(0, notification);

现在用的是NotificationCompat.Builder方式,它和Notification.Builder()方式没有很大的区别,NotificationCompat.Builder 是v4包中,而Notification是android.app 包中的,使用NotificationCompat.Builder 在兼容方面会更好一点

接下来看下几种通知
1,普通通知 最常见的方式

做了一下简单的封装,写了一个单独的NotificationManager类来管理通知,先来看下第一种方式的代码

 /**
     * 基本展示通知方法
     *
     * @param ctx      上下文
     * @param noticeId 通知id
     * @param title    通知title
     * @param message  通知内容
     * @param intent   PendingIntent
     */
    public static void showNotify(Context ctx, int noticeId, String title, String message, PendingIntent intent) {
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx, null);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setContentTitle(title);
        mBuilder.setContentText(message);
        mBuilder.setContentIntent(intent);
        mBuilder.setAutoCancel(true);
        mBuilder.setTicker(title);
        mBuilder.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH);
        Notification notification = mBuilder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        notification.defaults |= Notification.DEFAULT_SOUND;//默认声音
        notification.defaults |= Notification.DEFAULT_LIGHTS;//默认闪烁
        android.app.NotificationManager notificationManager = (android.app.NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            notificationManager.notify(noticeId, notification);
        }
    }

再来看下两种不同的调用方式
no.1

  /**
     * 第一种方式,普通通知
     */
    private void showNotification() {
        Intent resultIntent = new Intent(this, SecondActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        //(new Random().nextInt(1000) 这个地方这么写的原因,是部分低版本的不能跳转,比如说小米3
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, (new Random().nextInt(1000)),
                resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationManager.showNotify(this,0, getString(R.string.app_name),
                "普通通知", resultPendingIntent);
    }

no.2

  /**
     * 第二种方式的普通通知
     */
    private void showNotion() {
        Intent resultIntent = new Intent(this, SecondActivity.class);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(SecondActivity.class);
        stackBuilder.addNextIntent(resultIntent);
        PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        NotificationManager.showNotify(this,1, getString(R.string.app_name),
                "第二种方式的普通通知", resultPendingIntent);

    }

使用第二种方式的是时候需要注意,SecondActivity在androidMainfest.xml中需要设置一下,

原因:

默认情况下,从通知启动一个Activity,按返回键会回到主屏幕。但有时候产品经理要求按返回键仍然留在当前应用的需求,这就要用到TaskStackBuilder了

注意:如果Intent 有数据传递的话,需要在onNewIntent()方法中接收Intent传递过来的数据

 
            
        

为什么要这么设置呢?
因为在用户点击通知,进入到SecondActivity界面后点击返回按钮是就会回到你设置的那个父类界面,如果不设置的话,就会直接退出了

接下来看下自定义通知
注意:自定义普通视图通知高度限制为64dp,大视图通知高度限制为256dp。在使用自定义通知尽量简单,以提高兼容性。
 /**
     * 设置自定义通知 view
     *
     * @param ctx         上下文
     * @param remoteViews 自定义view
     * @param noticeId    通知id
     * @param ticker      ticker
     * @param intent      PendingIntent
     */
    public static void showCustomNotify(Context ctx, RemoteViews remoteViews, int noticeId, String ticker, PendingIntent intent) {
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx, null);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setContentIntent(intent);
        mBuilder.setAutoCancel(true);
        mBuilder.setContent(remoteViews);
        mBuilder.setTicker(ticker);
        mBuilder.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH);
        Notification notification = mBuilder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        notification.defaults |= Notification.DEFAULT_SOUND;//默认声音
        notification.defaults |= Notification.DEFAULT_LIGHTS;//默认闪烁
        android.app.NotificationManager notificationManager = (android.app.NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            notificationManager.notify(noticeId, notification);
        }
    }

在Mainactivity里面调用

  /**
     * 自定义view 的普通通知
     *
     * @param context
     */
    private static void showCustomNotification(Context context) {
        //自定义显示布局
        RemoteViews contentViews = new RemoteViews(context.getPackageName(), R.layout.custom_notification);
        //通过控件的Id设置属性
        contentViews.setImageViewResource(R.id.ImageView, R.mipmap.ic_launcher);
        contentViews.setTextViewText(R.id.title, "自定义通知标题");
        contentViews.setTextViewText(R.id.content, "自定义通知内容");

        Intent resultIntent = new Intent(context, SecondActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        //(new Random().nextInt(1000) 这个地方这么写的原因,是部分低版本的不能跳转,比如说小米3
        PendingIntent resultPendingIntent = PendingIntent.getActivity(context, (new Random().nextInt(1000)),
                resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationManager.showCustomNotify(context, contentViews,2, "ticker", resultPendingIntent);

    }

custom_notification.xml代码




    

    

    
    


从API16开始,通知出现了大视图的样式,在下拉视图时会呈现大视图的效果。大图通知一般分成3种,文字型(BigTextStyle)、图片型(BigPictureStyle)、列表型(InboxStyle)

BigTextStyle 方式的通知
/**
     * 设置 BigTextStyle通知
     *
     * @param ctx         上下文
     * @param noticeId    通知id
     * @param title       标题
     * @param summaryText 摘要
     * @param bigText     内容
     * @param intent      PendingIntent
     */
    public static void showBigTextNotify(Context ctx, int noticeId, String title, String summaryText, String bigText, PendingIntent intent) {
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx, null);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setContentIntent(intent);
        mBuilder.setAutoCancel(true);
        mBuilder.setTicker(title);
        mBuilder.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH);

        NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle();
        bigTextStyle.setBigContentTitle(title)
                .setSummaryText(summaryText)
                .bigText(bigText);
        mBuilder.setStyle(bigTextStyle);

        Notification notification = mBuilder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        notification.defaults |= Notification.DEFAULT_SOUND;//默认声音
        notification.defaults |= Notification.DEFAULT_LIGHTS;//默认闪烁
        android.app.NotificationManager notificationManager = (android.app.NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            notificationManager.notify(noticeId, notification);
        }
    }

在MainActivity里面调用

 /**
     * 展示bigTextStyle 通知
     */
    private void showBigTextNotification() {
        Intent resultIntent = new Intent(this, SecondActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, (new Random().nextInt(1000)),
                resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationManager.showBigTextNotify(this, 4,"标题", "摘要摘要", "bigTextStyle通知", resultPendingIntent);
    }
BigPictureStyle 样式的通知

    /**
     * 设置BigPictureStyle通知
     *
     * @param ctx         上下文
     * @param noticeId    通知id
     * @param title       标题
     * @param summaryText 摘要
     * @param intent      PendingIntent
     */
    public static void showBigPictureNotify(Context ctx, int noticeId, String title, String summaryText, PendingIntent intent) {
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx, null);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.icon_picture));
        mBuilder.setContentIntent(intent);
        mBuilder.setAutoCancel(true);
        mBuilder.setTicker(title);
        mBuilder.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH);

        NotificationCompat.BigPictureStyle bigTextStyle = new NotificationCompat.BigPictureStyle();
        bigTextStyle.setBigContentTitle(title)
                .setSummaryText(summaryText)
                .bigLargeIcon(BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.icon_picture))
                .bigPicture(BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.bg_big_picture));
        mBuilder.setStyle(bigTextStyle);

        Notification notification = mBuilder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        notification.defaults |= Notification.DEFAULT_SOUND;//默认声音
        notification.defaults |= Notification.DEFAULT_LIGHTS;//默认闪烁
        android.app.NotificationManager notificationManager = (android.app.NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            notificationManager.notify(noticeId, notification);
        }
    }

在MainActivity 中调用

 /**
     * 展示bigPictureStyle 通知
     */
    private void showBigPictureNotification() {
        Intent resultIntent = new Intent(this, SecondActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, (new Random().nextInt(1000)),
                resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationManager.showBigPictureNotification(this, "标题", "bigPictureStyle形式的通知", resultPendingIntent);
    }

InboxStyle 多行通知
  /**
     * 展示多行通知
     *
     * @param ctx         上下文
     * @param noticeId    通知id
     * @param title       标题
     * @param summaryText 摘要
     * @param lineList    需要展示多行数据的集合
     * @param intent      PendingIntent
     */
    public static void showInboxNotify(Context ctx, int noticeId, String title, String summaryText, List lineList, PendingIntent intent) {
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx, null);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setLargeIcon(BitmapFactory.decodeResource(ctx.getResources(), R.mipmap.icon_picture));
        mBuilder.setContentIntent(intent);
        mBuilder.setAutoCancel(true);
        mBuilder.setTicker(title);
        mBuilder.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH);

        NotificationCompat.InboxStyle bigTextStyle = new NotificationCompat.InboxStyle();
        bigTextStyle.setBigContentTitle(title)
                .setSummaryText(summaryText);
        for (int i = 0; i < lineList.size(); i++) {
            bigTextStyle.addLine(lineList.get(i));
        }
        mBuilder.setStyle(bigTextStyle);

        Notification notification = mBuilder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;
        notification.defaults |= Notification.DEFAULT_SOUND;//默认声音
        notification.defaults |= Notification.DEFAULT_LIGHTS;//默认闪烁
        android.app.NotificationManager notificationManager = (android.app.NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            notificationManager.notify(noticeId, notification);
        }
    }

在MainActivity中调用

 /**
     * 展示InboxStyle 通知
     */
    private void showInboxNotification() {
        Intent resultIntent = new Intent(this, SecondActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, (new Random().nextInt(1000)),
                resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        List lineList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            lineList.add("wwwwwwwwww" + i);
        }
        NotificationManager.showInboxNotify(this,5, "标题标题", "InboxStyle形式的通知", lineList, resultPendingIntent);
    }
进度条通知
Android Notification 史上最全面的解析_第3张图片
进度条通知.png
/**
     * 带有进度条的通知
     *
     * @param ctx     上下文
     * @param title   标题
     * @param message 内容信息
     * @param intent  PendingIntent 这里做了一个下载安装的案例,实际情况如果不需要则可以不加PendingIntent 参数,点击通知就不会有跳转
     * @return NotificationCompat.Builder
     */
    public static NotificationCompat.Builder showProgressNotify(Context ctx, String title, String message, PendingIntent intent) {
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx, null);
        mBuilder.setSmallIcon(R.mipmap.ic_launcher);
        mBuilder.setContentTitle(title);
        mBuilder.setContentText(message);
        mBuilder.setContentIntent(intent);
        mBuilder.setAutoCancel(true);
        mBuilder.setTicker(title);
        mBuilder.setPriority(NotificationManagerCompat.IMPORTANCE_HIGH);//设置通知的优先级
        return mBuilder;
    }

在前面MainActivity里调用

 /**
     * 进度条通知
     */
    private void showProgressNotification() {
        Intent resultIntent = new Intent(this, SecondActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent resultPendingIntent = PendingIntent.getActivity(this, (new Random().nextInt(1000)),
                resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        final NotificationCompat.Builder builder = NotificationManager.showProgressNotify(this, "下载", "正在下载", resultPendingIntent);

        Notification notification = builder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;//当通知被用户点击之后会自动被清除(cancel)
        final android.app.NotificationManager notificationManager = (android.app.NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            //这里最好使用固定的id,因为如果通知id不固定的话,每次更新就会弹出多个通知
            notificationManager.notify(PROGRESS_NO_ID, notification);
        }
        builder.setProgress(100, 0, false);
        //下载以及安装线程模拟
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    builder.setProgress(100, i, false);
                    notificationManager.notify(PROGRESS_NO_ID, builder.build());
                    //下载进度提示
                    builder.setContentText("下载" + i + "%");
                    try {
                        Thread.sleep(50);//演示休眠50毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //下载完成后更改标题以及提示信息
                builder.setContentTitle("开始安装");
                builder.setContentText("安装中...");
                //设置进度为不确定,用于模拟安装
                builder.setProgress(0, 0, true);
                notificationManager.notify(PROGRESS_NO_ID, builder.build());
            }
        }).start();
    }
接下来看下更新通知

更新通知很简单,只需再次发送相同ID(就是上面代码中的通知ID)的通知即可,如果之前的通知依然存在则会更新通知属性,如果之前通知不存在则重新创建。
如果用不同的ID,那么通知就会往上叠加,和IOS一样,
本文最后会Git地址中的代码写了两种方式方法,需要的话可以download下来看下

取消通知
  1. 点击通知栏的清除按钮,会清除所有可清除的通知

  2. 设置了 setAutoCancel() 或 FLAG_AUTO_CANCEL的通知,点击该通知时会清除它

  3. 通过 NotificationManager 调用 cancel() 方法清除指定ID的通知

  4. 通过 NotificationManager 调用 cancelAll() 方法清除所有该应用之前发送的通知

以下是需要注意的地方
通知必须要设置的属性
setSmallIcon() 设置小图标
setContentTitle() 设置通知标题
setContentText()  设置通知内容

注意:

1,如果是自定义通知的话
setSmallIcon() 这个方法一定要有
标题和内容可以放在自定义布局里面

2,文字型(BigTextStyle)、图片型(BigPictureStyle)、列表型(InboxStyle) 也是必须要设置setSmallIcon()
标题和内容可以通过对应的Style 方式设置

浮动通知

图片

设置setFullScreenIntent()

详情可以看我的代码中,这是Android5.0以后才有的。
如果你要用这个方法,最好做下sdk的判断,该方法很坑的

注意上面那个方法的使用,
注意点:
1. 设置改方法后,通知不会自动消失,需要用户手动滑掉,比如左滑就删除了,向上滑才会消失
2. 如果你的通知有跳转界面的话,使用该方法后部分手机会直接跳转,魅族和美图手机就出现了,(已踩坑)
3,在锁屏状态下收到通知时,同样的如果有跳转的话,会直接跳转到你APP 对应的界面,用户体验很不好

这个问题,官网说就是这样的,
暂时没有找到解决方法,如果有人找到的话,记得给俺留言

锁屏通知

Android5.0开始,通知可以显示在锁屏上。用户可以通过设置选择是否允许敏感的通知内容显示在安全的锁屏上。
你的应用可以通过setVisibility()控制通知的显示等级:
VISIBILITY_PRIVATE : 显示基本信息,如通知的图标,但隐藏通知的全部内容
VISIBILITY_PUBLIC : 显示通知的全部内容
VISIBILITY_SECRET : 不显示任何内容,包括图标
例如:
mBuilder.setVisibility(NotificationCompat.VISIBILITY_SECRET);//通知的显示等级

常见的Notification的Flag属性使用
Notification.DEFAULT_VIBRATE           //添加默认震动提醒  需要 VIBRATE permission
Notification.DEFAULT_SOUND             //添加默认声音提醒
Notification.DEFAULT_LIGHTS            // 添加默认三色灯提醒
Notification.DEFAULT_ALL               // 添加默认以上3种全部提醒
Notification.FLAG_SHOW_LIGHTS          //三色灯提醒,在使用三色灯提醒时候必须加该标志符
Notification.FLAG_ONGOING_EVENT        //发起正在运行事件(活动中)
Notification.FLAG_INSISTENT            //让声音、振动无限循环,直到用户响应(取消或者打开)
Notification.FLAG_ONLY_ALERT_ONCE      //发起Notification后,铃声和震动均只执行一次
Notification.FLAG_AUTO_CANCEL          //用户单击通知后自动消失
Notification.FLAG_NO_CLEAR             //只有全部清除时,Notification才会清除 ,不清除该通知 
Notification.FLAG_FOREGROUND_SERVICE   //表示正在运行的服务
调用方法
Notification notification = mBuilder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;////当通知被用户点击之后会自动被清除(cancel)
notification.defaults |= Notification.DEFAULT_SOUND;//默认声音
notification.defaults |= Notification.DEFAULT_LIGHTS;//默认闪烁
PendingIntent 和Intent区别
1, Intent 是及时启动,Intent 随着所在的Activity 消失而消失

2,PendingIntent是即将要发生的事情,在Notification中跳转页面,不是马上跳转,而是你点击通知后再跳转,
可以理解为延迟执行的Intent 

3,PendingIntent可以cancel,Intent 是不可以的另外
Intent在程序结束后即终止,而PendingIntent在程序结束后依然有效

4,Intent一般是用作Activity、Sercvice、BroadcastReceiver之间传递数据,而Pendingintent,一般用在Notification上,PendingIntent是对Intent一个包装。 

5, 如果你在项目中使用到了Intent,最好设置下setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
因为有些Android 4.4 以下的手机,跳转有问题

后续会继续补充更多说明
以上是给出的代码是项目中的一部分,详细的代码在Git 上
地址: https://github.com/shaozucheng/NotificationDemo

你可能感兴趣的:(Android Notification 史上最全面的解析)