友盟推送第一次安装时获取不到deviceToken的问题

最近又对接了一次友盟推送,但是没有之前的那么顺利,因为在华为P30手机上出现了之前没见过的现象。现象就是应用第一次安装到手机上大概率获取不到devicetoken。第二次启动应用后才能获取到token。而且手机在充电情况下是能正常获取的,不充电情况很难获取。也就是调用pushAgent.register()方法,没有任何回调。查看日志和友盟的源码之后发现了端倪。在了解了友盟的注册流程和代码原来之后,我根据其中逻辑建立了新的流程,能够解决刚才说的第一次启动获取不到devicetoken的问题。下面的内容就是探究和解决这个问题的过程。
先说实现方案:

pushAgent.register(new UPushRegisterCallback() {
            @Override
            public void onSuccess(String s) {
                String deviceToken = s;
            }

            @Override
            public void onFailure(String s, String s1) {
            }
        });

后面增加这段代码:

new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                String deviceToken = Config.f(context);
            }
        }, 0, 300);
//这段只是示例用循环来反复读取 Config.f(context),具体循环速度、次数、判空、回收资源等细节需自行把握。

-----下面开始探究之路------

顺着pushAgent.register()源码跟踪进去,进入到一个ak.class文件。主要注册逻辑在一个叫做c的私有方法中。下面是该方法中的部分代码:

y.b(new Runnable() {
                public void run() {
                    try {
                        UPLog.i("", new Object[]{"appkey:" + var2, " secret:" + var3});
                        TaobaoRegister.register(var1, "default", "umeng:" + var2, var3, "android@umeng", new IRegister() {
                            public void onSuccess(String var1x) {
                                UPLog.i("", new Object[]{"register success. deviceToken:" + var1x});
                                ak.this.a(var1x);
                            }

                            public void onFailure(String var1x, String var2x) {
                                UPLog.e("", new Object[]{"register failed! code:" + var1x + ", desc:" + var2x});
                                ak.this.a(var1x, var2x);
                                UMLog.aq(ao.a, 0, "\\|");
                            }
                        });
                    } catch (Throwable var2x) {
                        UPLog.e("", new Object[]{"register failed:" + var2x.getMessage()});
                    }

                }
            });

能看到这段代码调用了TaobaoRegister.register来注册,注册成功后会调用ak文件下的a方法:

    private void a(String var1) {
        ......
        UMJobIntentService.enqueueWork(am.b(), UmengMessageCallbackHandlerService.class, var2);
    }

a方法又启动执行了一个叫做UmengMessageCallbackHandlerService的JobIntentService,下面是UmengMessageCallbackHandlerService中的相关代码:

final Context var2 = am.b();
        if (var2 == null) {
            UPLog.i("MsgCallback", new Object[]{"context null!"});
        } else {
            String var3 = UMGlobalContext.getInstance(var2).getProcessName(var2);
            UPLog.i("MsgCallback", new Object[]{"processName:", var3});
            if (var1 != null && var1.getAction() != null) {
                UPLog.i("MsgCallback", new Object[]{"action:", var1.getAction()});
                String var4 = var1.getAction();
                byte var5 = -1;
                ...........
                final String var6;
                String var9;
                boolean var17;
                UPushSettingCallback var20;
                String var23;
                switch(var5) {
                case 0:
                    try {
                        var6 = var1.getStringExtra("registration_id");
                        boolean var22 = var1.getBooleanExtra("status", false);
                        UPLog.i("MsgCallback", new Object[]{"u-push regId:", var6, ", status:" + var22});
                        final UPushRegisterCallback var24 = PushAgent.getInstance(var2).getRegisterCallback();
                        if (var22) {
                            y.b(new Runnable() {
                                public void run() {
                                    String var1 = "";

                                    try {
                                        MessageSharedPrefs var2x = MessageSharedPrefs.getInstance(var2);
                                        var1 = var2x.getDeviceToken();
                                        if (var6 != null && var1 != null && !var6.equals(var1)) {
                                            var2x.setRegistered(false);
                                            var2x.setDeviceToken(var6);
                                            var2.getContentResolver().delete(aw.e, (String)null, (String[])null);
                                            var2x.resetTags();
                                        }
                                    } catch (Exception var3) {
                                    }

                                    if (var24 != null) {
                                        var24.onSuccess(var6);
                                    }

                                    an.a().a(var1);
                                    PushAgent.getInstance(var2).onAppStart();
                                }
                            });
                        } else if (var24 != null) {
                            var9 = var1.getStringExtra("s");
                            String var10 = var1.getStringExtra("s1");
                            var24.onFailure(var9, var10);
                        }
                    } catch (Throwable var16) {
                        var16.printStackTrace();
                    }
                    break;
                    .......

能看到UmengMessageCallbackHandlerService中的代码,如果注册成功获取到devicetoken之后,会有一系列操作,包括保存token,设置状态,调用成功的回调方法等等。根据日志能观察到,如果注册成功了,日志会输出MsgCallback u-push regId:xxxxxxxx,也就是输出devicetoken。

这一套流程就是,友盟调用TaobaoRegister.register()方法去注册,如果TaobaoRegister.register注册成功了会执行JobIntentService来发送注册成功的广播。对比了日志后发现,在出问题的情况,也就是收不到token 的时候,ak文件的a方法中

UMJobIntentService.enqueueWork(am.b(), UmengMessageCallbackHandlerService.class, var2);

这行代码正常情况下会启动UmengMessageCallbackHandlerService,并执行里面的回调,再去回调前面pushAgent的register的成功回调。但是这个Service不一定会如约启动,经过观察对比,如果手机没充着电,90%以上概率不会启动这个服务。具体原因还需要再研究,但是现象已经观测出来了。
总结流程:
1.pushAgent.register
2.TaobaoRegister.register
3.回调ak.a()
4.启动UmengMessageCallbackHandlerService
5.回调UPushRegisterCallback的onSuccess
6.获取到devicetoken
现在就是在第4步失败,不能启动Service。所以解决方案就在第4步之前去找。
关注第4步之前的TaobaoRegister.register,进到TaobaoRegister.register内部查看源码:

 public static synchronized void register(Context var0, String var1, String var2, String var3, String var4, IRegister var5) throws AccsException {
            .......
            com.taobao.accs.b var9 = ACCSManager.getAccsInstance(var0, var2, var1);
            var9.a(var7, var2, var3, var4, new h(var7, var0, var5, var2, var4, var9));
            ......
    }

var5就是注册结果响应的callback,var5被用来创建了一个h对象。进到h类中查看源码,var5传进去变成了h类的c参数,在onBindApp的方法中被调用:

 public void onBindApp(int var1, String var2) {
        ......
        if (this.c != null) {
            this.c.onSuccess(Config.f(this.a));
        }
        ......
    }

看到这里有个Config.f(this.a),也就是这个方法的返回值就是devicetoken,就是把它经过刚才的好几个步骤回调回去才是注册成功。然后查看一下这个类是个public的,方法也是public,所以通过这个方法就可以在第4步之前,实际上第2步就拿到devicetoken。
这时候只需要在pushAgent.register注册之后做一个循环,反复调用Config.f(context)方法,只到返回有值的时候就认为获取到了token。经过多次测试,最多在5秒就可以获取到。要记得,这一系列过程只是解决了应用第一次安装时没有token的问题,并不是每次启动应用都是需要最多5秒来获取token。
其中service为什么只在充电时候启动的原理还没有深入研究。初步检查源码应该是和电源管理,进程调度有关,需要再花时间研究一下。`

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

推荐阅读更多精彩内容