Android SingleInstance的Activity与悬浮窗切换逻辑(startActivity无反应)

1、需求

视频通话Activity可切换小窗(悬浮窗)显示。不懂的话就是和微信的视频通话差不多。

2、实现

视频通话Activity全局只能存在一个实例,遂用SingleInstance启动模式。切换到悬浮窗,因为要保存视频通话Activity的状态信息,所以不能销毁Activity,那要怎么样才能返回到上一个页面呢?这里我们利用SingleInstance启动模式的特性:新建一个Activity Task单独用于存放视频通话Activity的实例。我们应该知道Android系统有且仅有一个Activity Task会处于前台,当一个新的Task开启后,原来的Task就会默认退到后台(不可见),一般情况下,新的Task中的Activity销毁后,会返回到上一个Task中的TopActivity页面,但是悬浮窗显示的需求不允许我们销毁这个Task中的Activity,那要用什么方法才能返回到上一个页面而不销毁视频通话Activity呢?

/**
 * 把当前Activity示例所在的Task移动到后台
 * {@link android.app.Activity#moveTaskToBack(boolean nonRoot)}
 * @param nonRoot If false then this only works if the activity is the root
 *                of a task; if true it will work for any activity in
 *                a task.
 */
moveTaskToBack(true);

这样当前就达到了视频通话Activity在不销毁情况下隐藏的需求了。
关于悬浮窗如何实现这里不再赘述了,给大家贴几条链接看下吧。
Android悬浮窗操作使用总结
Andorid 应用内悬浮控件实践方案总结
EasyFloat:浮窗从未如此简单

3、问题

到现在基本实现了此需求的大致逻辑。
点击悬浮窗回到视频通话Activity很简单,调用startActivity重新打开视频通话Activity就好了。
自测没问题,提测后问题来了。。。

荣耀和小米5返回不了视频通话Activity,startActivity丝毫反应都没有,日志也什么也没有,我到最后也没查出个所以然来。所以索性另寻重启视频通话Activity的方法了。

private void moveToFront() {
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    if (manager != null) {
        List<ActivityManager.RunningTaskInfo> recentTasks = manager.getRunningTasks(Integer.MAX_VALUE);
        if (recentTasks != null && !recentTasks.isEmpty()) {
            for (ActivityManager.RunningTaskInfo taskInfo : recentTasks) {
                ComponentName cpn = taskInfo.baseActivity;
                if (null != cpn && TextUtils.equals(VideoChatActivity.class.getName(), cpn.getClassName())) {
                    manager.moveTaskToFront(taskInfo.id, ActivityManager.MOVE_TASK_NO_USER_ACTION);
                    break;
                }
            }
        }
    }
}

moveTaskToFront方法具体详情查看下方源码:

/**
 * Flag for {@link #moveTaskToFront(int, int)}: also move the "home"
 * activity along with the task, so it is positioned immediately behind
 * the task.
 */
public static final int MOVE_TASK_WITH_HOME = 0x00000001;

/**
 * Flag for {@link #moveTaskToFront(int, int)}: don't count this as a
 * user-instigated action, so the current activity will not receive a
 * hint that the user is leaving.
 */
public static final int MOVE_TASK_NO_USER_ACTION = 0x00000002;

/**
 * Equivalent to calling {@link #moveTaskToFront(int, int, Bundle)}
 * with a null options argument.
 *
 * @param taskId The identifier of the task to be moved, as found in
 * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
 * @param flags Additional operational flags.
 */
@RequiresPermission(android.Manifest.permission.REORDER_TASKS)
public void moveTaskToFront(int taskId, @MoveTaskFlags int flags) {
    moveTaskToFront(taskId, flags, null);
}

/**
 * Ask that the task associated with a given task ID be moved to the
 * front of the stack, so it is now visible to the user.
 *
 * @param taskId The identifier of the task to be moved, as found in
 * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
 * @param flags Additional operational flags.
 * @param options Additional options for the operation, either null or
 * as per {@link Context#startActivity(Intent, android.os.Bundle)
 * Context.startActivity(Intent, Bundle)}.
 */
@RequiresPermission(android.Manifest.permission.REORDER_TASKS)
public void moveTaskToFront(int taskId, @MoveTaskFlags int flags, Bundle options) {
    try {
        ActivityThread thread = ActivityThread.currentActivityThread();
        IApplicationThread appThread = thread.getApplicationThread();
        String packageName = mContext.getPackageName();
        getTaskService().moveTaskToFront(appThread, packageName, taskId, flags, options);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

4、总结

没问题的方法就是好方法,希望你能用上。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。