package com.huawei.android.hms.agent.common; import android.app.Activity; import android.app.Application; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Message; import com.huawei.android.hms.agent.HMSAgent; import com.huawei.hms.activity.BridgeActivity; import com.huawei.hms.api.ConnectionResult; import com.huawei.hms.api.HuaweiApiAvailability; import com.huawei.hms.api.HuaweiApiClient; import com.huawei.hms.support.api.game.HuaweiGame; import com.huawei.hms.support.api.hwid.HuaweiId; import com.huawei.hms.support.api.hwid.HuaweiIdSignInOptions; import com.huawei.hms.support.api.pay.HuaweiPay; import com.huawei.hms.support.api.push.HuaweiPush; import com.huawei.hms.support.api.sns.HuaweiSns; import java.util.ArrayList; import java.util.List; /** * Huawei Api Client 管理类 | Huawei API Client Management class * 负责HuaweiApiClient的连接,异常处理等 | Responsible for huaweiapiclient connection, exception handling, etc. */ public final class ApiClientMgr implements HuaweiApiClient.ConnectionCallbacks, HuaweiApiClient.OnConnectionFailedListener, IActivityResumeCallback, IActivityPauseCallback, IActivityDestroyedCallback { /** * 单实例 | Single Instance */ public static final ApiClientMgr INST = new ApiClientMgr(); /** * 应用市场包名 | HiApp's package name */ private static final String PACKAGE_NAME_HIAPP = "com.huawei.appmarket"; /** * 回调锁,避免连接回调紊乱 | Callback lock to avoid connection callback disorder */ private static final Object CALLBACK_LOCK = new Object(); /** * 静态注册回调锁,避免注册和回调紊乱 | Static registration callback lock to avoid registration and callback disturbances */ private static final Object STATIC_CALLBACK_LOCK = new Object(); /** * client操作锁,避免连接使用紊乱 | Client operation lock, avoid connection use disorder */ private static final Object APICLIENT_LOCK = new Object(); /** * api client 连接超时 | API Client Connection Timeout */ private static final int APICLIENT_CONNECT_TIMEOUT = 30000; /** * 解决升级错误时activity onResume 稳定在3秒时间判断BridgeActivity上面有没有其他activity * To resolve an upgrade error, activity Onresume stable at 3 seconds to determine if there are any other activity on bridgeactivity. */ private static final int UPDATE_OVER_ACTIVITY_CHECK_TIMEOUT = 3000; /** * api client 解决错误拉起界面超时 | API client Resolution error Pull interface timeout */ private static final int APICLIENT_STARTACTIVITY_TIMEOUT = 3000; /** * client 连接超时消息 | Client Connection Timeout Message */ private static final int APICLIENT_TIMEOUT_HANDLE_MSG = 3; /** * client 拉起activity超时消息 | Client starts Activity Timeout message */ private static final int APICLIENT_STARTACTIVITY_TIMEOUT_HANDLE_MSG = 4; /** * 解决升级错误时activity onResume 稳定在3秒时间判断BridgeActivity上面有没有其他activity * To resolve an upgrade error, activity Onresume stable at 3 seconds to determine if there are any other activity on bridgeactivity. */ private static final int UPDATE_OVER_ACTIVITY_CHECK_TIMEOUT_HANDLE_MSG = 5; /** * 最大尝试连接次数 | Maximum number of attempts to connect */ private static final int MAX_RESOLVE_TIMES = 3; /** * 上下文,用来处理连接失败 | Context to handle connection failures */ private Context context; /** * 当前应用包名 | Current Application Package Name */ private String curAppPackageName; /** * HuaweiApiClient 实例 | Huaweiapiclient instance */ private HuaweiApiClient apiClient; /** * 是否允许解决connect错误(解决connect错误需要拉起activity) * Whether to allow the connect error to be resolved (resolve connect error need to pull activity) */ private boolean allowResolveConnectError = false; /** * 是否正在解决连接错误 | Whether a connection error is being resolved */ private boolean isResolving; /** * HMSSDK 解决错误的activity | HMSSDK to solve the wrong activity */ private BridgeActivity resolveActivity; /** * 是否存在其他activity覆盖在升级activity之上 * Is there any other activity covering the escalation activity */ private boolean hasOverActivity = false; /** * 当前剩余尝试次数 | Current number of remaining attempts */ private int curLeftResolveTimes = MAX_RESOLVE_TIMES; /** * 连接回调 | Connection callback */ private List connCallbacks = new ArrayList(); /** * 注册的静态回调 | Registered Static callback */ private List staticCallbacks = new ArrayList(); /** * 超时handler用来处理client connect 超时 * Timeout handler to handle client connect timeout */ private Handler timeoutHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { boolean hasConnCallbacks; synchronized (CALLBACK_LOCK) { hasConnCallbacks = !connCallbacks.isEmpty(); } if (msg != null && msg.what == APICLIENT_TIMEOUT_HANDLE_MSG && hasConnCallbacks) { HMSAgentLog.d("connect time out"); resetApiClient(); onConnectEnd(HMSAgent.AgentResultCode.APICLIENT_TIMEOUT); return true; } else if (msg != null && msg.what == APICLIENT_STARTACTIVITY_TIMEOUT_HANDLE_MSG && hasConnCallbacks) { HMSAgentLog.d("start activity time out"); onConnectEnd(HMSAgent.AgentResultCode.APICLIENT_TIMEOUT); return true; } else if (msg != null && msg.what == UPDATE_OVER_ACTIVITY_CHECK_TIMEOUT_HANDLE_MSG && hasConnCallbacks) { HMSAgentLog.d("Discarded update dispose:hasOverActivity=" +hasOverActivity + " resolveActivity=" + StrUtils.objDesc(resolveActivity)); if (hasOverActivity && resolveActivity != null && !resolveActivity.isFinishing()) { onResolveErrorRst(ConnectionResult.CANCELED); } return true; } return false; } }); /** * 私有构造方法 | Private construction methods */ private ApiClientMgr () { } /** * 初始化 * @param app 应用程序 */ public void init(Application app) { HMSAgentLog.d("init"); // 保存应用程序context context = app.getApplicationContext(); // 取得应用程序包名 curAppPackageName = app.getPackageName(); // 注册activity onResume回调 ActivityMgr.INST.unRegisterActivitResumeEvent(this); ActivityMgr.INST.registerActivitResumeEvent(this); // 注册activity onPause回调 ActivityMgr.INST.unRegisterActivitPauseEvent(this); ActivityMgr.INST.registerActivitPauseEvent(this); // 注册activity onDestroyed 回调 ActivityMgr.INST.unRegisterActivitDestroyedEvent(this); ActivityMgr.INST.registerActivitDestroyedEvent(this); } /** * 断开apiclient,一般不需要调用 */ public void release() { HMSAgentLog.d("release"); isResolving = false; resolveActivity = null; hasOverActivity = false; HuaweiApiClient client = getApiClient(); if (client != null) { client.disconnect(); } synchronized (APICLIENT_LOCK) { apiClient = null; } synchronized (STATIC_CALLBACK_LOCK) { staticCallbacks.clear(); } synchronized (CALLBACK_LOCK) { connCallbacks.clear(); } } /** * 获取当前的 HuaweiApiClient * @return HuaweiApiClient 实例 */ public HuaweiApiClient getApiClient() { synchronized (APICLIENT_LOCK) { return apiClient != null ? apiClient : resetApiClient(); } } /** * 判断client是否已经连接 * @param client 要检测的client * @return 是否已经连接 */ public boolean isConnect(HuaweiApiClient client) { return client != null && client.isConnected(); } /** * 注册apiclient连接事件 * @param staticCallback 连接回调 */ public void registerClientConnect(IClientConnectCallback staticCallback){ synchronized (STATIC_CALLBACK_LOCK) { staticCallbacks.add(staticCallback); } } /** * 反注册apiclient连接事件 * @param staticCallback 连接回调 */ public void removeClientConnectCallback(IClientConnectCallback staticCallback) { synchronized (STATIC_CALLBACK_LOCK) { staticCallbacks.remove(staticCallback); } } /** * 重新创建apiclient * 2种情况需要重新创建:1、首次 2、client的状态已经紊乱 * @return 新创建的client */ private HuaweiApiClient resetApiClient(){ if (context == null) { HMSAgentLog.e("HMSAgent not init"); return null; } synchronized (APICLIENT_LOCK) { if (apiClient != null) { // 对于老的apiClient,1分钟后才丢弃,防止外面正在使用过程中这边disConnect了 disConnectClientDelay(apiClient, 60000); } HMSAgentLog.d("reset client"); // 这种重置client,极端情况可能会出现2个client都回调结果的情况。此时可能出现rstCode=0,但是client无效。 // 因为业务调用封装中都进行了一次重试。所以不会有问题 HuaweiIdSignInOptions signInOptions = new HuaweiIdSignInOptions.Builder(HuaweiIdSignInOptions.DEFAULT_SIGN_IN).requestAccessToken().requestOpenId().requestUnionId().build(); apiClient = new HuaweiApiClient.Builder(context) .addApi(HuaweiPay.PAY_API) .addApi(HuaweiGame.GAME_API) .addApi(HuaweiSns.API) .addApi(HuaweiId.SIGN_IN_API, signInOptions) .addApi(HuaweiPush.PUSH_API) .addConnectionCallbacks(INST) .addOnConnectionFailedListener(INST) .build(); return apiClient; } } /** * 连接 HuaweiApiClient, * @param callback 连接结果回调,一定不能为null,在子线程进行回调 * @param allowResolve 是否允许解决错误,解决错误时可能会拉起界面 */ public void connect(IClientConnectCallback callback, boolean allowResolve) { if (context == null) { aSysnCallback(HMSAgent.AgentResultCode.HMSAGENT_NO_INIT, callback); return; } HuaweiApiClient client = getApiClient(); // client 有效,则直接回调 if (client != null && client.isConnected()) { HMSAgentLog.d("client is valid"); aSysnCallback(HMSAgent.AgentResultCode.HMSAGENT_SUCCESS, callback); return; } else { // client无效,将callback加入队列,并启动连接 synchronized (CALLBACK_LOCK) { HMSAgentLog.d("client is invalid:size=" + connCallbacks.size()); allowResolveConnectError = allowResolveConnectError || allowResolve; if (connCallbacks.isEmpty()) { connCallbacks.add(callback); // 连接尝试最大次数 curLeftResolveTimes = MAX_RESOLVE_TIMES; startConnect(); } else { connCallbacks.add(callback); } } } } /** * 线程中进行Huawei Api Client 的连接 */ private void startConnect() { // 触发一次连接将重试次数减1 curLeftResolveTimes--; HMSAgentLog.d("start thread to connect"); ThreadUtil.INST.excute(new Runnable() { @Override public void run() { HuaweiApiClient client = getApiClient(); if (client != null) { HMSAgentLog.d("connect"); Activity curActivity = ActivityMgr.INST.getLastActivity(); // 考虑到有cp后台需要调用接口,HMSSDK去掉了activity不能为空的判断。这里只是取当前activity,可能为空 timeoutHandler.sendEmptyMessageDelayed(APICLIENT_TIMEOUT_HANDLE_MSG, APICLIENT_CONNECT_TIMEOUT); client.connect(curActivity); } else { HMSAgentLog.d("client is generate error"); onConnectEnd(HMSAgent.AgentResultCode.RESULT_IS_NULL); } } }); } /** * Huawei Api Client 连接结束方法 * @param rstCode client 连接结果码 */ private void onConnectEnd(final int rstCode) { HMSAgentLog.d("connect end:" + rstCode); synchronized (CALLBACK_LOCK) { // 回调各个回调接口连接结束 for (IClientConnectCallback callback : connCallbacks) { aSysnCallback(rstCode, callback); } connCallbacks.clear(); // 恢复默认不显示 allowResolveConnectError = false; } synchronized (STATIC_CALLBACK_LOCK) { // 回调各个回调接口连接结束 for (IClientConnectCallback callback : staticCallbacks) { aSysnCallback(rstCode, callback); } staticCallbacks.clear(); } } /** * 起线程回调各个接口,避免其中一个回调者耗时长影响其他调用者 * @param rstCode 结果码 * @param callback 回调 */ private void aSysnCallback(final int rstCode, final IClientConnectCallback callback) { ThreadUtil.INST.excute(new Runnable() { @Override public void run() { HuaweiApiClient client = getApiClient(); HMSAgentLog.d("callback connect: rst=" + rstCode + " apiClient=" + client); callback.onConnect(rstCode, client); } }); } /** * Activity onResume回调 * * @param activity 发生 onResume 事件的activity */ @Override public void onActivityResume(Activity activity) { // 通知hmssdk activity onResume了 HuaweiApiClient client = getApiClient(); if (client != null) { HMSAgentLog.d("tell hmssdk: onResume"); client.onResume(activity); } // 如果正在解决错误,则处理被覆盖的场景 HMSAgentLog.d("is resolving:" + isResolving); if (isResolving && !PACKAGE_NAME_HIAPP.equals(curAppPackageName)) { if (activity instanceof BridgeActivity) { resolveActivity = (BridgeActivity)activity; hasOverActivity = false; HMSAgentLog.d("received bridgeActivity:" + StrUtils.objDesc(resolveActivity)); } else if (resolveActivity != null && !resolveActivity.isFinishing()){ hasOverActivity = true; HMSAgentLog.d("received other Activity:" + StrUtils.objDesc(resolveActivity)); } timeoutHandler.removeMessages(UPDATE_OVER_ACTIVITY_CHECK_TIMEOUT_HANDLE_MSG); timeoutHandler.sendEmptyMessageDelayed(UPDATE_OVER_ACTIVITY_CHECK_TIMEOUT_HANDLE_MSG, UPDATE_OVER_ACTIVITY_CHECK_TIMEOUT); } } /** * Activity onPause回调 * * @param activity 发生 onPause 事件的activity */ @Override public void onActivityPause(Activity activity) { // 通知hmssdk,activity onPause了 HuaweiApiClient client = getApiClient(); if (client != null) { client.onPause(activity); } } /** * Activity onPause回调 * * @param activityDestroyed 发生 onDestroyed 事件的activity * @param activityNxt 下个要显示的activity */ @Override public void onActivityDestroyed(Activity activityDestroyed, Activity activityNxt) { if (activityNxt == null) { // 所有activity销毁后,重置client,否则公告的标志位还在,下次弹不出来 resetApiClient(); } } /** * connect fail 解决结果回调, 由 HMSAgentActivity 在 onActivityResult 中调用 * @param result 解决结果 */ void onResolveErrorRst(int result) { HMSAgentLog.d("result="+result); isResolving = false; resolveActivity = null; hasOverActivity = false; if(result == ConnectionResult.SUCCESS) { HuaweiApiClient client = getApiClient(); if (client != null && !client.isConnecting() && !client.isConnected() && curLeftResolveTimes > 0) { startConnect(); return; } } onConnectEnd(result); } /** * HMSAgentActivity 拉起拉了(走了onCreate) */ void onActivityLunched(){ HMSAgentLog.d("resolve onActivityLunched"); // 拉起界面回调,移除拉起界面超时 timeoutHandler.removeMessages(APICLIENT_STARTACTIVITY_TIMEOUT_HANDLE_MSG); isResolving = true; } /** * Huawe Api Client 连接成功回到 */ @Override public void onConnected() { HMSAgentLog.d("connect success"); timeoutHandler.removeMessages(APICLIENT_TIMEOUT_HANDLE_MSG); onConnectEnd(ConnectionResult.SUCCESS); } /** * 当client变成断开状态时会被调用。这有可能发生在远程服务出现问题时(例如:出现crash或资源问题导致服务被系统杀掉)。 * 当被调用时,所有的请求都会被取消,任何listeners都不会被执行。需要 CP 开发代码尝试恢复连接(connect)。 * 应用程序应该禁用需要服务的相关UI组件,等待{@link #onConnected()} 回调后重新启用他们。
* * @param cause 断开的原因. 常量定义: CAUSE_*. */ @Override public void onConnectionSuspended(int cause) { HMSAgentLog.d("connect suspended"); connect(new EmptyConnectCallback("onConnectionSuspended try end:"), true); } /** * 建立client到service的连接失败时调用 * * @param result 连接结果,用于解决错误和知道什么类型的错误 */ @Override public void onConnectionFailed(ConnectionResult result) { timeoutHandler.removeMessages(APICLIENT_TIMEOUT_HANDLE_MSG); if (result == null) { HMSAgentLog.e("result is null"); onConnectEnd(HMSAgent.AgentResultCode.RESULT_IS_NULL); return; } int errCode = result.getErrorCode(); HMSAgentLog.d("errCode=" + errCode + " allowResolve=" + allowResolveConnectError); if(HuaweiApiAvailability.getInstance().isUserResolvableError(errCode) && allowResolveConnectError) { Activity activity = ActivityMgr.INST.getLastActivity(); if (activity != null) { try { timeoutHandler.sendEmptyMessageDelayed(APICLIENT_STARTACTIVITY_TIMEOUT_HANDLE_MSG, APICLIENT_STARTACTIVITY_TIMEOUT); Intent intent = new Intent(activity, HMSAgentActivity.class); intent.putExtra(HMSAgentActivity.CONN_ERR_CODE_TAG, errCode); intent.putExtra(BaseAgentActivity.EXTRA_IS_FULLSCREEN, UIUtils.isActivityFullscreen(activity)); activity.startActivity(intent); return; } catch (Exception e) { HMSAgentLog.e("start HMSAgentActivity exception:" + e.getMessage()); timeoutHandler.removeMessages(APICLIENT_STARTACTIVITY_TIMEOUT_HANDLE_MSG); onConnectEnd(HMSAgent.AgentResultCode.START_ACTIVITY_ERROR); return; } } else { // 当前没有界面处理不了错误 HMSAgentLog.d("no activity"); onConnectEnd(HMSAgent.AgentResultCode.NO_ACTIVITY_FOR_USE); return; } } else { //其他错误码直接透传 } onConnectEnd(errCode); } private static void disConnectClientDelay(final HuaweiApiClient clientTmp, int delay) { new Handler().postDelayed(new Runnable() { @Override public void run() { clientTmp.disconnect(); } }, delay); } }