第一行代码学习笔记:手机多媒体----通知

通知

当某个应用程序希望向用户发出一些提示信息,而该应用程序不再前台运行时,就可以借助通知来实现。

通知的基本用法

通知既可以在活动里创建,可以在广播接收器里创建,也可以在服务里创建。在活动里创建通知的场景比较少,因为一般只有当程序进入后台的时候,才需要使用通知。

Android O 引入了 通知渠道(Notification Channels),以提供统一的系统来帮助用户管理通知,如果是针对 android O 为目标平台时,必须实现一个或者多个通知渠道,以向用户显示通知。

Notification 的创建主要涉及到 Notification.Builder 、 Notification 、 NotificationManager 。
Notification.Builer : 使用建造者模式构建 Notification 对象。由于 Notification.Builder 仅支持 Android 4.1及之后的版本,为了解决兼容性问题, Google 在 Android Support v4 中加入了 NotificationCompat.Builder 类。对于某些在 Android 4.1 之后才特性,即使 NotificationCompat.Builder 支持该方法,在之前的版本中也不能运行。点我 查看更多关于 Notification 兼容性问题处理。
Notification : 通知对应类,保存通知相关的数据。NotificationManager 向系统发送通知时会用到。

NotificationManager : NotificationManager 是通知管理类,它是一个系统服务。调用 NotificationManager 的 notify() 方法可以向系统发送通知。

创建通知的步骤为

1、获取 NotificationManager 对象:

NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

2、实例化 NotificationCompat.Builder 并设置相关属性

//第二步:实例化 NotificationCompat.Builder 并设置相关属性
        NotificationCompat.Builder builder;
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//判断是否是8.0Android.O
            String channelID = "1";
            String channelName = "channel_name";
            NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            notifyManager.createNotificationChannel(channel);
            channel.setLightColor(Color.GREEN);
            builder = new NotificationCompat.Builder(this, channelID);
        } else {
            builder = new NotificationCompat.Builder(this);
        }

        //设置小图标只能使用纯alpha图层的图片进行设置
        builder.setSmallIcon(R.mipmap.ic_launcher)
                //设置大图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.avator))
                //设置通知标题
                .setContentTitle("最简单的Notification")
                //设置通知内容
                .setContentText("text")
                .setAutoCancel(true);
        //第三步:通过builder.build()方法生成Notification对象,并发送通知,id=1,要保证,每个通知的id都是不同的
        notifyManager.notify(1, builder.build());

3、通过builder.build()方法生成Notification对象,并发送通知,id=1

 notifyManager.notify(1, builder.build());

到此通知设置完成

对通知设置点击事件:PendingIntent,同Intent一样,指明某一个意图,用于启动活动,启动服务,发送广播。Intent倾向于立即执行,PendingIntent倾向于在某个合适的时机去执行某个动作,延迟执行的Intent。PendingIntent提供了几个静态方法获取PendingIntent的实例,如getActivity(),getBroadcast(),getService(),他们的参数都是一致的。

新建NotificationActivity。

修改MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton sendNotice = findViewById(R.id.send_notice);
        sendNotice.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendNotification();
            }
        });
    }


    private void sendNotification() {
        //第一步:获取NotificationManager实例
        NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //第二步:实例化 NotificationCompat.Builder 并设置相关属性
        NotificationCompat.Builder builder;
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//判断是否是8.0Android.O
            String channelID = "1";
            String channelName = "channel_name";
            NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            notifyManager.createNotificationChannel(channel);
            channel.setLightColor(Color.GREEN);
            builder = new NotificationCompat.Builder(this, channelID);
        } else {
            builder = new NotificationCompat.Builder(this);
        }

        Intent intent = new Intent(this, NotificationActivity.class);
        //第一个参数 上下文对象
        //第二个参数 一般用不到。传入0 即可
        //第三个参数  Intent对象
        //第四个参数 用于确定PendingIntent的行为,有FLAG_ONE_SHOP等,一般设为0
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
        //设置小图标只能使用纯alpha图层的图片进行设置
        builder.setSmallIcon(R.mipmap.ic_launcher)
                //设置大图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.avator))
                //设置通知标题
                .setContentTitle("最简单的Notification")
                //设置通知内容
                .setContentText("text")
                .setContentIntent(pi)
                .setAutoCancel(true);
        //第三步:通过builder.build()方法生成Notification对象,并发送通知,id=1,要保证,每个通知的id都是不同的
        notifyManager.notify(1, builder.build());

    }
}

点击通知后的效果


更新 Notification

更新通知很简单,只需要再次发送相同 ID 的通知即可,如果之前的通知还未被取消,则会直接更新该通知相关的属性;如果之前的通知已经被取消,则会重新创建一个新通知。更新通知跟发送通知使用相同的方式。

取消 Notification

取消通知有如下 5 种方式:
1、点击通知栏的清除按钮,会清除所有可清除的通知
2、设置了 setAutoCancel() 或 FLAG_AUTO_CANCEL 的通知,点击该通知时会清除它
3、通过 NotificationManager 调用 cancel(int id) 方法清除指定 ID 的通知
4、通过 NotificationManager 调用 cancel(String tag, int id) 方法清除指定 TAG 和 ID 的通知
5、通过 NotificationManager 调用 cancelAll() 方法清除所有该应用之前发送的通知
如果你是通过 NotificationManager.notify(String tag, int id, Notification notify) 方法创建的通知,那么只能通过 NotificationManager.cancel(String tag, int id) 方法才能清除对应的通知,调用NotificationManager.cancel(int id) 无效。
通知的进阶技巧

设置Notification 的通知效果

给 Notification 设置诸如震动、铃声、呼吸灯等效果。

Notification 有震动、响铃、呼吸灯三种响铃效果,可以通过 setDefaults(int defualts) 方法来设置。 Default 属性有以下四种,一旦设置了 Default 效果,自定义的效果就会失效。

1、设置默认通知效果

//设置系统默认提醒效果,一旦设置默认提醒效果,则自定义的提醒效果会全部失效。
//添加默认震动效果,需要申请震动权限
//<uses-permission android:name="android.permission.VIBRATE" />
Notification.DEFAULT_VIBRATE
//添加系统默认声音效果,设置此值后,调用setSound()设置自定义声音无效
Notification.DEFAULT_SOUND
//添加默认呼吸灯效果,使用时须与 Notification.FLAG_SHOW_LIGHTS 结合使用,否则无效
Notification.DEFAULT_LIGHTS
//添加上述三种默认提醒效果

Notification.DEFAULT_ALL

2、除了以上几种设置 Notification 默认通知效果,还可以通过以下几种 FLAG 设置通知效果。
//提醒效果常用 Flag
//三色灯提醒,在使用三色灯提醒时候必须加该标志符
Notification.FLAG_SHOW_LIGHTS
//发起正在运行事件(活动中)
Notification.FLAG_ONGOING_EVENT
//让声音、振动无限循环,直到用户响应 (取消或者打开)
Notification.FLAG_INSISTENT
//发起Notification后,铃声和震动均只执行一次
Notification.FLAG_ONLY_ALERT_ONCE
//用户单击通知后自动消失
Notification.FLAG_AUTO_CANCEL
//只有调用NotificationManager.cancel()时才会清除
Notification.FLAG_NO_CLEAR
//表示正在运行的服务

Notification.FLAG_FOREGROUND_SERVICE

3、设置自定义通知效果

builder.setSmallIcon(R.mipmap.ic_launcher)
              .....
                //设置震动
                .setVibrate(new long[]{0, 1000, 1000, 1000})
                //设置音频 每一个手机的/system/media/audio/ringtones目录下都有很多音频
                //调用系统多媒体库内的铃声
                //.setSound(Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"2"));
                //调用自己提供的铃声,位于 /res/values/raw 目录下
               // .setSound(Uri.parse("android.resource://包名/" + R.raw.sound));
                .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/xxxx")))
                //设置呼吸灯
                .setLights(Color.GREEN, 1000, 1000)
                .........
        notifyManager.notify(1, builder.build());
通知的高级功能

设置长文字、信息、图片等需要使用setStytle()方法。

当设置的内容过长时,通知无法显示完整,多余的部分会用.....代替,若真的需要显示一段长文字,代码如下:

 builder.setSmallIcon(R.mipmap.ic_launcher)
                //设置大图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.avator))
                //设置通知标题
                .setContentTitle("title")
                //设置通知内容
                .setContentText("Learn how to build notifications ,send and sync data,and use voice actions.Get the official Android IDE and developer tools build apps for Android")
                .setContentIntent(pi)
                //设置震动
                .setVibrate(new long[]{0, 1000, 1000, 1000})
                //设置音频 每一个手机的/system/media/audio/ringtones目录下都有很多音频
                //调用系统多媒体库内的铃声
                //.setSound(Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"2"));
                //调用自己提供的铃声,位于 /res/values/raw 目录下
               // .setSound(Uri.parse("android.resource://包名/" + R.raw.sound));
                .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/xxxx")))
                //设置呼吸灯
                .setLights(Color.GREEN, 1000, 1000)
                .setStyle(new NotificationCompat.BigTextStyle().bigText("Learn how to build notifications ,send and sync data,and use voice actions." +
                       "Get the official Android IDE and developer tools build apps for Android"))
                .setAutoCancel(true);
        //第三步:通过builder.build()方法生成Notification对象,并发送通知,id=1,要保证,每个通知的id都是不同的
        //notifyManager.cancel(1);
        notifyManager.notify(1, builder.build());

设置通知的重要程度

builder.setSmallIcon(R.mipmap.ic_launcher)
                //设置大图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.avator))
                //设置通知标题
                .setContentTitle("title")
                //设置通知内容
                .setContentText("Learn how to build notifications ,send and sync data,and use voice actions.Get the official Android IDE and developer tools build apps for Android")
                .setContentIntent(pi)
                //设置震动
                .setVibrate(new long[]{0, 1000, 1000, 1000})
                //设置音频 每一个手机的/system/media/audio/ringtones目录下都有很多音频
                //调用系统多媒体库内的铃声
                //.setSound(Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"2"));
                //调用自己提供的铃声,位于 /res/values/raw 目录下
               // .setSound(Uri.parse("android.resource://包名/" + R.raw.sound));
                .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/xxxx")))
                //设置呼吸灯
                .setLights(Color.GREEN, 1000, 1000)
                .setStyle(new NotificationCompat.BigTextStyle().bigText("Learn how to build notifications ,send and sync data,and use voice actions." +
                       "Get the official Android IDE and developer tools build apps for Android"))
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setAutoCancel(true);
        //第三步:通过builder.build()方法生成Notification对象,并发送通知,id=1,要保证,每个通知的id都是不同的
        //notifyManager.cancel(1);
        notifyManager.notify(1, builder.build());

完整代码如下

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton sendNotice = findViewById(R.id.send_notice);
        sendNotice.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendNotification();
            }
        });
    }


    private void sendNotification() {
        //第一步:获取NotificationManager实例
        NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //第二步:实例化 NotificationCompat.Builder 并设置相关属性
        NotificationCompat.Builder builder;
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//判断是否是8.0Android.O
            String channelID = "1";
            String channelName = "channel_name";
            NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            notifyManager.createNotificationChannel(channel);
            channel.setLightColor(Color.GREEN);
            builder = new NotificationCompat.Builder(this, channelID);
        } else {
            builder = new NotificationCompat.Builder(this);
        }

        Intent intent = new Intent(this, NotificationActivity.class);
        //第一个参数 上下文对象
        //第二个参数 一般用不到。传入0 即可
        //第三个参数  Intent对象
        //第四个参数 用于确定PendingIntent的行为,有FLAG_ONE_SHOP等,一般设为0
        PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
        //设置小图标只能使用纯alpha图层的图片进行设置
        builder.setSmallIcon(R.mipmap.ic_launcher)
                //设置大图标
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.avator))
                //设置通知标题
                .setContentTitle("title")
                //设置通知内容
                .setContentText("Learn how to build notifications ,send and sync data,and use voice actions.Get the official Android IDE and developer tools build apps for Android")
                .setContentIntent(pi)
                //设置震动
                .setVibrate(new long[]{0, 1000, 1000, 1000})
                //设置音频 每一个手机的/system/media/audio/ringtones目录下都有很多音频
                //调用系统多媒体库内的铃声
                //.setSound(Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI,"2"));
                //调用自己提供的铃声,位于 /res/values/raw 目录下
               // .setSound(Uri.parse("android.resource://包名/" + R.raw.sound));
                .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/xxxx")))
                //设置呼吸灯
                .setLights(Color.GREEN, 1000, 1000)
                .setStyle(new NotificationCompat.BigTextStyle().bigText("Learn how to build notifications ,send and sync data,and use voice actions." +
                       "Get the official Android IDE and developer tools build apps for Android"))
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setAutoCancel(true);
        //第三步:通过builder.build()方法生成Notification对象,并发送通知,id=1,要保证,每个通知的id都是不同的
        //notifyManager.cancel(1);
        notifyManager.notify(1, builder.build());


    }
}

Notification的自定义布局

这里就需要提出一个新的知识点RemoteView,望文生义,即远程View。

RemoteView表示的是一种View结构,它可以在其他进程中显示(具体来讲是SystemServer进程),由于它是在其他进程中显示,为了更新它的界面,我们不能简单地使用普通View的那一套方法,RemoteView提供了一系列Set方法用于更新界面。

RemoteView没有findViewById方法,因此无法访问里面的View元素,而必须通过RemoteViews所提供的一系列set方法来完成,这是通过反射调用的。
通知栏由NotificationManager(NM)管理,而NM通过Binder和SystemService进程中的NotificationManagerService加载的,而它运行在系统的SystemService中,这就和我们进程构成了跨进程通讯。
构造方法
RemoteViews(String packageName, int layoutId),第一个参数是当前应用的包名,第二个参数是待加载的布局文件。
支持组件
布局:FrameLayout、LinearLayout、RelativeLayout、GridLayout
组件:Button、ImageButton、ImageView、ProgressBar、TextView、ListView、GridView、ViewStub等(例如EditText是不允许在RemoveViews中使用的,使用会抛异常)。
原理
系统将view操作封装成Action对象,Action同样实现了Parcelable接口,通过Binder传递到SystemServer进程。远程进程通过RemoteViews的apply方法来进行view的更新操作,RemoteViews的apply方法内部则会去遍历所有的action对象并调用它们的apply方法来进行view的更新操作。
这样做的好处是不需要定义大量的Binder接口,其次批量执行RemoteViews中的更新操作提高了程序性能。
工作流程

首先RemoteViews会通过Binder传递到SystemService进程,因为RemoteViews实现了Parcelable接口,因此它可以跨进程传输,系统会根据RemoteViews的包名等信息拿到该应用的资源;然后通过LayoutInflater去加载RemoteViews中的布局文件。接着系统会对View进行一系列界面更新任务,这些任务就是之前我们通过set来提交的。set方法对View的更新并不会立即执行,会记录下来,等到RemoteViews被加载以后才会执行。

给对应的布局View设置点击事件:
remoteViews.setOnClickPendingIntent(R.id.iv,pendingIntent1)
单击通知时的响应事件:

notification.contentIntent = pendingIntent//对应的是第一个pendingIntent

代码实现

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <android.support.v7.widget.AppCompatButton
        android:id="@+id/send_notice"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="send notice"/>

</android.support.v7.widget.LinearLayoutCompat>

activity_another.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NotificationActivity">

    <android.support.v7.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="24sp"
        android:text=" This is another layout"/>

</RelativeLayout>

activity_notification.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".NotificationActivity">

    <android.support.v7.widget.AppCompatTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:textSize="24sp"
        android:text=" This is notification layout"/>

</RelativeLayout>

remote_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/tv_rv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="removte"
        />
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="button"/>
</LinearLayout>
MainActivity
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        AppCompatButton sendNotice = findViewById(R.id.send_notice);
        sendNotice.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendNotification();
            }
        });
    }


    private void sendNotification() {
        //第一步:获取NotificationManager实例
        NotificationManager notifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //第二步:实例化 NotificationCompat.Builder 并设置相关属性
        NotificationCompat.Builder builder;
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//判断是否是8.0Android.O
            String channelID = "1";
            String channelName = "channel_name";
            NotificationChannel channel = new NotificationChannel(channelID, channelName, NotificationManager.IMPORTANCE_HIGH);
            notifyManager.createNotificationChannel(channel);
            channel.setLightColor(Color.GREEN);
            builder = new NotificationCompat.Builder(this, channelID);
        } else {
            builder = new NotificationCompat.Builder(this);
        }
        builder.setSmallIcon(R.mipmap.ic_launcher)
               // .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.avator))
              //  .setContentTitle("RemoteViews Notification title")
               // .setContentText("RemoteViews Notification text")
                .setWhen(System.currentTimeMillis())
                .setAutoCancel(true);
       Notification notification = builder.build();
        //设置用户点击notification的动作
        // pendingIntent 延期的意图
        Intent intent = new Intent(this, NotificationActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        //自定义界面
        RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.remote_view);
        remoteViews.setTextViewText(R.id.tv_rv, "我是自定义的 notification");
        Intent intent1 = new Intent(this, AnotherActivity.class);
        PendingIntent pendingIntent1 = PendingIntent.getActivity(this, 0, intent1, 0);
        //对自定义布局中的控件的点击事件
        remoteViews.setOnClickPendingIntent(R.id.button, pendingIntent1);
        notification.contentView = remoteViews;
        //对通知的点击事件
        notification.contentIntent = pendingIntent;
        //把定义的notification 传递给 notificationmanager
        notifyManager.notify(1, notification);

    }

}

参考文章

https://www.jianshu.com/p/ca92797d925a

https://blog.csdn.net/tianhui1234567/article/details/79505844

https://blog.csdn.net/SweetInsomnia/article/details/72466345

https://www.jianshu.com/p/9cefaa07a090

在此感谢你们的无私分享。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值