技术问题
日志显示初始化失败,appkey、签名、应用包名无效该如何处理?
- 检查工程配置的 appkey 与极光控制台上[应用设置]-[应用信息]上显示的是否一致。
- 检查工程配置的应用包名、Bundle ID 与极光控制台上[极光认证]-[认证设置]-[集成设置]中显示的是否一致。
- 安装 应用签名获取工具,将获取的 Android 应用签名与极光控制台上[极光认证]-[认证设置]-[集成设置]中显示的是否一致。
一键登录返回 6004 错误码,该如何处理?
问题场景:
- 需要从授权页面,跳转到其他自定义页面(如验证码登录页面)后,重新返回授权页面。重新返回授权页面的时候,再次调用请求授权一键登录接口,此时会返回 6004 错误码。
问题分析:
- 出现该问题是由于在授权页还没有关闭的情况下,再次调用请求授权一键登录接口。
解决方案(选其一):
在不关闭授权页面的情况下,从自定义页面返回授权页面时,不需要再次调用一键登录接口,直接关闭当前页面即可回到授权页面。
如果必须要关闭授权页面,需要在跳转自定义页面之前,调用 dismissLogin 接口关闭授权页面,之后再在自定义页面重新调用请求授权一键登录接口。
调用 loginTokenVerify API,返回 9021/9020 该如何处理?
检查客户端和后台配置的 appkey 是否一致。
若 appkey 配置一致,检查 loginToken 是否正确,注意 loginToken 不是号码认证的 token。loginToken 获取方法参考:Android SDK 拉起授权页面、iOS SDK 拉起授权页面 。
若后台使用 PHP 开发,请确认后台传的 loginToken 与客户端获取的 loginToken 是否一致,若客户端传到后台的 loginToken 的值有 “+”,PHP 会默认把 “+” 替换成空格,导致 loginToken 校验失败。
号码认证和一键登录的 token 具有 使用有效期,且每个 token 只能使用一次。
生成秘钥对的时候不要设置证书密码,密钥格式设置为 PKCS#8 。
RSA 私钥解密失败,该如何处理?
- 在 公私钥校验工具 检查公私钥是否匹配。
- 检查解密参数是否为 loginTokenVerify API 获取的加密后的手机号码。
- 使用 RSA 解密工具 解密,若能解密成功则说明解密代码有问题,需要排查解密代码逻辑。
H5 号码认证返回 2002 错误码,该如何处理?
生产上有 https 访问的,https 请求跨域,会导致上报的 referer 为空,请在 head 中添加代码:
<meta content="always" name="referrer">
如果有强转 https 的设置,需要去掉此设置。
如何解决状态栏白底白字问题?
部分手机出现状态栏白底白字的情况,可以用以下代码解决:
// 在application onCreate方法中调用如下代码
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity
, @Nullable Bundle savedInstanceState) {
String activityName = activity.getClass().getSimpleName();
if ("GenLoginAuthActivity".equals(activityName)
|| "CtLoginActivity".equals(activityName)
|| "LoginAuthActivity".equals(activityName)) {
if (Build.VERSION.SDK_INT >= 21) {
// 修改状态栏字体颜色,用AndroidX官方兼容API
WindowInsetsControllerCompat wic = WindowCompat
.getInsetsController(activity.getWindow(),
activity.getWindow().getDecorView());
// true表示Light Mode,状态栏字体呈黑色,反之呈白色
wic.setAppearanceLightStatusBars(true);
}
}
}
......
});
}
如何解决 UI 资源加载异常?
问题描述:
- UI 资源加载异常,debug 模式下正常,release 模式下异常。
问题分析:
- 使用自动集成,所有资源文件正常导入。
- 用户开启了混淆,并且防混淆代码配置正常。
- 猜测是 shrinkResources 设置为 true 导致资源被混淆,将 shrinkResources 设置为 false 后报错问题得到解决。
解决方法(选其一):
- 将 shrinkResources 设置为 false。
- 在 res 下创建 raw 文件夹,添加 keep.xml 文件,需要对以下资源配置防混淆:
@drawable/umcsdk*,@drawable/jverify*,@anim/umcsdk*
登录页面中使用的图片资源都需要添加防混淆。例如通过 setLogoImgPath 设置了 logo 图片,对应的图片在开启资源混淆时也需要添加防混淆配置。 比如上图中设置的图标是 ic_icon,最终的防混淆配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@drawable/umcsdk*,@drawable/jverify*,@anim/umcsdk*,@drawable/ic_icon" />
如何在一键登录授权页上进行弹窗处理?
场景描述:
在一键登录授权页上,添加自定义控件,通过点击自定义控件进行弹窗,展现形式如下图:
解决方案:
由于此授权页是运营商直接返回的,开发者无法直接拿到授权页对应的 activity,故无法直接在授权页上弹窗,解决方案如下:
- 通过 Application 的 registerActivityLifecycleCallbacks 回调,监听所有的 activity(详情可百度如何监听),通过匹配运营商的 activity 来获取授权页 activity(context)。
- 点击自定义控件时,在顶层 activity 上进行弹窗,详情如下:
// 在application onCreate方法中调用如下代码
public class MyApplication extends Application {
private static WeakReference<Activity> TOP_ACTIVITY;
public static WeakReference<Activity> getTopActivity() {
return TOP_ACTIVITY;
}
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity
, @Nullable Bundle savedInstanceState) {
TOP_ACTIVITY = new WeakReference<>(activity);
}
......
});
}
}
public class MainActivity extends AppCompatActivity {
List<View> views;
boolean isChecked = false;
LoginSettings settings = new LoginSettings();
settings.setAuthPageEventListener(new AuthPageEventListener() {
@Override
public void onEvent(int i, String s) {
Log.i("jiguang-log", "[login] code = " + i + " result = " + s);
if (i == 2) { //进入登录页后添加监听
isChecked = false;
addDialogIfUnCheck();
} else if (i == 6) { //用户勾选后移除监听
isChecked = true;
removeDialogIfChecked();
} else if (i == 7) { //用户取消勾选后添加监听
isChecked = false;
addDialogIfUnCheck();
}
}
});
/
* 客户未勾选隐私条款需要添加监听
*/
private void addDialogIfUnCheck() {
if (isChecked) return;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (views != null) views.clear();
views = getAllChildViews();
views.get(13).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Activity activity = MyApplication.getTopActivity().get();
dialog(activity);
}
});
ViewGroup vp = (RelativeLayout) views.get(12);
int high = vp.getHeight();
int width = vp.getWidth();
((TextView) views.get(13)).setWidth(width);
((TextView) views.get(13)).setHeight(high);
((TextView) views.get(13)).setGravity(Gravity.CENTER);
views.get(13).setClickable(true);
views.get(13).setFocusable(true);
views.get(13).setEnabled(true);
}
});
}
/
* 客户勾选隐私条款后需要移除监听
*/
private void removeDialogIfChecked() {
if (!isChecked) return;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
views.get(13).setOnClickListener(null);
views.get(13).setClickable(false);
views.get(13).setFocusable(false);
views.get(13).setEnabled(false);
}
});
}
/
* 获取该activity所有view
*/
public List<View> getAllChildViews() {
Activity activity = MyApplication.getTopActivity().get();
View view = activity.getWindow().getDecorView();
return getAllChildViews(view);
}
private List<View> getAllChildViews(View view) {
List<View> allchildren = new ArrayList<View>();
if (view instanceof ViewGroup) {
ViewGroup vp = (ViewGroup) view;
for (int i = 0; i < vp.getChildCount(); i++) {
View viewchild = vp.getChildAt(i);
allchildren.add(viewchild);
allchildren.addAll(getAllChildViews(viewchild));
}
}
return allchildren;
}
private void dialog(Context context) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
alertDialogBuilder.setMessage("请同意页面底部隐私条款");
AlertDialog alertDialog = alertDialogBuilder.create();
alertDialog.show();
}
}