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();
}
}
}
}
}