Android如何接入渠道SDK的闪屏页?一个activity就够了

  • 如果你是做SDK开发的,应该接入过很多渠道SDK,常见的渠道有华为、小米、VIVO、OPPO、应用宝、UC九游、三星、小七等,还有一些小的渠道以及一些聚合渠道SDK,成千上万个渠道,其中部分渠道是需要接入他们的闪屏页的
  • 一个公司肯定会有多款游戏,会上不同的渠道,接入不同的渠道SDK,如果每个游戏都去接入渠道SDK,那代价就太大了,所以需要开发一个聚合渠道的统一SDK,然后每个游戏就只接入这一个聚合SDK就行了,今天我们就来探讨一下聚合SDK的闪屏页解决方案
  • 首先定义一个闪屏页Activity,比如就叫QiaoQiaoSplashActivity
    package com.lcq.qiaoqiaosplash.sdk;
    
    import android.animation.Animator;
    import android.animation.ValueAnimator;
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.Window;
    import android.view.WindowManager;
    import android.view.animation.Interpolator;
    import android.widget.ImageView;
    
    public abstract class QiaoQiaoSplashActivity extends Activity {
        protected ImageView img;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            int splashImgId = getResources().getIdentifier("splash_img", "drawable", getPackageName());
            if (splashImgId <= 0) {
                return;
            }
            if (!isTaskRoot()) {//处理应用切到后台,再切回来,重复显示闪屏页的问题
                final Intent intent = getIntent();
                final String intentAction = intent.getAction();
                if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null
                        && intentAction.equals(Intent.ACTION_MAIN)) {
                    finish();
                    return;
                }
            }
            this.requestWindowFeature(Window.FEATURE_NO_TITLE);
            this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
            setContentView(getResources().getIdentifier("splash_img", "layout", getPackageName()));
    
            //加载闪屏图片
            img = findViewById(getResources().getIdentifier("img", "id", getPackageName()));
            img.setImageResource(splashImgId);
    
            startAnim();
        }
    
        public abstract void onQiaoQiaoSplashEnd();
    
        public void startAnim() {
            ValueAnimator va = ValueAnimator.ofFloat(0, 1, 1, 0);
            va.addUpdateListener(animation -> {
                float params = (float) animation.getAnimatedValue();
                img.setAlpha(params);
            });
            va.setInterpolator((Interpolator) input -> input);
            va.setDuration(3000);
            va.setRepeatCount(0);
            va.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    onQiaoQiaoSplashEnd();
                }
    
                @Override
                public void onAnimationCancel(Animator animation) {
                }
    
                @Override
                public void onAnimationRepeat(Animator animation) {
                }
            });
            va.start();
        }
    
        @Override
        public void onBackPressed() {
            //处理闪屏页按返回键不finish
        }
    }
    

    这个类主要就是加载一个ImageView的布局文件splash_img.xml,然后倒计时3秒后回调我们定义的虚方法onQiaoQiaoSplashEnd();

  •  if (splashImgId <= 0)这个用来判断是否存在闪屏图片,如果不存在就直接调用onQiaoQiaoSplashEnd()方法,相当于如果不配置图片就不会显示闪屏页,适用于那些不需要闪屏页的渠道SDK,当然我们游戏可以默认使用一个自己的闪屏页
  • 做isTaskRoot()判定主要是解决应用在切换到后台,再从桌面切换来的时候再次启动了闪屏页,
  • splash_img.xml布局文件就只使用了一个ImageView,用来展示一张图片,充满全屏
    <?xml version="1.0" encoding="utf-8"?>
    <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/img"
        android:layout_width="match_parent"
        android:scaleType="fitXY"
        android:layout_height="match_parent" />
  • 我们的倒计时采用了动画ValueAnimator的方式,通过设置透明度的变化来达到动态效果的目的,当然你也可以对ImageView控件进行缩放处理,
  • 当动画结束回调onAnimationEnd(Animator animation)方法,我们就调用定义的虚方法 onQiaoQiaoSplashEnd(),这个方法由Cp端的闪屏页去实现,因为CP端需要定义一个闪屏页继承我们的QiaoQiaoSplashActivity
  • 一般我们做SDK的都尽量不要使用包名+R的方式去引用资源ID,因为二次打包的时候进行合并的时候是需要去处理这个R文件的,如果不处理会找不到资源ID,导致崩溃问题,所以我们采用了getResources().getIdentifier()的方式去获取资源ID
  • 接下来CP端就需要新建一个类来继承QiaoQiaoSplashActivity了
    package com.lcq.qiaoqiaosplash;
    
    import android.content.Intent;
    
    import com.lcq.qiaoqiaosplash.sdk.QiaoQiaoSplashActivity;
    
    public class CpSplashActivity extends QiaoQiaoSplashActivity {
        @Override
        public void onQiaoQiaoSplashEnd() {
            startActivity(new Intent(this, MainActivity.class));
            finish();
        }
    }
    

    这个类主要干的事情就是实现父类的onQiaoQiaoSplashEnd()方法,主要逻辑就是启动游戏的主界面,然后finish掉自己

  • CP端定义了闪屏页自然也需要在AndroidManifest.xml里面进行申明了

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.lcq.qiaoqiaosplash">
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.QiaoQiaoSplash">
            <activity
                android:name="com.lcq.qiaoqiaosplash.CpSplashActivity"
                android:exported="true"
                android:screenOrientation="portrait">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <activity
                android:name="com.lcq.qiaoqiaosplash.MainActivity"
                android:exported="true"
                android:screenOrientation="portrait" />
        </application>
    
    </manifest>

    CP端申明Activity的时候需要指定屏幕方向,设置android:screenOrientation属性,为了兼容android 12 ,需要申明android:exported属性

  • 当我们接入渠道SDK的时候,是需要显示渠道SDK的闪屏页的,部分渠道SDK是要求显示他们给的图片,这个就最简单了直接替换splash_img.png为渠道给的图片就行,只要名称保持为splash_img.png就行,如果是横屏游戏就给张横屏图片,是竖屏游戏就给张竖屏图片;不管是通过AS出包还是打包工具进行出渠道包,根据渠道来替换掉图片就行
  • 部分渠道需要你去继承渠道方的闪屏页,这个也简单,我们的QiaoQiaoSplashActivity 继承他们的闪屏页Activity就行,然后按要求实现他们的虚方法,或者其他逻辑就行,在他们的闪屏页,也就是QiaoQiaoSplashActivity 的父类回调回来后,我们再执行onQiaoQiaoSplashEnd()方法回调给CP端的闪屏页,这个是比较常规的处理方式,还有一些比较麻烦的闪屏页处理方式
  • 比如有个渠道要求你接入他们的SDK时候,不是去继承他们的闪屏页,而是启动他们的闪屏页,居然是启动人家的闪屏页,那我们自然是不知道,他们的闪屏页是啥时候结束的了,那么我们就没办法通知CP端闪屏结束,进而就不能启动游戏主界面了,就卡在我们的闪屏页了,这个问题我们咋个解决呢,肯定是具体问题具体分析了
  • 如果渠道的闪屏页没干其他啥事,就只是显示一张图片,那么我们拷贝一份他们的图片,并写在我们的闪屏页里面就可以了,如果渠道的闪屏页做了其他事情,比如初始化内部的SDK等不可删除的逻辑,那就必须去启动人家的闪屏页了
  • 要启动渠道的闪屏页,一种是我们去继承渠道的闪屏页,CP端继承我们的闪屏页,这样就可以通过CP端来启动渠道的闪屏页,但是渠道的闪屏页是不会给回调的,我们不知道啥时候回调给CP端闪屏结束,我们可以重写Activity的onFinish()方法,在这个里面去调用 onQiaoQiaoSplashEnd()方法
  • 我们模拟一个渠道的闪屏页,如下
    package com.lcq.qiaoqiaosplash.channel;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.util.Log;
    
    import androidx.annotation.Nullable;
    
    public class ChannelActivity extends Activity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(getResources().getIdentifier("channel_splash_img", "layout", getPackageName()));
            Log.d("", "执行我,不然我就崩溃给你看");
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    Log.d("", "我自己finish了,不通知你,恶心下你");
                    finish();
                }
            }, 3000);
        }
    }
    

    这个ChannelActivity渠道闪屏页不会回调给子类,只会在三秒后finish掉,并执行了一些必要的代码逻辑

  • 然后我们重新改下QiaoQiaoSplashActivity的代码
    package com.lcq.qiaoqiaosplash.sdk;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.widget.ImageView;
    
    import com.lcq.qiaoqiaosplash.channel.ChannelActivity;
    
    public abstract class QiaoQiaoSplashActivity extends Activity {
        protected ImageView img;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (!isTaskRoot()) {//处理应用切到后台,再切回来,重复显示闪屏页的问题
                final Intent intent = getIntent();
                final String intentAction = intent.getAction();
                if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null
                        && intentAction.equals(Intent.ACTION_MAIN)) {
                    finish();
                    return;
                }
            }
            setContentView(getResources().getIdentifier("splash_img", "layout", getPackageName()));
    
            //加载闪屏图片
            img = findViewById(getResources().getIdentifier("img", "id", getPackageName()));
    
            startActivityForResult(new Intent(this, ChannelActivity.class), 1000);
        }
    
        public abstract void onQiaoQiaoSplashEnd();
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == 1000) {
                onQiaoQiaoSplashEnd();
            }
        }
    
        @Override
        public void onBackPressed() {
            //处理闪屏页按返回键不finish
        }
    }
    

    我们在onCreate()方法里面使用startActivityForResult()去启动渠道的闪屏页ChannelActivity,然后重写onActivityResult()方法,主要调用我们的onQiaoQiaoSplashEnd();方法,这样就可以回调给CP端了

  • 还有一种方式,只需要修改下我们的闪屏页代码就行,如下
    package com.lcq.qiaoqiaosplash.sdk;
    
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.widget.ImageView;
    
    import com.lcq.qiaoqiaosplash.channel.ChannelActivity;
    
    public abstract class QiaoQiaoSplashActivity extends Activity {
        protected ImageView img;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (!isTaskRoot()) {//处理应用切到后台,再切回来,重复显示闪屏页的问题
                final Intent intent = getIntent();
                final String intentAction = intent.getAction();
                if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null
                        && intentAction.equals(Intent.ACTION_MAIN)) {
                    finish();
                    return;
                }
            }
             
            onQiaoQiaoSplashEnd();
            startActivity(new Intent(this, ChannelActivity.class));
        }
    
        public abstract void onQiaoQiaoSplashEnd();
    
        @Override
        public void onBackPressed() {
            //处理闪屏页按返回键不finish
        }
    }
    

    在onCreate方法里面直接调用onQiaoQiaoSplashEnd(),然后通过startActivity()启动渠道的闪屏页,onQiaoQiaoSplashEnd()必须先于startActivity()调用,保证先回调给CP端,CP端先启动游戏主页面,之后再启动渠道闪屏页,这样渠道闪屏页会覆盖在游戏主页上面,等渠道的闪屏页finish掉后,就显示主页面了,如果调换了顺序的话,渠道的闪屏页就看不到了

  • 两种方式都能解决这个问题,但是不管哪种方式,都会有视觉误差,毕竟我们启动了一个多余的Activity,我们必选将自己的闪屏背景设置为透明,以便用户无感知中间还有一个Activity
  • 第二种方式的话,用户就看不到游戏的启动过程了,因为用户只是看到了3秒后的游戏主界面,中间的3秒被渠道闪屏页遮住了,其实也还好,能接受吧
  • 其实那么多渠道,肯定还有很多的坑等着我们去踩的,比如游戏渠道的闪屏页信息需要配置在AndroidManifest.xml中,通过meta-data去读取之类的,有些渠道还需要配置游戏主界面的Activity类路径在meta-data中,然后通过反射,SDK自己去启动游戏主界面,这样我们就需要考虑这些渠道的闪屏页如何兼容到我们的闪屏页中来了
  • 如果对闪屏页有兴趣的,需要demo代码的可以访问QiaoQiaoSplash: 聚合SDK渠道SDK闪屏页的解决方案

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值