activity主题透明显示系统桌面的问题

来源:互联网 发布:java迷你图书馆 编辑:程序博客网 时间:2024/04/29 16:39

转载请标明出处:http://blog.csdn.net/EdisonChang/article/details/50986353

前段时间,优化android应用的启动速度时被activity主题是否透明带来的隐患所牵制,虽然辗转找到一个取巧的方案,感兴趣的朋友可以阅读关于Android App启动那些事。但是对于activity透明导致侧滑时露出系统桌面的问题,一直都没有找到其中缘由,心里也是非常的不畅快。

这段时间再次捡起来,也是希望能够找到一些问题。说到这边不得不吐槽下android的运行环境,由于各个厂商对android rom的各种定制修改,在开发中经常会碰到各种适配的问题,处理一些闹心的疑难杂症或者崩溃问题,也都是后知后觉,得等出现问题才去慢慢的填坑。包括这次遇到的问题也一样,activity透明,侧滑出现系统桌面的问题在android原生系统上也是不出现的。所以从grepcode的源码中试图找到问题似乎无迹可寻,博主找了一款出现该问题的手机,试图从它的framework.jar中找出一些端倪,但最终也都失败了,然而博主并没有放弃。

为了寻求其中缘由,还是需要简化应用模型,博主新建一个project,其中只有两个activity,(记为a、b),下面就不再重复介绍,分别测试两个activity配置不同主题时,当a切换到b时,a的生命周期变化 。这里说的不同主题,其实还是指是否指定为透明,

<item name="android:windowIsTranslucent">true</item>

具体的代码,我就不再贴出,有兴趣的朋友可以自行尝试,博主选择出现问题的手机进行测试,有以下一些case:

(1)如果a的主题不透明,b透明,a–>b, a调用onPause方法
(2)如果a的主题不透明,b不透明,a–>b,a调用onPause、onStop方法
(3)如果a的主题透明,b不透明,a–>b ,a调用onPause 、onStop方法
(4)如果a的主题透明,b透明,a–>b,a调用onPause,onStop方法

这里写图片描述

这里写图片描述

那么也就是说当a透明时,无论b是否透明,a–>b均会调用onPause、onStop方法 。
没有对比就没有伤害,anyway,总得在原生系统上做个比较,因为前面博主也提到了,在原生系统上,是不会出现问题的。同样,我们也分成四种情况,

(1)如果a的主题不透明,b透明,a–>b, a调用onPause方法
(2)如果a的主题不透明,b不透明,a–>b,a调用onPause、onStop方法
(3)如果a的主题透明,b不透明,a–>b ,a调用onPause 、onStop方法
(4)如果a的主题透明,b透明,a–>b,a调用onPause方法

从上面的对比,我们可以看出在a、b均为透明时,当a–>b,在原生系统上a只会调用onPause方法,而在出现问题的机器上,在b 调用onResume后,a还会再次调用onStop方法。不得不说此时博主的心情是有点小兴奋的, 让我们来重温一下,activity的onPause、onStop方法的调用时期。

**onPause :**Called when the system is about to start resuming a previous
activity. This is typically used to commit unsaved changes to
persistent data, stop animations and other things that may be
consuming CPU, etc. Implementations of this method must be very quick
because the next activity will not be resumed until this method
returns. Followed by either onResume() if the activity returns back
to the front, or onStop() if it becomes invisible to the user.

**onStop:**Called when the activity is no longer visible to the user,
because another activity has been resumed and is covering this one.
This may happen either because a new activity is being started, an
existing one is being brought in front of this one, or this one is
being destroyed. Followed by either onRestart() if this activity is
coming back to interact with the user, or onDestroy() if this activity
is going away.

通过上面的介绍,我们可以知道在activity回调onPause 时,activiy依然是可见的,但是回调onStop时,是系统认为activity已经对用户不可见,因为系统认为已经有另外的activity已经覆盖在它上面。

回到我们的应用场景,当b为透明时,b如果不设置非透明背景或者背景没有完全充满全屏,对于用户来说a其实仍然是可见。或者可以这么说,当b为透明时,a依然有可见的可能性,当a如果被系统设置为不可见就会出现b没有背景时可以看到桌面,或者使用侧滑功能时出现桌面的问题。 说到这里,其实也明白了就是不同rom对于处理这一类activity的生命周期不同导致了这个问题,后来我将这个问题反馈到一个做rom友商的技术,通过他的协助有一些发现,

a–>b, 如果a的主题是floating或是透明,当b的属性也是透明或者floating,按照我们前面的理解,a不应该调用onstop()。现在调用了onstop的原因是 com.android.server.am.ActivityRecordjava中并没有判断a的主题的类型,导致了a stop了,以下是他给的一些调用代码片段,这些在原生代码中是没有的,

       if (MultiWindowProxy.isFeatureSupport()){                floating = ent != null && ent.array.getBoolean(                        com.android.internal.R.styleable.Window_windowIsFloating, false);                translucent = ent != null && ent.array.getBoolean(                        com.android.internal.R.styleable.Window_windowIsTranslucent, false);                // If this activity is in disable list ,do not modify fullscreen flag.                 /// M: [ALPS02071575] Check packagename if disbled for floating                if ((intent.getFlags()&Intent.FLAG_ACTIVITY_FLOATING) != 0                         && MultiWindowProxy.getInstance() != null                         && !MultiWindowProxy.getInstance().matchDisableFloatActivityList(shortComponentName)                        && !MultiWindowProxy.getInstance().matchDisableFloatPkgList(packageName)){                        fullscreen = false;                }          }

ActivityRecord只有在多窗口的是否才去读activity的主题的属性值, 决定a是否stop的方法是, ActivityStack.java中的stopActivityLocked

r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);

如果r也就是a(activity) 不可见那么就会调stop,因为ActivityThread.java 中是根据a是否可见来判断是否stop,

public final void scheduleStopActivity(IBinder token, boolean showWindow,                int configChanges) {           sendMessage(                showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,                token, 0, configChanges);        }       case STOP_ACTIVITY_SHOW:                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");                    handleStopActivity((IBinder)msg.obj, true, msg.arg2);                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                    break;      case STOP_ACTIVITY_HIDE:                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop");                    handleStopActivity((IBinder)msg.obj, false, msg.arg2);                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                    break;

handleStopActivity(IBinder token, boolean show, int configChanges) 根据show变量决定a是否显示和隐藏。假设我们能够在调用stop之前,将ActivityStack.java中的r.visible置为true,也许就能解决这个问题。

紧接着我发现在ActivityStack中还有这么一逻辑,

// Checks if any of the stacks above this one has a fullscreen activity behind it.    // If so, this stack is hidden, otherwise it is visible.    private boolean isStackVisible() {        if (!isAttached()) {            return false;        }        if (mStackSupervisor.isFrontStack(this)) {            return true;        }        /**         * Start at the task above this one and go up, looking for a visible         * fullscreen activity, or a translucent activity that requested the         * wallpaper to be shown behind it.         */        for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {            final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();            for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {                final TaskRecord task = tasks.get(taskNdx);                final ArrayList<ActivityRecord> activities = task.mActivities;                for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {                    final ActivityRecord r = activities.get(activityNdx);                    // Conditions for an activity to obscure the stack we're                    // examining:                    **// 1. Not Finishing AND Visible AND:                    // 2. Either:                    // - Full Screen Activity OR                    // - On top of Home and our stack is NOT home**                    if (!r.finishing && r.visible && (r.fullscreen ||                            (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {                        return false;                    }                }            }        }        return true;    }

若栈中a之上非透明且非悬浮状态的activity的个数>0,那么a的状态应该是onStop
反之a应该只能onPause状态。到这里我就在想,当a为透明时,我只要判断a之上非透明或是悬浮状态的activity的个数为0,就将a的visiable 属性设置为true,岂不是就能够解决问题。

遗憾的是,管理所有的activity的com.android.server.am.ActivityStack 运行在系统进程,在非root的情况下是干预不了的,(root情况下我并没有试验)。本地应用的进程中的ActivtiyThread里面也有一份ActivityRecord,但是这里面是存储是一个activity的信息,修改它也无济于事。不过,欣慰的是对方已经在rom系统层面上修改了这个问题,相信通过rom的升级,这样的问题会逐渐越少。

到这里就介绍完了,欢迎大家指正。

0 0