Android开发笔记(五十二)通知推送Notification

PendingIntent

准备工作复习一下PendingIntent,前面的博文《Android开发笔记(五十)定时器AlarmManager》已经提到了它。PendingIntent意即延迟的Intent,主要用于非立即响应的通信场合。上回的博文,博主介绍了PendingIntent的用法,下面再列出有用到它的场合: 1、用于定时器AlarmManager,处理时刻到达后的提醒动作 2、用于通知推送Notification,处理点击通知后的相应动作 3、用于远程视图RemoteViews,处理远程控件上的点击动作 4、用于发送短信SmsManager,处理短信发送完的后续动作

Notification

Android的消息通知栏放的是APP想即时提醒用户的消息,Notification就是这么一个由APP提供的通知推送内容,每条通知基本都有这些元素:图标、标题、内容、时间等等,它的参数通过建造者模式来构建。下面是Notification.Builder常用的构建参数方法: setWhen : 设置推送时间,以“小时:分钟”格式显示 setShowWhen : 设置是否显示推送时间 setUsesChronometer : 设置是否显示时间计数。为true时将不显示推送时间,动态显示从通知被推送到当前的时间间隔,以“分钟:秒钟”格式显示 setSmallIcon : 设置状态栏里面的图标(小图标) setTicker : 设置状态栏里面的提示文本 setLargeIcon : 设置下拉列表里面的图标(大图标) setContentTitle : 设置下拉列表里面的标题文本 setContentText : 设置下拉列表里面的内容文本 setSubText : 设置下拉列表里面的附加说明文本,位于内容文本下方。若调用该方法,则setProgress的设置将失效 setProgress : 设置进度条与当前进度。进度条位于标题文本与内容文本中间 setNumber : 设置下拉列表右下方的数字,可与setProgress联合使用,表示进度条的当前进度数值 setContentInfo : 设置下拉列表右下方的文本。若调用该方法,则setNumber的设置将失效 setContentIntent : 设置内容的PendingIntent,在点击该通知时触发Intent动作 setDeleteIntent : 设置删除的PendingIntent,在滑掉该通知时触发Intent动作 setAutoCancel : 设置该通知是否自动清除。若为true,点击该通知后,通知会自动消失;若为false,点击该通知后,通知不会消失。 setSound : 设置通知推送时的声音 setVibrate : 设置通知推送时的震动方式 setOngoing : 设置该通知是否保持在下拉列表中。为true时用户将不能从下拉列表中去掉该通知 setPriority : 设置该通知的优先级 setExtras : 设置该通知的Bundle参数信息 setContent : 设置一个定制视图RemoteViews,用于取代Builder的默认视图模板 build : 构建方法。在以上参数都设置完毕后,调用该方法会返回Notification对象

NotificationManager

Notification只是生成通知的内容,实际推送动作还需要借助于系统的通知服务来实现。NotificationManager便是系统通知服务的管理类,它的常用方法如下: notify : 推送指定通知到状态栏和下拉列表 cancel : 取消指定通知。调用该方法后,状态栏和下拉列表中的指定通知将消失 cancelAll : 取消所有通知 下面是NotificationManager的调用代码例子:

			NotificationManager notifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
			notifyMgr.notify(R.string.app_name, notify);

Notification除了可由NotificationManager进行推送,也可由Service的startForeground方法推送,从而使得后台服务保持在前台运行,详细说明见《Android开发笔记(四十一)Service的生命周期》。

RemoteViews

远程视图RemoteViews与页面视图一样也是从layout下的布局文件中得到,二者之间的区别主要有: 1、远程视图主要用于桌面部件与通知栏部件,而页面视图用于APP页面; 2、远程视图只支持少数几种控件,如TextView、ImageView、Button、ImageButton、ProcessBar、Chronometer(计时器)、AnalogClock(模拟时钟); 3、远程视图不可直接设置控件信息,只能通过RemoteViews对象的set方法来设置; 下面是RemoteViews的常用方法: RemoteViews : 构造函数。第一个参数是包名,第二个参数是布局文件id setViewVisibility : 设置控件是否可见 setViewPadding : 设置控件的间距 setTextViewText : 设置TextView和Button的文本内容 setTextViewTextSize : 设置TextView和Button的文本大小 setTextColor : 设置TextView和Button的文本颜色 setTextViewCompoundDrawables : 设置TextView的周围图标 setImageViewResource : 设置ImageView和ImageButton的图片id setImageViewBitmap : 设置ImageView和ImageButton的图片位图 setChronometer : 设置计时器信息 setProgressBar : 设置进度条信息 setOnClickPendingIntent : 设置控件点击的响应动作

使用示例

为演示本文提到的几种推送,博主编码实现了下列三种方式的推送: 1、采用默认模板推送一个通知; 2、采用RemoteViews方式推送通知,并可根据部件上的点击事件改变通知内容; 3、采用RemoteViews方式让后台服务保持在前台运行,并可由后台服务的运行进度来实时更新通知内容; 下面是采用RemoteViews方式的通知推送效果图:

下面是通知推送的示例代码:

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RemoteViews;

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class NotificationActivity extends Activity implements OnClickListener {

	private static final String TAG = "NotificationActivity";
	private String mSong = "《两只老虎》";
	private String PLAY_EVENT = "";
	private boolean bPlay = true;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_notification);

		Button btn_default = (Button) findViewById(R.id.btn_default);
		Button btn_custom = (Button) findViewById(R.id.btn_custom);
		Button btn_service = (Button) findViewById(R.id.btn_service);
		btn_default.setOnClickListener(this);
		btn_custom.setOnClickListener(this);
		btn_service.setOnClickListener(this);

		PLAY_EVENT = getResources().getString(R.string.play_event);
	}
	
	private void sendDefaultNotify() {
		Intent clickIntent = new Intent(this, MainActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(this,
				R.string.app_name, clickIntent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		Intent cancelIntent = new Intent(this, MessengerActivity.class);
		PendingIntent deleteIntent = PendingIntent.getActivity(this,
				R.string.app_name, cancelIntent,
				PendingIntent.FLAG_UPDATE_CURRENT);

		Notification.Builder builder = new Notification.Builder(this);
		builder.setContentIntent(contentIntent)
				.setDeleteIntent(deleteIntent)
				.setUsesChronometer(true)
				.setProgress(100, 60, false)
				.setSubText("这里是副本")
				.setNumber(99)
				.setAutoCancel(false)
				.setSmallIcon(R.drawable.tt_s)
				.setTicker("提示文本")
				//.setWhen(System.currentTimeMillis())
				.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.tt_s))
				.setContentTitle("标题文本")
				.setContentText("内容文本");
		Notification notify = builder.build();

		NotificationManager notifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notifyMgr.notify(R.string.app_name, notify);
	}
	
	private void sendCustomNotify(boolean is_play) {
		bPlay = !bPlay;
		Intent pIntent = new Intent(PLAY_EVENT);
		PendingIntent nIntent = PendingIntent.getBroadcast(
				this, R.string.app_name, pIntent, PendingIntent.FLAG_UPDATE_CURRENT);
		RemoteViews widget_notify = new RemoteViews(getPackageName(), R.layout.widget_notify);
		if (is_play == true) {
			widget_notify.setTextViewText(R.id.btn_play, "暂停");
			widget_notify.setTextViewText(R.id.tv_play, mSong+"正在播放");
			widget_notify.setProgressBar(R.id.pb_play, 100, 10, false);
			widget_notify.setOnClickPendingIntent(R.id.btn_play, nIntent);
			widget_notify.setChronometer(R.id.chr_play, SystemClock.elapsedRealtime(), "%s", true);
		} else {
			widget_notify.setTextViewText(R.id.btn_play, "继续");
			widget_notify.setTextViewText(R.id.tv_play, mSong+"暂停播放");
			widget_notify.setProgressBar(R.id.pb_play, 100, 60, false);
			widget_notify.setChronometer(R.id.chr_play, SystemClock.elapsedRealtime(), "%s", false);
		}
		widget_notify.setOnClickPendingIntent(R.id.btn_play, nIntent);
		Intent intent = new Intent(this, MainActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(this,
				R.string.app_name, intent,
				PendingIntent.FLAG_UPDATE_CURRENT);

		Notification.Builder builder = new Notification.Builder(this);
		builder.setContentIntent(contentIntent)
				.setContent(widget_notify)
				.setTicker(mSong)
				.setSmallIcon(R.drawable.tt_s);
		Notification notify = builder.build();
		
		NotificationManager notifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notifyMgr.notify(R.string.app_name, notify);
	}

	@Override
	public void onClick(View v) {
		if (v.getId() == R.id.btn_default) {
			sendDefaultNotify();
		} else if (v.getId() == R.id.btn_custom) {
			sendCustomNotify(bPlay);
		} else if (v.getId() == R.id.btn_service) {
			Intent intent = new Intent(this, MusicService.class);
			intent.putExtra("is_play", bPlay);
			intent.putExtra("song", mSong);
			if (bPlay == true) {
				startService(intent);
			} else {
				stopService(intent);
			}
			bPlay = !bPlay;
		}
	}

	@Override
	public void onStart() {
		super.onStart();
		notifyReceiver = new NotifyReceiver();
		IntentFilter filter = new IntentFilter(PLAY_EVENT);
        registerReceiver(notifyReceiver, filter);
	}

	@Override
	public void onStop() {
        unregisterReceiver(notifyReceiver);
		super.onStop();
	}

    private NotifyReceiver notifyReceiver;
    public class NotifyReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
        		sendCustomNotify(bPlay);
            }
        }
    }

}

下面是后台服务的示例代码:

import android.annotation.TargetApi;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import android.widget.RemoteViews;

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class MusicService extends Service {
	private static final String TAG = "MusicService";
	private final IBinder mBinder = new LocalBinder();

	public class LocalBinder extends Binder {
		public MusicService getService() {
			return MusicService.this;
		}
	}

	@Override
	public IBinder onBind(Intent intent) {
		Log.d(TAG, "onBind");
		return mBinder;
	}

	private String mSong;
	private String PAUSE_EVENT = "";
	private boolean bPlay = true;

	private long mBaseTime;
	private long mPauseTime = 0;
	private int mProcess = 0;
	private Handler mHandler = new Handler();
	private Runnable mPlay = new Runnable() {
		@Override
		public void run() {
			if (bPlay == true) {
				if (mProcess < 100) {
					mProcess+=2;
				} else {
					mProcess = 0;
				}
				mHandler.postDelayed(this, 200);
			}
			refresh();
		}
	};
	
	private void refresh() {
		Intent pIntent = new Intent(PAUSE_EVENT);
		PendingIntent nIntent = PendingIntent.getBroadcast(
				this, R.string.app_name, pIntent, PendingIntent.FLAG_UPDATE_CURRENT);
		RemoteViews widget_notify = new RemoteViews(getPackageName(), R.layout.widget_notify);
		if (bPlay == true) {
			widget_notify.setTextViewText(R.id.btn_play, "暂停");
			widget_notify.setTextViewText(R.id.tv_play, mSong+"正在播放");
			widget_notify.setProgressBar(R.id.pb_play, 100, mProcess, false);
			widget_notify.setOnClickPendingIntent(R.id.btn_play, nIntent);
			widget_notify.setChronometer(R.id.chr_play, mBaseTime, "%s", true);
		} else {
			widget_notify.setTextViewText(R.id.btn_play, "继续");
			widget_notify.setTextViewText(R.id.tv_play, mSong+"暂停播放");
			widget_notify.setProgressBar(R.id.pb_play, 100, mProcess, false);
			widget_notify.setChronometer(R.id.chr_play, mBaseTime, "%s", false);
		}
		widget_notify.setOnClickPendingIntent(R.id.btn_play, nIntent);
		Intent intent = new Intent(this, MainActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(this,
				R.string.app_name, intent,
				PendingIntent.FLAG_UPDATE_CURRENT);

		Notification.Builder builder = new Notification.Builder(this);
		builder.setContentIntent(contentIntent)
				.setContent(widget_notify)
				.setTicker(mSong)
				.setSmallIcon(R.drawable.tt_s);
		Notification notify = builder.build();
		startForeground(1, notify);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startid) {
		mBaseTime = SystemClock.elapsedRealtime();
		bPlay = intent.getBooleanExtra("is_play", true);
		mSong = intent.getStringExtra("song");
		Log.d(TAG, "bPlay="+bPlay+", mSong="+mSong);
		mHandler.postDelayed(mPlay, 200);
		
		return START_STICKY;
	}
	
	@Override
	public void onCreate() {
		PAUSE_EVENT = getResources().getString(R.string.pause_event);
		pauseReceiver = new PauseReceiver();
		IntentFilter filter = new IntentFilter(PAUSE_EVENT);
        registerReceiver(pauseReceiver, filter);
		super.onCreate();
	}
	
	@Override
	public void onDestroy() {
        unregisterReceiver(pauseReceiver);
		super.onDestroy();
	}

    private PauseReceiver pauseReceiver;
    public class PauseReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
            	bPlay = !bPlay;
            	if (bPlay == true) {
            		mHandler.postDelayed(mPlay, 200);
            		if (mPauseTime > 0) {
                		long gap = SystemClock.elapsedRealtime() - mPauseTime;
                		mBaseTime += gap;
            		}
            	} else {
            		mPauseTime = SystemClock.elapsedRealtime();
            	}
            }
        }
    }

}

点此查看Android开发笔记的完整目录

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏做全栈攻城狮

程序员带你学习安卓开发-XML文档的创建与解析

这是程序员带你学习安卓开发系列教程。本文章致力于面向对象程序员可以快速学习开发安卓技术。

7820
来自专栏做全栈攻城狮

安卓开发基础教程-Android多界面应用程序开发

本套教程主要讲解安卓开发的相关知识,从基础到精通。一方面可以巩固自己所得,另一方面可以帮助对安卓开发感兴趣的朋友。

20530
来自专栏做全栈攻城狮

安卓开发基础教程-Android多界面应用程序开发

本套教程主要讲解安卓开发的相关知识,从基础到精通。一方面可以巩固自己所得,另一方面可以帮助对安卓开发感兴趣的朋友。

13540
来自专栏做全栈攻城狮

安卓开发-使用异步网络请求框架、多线程文件下载

相信对于前面的教程,大家已经很熟悉安卓网络编程了。这篇文章主要讲解一下异步网络编程和文件下载。学习编程重在写代码,只有自己的代码量上去了,自己才能完全理解。所以...

21740
来自专栏做全栈攻城狮

用Android最火的快速开发框架XUtils,进行文件下载

更多原创教程,关注微信公众平台:做全栈攻城狮。及做全栈攻城狮官网:www.8z5.net

46030
来自专栏做全栈攻城狮

程序员带你学习安卓开发系列-Android文件存储

输入帐号密码,并勾选记住帐号 ,点击登录时,保存帐号信息。下次登陆可以直接显示上次保存的QQ帐号。

17520
来自专栏做全栈攻城狮

做全栈攻城狮-安卓开发教程目录

1.程序员带你学习安卓开发,十天快速入门-安卓学习必要性:http://www.toutiao.com/i6319356348286894594/

16860
来自专栏做全栈攻城狮

React Native APP签名打包release版本APK

首先React Native开发的APP是无法通过Android Studio进行打包的,因为AS打包的APK,也是和debug版本一样,需要进行依托local...

27220
来自专栏做全栈攻城狮

React Native开发APP中的常用命令,如有遗漏请补充(不断更新...)

13530
来自专栏做全栈攻城狮

安卓开发基础教程-使用隐式意图打开系统内置应用,干货

本教程致力于讲解及快速进行安卓开发的学习。除安卓开发教程之外,还有Python、C#、网站建设、SEO等教程。对电脑技术感兴趣的朋友直接点击上方“关注”。

17520

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励