Android 5.0 新技术研究-锁屏通知(一)

下面是几个相关部分:

(1)通知Notification API

  • NotificationManager–获取通知栏

  • NotificationManager.Builder–通知栏构造器

  • Notification显示样式设置

  • Notification的Action调用PendingIntent机制

  • PendingIntent—-实现

(2)锁屏状态 API

  • PowerManager–电源管理

  • PowerManager.WakeLock–唤醒机制

  • KeyguardManager–锁屏管理

  • KeyguardManager.disableKeyguard()–解锁屏幕

  • KeyguardManager.reenableKeyguard()–反解锁屏幕

(3)判断application是否在后台运行,退到后台运行之后点击通知,让他重新运行起来

  • ActivityManager–AMS(Activity Manager Service)

  • ActivityManager–AMS(Activity Manager Service)

  • ActivityManager.RuningAppProcessInfo–所有后台运行的Application

下面开始正式介绍各种实现方式:

(1)实现一个通知的步骤:

  • 1.获取状态通知栏管理
NotificationManager notificationManager = (NotificationManager) this
.getSystemService(NOTIFICATION_SERVICE);
  • 2.实例化通知栏构造器NotificationCompat.Builder
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);  
  • 3.设置Notification的显示内容等属性(NotificationCompat.Builder)
mBuilder.setContentTitle("测试标题")//设置通知栏标题  
    .setContentText("测试内容") //设置通知栏显示内容
    .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图  
//  .setNumber(number) //设置通知集合的数量  
    .setTicker("测试通知来啦") //通知首次出现在通知栏,带上升动画效果的  
    .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间  
    .setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级  
//  .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消    
    .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)  
    .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合  
    //Notification.DEFAULT_ALL  Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission  
    .setSmallIcon(R.drawable.ic_launcher);//设置通知小ICON  
mBuilder.setContentTitle("测试标题")//设置通知栏标题
    .setContentText("测试内容") //设置通知栏显示内容
    .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL)) //设置通知栏点击意图
//  .setNumber(number) //设置通知集合的数量
    .setTicker("测试通知来啦") //通知首次出现在通知栏,带上升动画效果的
    .setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
    .setPriority(Notification.PRIORITY_DEFAULT) //设置该通知优先级
//  .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消  
    .setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
    .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
    //Notification.DEFAULT_ALL  Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
    .setSmallIcon(R.drawable.ic_launcher);//设置通知小ICON

这里基本包括了所有可能出现的一个Notification设置的选项,可根据自己的需求来选择其中的某几项或者全部来定制自己的Notification

  • 4.设置PendingIntent
    还有一点很重要,可以为Notification指定一个Action,通过PendingIntent来具体设置需要进行的Action
Intent intent = new Intent(context,XXX.class);  
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);  
mBuilder.setContentIntent(pendingIntent)  

如果此时需要代码中清楚当前所有Notification,就需要使用PendingIntent来设置:

Intent deleteIntent = new Intent();
deleteIntent.setClass(context, DeleteReceiver.class);
deleteIntent.setAction(DELETE_ACTION); // DeleteReceiver的action就是DELETE_ACTION
notification.deleteIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);

或者这样:

    public void clearAllNotify() {
        mNotificationManager.cancelAll();// 删除你发的所有通知
    }

这样(清除指定的Notification):

public void clearNotify(int notifyId){
        mNotificationManager.cancel(notifyId);//删除一个特定的通知ID对应的通知
    }

上面方法中的notifyId怎么设置?

    private int notifyId = 100;//注意每一个Notification都是唯一的Id
    ...
    ...
    ...
    mNotificationManager.notify(notifyId, mBuilder.build());
  • 5.显示(只前面四步准备完毕,显示是最简单不过的)
    mNotificationManager.notify(notifyId, mBuilder.build());

下面几种常用的处理方式实例:

  • (1)显示常驻的通知栏,也就是只有在代码中调用cancel()方法才可以清除的,通过系统的左滑或者其他方式没有办法去掉
    public void showAlwaysNotify(){
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
        PendingIntent pendingIntent=PendingIntent.getActivity(this, 0, getIntent(), 0);
        mBuilder.setSmallIcon(R.drawable.ic_launcher)
                .setTicker(getString(R.string.s))
                .setContentTitle(getString(R.string.d))
                .setContentText(getString(R.string.f))
                .setContentIntent(pendingIntent);
        Notification mNotification = mBuilder.build();
        mNotification.icon = R.drawable.ic_launcher;
        //在通知栏上点击此通知后自动清除此通知
        //FLAG_ONGOING_EVENT 在顶部常驻,可以调用下面的清除方法去除  
        //FLAG_AUTO_CANCEL 点击和清理(例如左滑)可以去调
        mNotification.flags = Notification.FLAG_ONGOING_EVENT;
        mNotification.defaults = Notification.DEFAULT_VIBRATE;
        mNotification.tickerText = getString(R.string.g);
        mNotification.when=System.currentTimeMillis();
        mNotificationManager.notify(notifyId, mNotification);
    }
  • (2)显示通知栏点击跳转到指定Activity(注意PendingIntent的使用)
public void showIntentActivityNotify(){
        mBuilder.setAutoCancel(true)
                .setContentTitle(getString(R.string.h))
                .setContentText(getString(R.string.j))
                .setTicker(getString(R.string.k));
        Intent resultIntent = new Intent(this, MainActivity.class);
        resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(pendingIntent);
        mNotificationManager.notify(notifyId, mBuilder.build());
    }
  • (3)显示通知栏点击打开Apk,这里的这个APK是放在assets文件夹下,获取路径不能直接读取的,要通过copy出去再读或者直接读取自己本地的path,这边只是做一个跳转APK,实际上是打不开的
    public void showIntentApkNotify(){
        mBuilder.setAutoCancel(true)
                .setContentTitle(getString(R.string.l))
                .setContentText(getString(R.string.z))
                .setTicker(getString(R.string.x));
        //我们这里需要做的是打开一个安装包
        Intent apkIntent = new Intent();
        apkIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        apkIntent.setAction(android.content.Intent.ACTION_VIEW);
        String apk_path = "file:///android_asset/iOffice.apk";
        Uri uri = Uri.fromFile(new File(apk_path));
        apkIntent.setDataAndType(uri, "application/vnd.android.package-archive");
        PendingIntent contextIntent = PendingIntent.getActivity(this, 0,apkIntent, 0);
        mBuilder.setContentIntent(contextIntent);
        mNotificationManager.notify(notifyId, mBuilder.build());
    }

下面单独介绍两种Notification(带有进度条的Notification & 带有Button的Notification)
- (1)ProcesNotification
下面先贴出源码(我知道你们需要)

/** 初始化通知栏 */
    private void initNotify() {
        mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setWhen(System.currentTimeMillis())// 通知产生的时间,会在通知信息里显示
                .setContentIntent(getDefalutIntent(0))
                // .setNumber(number)//显示数量
                .setPriority(Notification.PRIORITY_DEFAULT)// 设置该通知优先级
                // .setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消
                .setOngoing(false)// ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
                .setDefaults(Notification.DEFAULT_VIBRATE)// 向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:
                // Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 //
                // requires VIBRATE permission
                .setSmallIcon(R.drawable.icon);
    }

    /** 显示带进度条通知栏 */
    public void showProgressNotify() {
        mBuilder.setContentTitle(getString(R.string.zz))
                .setContentText(getString(R.string.xx))
                .setTicker(getString(R.string.cc));// 通知首次出现在通知栏,带上升动画效果的
        if(indeterminate){
            //不确定进度的
            mBuilder.setProgress(0, 0, true);
        }else{
            //确定进度的
            mBuilder.setProgress(100, progress, false); // 这个方法是显示进度条  设置为true就是不确定的那种进度条效果
        }
        mNotificationManager.notify(notifyId, mBuilder.build());
    }

    /** 显示自定义的带进度条通知栏 */
    private void showCustomProgressNotify(String status) {
        RemoteViews mRemoteViews = new RemoteViews(getPackageName(), R.layout.view_custom_progress);
        mRemoteViews.setImageViewResource(R.id.custom_progress_icon, R.drawable.icon);
        mRemoteViews.setTextViewText(R.id.tv_custom_progress_title, getString(R.string.jinri));
        mRemoteViews.setTextViewText(R.id.tv_custom_progress_status, status);
        if(progress >= 100 || downloadThread == null){
            mRemoteViews.setProgressBar(R.id.custom_progressbar, 0, 0, false);
            mRemoteViews.setViewVisibility(R.id.custom_progressbar, View.GONE);
        }else{
            mRemoteViews.setProgressBar(R.id.custom_progressbar, 100, progress, false);
            mRemoteViews.setViewVisibility(R.id.custom_progressbar, View.VISIBLE);
        }
        mBuilder.setContent(mRemoteViews)
                .setContentIntent(getDefalutIntent(0))
                .setTicker(getString(R.string.vv));
        Notification notification = mBuilder.build();
        notification.contentView = mRemoteViews;
        mNotificationManager.notify(notifyId, notification);
    }

    /** 开始下载 */
    public void startDownloadNotify() {
        isPause = false;
        if (downloadThread != null && downloadThread.isAlive()) {
//          downloadThread.start();
        }else{
            downloadThread = new DownloadThread();
            downloadThread.start();
        }
    }

    /** 暂停下载 */
    public void pauseDownloadNotify() {
        isPause = true;
        if(!isCustom){
            mBuilder.setContentTitle(getString(R.string.bb));
            setNotify(progress);
        }else{
            showCustomProgressNotify(getString(R.string.qqq));
        }
    }

    /** 取消下载 */
    public void stopDownloadNotify() {
        if (downloadThread != null) {
            downloadThread.interrupt();
        }
        downloadThread = null;
        if(!isCustom){
            mBuilder.setContentTitle(getString(R.string.eee)).setProgress(0, 0, false);
            mNotificationManager.notify(notifyId, mBuilder.build());
        }else{
            showCustomProgressNotify(getString(R.string.rrr));
        }
    }

    /** 设置下载进度 */
    public void setNotify(int progress) {
        mBuilder.setProgress(100, progress, false);
        mNotificationManager.notify(notifyId, mBuilder.build());
    }

    /**
     * 下载线程,这里进度都是设置好的hard code,不过基思想就是这样,如有真正的现在进程,也是同样的
     */
    class DownloadThread extends Thread {

        @Override
        public void run() {
            int now_progress = 0;
            while (now_progress <= 100) {
                if(downloadThread == null){
                    break;
                }
                if (!isPause) {
                    progress = now_progress;
                    if(!isCustom){
                        mBuilder.setContentTitle(getString(R.string.ttt));
                        if(!indeterminate){
                            setNotify(progress);
                        }
                    }else{
                        showCustomProgressNotify(getString(R.string.yyy));
                    }
                    now_progress += 10;
                }
                try {
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                }
            }
            if(downloadThread != null){
                if(!isCustom){
                    mBuilder
                    .setContentText(getString(R.string.uuu))
                    .setProgress(0, 0, false);
                    mNotificationManager.notify(notifyId, mBuilder.build());
                }else{
                    showCustomProgressNotify(getString(R.string.iii));
                }
            }
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.btn_show_progress:
            downloadThread = null;
            isCustom = false;
            indeterminate = false;
            showProgressNotify();
            break;
        case R.id.btn_show_un_progress:
            downloadThread = null;
            isCustom = false;
            indeterminate = true;
            showProgressNotify();
            break;
        case R.id.btn_show_custom_progress:
            downloadThread = null;
            isCustom = true;
            indeterminate = false;
            showCustomProgressNotify(getString(R.string.aaa));
            break;
        case R.id.btn_download_start:
            startDownloadNotify();
            break;
        case R.id.btn_download_pause:
            pauseDownloadNotify();
            break;
        case R.id.btn_download_cancel:
            stopDownloadNotify();
            break;
        default:
            break;
        }
    }
  • 首先介绍下Notification的自定义布局的使用,也就是RemoteViews这个类,需要注意:
    Notification的自定义布局是RemoteViews,和其他RemoteViews一样,在自定义视图布局文件中,仅支持FrameLayout、LinearLayout、RelativeLayout三种布局控件和AnalogClock、Chronometer、Button、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView和AdapterViewFlipper这些显示控件,不支持这些类的子类或Android提供的其他控件。否则会引起ClassNotFoundException异常

步骤如下:
1)创建自定义视图
2)获取远程视图对象(注:Notification的contentView不能为空)
3)设置PendingIntent(来响应各种事件)
4)发起Notification

因此ProcessNotification实现起来其实并不难,builder本身就有设置process的方式
mBuilder.setProgress(0, 0, true);
然后动态的设置进就没问题了,注意这里的notify()中的Id没有改变,只是更新Notification的内容

    public void setNotify(int progress) {
        mBuilder.setProgress(100, progress, false);
        mNotificationManager.notify(notifyId, mBuilder.build());
    }

在使用Notification中遇到的额问题:(注:下面所指的低版本是指2.3及2.3以下版本)

  • 1.如何取消掉通知栏上的通知(这个上面的注释中提到过)
    (1)设置对应的flags,让用户点击既被消除:
    notification.flags = FLAG_AUTO_CANCEL;
    (2)通过手动消除某项或则全部通知
    mNotificationMgr.cancle(NOTIFICATION_ID);//消除对应ID的通知
    mNotificationMgr.cancleAll();//消除创建的所有通知

  • 2.低版本中的部分方法已经被弃用的

    (1)Notification.Builder(this).getNotification()
    (2)mNotification.setLatestEventInfo(this, “title”, “content”, null);
    这些方法都已经被启用,虽然还有效果,可是不建议使用。所以开发过程中尽量使用NotificationCompat.Builder(this)的构建方法去创建一个通知类。

  • 3.低版本中会报的错误及解决方案:

  • (1)错误代码:Java.lang.IllegalArgumentException: contentIntent required: pkg=com.example.notifications id=100 notification=Notification(vibrate=default,sound=null,defaults=0x2,flags=0x0)
    解决方案:如果在高版本不会出错,而在2.3上面报了这个错误,通过开发文档中的以下知道你可以找打:
    For this reason, you should always ensure that UI controls in a notification are also available in an Activity in your app, and you should always start that Activity when users click the notification. To do this, use the setContentIntent() method.
    你就应该知道,缺少了setContentIntent() 这个方法,在2.3及更低的版本中,必须给它设置设置contentIntent,如果你点击没有意图,可以在赋值的的Intent中设置为new Intent()既可,切记contentIntent不能为空。
    代码如下:
public PendingIntent getDefalutIntent(int flags){  
    PendingIntent pendingIntent= PendingIntent.getActivity(this, 1, new Intent(), flags);  
    return pendingIntent;  
}  
    public PendingIntent getDefalutIntent(int flags){
        PendingIntent pendingIntent= PendingIntent.getActivity(this, 1, new Intent(), flags);
        return pendingIntent;
    }
  • (2)错误代码:
android.app.RemoteServiceException: Bad notification posted from package com.example.notifications: Couldn't expand RemoteViews for: StatusBarNotification(package=com.example.notifications id=101 tag=null notification=Notification(vibrate=null,sound=null,defaults=0x0,flags=0x2))

解决方法:
在自定义的时候,发现了这个问题,解决:每次更新时都必须把RemoteViews给new出来才行,不能利用已有的notification.contentView直接操作!

  • 4.低版本中,自定义的通知栏中如果带有按钮,可能按钮点击事件会失灵

    解决方法:看其它的应用,好像在低版本都会隐藏掉那些按钮,就是为了不影响用户体验,所以应该就这么解决,判断版本号在去决定是否现在按钮。

  • 5.低版本中,自定义布局中的字体颜色看不清

    解决方案:
    由于2.3及之前版本,背景设是白色的那我们定义字体颜色为系统预设的颜色:
    ?android:attr/textColorPrimary
    在资源的src/values目录中的style.xml文件中设置它标题和内容的样式为:

<resources>    
    <style name="NotificationContent">    
        <item name="android:textColor">?android:attr/textColorPrimary</item>    
    </style>    

    <style name="NotificationTitle">    
        <item name="android:textColor">?android:attr/textColorPrimary</item>    
        <item name="android:textStyle">bold</item>    
    </style>    
</resources>  
<resources>  

    <style name="NotificationContent">  
        <item name="android:textColor">?android:attr/textColorPrimary</item>  
    </style>  

    <style name="NotificationTitle">  
        <item name="android:textColor">?android:attr/textColorPrimary</item>  
        <item name="android:textStyle">bold</item>  
    </style>  

</resources>

在2.3之后的版本中(即API >=9的版本中),在资源文件下的src/values-v9目录中的style.xml文件中设置它标题和内容的样式为:

<resources>    

    <style name="NotificationContent" parent="android:TextAppearance.StatusBar.EventContent" />    

    <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />    

</resources>    
<resources>  

    <style name="NotificationContent" parent="android:TextAppearance.StatusBar.EventContent" />  

    <style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />  

</resources>  

内容对应的style即可。
对应解决网址:
解决
1.http://stackoverflow.com/questions/6250356/how-to-use-default-notification-style
2.http://stackoverflow.com/questions/4867338/custom-notification-layouts-and-text-colors/7320604#7320604
3.http://developer.android.com/guide/topics/ui/notifiers/notifications.html#CustomExpandedView (官方文档)
http://developer.android.com/about/versions/android-2.2-highlights.html

  • 6.低版本中mBuilder.setProgress(100, progress, false);没用,不显示进度条

    解决方法:此方法在4.0及以后版本才有用,如果为早期版本:需要自定义通知布局,其中包含ProgressBar视图

  • 7.自定义布局的时候,不同版本方法不一样。(弄了半天,在2.3版本不显示,原来是方法不兼容)

    2.3及2.3之前:
    通过如下方法赋予VIEW。

Notification notify = mBuilder.build();  
notify.contentView = view_custom;  
mNotificationManager.notify(notifyId, notify)  
        Notification notify = mBuilder.build();
        notify.contentView = view_custom;
        mNotificationManager.notify(notifyId, notify)

2.3之后:通过Builder以下方法赋于自定义布局。

mBuilder.setContent(view_custom)

本篇博客就介绍到这里,接下来介绍关于锁屏的相关开发Android 5.0 新技术研究-锁屏通知(二)
本人刚刚开始写博客,很多东西都是从大神哪里学习整理过来的,能够和大家一起学习Android开发很开心!

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐