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

2022-02-21  本文已影响0人  碧桃鹦鹉

最近又对接了一次友盟推送,但是没有之前的那么顺利,因为在华为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为什么只在充电时候启动的原理还没有深入研究。初步检查源码应该是和电源管理,进程调度有关,需要再花时间研究一下。`

上一篇下一篇

猜你喜欢

热点阅读