Android6.0 WMS(六) WMS动画管理

Android的应用启动时,或者切换Activity时都会以动画的方式显示前后两屏的切换过程。动画的原理很简单,把一帧一帧的图像按一定时间间隔显示出来就完成了。

动画的绘制需要定时驱动,通常的做法是启动一个定时消息,每隔一定时间发一个消息,收到消息后输出一帧画面。Android支持VSync信号后,动画的驱动就有VSync信号承担了。

窗口动画的基本元素是窗口Surface中保存的图像,通过对窗口的Surface设置不同的变换矩阵和透明度,然后强制Surface刷新,就能在屏幕上显示出窗口的变化过程。

Choreographer对象初始化

我们先来看WMS中的mChoreographer 变量

    final Choreographer mChoreographer = Choreographer.getInstance();

该变量是一个线程局部存储变量,在它的initialValue中创建了Choreographer对象并返回。这里使用线程局部存储的目录就是保证在线程中只有一个Choreographer对象。

    public static Choreographer getInstance() {
        return sThreadInstance.get();
    }
    private static final ThreadLocal<Choreographer> sThreadInstance =
            new ThreadLocal<Choreographer>() {
        @Override
        protected Choreographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Choreographer(looper);
        }
    };

再来看下Choreographer的构造函数,这里主要是创建了FrameDisplayEventReceiver用来接受VSync信号的对象。

    private Choreographer(Looper looper) {
        mLooper = looper;
        mHandler = new FrameHandler(looper);
        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;//接受VSync信号对象
        mLastFrameTimeNanos = Long.MIN_VALUE;

        mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());//计算刷新的时间间隔

        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
    }

FrameDisplayEventReceiver接受VSync信号

我们在http://blog.csdn.net/kc58236582/article/details/52892384( Android6.0 VSync信号如何到用户进程 )这篇博客已经分析过FrameDisplayEventReceiver的原理了,当VSync信号过来时,最后会调用到FrameDisplayEventReceiver类的onVsync函数:

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                scheduleVsync();
                return;
            }

            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

这主要是发送了一个信号,而是是Runnable的那种消息。

因此我们主要看下这个类的run函数,这里就是调用了Choreographer的doFrame函数。

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }

doFrame函数

doFrame函数主要有一些VSync时间逻辑处理如果抛弃该VSync信号的话会调用scheduleVsyncLocked函数让SurfaceFlinger发送一个VSync信号,如果正常会调用4个doCallBacks函数。

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            ......

            long intendedFrameTimeNanos = frameTimeNanos;
            startNanos = System.nanoTime();
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= mFrameIntervalNanos) {
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                frameTimeNanos = startNanos - lastFrameOffset;
            }

            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                            + "previously skipped frame.  Waiting for next vsync.");
                }
                scheduleVsyncLocked();//让SurfaceFlinger立马发送一个VSync信号
                return;
            }

            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }

        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);//按键相关

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);//动画相关

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);//power相关

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

doCallbacks函数,我们首先会检查当前这个CallBackType是否有对应的CallBack回调,如果没有直接return,如果有的话会调用其回调的run函数。

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {//没有对应CallBack回调
                return;
            }
            mCallbacksRunning = true;

            // safe by ensuring the commit time is always at least one frame behind.
            if (callbackType == Choreographer.CALLBACK_COMMIT) {
                final long jitterNanos = now - frameTimeNanos;
                Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                if (jitterNanos >= 2 * mFrameIntervalNanos) {
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                            + mFrameIntervalNanos;
                    if (DEBUG_JANK) {
                        mDebugPrintNextFrameTimeDelta = true;
                    }
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                }
            }
        }
        try {
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                c.run(frameTimeNanos);//调用回调run函数
            }
        }
        ......
    }

这也就意味着当你没有CallBackType对应的回调,每次VSync信号过来到doFrame函数再到doCallBacks函数都是没有意义的。

WMS启动动画

那我们下面看在哪里把CallBackType对应的回调加入了,这里我们只关注动画相关的。

上面我们说到VSync会不断的发送,每秒60多次,但是动画不会不停的播放,就是这个CallBackType对应的回调没有。哪动画的启动和结束也就是受这个影响,而就是在WMS中调用scheduleAnimationLocked函数发起的动画启动。

    void scheduleAnimationLocked() {
        if (!mAnimationScheduled) {
            mAnimationScheduled = true;
            mChoreographer.postFrameCallback(mAnimator.mAnimationFrameCallback);
        }
    }

这里就是调用Choreographer设置CallBackType,相关的回调。这里我们的callbackType是CALLBACK_ANIMATION

    public void postFrameCallback(FrameCallback callback) {
        postFrameCallbackDelayed(callback, 0);
    }

    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }

        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

我们最后看postCallbackDelayedInternal函数,就是在mCallBackQueues对应的CallBackType中增加相应的回调。这里也就是前面在WMS的scheduleAnimationLocked的参数mAnimator.mAnimationFrameCallback就是回调。

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

我们来看下scheduleFrameLocked函数,我们注意mFrameScheduled这个变量,这个时候赋值为true,后面就是用这个变量来控制每次VSync信号过来调用doFrame函数的时候是否要播放动画

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;//注意这个变量
            if (USE_VSYNC) {
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();//尽快让SurfaceFlinger中的EventThread发送一个VSync信号
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

我们再回过头来看doFrame函数,当mFrameScheduled为false时,VSync信号过来该函数直接return不会播放动画。

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }

继续看postCallbackDelayedInternal函数中增加的回调,这个回调在WindowAnimator的构造函数中就新建了Choreographer.FrameCallback回调

    WindowAnimator(final WindowManagerService service) {
        mService = service;
        mContext = service.mContext;
        mPolicy = service.mPolicy;

        mAnimationFrameCallback = new Choreographer.FrameCallback() {
            public void doFrame(long frameTimeNs) {
                synchronized (mService.mWindowMap) {
                    mService.mAnimationScheduled = false;
                    animateLocked(frameTimeNs);
                }
            }
        };
    }

我们最后看回调的run函数,如果是FRAME_CALLBACK_TOKEN,就是调用回调的doFrame函数。

    private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
    }

播放动画

在上面doFrame函数启动动画,而动画的播放主要在WindowAnimator的animateLocked函数。

    private void animateLocked(long frameTimeNs) {
        ......
        boolean wasAnimating = mAnimating;
        mAnimating = false;//设置mAnimating为false
        mAppWindowAnimating = false;

        SurfaceControl.openTransaction();
        SurfaceControl.setAnimationTransaction();
        try {
            final int numDisplays = mDisplayContentsAnimators.size();
            for (int i = 0; i < numDisplays; i++) {
                final int displayId = mDisplayContentsAnimators.keyAt(i);
                updateAppWindowsLocked(displayId);
                ......

                // Update animations of all applications, including those
                // associated with exiting/removed apps
                updateWindowsLocked(displayId);
                updateWallpaperLocked(displayId);

                final WindowList windows = mService.getWindowListLocked(displayId);
                final int N = windows.size();
                for (int j = 0; j < N; j++) {
                    windows.get(j).mWinAnimator.prepareSurfaceLocked(true);//输出动画帧
                }
            }

            for (int i = 0; i < numDisplays; i++) {
                final int displayId = mDisplayContentsAnimators.keyAt(i);

                testTokenMayBeDrawnLocked(displayId);

                final ScreenRotationAnimation screenRotationAnimation =
                        mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation;
                if (screenRotationAnimation != null) {
                    screenRotationAnimation.updateSurfacesInTransaction();
                }

                mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();

                if (mService.mAccessibilityController != null
                        && displayId == Display.DEFAULT_DISPLAY) {
                    mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
                }
            }

            if (mAnimating) {//为true,继续调用WMS的scheduleAnimationLocked播放下一帧
                mService.scheduleAnimationLocked();
            }

            ......
        finally {
            SurfaceControl.closeTransaction();
        }

        ......
        boolean doRequest = false;
        if (mBulkUpdateParams != 0) {
            doRequest = mService.copyAnimToLayoutParamsLocked();
        }

        if (hasPendingLayoutChanges || doRequest) {
            mService.requestTraversalLocked();//重新刷新UI
        }

        if (!mAnimating && wasAnimating) {
            mService.requestTraversalLocked();
        }
    }

animateLocked方法先将mAnimating 设置为false,然后调用updateWindowsLocked函数和updateWallpaperLocked函数,updateWindowsLocked这个函数会调用WindowStateAnimator类的stepAnimationLocker方法,如果动画已经显示完最后一帧,stepAnimationLocker方法将会WindowStateAnimator类的mAnimating设置为false,表示该窗口的动画已经结束。而在updateWallpaperLocked函数中会判断所有窗口的动画是否已经结束,只要有一个动画没结束,就会将winAnimator的mAnimating设置为true。

        for (int i = windows.size() - 1; i >= 0; i--) {
            final WindowState win = windows.get(i);
            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (winAnimator.mSurfaceControl == null) {
                continue;
            }

            final int flags = win.mAttrs.flags;

            if (winAnimator.mAnimating) {
                ......
                mAnimating = true;
            }
        ......

再回到animatelocked方法,当mAnimating为true是会调用WMS的scheduleAnimationLocked方法继续显示动画,否则动画显示就结束了。

下面我们总结下动画的播放过程:需要播放动画时,先会调用WMS的scheduleAnimationLocked方法。调用这个方法后,才会接受并处理一次VSync信号,对VSync信号的处理,就是所有需要绘制的窗口根据各自动画的谁知重新调整窗口Surface的变化矩阵和透明度;如果还有窗口动画需要显示,继续调用scheduleAnimationLocked方法准备下一帧。

准备一帧动画的时间可以跨越多个VSync信号周期,但是只有收到VSync信号才能更新窗口的Surface的属性和内容,对应用而言收到VSync信号意味着SurfaceFlinger中已经把上次设置的动画数据取走了,可以安全地设置下一帧动画的属性和内容了。

窗口动画对象WindowStateAnimator

窗口对象WindowState中定义了一个类型为WindowStateAnimator的成员变量mWinAnimator,用来表示窗口的动画对象。

下面是一些成员变量

    boolean mAnimating;//表示是否正在显示动画
    boolean mLocalAnimating;//表示窗口动画是否已经初始化
    Animation mAnimation;//表示窗口动画对象
    boolean mAnimationIsEntrance;//
    boolean mHasTransformation;//表示当前动画的mTransformation是否可用
    boolean mHasLocalTransformation;//表示当前动画时一个窗口动画还是Activity动画
    final Transformation mTransformation = new Transformation();//变换矩阵对象

当前正在显示的动画有两种类型,一种的窗口切换动画,一种是Activity切换动画,这里使用了mLocalAnimating和mHasLocalTransformation分别表示窗口动画的状态。

stepAnimationLocked是WindowStateAnimator类中显示动画首先调用的方法,它会初始化WindowStateAnimator对象的一些成员变量

    boolean stepAnimationLocked(long currentTime) {
        final DisplayContent displayContent = mWin.getDisplayContent();
        if (displayContent != null && mService.okToDisplay()) {

            if (mWin.isDrawnLw() && mAnimation != null) {//窗口准备好绘制了,窗口动画对象不为空
                mHasTransformation = true;
                mHasLocalTransformation = true;
                if (!mLocalAnimating) {//还没有初始化窗口对象
                    final DisplayInfo displayInfo = displayContent.getDisplayInfo();
                    if (mAnimateMove) {
                        mAnimateMove = false;
                        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),//初始化窗口对象
                                mAnimDw, mAnimDh);
                    } else {
                        mAnimation.initialize(mWin.mFrame.width(), mWin.mFrame.height(),
                                displayInfo.appWidth, displayInfo.appHeight);
                    }
                    mAnimDw = displayInfo.appWidth;
                    mAnimDh = displayInfo.appHeight;
                    mAnimation.setStartTime(mAnimationStartTime != -1
                            ? mAnimationStartTime
                            : currentTime);
                    mLocalAnimating = true;// 设置为true代表已经初始化窗口对象
                    mAnimating = true;
                }
                if ((mAnimation != null) && mLocalAnimating) {
                    mLastAnimationTime = currentTime;
                    if (stepAnimation(currentTime)) {//通过时间判断动画是否显示完毕
                        return true;
                    }
                }
            }
            mHasLocalTransformation = false;
            if ((!mLocalAnimating || mAnimationIsEntrance) && mAppAnimator != null//没有设置窗口动画或者窗口动画结束了
                    && mAppAnimator.animation != null) {
                // 如果有Activity动画,将mAnimating设为true
                mAnimating = true;
                mHasTransformation = true;
                mTransformation.clear();
                return false;
            } else if (mHasTransformation) {
                // Little trick to get through the path below to act like
                // we have finished an animation.
                mAnimating = true;
            } else if (isAnimating()) {
                mAnimating = true;
            }
        } else if (mAnimation != null) {
            mAnimating = true;
        }

        if (!mAnimating && !mLocalAnimating) {
            return false;
        }

        mAnimating = false;
        mKeyguardGoingAwayAnimation = false;
        mAnimatingMove = false;
        mLocalAnimating = false;
        ......
        mHasLocalTransformation = false;
        ......
        mTransformation.clear();
        ......

        return false;
    }

该方法的工作就是设置WindowStateAnimator对象的几个成员变量,首先调用WindowState对象的isDrawnLw来判断窗口系统的状态,只有准备好了才能显示,接着判断mAnimation是否为空,不为空代表已经设置好了动画对象。

接下来判断mLocalAnimating变量的值,为false则调用mAnimation的intialize方法来完成动画对象的初始化(主要设置动画的高度和宽度),然后将mLocalAnimating和mAnimating设为true。完成初始化后,接着调用stepAnimation方法来判断动画是否已经显示完成,没有完成返回true。

如果没有设置动画或者动画已经结束了,则还有判断窗口所在的Activity是否还存在动画,如果有,将mAnimating设置true(表示还要继续播放动画),如果同时mHasTransformation的值仍然为true,或者isAnimating方法返回true,也将mAnimating设置为true。

isAnimating会根据当前动画对象mAnimation是否为空,它的附加窗口的动画对象是否为空,以及窗口所在的Activity的动画对象是否为空等条件来判断,这表示只要有可能mAnimating就会设置为true。这样的目的尽量让动画完成显示,即使没有可显示的动画,多刷新几次不会有副作用,但如果少画了一次,屏幕上就可能留下不正确画面了。

    boolean isAnimating() {
        return mAnimation != null
                || (mAttachedWinAnimator != null && mAttachedWinAnimator.mAnimation != null)
                || (mAppAnimator != null && mAppAnimator.isAnimating());
    }

我们再看看动画的生成过程,WindowStateAnimator的prepareSurfaceLocked方法来完成计算一帧动画并显示工作:

    public void prepareSurfaceLocked(final boolean recoveringMemory) {
        ......
        computeShownFrameLocked();//计算要显示的动画帧

        setSurfaceBoundariesLocked(recoveringMemory);

        if (mIsWallpaper && !mWin.mWallpaperVisible) {
            hide();//如果是壁纸窗口,隐藏
        } else if (w.mAttachedHidden || !w.isOnScreen()) {
            hide();//如果窗口不可见,隐藏
            ......
        } else if (mLastLayer != mAnimLayer
                || mLastAlpha != mShownAlpha
                || mLastDsDx != mDsDx
                || mLastDtDx != mDtDx
                || mLastDsDy != mDsDy
                || mLastDtDy != mDtDy
                || w.mLastHScale != w.mHScale
                || w.mLastVScale != w.mVScale
                || mLastHidden) {//每个值是否有变化
            displayed = true;
            mLastAlpha = mShownAlpha;
            mLastLayer = mAnimLayer;
            mLastDsDx = mDsDx;
            mLastDtDx = mDtDx;
            mLastDsDy = mDsDy;
            mLastDtDy = mDtDy;
            w.mLastHScale = w.mHScale;
            w.mLastVScale = w.mVScale;

            if (mSurfaceControl != null) {
                try {
                    mSurfaceAlpha = mShownAlpha;
                    mSurfaceControl.setAlpha(mShownAlpha);
                    mSurfaceLayer = mAnimLayer;
                    mSurfaceControl.setLayer(mAnimLayer);
                    mSurfaceControl.setMatrix(
                            mDsDx * w.mHScale, mDtDx * w.mVScale,
                            mDsDy * w.mHScale, mDtDy * w.mVScale);

                    if (mLastHidden && mDrawState == HAS_DRAWN) {
                        if (showSurfaceRobustlyLocked()) {//输出动画帧
                            mLastHidden = false;
                            if (mIsWallpaper) {
                                mService.dispatchWallpaperVisibility(w, true);
                            }
                            mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                                    WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
                        } else {
                            w.mOrientationChanging = false;
                        }
                    }
                    if (mSurfaceControl != null) {
                        w.mToken.hasVisible = true;
                    }
                } catch (RuntimeException e) {
                    Slog.w(TAG, "Error updating surface in " + w, e);
                    if (!recoveringMemory) {
                        mService.reclaimSomeSurfaceMemoryLocked(this, "update", true);
                    }
                }
            }
        }
        ......
    }

该函数先调用了computeShownFrameLocked函数计算当前需要显示的动画帧数据,mAnimLayer表示窗口的Z轴、mShownAlpha窗口透明度;mDsDx、mDtDx、mDsDy和mDtDy表示二维变换矩阵;w.mHScale w.mVScale表示窗口的缩放比例

只有计算出的数据和上一次数据不一样才会调用showSurfaceRobustlyLocked输出动画帧。



时间: 2024-12-15 19:55:06

Android6.0 WMS(六) WMS动画管理的相关文章

Android6.0运行时权限管理

自从Android6.0发布以来,在权限上做出了很大的变动,不再是之前的只要在manifest设置就可以任意获取权限,而是更加的注重用户的隐私和体验,不会再强迫用户因拒绝不该拥有的权限而导致的无法安装的事情,也不会再不征求用户授权的情况下,就可以任意的访问用户隐私,而且即使在授权之后也可以及时的更改权限.这就是6.0版本做出的更拥护和注重用户的一大体现. 一.认知 今天我们就来学习下Android6.0的权限管理. Android6.0系统把权限分为两个级别: 一个是Normal Permiss

Android拍照,相册选择图片以及Android6.0权限管理

概述 在android开发过程中,拍照或者从相册中选择图片是很常见的功能.下面要说得这个案例比较简单,用户点击按钮选择拍照或者打开相册选择图片,然后将选中的图片显示在手机上.android6.0后,推出了动态权限管理.以往我们将涉及到的权限全部写在清单文件中,只要用户安装了该程序,程序在运行过程中都会获得相应权限.android6.0后,对于一些特别敏感的权限,开发者必须在程序中进行声明.拍照和从相册选择图片都是涉及到用户隐私的敏感权限,必须在程序中进行声明. 大概的流程 创建布局文件,这里不多

Android6.0权限管理以及使用权限该注意的地方

Android 6.0 Marshmallow首次增加了执行时权限管理,这对用户来说,能够更好的了解.控 制 app 涉及到的权限.然而对开发人员来说却是一件比較蛋疼的事情.须要兼容适配,并保证程序功能的正常执行. 什么叫执行时权限管理呢?在Android 6.0以下的系统中,当我们在安装应用的时候,该应用就会提示我们这个应用所须要的权限,假设你要安装.那就必须允许赋予全部权限,可是假设不允许,那就仅仅能取消安装了,有点流氓.并且安装完后,你不能够收回这个权限. 而6.0就做到了执行时权限管理.

Android6.0权限管理

Android 6.0 Marshmallow首次加入了运行时权限管理,这对用户来说,可以更好的了解.控 制 app 涉及到的权限.然而对开发者来说却是一件比较蛋疼的事情,需要兼容适配,并保证程序功能的正常运行. 什么叫运行时权限管理呢?在Android 6.0以下的系统中,当我们在安装应用的时候,该应用就会提示我们这个应用所需要的权限,如果你要安装,那就必须同意赋予所有权限,但是如果不同意,那就只能取消安装了,有点流氓.而且安装完后,你不可以收回这个权限. 而6.0就做到了运行时权限管理,即使

(六)Unity5.0新特性------新动画功能

?? unity 5.0 中的新动画功能 这里是你可以期待的新动画功能快速概述 ! State Machine Behaviours状态机行为 在Unity 5 中,你会能够将StateMachineBehaviour 脚本添加到您的states,当played状态时能接收callbacks回调: ?OnStateEnter ?OnStateUpdate ?OnStateExit ?OnStateMove ?OnStateIK 在你的状态,您可以创建尽可能多的StateMachineBehavi

Android6.0动态权限管理库RuntimePermission

在Android开发中,申请个权限本来很简单的事情,可就因为Android6.0出个动态权限,整的那个复杂度是几何倍增.本着我们开发向简的宗旨,今天我在这推荐一个Android6.0动态权限管理库RuntimePermission,下面我就来介绍它的大用处--化繁为简! 首先,看一下效果演示图: 接下来,我们介绍一下库的功能及其使用方法: 1.功能 Android6.0动态权限管理库,回调模式管理动态权限,无需关注申请权限各种状态,只用关心回调是否有权限,然后做不同逻辑处理! 2.Android

android6.0权限管理工具EasyPermissionUtil

前言 android6.0開始,权限的申请发生了改变,申请变的动态化,也就是执行时权限,和iOS相仿,动态化的意思是指,在每次使用须要危急权限的方法的时候.须要检查程序是否获得了该权限的许可.动态化的权限申请能够让用户更加清晰的知道程序须要什么权限.以及程序中哪些地方的操作须要涉及用户安全. 不再是只在程序安装的时候,一次性把所须要的普通的.危急级别的权限一次性列出来.然后展示给用户. 当project项目的target Sdk为23时,因为考虑到用户假设没有进行权限的动态检查,那么在执行到须要

Android6.0系统添加那些新特性

??? 北京时间9月30日凌晨在美国旧金山举行2015年秋季新品公布会.在公布会上代号为"Marshmallow(棉花糖)"的安卓6.0系统正式推出.新系统的总体设计风格依旧保持扁平化的MeterialDesign风格. Android6.0在对软件体验与执行性能上进行了大幅度的优化.安卓权限系统被又一次设计了. ??? 全新的Android M相比眼下的Android Lollipop(5.0)有二十项重大的改进: ? ? 原文博客请參考:点击打开链接 ??? 一:App Permi

Android6.0系统权限那些事

Android6.0带来了新的权限管理方式,本文主要来源于官方文档,加入了自己的理解,目的是想总结Android6.0权限管理的新方式,其他部分可能主要是总结式的带过,后续再详细分析. 一.Security Architecture(安全体系结构) Android安全体系结构的核心是: 默认情况下没有任何应用有权限去执行对其他应用.操作系统.用户有不利影响的操作.这是一个核心的设计理念.记住这句话对后面的权限管理可以很好的理解. 正式由于这样的设计理念,默认情况下应用不能去读写用户的私有数据(比