Android6.0 wakelock深入分析

原创 2016年06月02日 14:30:21

这篇博客我们分析下Power的持锁,从PowerManager到PowerManagerService再到hal分析。

一、PowerManager的持锁接口

我们先来看下PowerManager对应用提供的接口:

    public WakeLock newWakeLock(int levelAndFlags, String tag) {
        validateWakeLockParameters(levelAndFlags, tag);//验证wakelock的flag是否有效
        return new WakeLock(levelAndFlags, tag, mContext.getOpPackageName());
    }

validateWakeLockParameters函数如下:主要对flag没有下面这些flag做过滤

    public static void validateWakeLockParameters(int levelAndFlags, String tag) {
        switch (levelAndFlags & WAKE_LOCK_LEVEL_MASK) {
            case PARTIAL_WAKE_LOCK://cpu锁
            case SCREEN_DIM_WAKE_LOCK://屏幕微亮,键盘暗
            case SCREEN_BRIGHT_WAKE_LOCK://屏幕亮,键盘暗
            case FULL_WAKE_LOCK://全亮
            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
            case DOZE_WAKE_LOCK:
            case DRAW_WAKE_LOCK:
                break;
            default:
                throw new IllegalArgumentException("Must specify a valid wake lock level.");
        }
        if (tag == null) {
            throw new IllegalArgumentException("The tag must not be null.");
        }
    }

我们再看WakeLock类,先看下面两个持锁,第二个timeout的持锁,先持锁,然后发送一个延迟消息再解锁。

        public void acquire() {
            synchronized (mToken) {
                acquireLocked();
            }
        }

        public void acquire(long timeout) {
            synchronized (mToken) {
                acquireLocked();
                mHandler.postDelayed(mReleaser, timeout);
            }
        }
        private final Runnable mReleaser = new Runnable() {
            public void run() {
                release();
            }
        };

再来看acquireLocked,流程最后是调用了service的acquireWakeLock,但是有一个细节我们注意下,这里有一个mCount的计数。也就是没调用一次这个函数,都会对wakelock的mCount加1。

        private void acquireLocked() {
            if (!mRefCounted || mCount++ == 0) {
                // Do this even if the wake lock is already thought to be held (mHeld == true)
                // because non-reference counted wake locks are not always properly released.
                // For example, the keyguard's wake lock might be forcibly released by the
                // power manager without the keyguard knowing.  A subsequent call to acquire
                // should immediately acquire the wake lock once again despite never having
                // been explicitly released by the keyguard.
                mHandler.removeCallbacks(mReleaser);
                Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
                try {
                    mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,
                            mHistoryTag);
                } catch (RemoteException e) {
                }
                mHeld = true;
            }
        }

我们再来看看release函数,上面说的mCount计数,这里就会对这个计数进行判断,只有当计数为0,才会去调用service的releaseWakeLock函数。

        public void release(int flags) {
            synchronized (mToken) {
                if (!mRefCounted || --mCount == 0) {
                    mHandler.removeCallbacks(mReleaser);
                    if (mHeld) {
                        Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                        try {
                            mService.releaseWakeLock(mToken, flags);
                        } catch (RemoteException e) {
                        }
                        mHeld = false;
                    }
                }
                if (mCount < 0) {
                    throw new RuntimeException("WakeLock under-locked " + mTag);
                }
            }
        }

所以我们总结下,使用PowerManager的wakelock持锁和释放锁。必须成对出现,当持了2次锁,也必须释放两次锁。才会调用service的释放锁,去真正释放。


二、PowerManagerService相关接口

Service的acquireWakeLock函数我们就不看了,主要是对wakelock的flag做验证,然后调用了acquireWakeLockInternal函数:

    private void acquireWakeLockInternal(IBinder lock, int flags, String tag, String packageName,
            WorkSource ws, String historyTag, int uid, int pid) {
        synchronized (mLock) {
            WakeLock wakeLock;
            int index = findWakeLockIndexLocked(lock);//查找wakelock,IBinder对象为wakelock唯一性
            boolean notifyAcquire;
            if (index >= 0) {
                wakeLock = mWakeLocks.get(index);
                if (!wakeLock.hasSameProperties(flags, tag, ws, uid, pid)) {
                    // Update existing wake lock.  This shouldn't happen but is harmless.
                    notifyWakeLockChangingLocked(wakeLock, flags, tag, packageName,
                            uid, pid, ws, historyTag);
                    wakeLock.updateProperties(flags, tag, packageName, ws, historyTag, uid, pid);//更新参数
                }
                notifyAcquire = false;
            } else {
                wakeLock = new WakeLock(lock, flags, tag, packageName, ws, historyTag, uid, pid);//新建
                try {
                    lock.linkToDeath(wakeLock, 0);
                } catch (RemoteException ex) {
                    throw new IllegalArgumentException("Wake lock is already dead.");
                }
                mWakeLocks.add(wakeLock);
                setWakeLockDisabledStateLocked(wakeLock);
                notifyAcquire = true;
            }

            applyWakeLockFlagsOnAcquireLocked(wakeLock, uid);//看是否需要唤醒设备
            mDirty |= DIRTY_WAKE_LOCKS;
            updatePowerStateLocked();// 更新电源状态
            if (notifyAcquire) {
                notifyWakeLockAcquiredLocked(wakeLock);
            }
        }
    }

updatePowerStateLocked函数我们放在后面说,我们再来看看applyWakeLockFlagsOnAcquireLocked函数:

    private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
        if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0//wakelock中的flag有这个flag就需要唤醒设备
                && isScreenLock(wakeLock)) {
            String opPackageName;
            int opUid;
            if (wakeLock.mWorkSource != null && wakeLock.mWorkSource.getName(0) != null) {
                opPackageName = wakeLock.mWorkSource.getName(0);
                opUid = wakeLock.mWorkSource.get(0);
            } else {
                opPackageName = wakeLock.mPackageName;
                opUid = wakeLock.mWorkSource != null ? wakeLock.mWorkSource.get(0)
                        : wakeLock.mOwnerUid;
            }
            wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
                    opPackageName, opUid);
        }
    }

同样release,我们也直接分析releaseWakeLockInternal函数:

    private void releaseWakeLockInternal(IBinder lock, int flags) {
        synchronized (mLock) {
            int index = findWakeLockIndexLocked(lock);//找wakelock
            if (index < 0) {
                if (DEBUG_LC) {
                    Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock)
                            + " [not found], flags=0x" + Integer.toHexString(flags));
                }
                return;
            }

            WakeLock wakeLock = mWakeLocks.get(index);
            if (DEBUG_LC) {
                Slog.d(TAG, "releaseWakeLockInternal: lock=" + Objects.hashCode(lock)
                        + " [" + wakeLock.mTag + "], flags=0x" + Integer.toHexString(flags));
            }

            if ((flags & PowerManager.RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY) != 0) {//距离传感器相关
                mRequestWaitForNegativeProximity = true;
            }

            wakeLock.mLock.unlinkToDeath(wakeLock, 0);
            removeWakeLockLocked(wakeLock, index);
        }
    }

removeWakeLockLocked函数,去除wakelock,最后调用updatePowerStateLocked

    private void removeWakeLockLocked(WakeLock wakeLock, int index) {
        mWakeLocks.remove(index);//去除wakelock
        notifyWakeLockReleasedLocked(wakeLock);

        applyWakeLockFlagsOnReleaseLocked(wakeLock);//是否触发userActivity
        mDirty |= DIRTY_WAKE_LOCKS;
        updatePowerStateLocked();
    }

applyWakeLockFlagsOnReleaseLocked函数:

    private void applyWakeLockFlagsOnReleaseLocked(WakeLock wakeLock) {
        if ((wakeLock.mFlags & PowerManager.ON_AFTER_RELEASE) != 0//有这个flag触发userActivity
                && isScreenLock(wakeLock)) {
            userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
                    PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS,
                    wakeLock.mOwnerUid);
        }
    }

下面我们就来看看之前acquire和realease都调用的函数updatePowerStateLocked:

    private void updatePowerStateLocked() {
        if (!mSystemReady || mDirty == 0) {
            return;
        }
        if (!Thread.holdsLock(mLock)) {
            Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
        }

        Trace.traceBegin(Trace.TRACE_TAG_POWER, "updatePowerState");
        try {
            // Phase 0: Basic state updates.
            updateIsPoweredLocked(mDirty);
            updateStayOnLocked(mDirty);
            updateScreenBrightnessBoostLocked(mDirty);

            // Phase 1: Update wakefulness.
            // Loop because the wake lock and user activity computations are influenced
            // by changes in wakefulness.
            final long now = SystemClock.uptimeMillis();
            int dirtyPhase2 = 0;
            for (;;) {
                int dirtyPhase1 = mDirty;
                dirtyPhase2 |= dirtyPhase1;
                mDirty = 0;

                updateWakeLockSummaryLocked(dirtyPhase1);
                updateUserActivitySummaryLocked(now, dirtyPhase1);
                if (!updateWakefulnessLocked(dirtyPhase1)) {
                    break;
                }
            }

            // Phase 2: Update display power state.
            boolean displayBecameReady = updateDisplayPowerStateLocked(dirtyPhase2);

            // Phase 3: Update dream state (depends on display ready signal).
            updateDreamLocked(dirtyPhase2, displayBecameReady);

            // Phase 4: Send notifications, if needed.
            finishWakefulnessChangeIfNeededLocked();

            // Phase 5: Update suspend blocker.
            // Because we might release the last suspend blocker here, we need to make sure
            // we finished everything else first!
            updateSuspendBlockerLocked();
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_POWER);
        }
    }

其实这个函数我们先看下updateWakeLockSummaryLocked函数,根据wakelock来指定mWakeLockSummary

    private void updateWakeLockSummaryLocked(int dirty) {
        if ((dirty & (DIRTY_WAKE_LOCKS | DIRTY_WAKEFULNESS)) != 0) {
            mWakeLockSummary = 0;

            final int numWakeLocks = mWakeLocks.size();
            for (int i = 0; i < numWakeLocks; i++) {
                final WakeLock wakeLock = mWakeLocks.get(i);
                switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                    case PowerManager.PARTIAL_WAKE_LOCK:
                        if (!wakeLock.mDisabled) {
                            // We only respect this if the wake lock is not disabled.
                            mWakeLockSummary |= WAKE_LOCK_CPU;//持cpu锁
                        }
                        break;
                    case PowerManager.FULL_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
                        break;
                    case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT;
                        break;
                    case PowerManager.SCREEN_DIM_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_SCREEN_DIM;
                        break;
                    case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_PROXIMITY_SCREEN_OFF;
                        break;
                    case PowerManager.DOZE_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_DOZE;
                        break;
                    case PowerManager.DRAW_WAKE_LOCK:
                        mWakeLockSummary |= WAKE_LOCK_DRAW;
                        break;
                }
            }

            // Cancel wake locks that make no sense based on the current state.
            if (mWakefulness != WAKEFULNESS_DOZING) {
                mWakeLockSummary &= ~(WAKE_LOCK_DOZE | WAKE_LOCK_DRAW);
            }
            if (mWakefulness == WAKEFULNESS_ASLEEP
                    || (mWakeLockSummary & WAKE_LOCK_DOZE) != 0) {
                mWakeLockSummary &= ~(WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM
                        | WAKE_LOCK_BUTTON_BRIGHT);
                if (mWakefulness == WAKEFULNESS_ASLEEP) {
                    mWakeLockSummary &= ~WAKE_LOCK_PROXIMITY_SCREEN_OFF;
                }
            }

            // Infer implied wake locks where necessary based on the current state.
            if ((mWakeLockSummary & (WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_SCREEN_DIM)) != 0) {
                if (mWakefulness == WAKEFULNESS_AWAKE) {
                    mWakeLockSummary |= WAKE_LOCK_CPU | WAKE_LOCK_STAY_AWAKE;
                } else if (mWakefulness == WAKEFULNESS_DREAMING) {
                    mWakeLockSummary |= WAKE_LOCK_CPU;
                }
            }
            if ((mWakeLockSummary & WAKE_LOCK_DRAW) != 0) {
                mWakeLockSummary |= WAKE_LOCK_CPU;
            }

            if (DEBUG_SPEW) {
                Slog.d(TAG, "updateWakeLockSummaryLocked: mWakefulness="
                        + PowerManagerInternal.wakefulnessToString(mWakefulness)
                        + ", mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
            }
        }
    }

最后我们主要看下updateSuspendBlockerLocked这个函数:

    private void updateSuspendBlockerLocked() {
        final boolean needWakeLockSuspendBlocker = ((mWakeLockSummary & WAKE_LOCK_CPU) != 0);//是否需要持锁
        final boolean needDisplaySuspendBlocker = needDisplaySuspendBlockerLocked();
        final boolean autoSuspend = !needDisplaySuspendBlocker;
        final boolean interactive = mDisplayPowerRequest.isBrightOrDim();

        // Disable auto-suspend if needed.
        // FIXME We should consider just leaving auto-suspend enabled forever since
        // we already hold the necessary wakelocks.
        if (!autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {//配置是否需要自动持锁开启
            setHalAutoSuspendModeLocked(false);
        }

        // First acquire suspend blockers if needed.
        if (needWakeLockSuspendBlocker && !mHoldingWakeLockSuspendBlocker) {
            mWakeLockSuspendBlocker.acquire();//wakelock持锁
            mHoldingWakeLockSuspendBlocker = true;
        }
        if (needDisplaySuspendBlocker && !mHoldingDisplaySuspendBlocker) {
            mDisplaySuspendBlocker.acquire();//Display持锁
            mHoldingDisplaySuspendBlocker = true;
        }

        ......

        // Then release suspend blockers if needed.
        if (!needWakeLockSuspendBlocker && mHoldingWakeLockSuspendBlocker) {
            mWakeLockSuspendBlocker.release();//wakelock锁释放
            mHoldingWakeLockSuspendBlocker = false;
        }
        if (!needDisplaySuspendBlocker && mHoldingDisplaySuspendBlocker) {
            mDisplaySuspendBlocker.release();//Display锁释放
            mHoldingDisplaySuspendBlocker = false;
        }

        // Enable auto-suspend if needed.
        if (autoSuspend && mDecoupleHalAutoSuspendModeFromDisplayConfig) {//配置设置&&可以自动持锁
            setHalAutoSuspendModeLocked(true);//自动持锁开启
        }
    }

先来看看needDisPlaySuspendBlockerLocked函数,是否将Display锁释放

    private boolean needDisplaySuspendBlockerLocked() {
        if (!mDisplayReady) {
            return true;//需要持锁
        }
        if (mDisplayPowerRequest.isBrightOrDim()) {
            // If we asked for the screen to be on but it is off due to the proximity
            // sensor then we may suspend but only if the configuration allows it.
            // On some hardware it may not be safe to suspend because the proximity
            // sensor may not be correctly configured as a wake-up source.
            if (!mDisplayPowerRequest.useProximitySensor || !mProximityPositive
                    || !mSuspendWhenScreenOffDueToProximityConfig) {
                return true;
            }
        }
        if (mScreenBrightnessBoostInProgress) {
            return true;
        }
        // Let the system suspend if the screen is off or dozing.
        return false;
    }

这里有两个锁Display和WakeLocks锁,但是这两个锁和之前PowerManager的锁意义不一样,这两个锁是针对hal层的是真正的锁。我们来看下这两个锁。


在PowerManagerService的构造函数中就创建了这两个锁。

            mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
            mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
createSuspendBlockerLocked函数就是新建SuspendBlockerImpl对象:
    private SuspendBlocker createSuspendBlockerLocked(String name) {
        SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);
        mSuspendBlockers.add(suspendBlocker);
        return suspendBlocker;
    }
再来看看SuspendBlockerImpl 类:

    private final class SuspendBlockerImpl implements SuspendBlocker {
        private final String mName;
        private final String mTraceName;
        private int mReferenceCount;

        public SuspendBlockerImpl(String name) {
            mName = name;
            mTraceName = "SuspendBlocker (" + name + ")";
        }

        @Override
        protected void finalize() throws Throwable {
            try {
                if (mReferenceCount != 0) {
                    Slog.wtf(TAG, "Suspend blocker \"" + mName
                            + "\" was finalized without being released!");
                    mReferenceCount = 0;
                    nativeReleaseSuspendBlocker(mName);
                    Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                }
            } finally {
                super.finalize();
            }
        }

        @Override
        public void acquire() {
            synchronized (this) {
                mReferenceCount += 1;
                if (mReferenceCount == 1) {//这里也使用了计数
                    if (DEBUG_SPEW) {
                        Slog.d(TAG, "Acquiring suspend blocker \"" + mName + "\".");
                    }
                    Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, mTraceName, 0);
                    nativeAcquireSuspendBlocker(mName);//持锁调用hal层函数
                }
            }
        }

        @Override
        public void release() {
            synchronized (this) {
                mReferenceCount -= 1;
                if (mReferenceCount == 0) {//使用计数
                    if (DEBUG_SPEW) {
                        Slog.d(TAG, "Releasing suspend blocker \"" + mName + "\".");
                    }
                    nativeReleaseSuspendBlocker(mName);//释放锁调用hal层
                    Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, mTraceName, 0);
                } else if (mReferenceCount < 0) {
                    Slog.wtf(TAG, "Suspend blocker \"" + mName
                            + "\" was released without being acquired!", new Throwable());
                    mReferenceCount = 0;
                }
            }
        }


三、hal层持锁相关函数

hal层的持锁和释放锁的函数如下,在com_android_server_power_PowerManagerService.cpp文件

static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str());
}

static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) {
    ScopedUtfChars name(env, nameStr);
    release_wake_lock(name.c_str());
}
我们再看看power.c的代码,在hardware目录下。先来看持锁:

int
acquire_wake_lock(int lock, const char* id)
{
    initialize_fds();

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];
    }
    else {
        return EINVAL;
    }

    return write(fd, id, strlen(id));
}

先来看initialize_fds函数,调用open_file_descriptors函数:

static inline void
initialize_fds(void)
{
    if (g_initialized == 0) {
        if(open_file_descriptors(NEW_PATHS) < 0)
            open_file_descriptors(OLD_PATHS);
        g_initialized = 1;
    }
}

我们先来看看NEW_PATHS

const char * const NEW_PATHS[] = {
    "/sys/power/wake_lock",
    "/sys/power/wake_unlock",
};

再来看看open_file_descriptors函数:

static int
open_file_descriptors(const char * const paths[])
{
    int i;
    for (i=0; i<OUR_FD_COUNT; i++) {
        int fd = open(paths[i], O_RDWR | O_CLOEXEC);
        if (fd < 0) {
            fprintf(stderr, "fatal error opening \"%s\"\n", paths[i]);
            g_error = errno;
            return -1;
        }
        g_fds[i] = fd;//填充g_fds,一个是wake_lock另一个是wake_unlock
    }

    g_error = 0;
    return 0;
}
现在再来看看这两个函数
int
acquire_wake_lock(int lock, const char* id)
{
    initialize_fds();

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {
        fd = g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];//获取wake_lock的fd
    }
    else {
        return EINVAL;
    }

    return write(fd, id, strlen(id));//往里面写值
}

int
release_wake_lock(const char* id)
{
    initialize_fds();

    if (g_error) return g_error;

    ssize_t len = write(g_fds[RELEASE_WAKE_LOCK], id, strlen(id));//获取wake_unlock的fd,往里面写值
    return len >= 0;
}
这样看上层PowerManager的两个锁是否存在我们可以查看/sys/power/wake_lock和/sys/power/wake_unlock这两个目录。

root@lte26007:/sys/power # cat wake_lock
PowerManagerService.Display PowerManagerService.WakeLocks

root@lte26007:/sys/power # cat wake_unlock
KeyEvents radio-interface

如果灭屏了,Display锁会释放

root@lte26007:/sys/power # cat wake_unlock
KeyEvents PowerManagerService.Broadcasts PowerManagerService.Display radio-interface

autosuspend我们这里也就不讲了。


四、总结

这篇博客我们主要分析了从PowerManager的持锁,然后到PowerManagerService的一些逻辑处理。最后由PowerManagerService调用hal层真正的锁来让cpu保持工作。



版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Android6.0 wakelock深入分析

这篇博客我们分析下Power的持锁,从PowerManager到PowerManagerService再到hal分析。 一、PowerManager的持锁接口 我们先来看下PowerManager...

Android7.0 Doze模式分析(二)wakelock

前面我们介绍Doze模式的时候介绍过WakeLock白名单,WakeLock进入Doze接口等。这篇博客我们详细分析下,WakeLock在Doze模式下如何生效。 设置白名单 在Devic...

I/rmt_storage( 187): wakelock acquired: 1, error no: 110

一直出現 I/rmt_storage(  187): wakelock acquired: 1, error no: 110 k84s4

Android电源管理之三:PowerManager.WakeLock源码详读

PowerManager.WakeLock 有加锁和解锁两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非显式的放开,是不会解锁的,所以这种锁用起来要非常的小心。第二种锁是超时锁,这种锁会在锁...

基于Android5.1双APN的实现---接上篇

双APN

Android之HttpClient 和HttpResponse 小结

三步曲:  1.创建HttpGet或者HttpPost对象,将要请求的URL对象构造方法传入HttpGet、HttpPost对象  2.通过HttpClent接口的实现类DefaultClent.的e...

android的hook技术之hook所有view的监听器

这里先声明一下,由于这篇文章早已有人写过,但是并非盗取他的成果,这里的雷同确实有些偶然。。。这是做到一半的时候一个同事跟我说网上有,于是乎我看了他的思路以及demo,基本跟我差不多,只是他的代码写的可...

android 6.0 权限管理的学习资料和使用例子

别人翻译的资料(很详细的) http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0830/3387.html 一个开源的权限管...

Android 电源管理 -- wakelock机制

Wake Lock是一种锁的机制, 只要有人拿着这个锁,系统就无法进入休眠, 可以被用户态程序和内核获得. 这个锁可以是有超时的 或者 是没有超时的, 超时的锁会在时间过去以后自动解锁。如果没有锁了或...

教你写Android网络框架之Request、Response类与请求队列

在教你写Android网络框架之基本架构一文中我们已经介绍了SimpleNet网络框架的基本结构,今天我们就开始从代码的角度来开始切入该网络框架的实现,在剖析的同时我们会分析设计思路,以及为什么要这样...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)