通知栏推送点击跳转以及返回主界面-第二弹-完善纠正待续

之前我们弄过一篇关于这个跳转的问题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,比较花时间....放到第二篇吧。。。顺便探索下其他不同的方案.....

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容