之前我们弄过一篇关于这个跳转的问题https://zhuanlan.zhihu.com/p/37961567,已经上线项目貌似没有什么问题。可是了最近重新的新项目发现一些判断的问题导致点击Home键貌似有点问题。不太纠结什么了,我们就直接重新捋一捋。 而且之前的方式:点击通知栏跳转到的页面的finish里面做判断(是否HomeActivity还活着的那种方式总是给人感觉不太好,虽然有人也是这样干的)。
目前主流的做法是创建Intent[] 数组,然后里面依次填上:1. 从详情页退出后返回的主页面 2。 和点击通知栏后要跳转的详情页面。
A. 我们再跳转的时候再判断下当前应用进程的活动状态针对性选择是否直接跳转到详情,还是说先启动应用,然后跳转到详情页(参数通过splash传递到homeactivity,然后再传递到碎片,进而传递启动跳转到详情。 这个也是符合产品需求的逻辑)。
B. 当然了,有些人可能是直接跳转到详情,然后再详情返回的地方去处理是重新启动应用还是说finish当前页面即可。
我们针对上面两种情况都来搞一搞吧。。我们先把情况A的方式说下。 然后搞下情况B。情况B,如果你没有友盟这样的推送,我们可以启动一个进程服务,通过服务发送通知,然后模拟消息推送和跳转。
先说A:来自xx盟的推送类:
**public class **UPushIntentService **extends **UmengMessageService {
在public void onMessage(Context context, Intent intent) {}里面我们获取到推送的内容:extra = msg.extra;
然后接着就可以进行通知栏通知了:
我们分两种情况:
app进程是否存活:
1. 如果存活:
1.1 虽然进程存活,同时考虑下任务栈也空了
{
先启动HomeActivity,然后启动推送详情页面 - 涉及到Intents数组的使用,通知栏需要如下设置
PendingIntent contentIntent = PendingIntent.getActivities(context, requestCode,
intents, PendingIntent.FLAG_UPDATE_CURRENT);
}
2. 如果不存活,可以基本断定程序肯定死翘翘了...
{
这个时候我们就启动应用,比如SplashActivity,然后通过SplashActivity将参数传递给HomeActivity,然后进入启动详情页面并将参数传递过去.......
}
看看判断进程活着的方法吧(从Activity任务栈获取运行进程信息,然后遍历活着进程名称与需要判断的进程名称做对比即可 - 有时候页面都已经退出来,进程还活着妮):
/**
* 判断进程是否运行
*
* @param context
* @param proessName 应用程序的主进程名一般为包名
* @return
*/
public static boolean isProessRunning(Context context, String proessName) {
boolean isRunning = false;
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo info : lists) {
if (info.processName.equals(proessName)) {
isRunning = true;
}
}
return isRunning;
}
我们再看下getActivities的说明吧..
getActivities
added in API level 11
public static PendingIntent getActivities (Context context,
int requestCode,
Intent[] intents,
int flags)
Like getActivity(Context, int, Intent, int), but allows an array of Intents to be supplied. The last Intent in the array is taken as the primary key for the PendingIntent, like the single Intent given to getActivity(Context, int, Intent, int). Upon sending the resulting PendingIntent, all of the Intents are started in the same way as they would be by passing them to Context.startActivities(Intent[]).
The first intent in the array will be started outside of the context of an existing activity, so you must use the Intent.FLAG_ACTIVITY_NEW_TASK launch flag in the Intent. (Activities after the first in the array are started in the context of the previous activity in the array, so FLAG_ACTIVITY_NEW_TASK is not needed nor desired for them.)
The last intent in the array represents the key for the PendingIntent. In other words, it is the significant element for matching (as done with the single intent given to getActivity(Context, int, Intent, int), its content will be the subject of replacement by send(Context, int, Intent) and FLAG_UPDATE_CURRENT, etc. This is because it is the most specific of the supplied intents, and the UI the user actually sees when the intents are started.
For security reasons, the Intent objects you supply here should almost always be explicit intents, that is specify an explicit component to be delivered to through Intent.setClass
Parameters
context Context: The Context in which this PendingIntent should start the activity.
requestCode int: Private request code for the sender
intents Intent: Array of Intents of the activities to be launched.
This value must never be null.
flags int: May be FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT, or any of the flags as supported by Intent.fillIn() to control which unspecified parts of the intent that can be supplied when the actual send happens.
Returns
PendingIntent Returns an existing or new PendingIntent matching the given parameters. May return null only if FLAG_NO_CREATE has been supplied.
从上面的意思中可以看出,数组中第一个Intent对象将会被额外的启动一个栈,于是,我就将MainActivity设置为intent[0]对象.
而intent数组中最后一个,将作为PendIntent的关键,也就是点击之后需要跳转的第一个类文件。
注意HomeActivity被启动了一个栈,而我们页面跳转到的是详情。此时进入详情后,我们退出详情页面,此时就相当于回到了HomeActivity的任务栈中,此时将会加载主页面哟!!! 给人感觉就是我们进入详情,返回时回到主页面的,intent数组第一个就是我们需要返回的主页面...
基本了解一些以后我们直接就可以搞了(关于通知栏详细的分析还没搞,后面会专门分析下。最近Android开发艺术探索看了一大半了,妈的,还是不简单,要补的东西有点多呀):
Let's coding it:
/**
* 友盟消息自定义处理
*/
public class UPushIntentService extends UmengMessageService {
private static final String TAG = UPushIntentService.class.getName();
private static final String InfoChannel = "InfoChannel";
private String messageType = "";
private Map<String, String> extra = null;
public UPushIntentService() {
}
@Override
public void onMessage(Context context, Intent intent) {
try {
String message = intent.getStringExtra(AgooConstants.MESSAGE_BODY);
//Log.e("UPushIntentService", "message=" + message);
UMessage msg = new UMessage(new JSONObject(message));
Intent intentAct;
int requestCode = 0;
String schemeStr = "";
if (msg != null) {
messageType = msg.display_type;
extra = msg.extra;
}
/*
* 强制下线
*/
if (messageType.equals("1000")) {
//intentAct.setClass(context, LoginActivity.class);
}
if (null != extra) {
schemeStr = extra.get("scheme");
requestCode = Integer.parseInt(schemeStr.substring(schemeStr.lastIndexOf("/") + 1));
///< 判断app进程是否存活
if (SystemUtils.isProessRunning(context, "com.xxxxx.app")) {
//如果存活的话,就直接启动DetailActivity,但要考虑一种情况,就是app的进程虽然仍然在
//但Task栈已经空了,比如用户点击Back键退出应用,但进程还没有被系统回收,如果直接启动
//DetailActivity,再按Back键就不会返回MainActivity了。所以在启动
//DetailActivity前,要先启动HomeActivity。
Intent mainIntent = new Intent(context, HomeActivity.class);
//将HomeActivity的launchMode设置成SingleTask, 或者在下面flag中加上Intent.FLAG_CLEAR_TOP,
//如果Task栈中有HomeActivity的实例,就会把它移到栈顶,把在它之上的Activity都清理出栈,
//如果Task栈不存在HomeActivity实例,则在栈顶创建
mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intentAct = new Intent();
intentAct.putExtra("id", requestCode);
///< 如果推送的是资讯页面,直接跳转资讯详情页面
if (schemeStr.contains("post")) {
intentAct.putExtra("html", "");
intentAct.setClass(context, InfoNewsDetailActivity.class);
}
///< 如果推送的是快讯,直接跳转到快讯详情页面
else if (schemeStr.contains("news")) {
intentAct.setClass(context, InfoFlashDetailsActivity.class);
}
///< 进入详情Activity,返回后就会显示主页面
Intent[] intents = {mainIntent, intentAct};
///< 展示通知
showNotifications(context, msg, intents, requestCode);
} else {
//如果app进程已经被杀死,先重新启动app,将DetailActivity的启动参数传入Intent中,参数经过
//SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入
// 参数跳转到DetailActivity中去了
intentAct = context.getPackageManager().
getLaunchIntentForPackage("com.xxxxxx.app");
intentAct.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
/**
* 获取schem值,跳转应用的时候方便解析
*/
if (null != extra) {
intentAct.putExtra("scheme", extra.get("scheme"));
}
///< 展示通知
showNotificationsSplash(context, msg, intentAct, requestCode);
}
}
///< 对完全自定义消息的处理方式,点击或者忽略
boolean isClickOrDismissed = true;
if (isClickOrDismissed) {
///< 完全自定义消息的点击统计
UTrack.getInstance(getApplicationContext()).trackMsgClick(msg);
} else {
///< 完全自定义消息的忽略统计
UTrack.getInstance(getApplicationContext()).trackMsgDismissed(msg);
}
} catch (Exception e) {
UmLog.e(TAG, e.getMessage());
}
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* 自定义通知布局
*
* @param context 上下文
* @param msg 消息体
* @param intents
*/
public void showNotifications(Context context, UMessage msg, Intent[] intents, int requestCode) {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
///< 过时了
//NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
/**
* Android8.0在Notification上多了一个类
NotifycationChannel 渠道
其实说白了就是用来分开设置标题文本、以及闪光、振动等等。
*/
NotificationCompat.Builder builder;
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel mChannel = new NotificationChannel(InfoChannel, InfoChannel, NotificationManager.IMPORTANCE_HIGH);
// mChannel.setDescription(description);
// mChannel.enableLights(true);
// mChannel.setLightColor(Color.RED);
// mChannel.enableVibration(true);
// mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
builder = new NotificationCompat.Builder(context, InfoChannel);
} else {
builder = new NotificationCompat.Builder(context);
}
///< 自定义通知栏样式 - 一W个是解决自定义图标问题,另一个以后可以扩展
RemoteViews myNotificationView = new RemoteViews(context.getPackageName(),
R.layout.notifylayout);
myNotificationView.setTextViewText(R.id.textView1, msg.title);
myNotificationView.setTextViewText(R.id.textView2, msg.text);
builder
//自定义布局
.setContent(myNotificationView)
//第一行内容 通常作为通知栏标题
//.setContentTitle(msg.title)
//第二行内容 通常是通知正文
//.setContentText(msg.text)
//Ticker是状态栏显示的提示
.setTicker(msg.ticker)
//弹出时间
.setWhen(System.currentTimeMillis())
//系统状态栏显示的小图标
.setSmallIcon(R.drawable.notification)
//下拉显示的大图标
//.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.shareicon))
//.setColor(Color.parseColor("#41b5ea"))
//.setDefaults(Notification.DEFAULT_ALL)
//可以点击通知栏的删除按钮删除
.setAutoCancel(true);
PendingIntent contentIntent = PendingIntent.getActivities(context, requestCode,
intents, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
mNotificationManager.notify((int) System.currentTimeMillis(), builder.build());
}
/**
* 自定义通知布局
*
* @param context 上下文
* @param msg 消息体
* @param intent intent
*/
public void showNotificationsSplash(Context context, UMessage msg, Intent intent, int requestCode) {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
///< 过时了
//NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
/**
* Android8.0在Notification上多了一个类
NotifycationChannel 渠道
其实说白了就是用来分开设置标题文本、以及闪光、振动等等。
*/
NotificationCompat.Builder builder;
if (Build.VERSION.SDK_INT >= 26) {
NotificationChannel mChannel = new NotificationChannel(InfoChannel, InfoChannel, NotificationManager.IMPORTANCE_HIGH);
// mChannel.setDescription(description);
// mChannel.enableLights(true);
// mChannel.setLightColor(Color.RED);
// mChannel.enableVibration(true);
// mChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(mChannel);
builder = new NotificationCompat.Builder(context, InfoChannel);
} else {
builder = new NotificationCompat.Builder(context);
}
///< 自定义通知栏样式 - 一W个是解决自定义图标问题,另一个以后可以扩展
RemoteViews myNotificationView = new RemoteViews(context.getPackageName(),
R.layout.notifylayout);
myNotificationView.setTextViewText(R.id.textView1, msg.title);
myNotificationView.setTextViewText(R.id.textView2, msg.text);
builder
//自定义布局
.setContent(myNotificationView)
//第一行内容 通常作为通知栏标题
//.setContentTitle(msg.title)
//第二行内容 通常是通知正文
//.setContentText(msg.text)
//Ticker是状态栏显示的提示
.setTicker(msg.ticker)
//弹出时间
.setWhen(System.currentTimeMillis())
//系统状态栏显示的小图标
.setSmallIcon(R.drawable.notification)
//下拉显示的大图标
//.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.shareicon))
//.setColor(Color.parseColor("#41b5ea"))
//.setDefaults(Notification.DEFAULT_ALL)
//可以点击通知栏的删除按钮删除
.setAutoCancel(true);
PendingIntent contentIntent = PendingIntent.getActivity(context, requestCode,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
mNotificationManager.notify((int) System.currentTimeMillis(), builder.build());
}
}
上面可以直接拿去用或者摘取一部分就行。另外提下,关于页面路由其实可以用ARoute,阿里的页面路由框架。 我个人觉得没那么复杂的跳转,就没用。。。
基本上关于推送跳转返回应该是没什么问题了。目前两个项目都在用。下面我们来说下B方案吧。。。。临时有点事情,先缓一缓。。后面补一下,因为涉及到需要实际搞搞demo,比较花时间....放到第二篇吧。。。顺便探索下其他不同的方案.....
网友评论