Android墓碑机制(AndroidU)

这篇具有很好参考价值的文章主要介绍了Android墓碑机制(AndroidU)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一:背景

    Android为了加速进程的启动速度,在进程退出前台之后并不会立刻杀死进程,而是会尽可能的保留进程,当用户下次启动进程时,进程就会由冷启动变成温启动,启动速度大大加快。但后台进程过多时,会抢占CPU、memory等有限的系统资源,那么如果保证在不杀死进程的情况下,避免进程抢占系统资源呢?墓碑机制(进程冻结)应运而生。

二:功能详解

  2.1 进程冻结原理

    Android将进程按照优先级从高到低分为: 前台进程 -> 可感知进程 -> 服务进程 -> Cached进程。Freezer通过冻住cached进程来释放这些进程占用的系统资源。

  2.2 进程冻结流程图

    进程冻结的触发条件是进程优先级(adj)的改变,当有进程变成cache进程(adj>=900)时,就会触发cache进程的冻结。关于进程优先级的更新、计算等具体逻辑,可以参考Android进程管理-进程优先级(AndroidU)这篇文章,这里就不详细展开了。

安卓的墓碑机制,Performance,android

  2.3 进程冻结功能实现

    2.3.1 进程冻结的初始化

    2.3.1.1 SystemServer.run

    Android启动,会调用到SystemServer.main,在main函数中会调用自身的run函数来启动各个系统服务。具体Android启动流程这里就不详述了,以后会出专门的文章来解析。

    private void run() {
        ...
        startBootstrapServices(t);//初始化引导类型系统服务
        startCoreServices(t);//初始化核心系统服务
        startOtherServices(t);//初始化其他系统服务
        startApexServices(t);//初始化apex系统服务
        updateWatchdogTimeout(t);//更新看门狗超时时间
        ...
    }
  2.3.1.2 SystemServer.startOtherServices

  会调用ContentProviderHelper.installSystemProviders函数来初始化SettingsProvider

    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        ...
        t.traceBegin("InstallSystemProviders");
        //初始化SettingsProvider
        mActivityManagerService.getContentProviderHelper().installSystemProviders();
        // Device configuration used to be part of System providers
        mSystemServiceManager.startService(UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS);
        // Now that SettingsProvider is ready, reactivate SQLiteCompatibilityWalFlags
        SQLiteCompatibilityWalFlags.reset();
        t.traceEnd();
        ...
    }
2.3.1.3 ContentProviderHelper.installSystemProviders

会调用OomAdjuster.initSettings来初始化OomAdjuster

    public final void installSystemProviders() {
        ...
        mService.mOomAdjuster.initSettings();//初始化OomAdjuster
        ...
    }
2.3.1.4 OomAdjuster.initSettings

会调用CachedAppOptimizer.init来初始化CachedAppOptimizer

    void initSettings() {
        mCachedAppOptimizer.init();
        mCacheOomRanker.init(ActivityThread.currentApplication().getMainExecutor());
        ...
    }
2.3.1.5 CachedAppOptimizer.init

会调用CachedAppOptimizer.updateUseFreezer来初始化进程冻结功能

    public void init() {
        ...
        updateUseFreezer();//进程冻结初始化
        ...
    }
2.3.1.6 CachedAppOptimizer.updateUseFreezer

    会先判断CACHED_APPS_FREEZER_ENABLED数据库的值,如果是disabled,会把mUseFreezer设置为false,接着会调用enableFreezer(false);直接返回false。

    如果CACHED_APPS_FREEZER_ENABLED数据库的值是enabled或者use_freezer对应的DeviceConfig的值是true,则会调用isFreezerSupported函数判断驱动是否支持,接着更新Debounce超时时间(10s)。

    如果mUseFreezer是true,调用enableFreezer来使能冻结功能,并启动CachedAppOptimizerThread线程来做进程冻结的操作。

private void updateUseFreezer() {
        //获取CACHED_APPS_FREEZER_ENABLED数据库的值
        final String configOverride = Settings.Global.getString(mAm.mContext.getContentResolver(),
                Settings.Global.CACHED_APPS_FREEZER_ENABLED);

        if ("disabled".equals(configOverride)) {//如果是disabled,则不支持进程冻结
            mUseFreezer = false;
        } else if ("enabled".equals(configOverride)
                || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
                    KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) {//如果CACHED_APPS_FREEZER_ENABLED数据库的值是enabled或者use_freezer对应的config的值是true
            mUseFreezer = isFreezerSupported();//判断驱动支不支持Freezer
            updateFreezerDebounceTimeout();//更新Debounce超时时间
            updateFreezerExemptInstPkg();//将软件包标记/取消标记为自动安装
        } else {//CACHED_APPS_FREEZER_ENABLED数据库没有定义且use_freezer对应的config值为false
            mUseFreezer = false;
        }

        final boolean useFreezer = mUseFreezer;
        // enableFreezer() would need the global ActivityManagerService lock, post it.
        mAm.mHandler.post(() -> {
            if (useFreezer) {
                Slog.d(TAG_AM, "Freezer enabled");
                enableFreezer(true);//使能进程冻结功能

                if (!mCachedAppOptimizerThread.isAlive()) {
                    mCachedAppOptimizerThread.start();//启动CachedAppOptimizerThread线程
                }

                if (mFreezeHandler == null) {
                    mFreezeHandler = new FreezeHandler();
                }

                Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
                        Process.THREAD_GROUP_SYSTEM);//设置CachedAppOptimizerThread线程的cgroup
            } else {
                Slog.d(TAG_AM, "Freezer disabled");
                enableFreezer(false);
            }
        });
    }
2.3.1.7 CachedAppOptimizer.isFreezerSupported

先读取/sys/fs/cgroup/uid_0/pid_*/cgroup.freeze节点的值,如果是1或者0,则调用getBinderFreezeInfo判断驱动是否支持freezer(kernel版本较低的话,默认不会实现BINDER_GET_FROZEN_INFO,就不支持freezer,kernel-5.4, kernel-5.10或者更高版本是支持的),接着调用isFreezerProfileValid检查task_profiles.json是否包含进程冻结相关的Profiles

    public static boolean isFreezerSupported() {
        boolean supported = false;
        FileReader fr = null;

        try {
            String path = getFreezerCheckPath();// /sys/fs/cgroup/uid_0/pid_*/cgroup.freeze
            fr = new FileReader(path);
            char state = (char) fr.read();

            if (state == '1' || state == '0') {
                getBinderFreezeInfo(Process.myPid());//判断驱动是否支持freezer

                // Check if task_profiles.json contains invalid profiles
                supported = isFreezerProfileValid();//检查task_profiles.json是否包含进程冻结相关的Profiles
            } else {
                Slog.e(TAG_AM, "Unexpected value in cgroup.freeze");
            }
        }

        ...
        return supported;
    }

getBinderFreezeInfo实现逻辑中,通过BINDER_GET_FROZEN_INFO 系统调用到 binder driver
status_t IPCThreadState::getProcessFreezeInfo(pid_t pid, uint32_t *sync_received,
                                              uint32_t *async_received)
{
    int ret = 0;
    binder_frozen_status_info info = {};
    info.pid = pid;

#if defined(__ANDROID__)
    if (ioctl(self()->mProcess->mDriverFD, BINDER_GET_FROZEN_INFO, &info) < 0)
        ret = -errno;
#endif
    *sync_received = info.sync_recv;
    *async_received = info.async_recv;

    return ret;
}

  2.3.2 进程冻结的触发

    前文有提到进程冻结的触发条件是进程优先级(adj)的改变,这里以Activity执行onDestroy举例来跟踪触发流程。

    2.3.2.1 ActivityRecord.setState

      Activity调用onDestroy函数最终会调用到ActivityRecord的setState函数,具体的调用逻辑后面会出专门的文章说明,流程很长,这里就不详述了。

     跟踪ActivityRecord.setState函数流程,会调用到OomAdjuster.updateOomAdjLocked来继续更新adj的流程。

##frameworks\base\services\core\java\com\android\server\wm\ActivityRecord.java
    void setState(State state, String reason) {
        ...
        switch (state) {
            case DESTROYING:
                if (app != null && !app.hasActivities()) {
                    app.updateProcessInfo(true /* updateServiceConnectionActivities */,
                            false /* activityChange */, true /* updateOomAdj */,
                            false /* addPendingTopUid */);
                }
                break;
        }

##frameworks\base\services\core\java\com\android\server\wm\WindowProcessController.java
    void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,
            boolean updateOomAdj, boolean addPendingTopUid) {
        ...
        //这里是延迟去调用mListener的WindowProcessListener::updateProcessInfo方法,而mListener实际是实现了WindowProcessListener接口的ProcessRecord
        final Message m = PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo,
                mListener, updateServiceConnectionActivities, activityChange, updateOomAdj);
        mAtm.mH.sendMessage(m);
    }

##frameworks\base\services\core\java\com\android\server\am\ProcessRecord.java
    public void updateProcessInfo(boolean updateServiceConnectionActivities, boolean activityChange,
            boolean updateOomAdj) {
        synchronized (mService) {
            ...
            if (updateOomAdj) {
                mService.updateOomAdjLocked(this, OOM_ADJ_REASON_ACTIVITY);
            }
        }
    }

##frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
    final boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
        return mOomAdjuster.updateOomAdjLocked(app, oomAdjReason);
    }
    2.3.2.2 OomAdjuster.updateOomAdjLocked

    经过adj的计算和应用,如果adj大于cache进程(adj>=900),调用CachedAppOptimizer的freezeAppAsyncLSP函数来进行进程冻结

##frameworks\base\services\core\java\com\android\server\am\OomAdjuster.java
    boolean updateOomAdjLocked(ProcessRecord app, @OomAdjReason int oomAdjReason) {
        synchronized (mProcLock) {
            return updateOomAdjLSP(app, oomAdjReason);
        }
    }

    private boolean updateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
        ...
        return performUpdateOomAdjLSP(app, oomAdjReason);
        ...
    }

    private boolean performUpdateOomAdjLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
        ...
        updateOomAdjInnerLSP(oomAdjReason, topApp, processes, uids, containsCycle, false);
        ...
    }

    private void updateOomAdjInnerLSP(@OomAdjReason int oomAdjReason, final ProcessRecord topApp,
            ArrayList<ProcessRecord> processes, ActiveUids uids, boolean potentialCycles,
            boolean startProfiling) {
        ...
        //计算adj
        computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, fullUpdate, now, false,
                        computeClients);
        ...
        boolean allChanged = updateAndTrimProcessLSP(now, nowElapsed, oldTime, activeUids,
                oomAdjReason);
        ...
    }

    private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,
            final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) {
        ...
        applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
        ...
    }

    private boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now,
            long nowElapsed, @OomAdjReason int oomAdjReson) {
        ...
        updateAppFreezeStateLSP(app, oomAdjReson);
        ...
    }

    private void updateAppFreezeStateLSP(ProcessRecord app, @OomAdjReason int oomAdjReason) {
        if (!mCachedAppOptimizer.useFreezer()) {//如果不支持进程冻结,直接返回
            return;
        }
        ...
        if (state.getCurAdj() >= CACHED_APP_MIN_ADJ && !opt.isFrozen()
                && !opt.shouldNotFreeze()) {
            mCachedAppOptimizer.freezeAppAsyncLSP(app);
        } else if (state.getSetAdj() < CACHED_APP_MIN_ADJ) {
            mCachedAppOptimizer.unfreezeAppLSP(app,
                    CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
        }
    }
2.3.2.3 CachedAppOptimizer.freezeAppAsyncLSP

  延时10s发送进程冻结的消息,在10s内如果收到进程解冻的消息,会把进程冻结消息移除,也就不会执行进程冻结的操作。调用freezeProcess进行进程冻结。

    void freezeAppAsyncLSP(ProcessRecord app) {
        freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, mFreezerDebounceTimeout));
    }

    private void freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) {
        freezeAppAsyncInternalLSP(app, delayMillis, false);
    }

    void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis,
            boolean force) {
        ...
        mFreezeHandler.sendMessageDelayed(
                mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
                delayMillis);//延时10s发送进程冻结的消息
        ...
    }

    private final class FreezeHandler extends Handler implements
            ProcLocksReader.ProcLocksReaderCallback {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SET_FROZEN_PROCESS_MSG: {
                    ProcessRecord proc = (ProcessRecord) msg.obj;
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,"freezeProcess");
                    synchronized (mAm) {
                        freezeProcess(proc);//冻结进程
                    }
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    if (proc.mOptRecord.isFrozen()) {//进程冻结成功
                        onProcessFrozen(proc);//压缩进程
                        removeMessages(DEADLOCK_WATCHDOG_MSG);
                        sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);//延时1s发送消息检查文件锁的持有情况
                    }
                } break;
                case DEADLOCK_WATCHDOG_MSG: {
                    try {
                        mProcLocksReader.handleBlockingFileLocks(this);//检查是否持有文件锁"/proc/locks"
                    } catch (IOException e) {
                        Slog.w(TAG_AM, "Unable to check file locks");
                    }
                } break;
2.3.2.4 CachedAppOptimizer.freezeProcess

先调用freezeBinder冻结进程的Binder端,再调用Process.setProcessFrozen冻结进程自身。

    private final class FreezeHandler extends Handler implements
            ProcLocksReader.ProcLocksReaderCallback {
        private void freezeProcess(final ProcessRecord proc) {
            ...
            synchronized (mProcLock) {
                ...
                try {
                    //先冻结进程的binder端
                    if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
                        handleBinderFreezerFailure(proc, "outstanding txns");
                        return;
                    }
                }
                ...
                try {
                    ...
                    //冻结进程
                    Process.setProcessFrozen(pid, proc.uid, true);
                    ...
                }
                ...
                final UidRecord uidRec = proc.getUidRecord();
                if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) {
                    uidRec.setFrozen(true);//如果UidRecord中所有进程都被冻结了,设置mUidIsFrozen为true

                    postUidFrozenMessage(uidRec.getUid(), true);
                }
                ...
            }
        }
    }
2.3.2.5 com_android_server_am_CachedAppOptimizer.com_android_server_am_CachedAppOptimizer_freezeBinder

  freezeBinder是一个native函数,最终会调用到com_android_server_am_CachedAppOptimizer.com_android_server_am_CachedAppOptimizer_freezeBinder

##frameworks/base/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
  static jint com_android_server_am_CachedAppOptimizer_freezeBinder(JNIEnv* env, jobject clazz,
                                                                    jint pid, jboolean freeze,
                                                                    jint timeout_ms) {
      jint retVal = IPCThreadState::freeze(pid, freeze, timeout_ms);
      if (retVal != 0 && retVal != -EAGAIN) {
          jniThrowException(env, "java/lang/RuntimeException", "Unable to freeze/unfreeze binder");
      }
  
      return retVal;
  }

##frameworks/native/libs/binder/IPCThreadState.cpp
  status_t IPCThreadState::freeze(pid_t pid, bool enable, uint32_t timeout_ms) {
      struct binder_freeze_info info;
      int ret = 0;
  
      info.pid = pid;
      info.enable = enable;
      info.timeout_ms = timeout_ms;
  
  
  #if defined(__ANDROID__)
      //这里进行binder冻结的操作,再具体的代码细节,这里就不继续跟了
      if (ioctl(self()->mProcess->mDriverFD, BINDER_FREEZE, &info) < 0)
          ret = -errno;
  #endif

      return ret;
  }
2.3.2.6 Process.setProcessFrozen

setProcessFrozen是一个native函数,会调用到android_util_Process的android_os_Process_setProcessFrozen函数,在此函数里会调用cgroup中间抽象层libprocessgroup的API,通过cgroup本身的freezer子系统来实现进程冻结功能

##frameworks\base\core\java\android\os\Process.java
public static final native void setProcessFrozen(int pid, int uid, boolean frozen);

##frameworks\base\core\jni\android_util_Process.cpp
void android_os_Process_setProcessFrozen(
        JNIEnv *env, jobject clazz, jint pid, jint uid, jboolean freeze)
{
    bool success = true;

    if (freeze) {
        success = SetProcessProfiles(uid, pid, {"Frozen"});//调用cgroup中间抽象层libprocessgroup的API来实现进程冻结
    } else {
        success = SetProcessProfiles(uid, pid, {"Unfrozen"});
    }

    if (!success) {
        signalExceptionForGroupError(env, EINVAL, pid);
    }
}
2.3.2.7 processgroup.SetProcessProfiles

通过接口SetProcessProfiles()精细是SetCgroupAction类型的profile,最终调用 ExecuteForProcess(),先通过Controller的GetProcsFilePath()接口获取该profile需要修改的path,最终写的文件就是CGROUP_PROCS_FIL,也就是cgroup.procs文件

##system\core\libprocessgroup\processgroup.cpp
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
    return TaskProfiles::GetInstance().SetProcessProfiles(
            uid, pid, std::span<const std::string>(profiles), false);
}

##system\core\libprocessgroup\task_profiles.cpp
bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles,
                                      bool use_fd_cache) {
    bool success = true;
    for (const auto& name : profiles) {
        TaskProfile* profile = GetProfile(name);
        if (profile != nullptr) {
            ...
            if (!profile->ExecuteForProcess(uid, pid)) {
                LOG(WARNING) << "Failed to apply " << name << " process profile";
                success = false;
            }
        } else {
            LOG(WARNING) << "Failed to find " << name << " process profile";
            success = false;
        }
    }
    return success;
}

bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
    for (const auto& element : elements_) {
        if (!element->ExecuteForProcess(uid, pid)) {
            LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
            return false;
        }
    }
    return true;
}

bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
    if (result != ProfileAction::UNUSED) {
        return result == ProfileAction::SUCCESS;
    }

    // fd was not cached or cached fd can't be used
    //通过Controller的GetProcsFilePath()接口获取该profile需要修改的path
    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
    if (tmp_fd < 0) {
        PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
        return false;
    }
    if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
        LOG(ERROR) << "Failed to add task into cgroup";
        return false;
    }

    return true;
}

三:小结

  3.1 如何手动开启或者关闭进程冻结功能

    方式一:开发者选项 “Suspend execution for cached apps”。选择Enabled或者Disabled

    方式二:通过adb命令  adb shell settings put global cached_apps_freezer <enabled|disabled|default>

    以上两种方式都需要重启手机才能生效

  3.2 进程冻结是如何实现的

    进程冻结最终是通过cgroup的Freezer子系统来实现的

  3.3 进程冻结的版本要求

    进程冻结Android版本最低是R,但是R上有bug,推荐在S及之后版本上正式使用

    进程冻结kernel版本要求是kernel-5.4,kernel-5.10或者更高版本

  3.4 进程冻结针对哪类进程

    进程冻结仅仅针对cache进程

  3.5 进程冻结后,何时解冻,如何解冻

    进程冻结后,当进程变成非cache进程时(例如用户手动启动该进程),会触发CachedAppOptimizer的unfreezeAppLSP函数来进行进程解冻

  3.6 进程冻结的优缺点

    优点:冻结cache进程,可以释放cache进程所占用的memory给系统其他进程使用,也防止了该类进程抢占CPU资源

    缺点:如果频繁的冻结、解冻进程,可能会导致卡顿或者响应延迟,影响用户体验

四:番外篇

因为进程冻结最终实现是通过cgroup,这里简单介绍下cgroup。

  cgroups (全称:control groups) 是 Linux 内核提供的一种可以限制单个进程或者多个进程所使用资源的机制,可以对 CPU、memory 等资源实现精细化的控制。cgroup有blkio、cpu、cpuset、memory、freezer等子系统,本文的进程冻结就是通过freezer子系统来实现的。

  cgroup中间抽象层libprocessgroup,主要提供两个功能,其一在启动阶段,根据cgroups.json 来装载具体的cgroup; 其二根据task_profiles.json来定义对cgroup具体的操作以及参数。主要代码路径:/system/core/libprocessgroup/ ,其中cgroups.json和task_profiles.json非常重要。

  后面会写文章来专门介绍cgroup,这里暂时就先介绍这些吧。。

  文章来源地址https://www.toymoban.com/news/detail-858387.html

到了这里,关于Android墓碑机制(AndroidU)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 安卓android记单词软件

    课程设计项目总结,基于安卓的记单词软件,分为前端和服务器端,前端使用安卓,后端使用SSM框架。 具体技术栈为: 前端:android、okhttp、sqlite、litepal等 后端:SSM(SpringMVC+Spring+Mybatis)、mysql数据库、tomcat服务器等 项目的总体介绍 基于安卓的记单词软件,用户在第一次使用的时

    2024年02月05日
    浏览(36)
  • 安卓学习笔记:安卓11访问/读写 Android/data 目录

    省流提示:采用android studio工具开发,记录一次低级的开发,避免以后忘记或者踩坑。 最近有个业余项目开发到一小半,过程中需要读写 Android/data目录的文件,采用常规的文件操作总是提示权限被拒绝,无奈上网参考了很多资料,终于得到了解决。 无法访问Android/data 的原因

    2024年02月13日
    浏览(45)
  • 【android】安卓大作业「课程日历」

    项目: 课程日历 -项目概况: 项目记录一周的课程信息。 •用户可以编辑周一~周五的课程表( 每天最多安排5门课程 ) ; •用户可以按日查看课程表。 -项目要求: •项目包含2个活动及其对应的布局: ① 显示课程活动:单击“周一”~“周五”按钮中的一个,按时间顺

    2024年02月04日
    浏览(42)
  • 安卓进阶之android系统架构

    安卓进阶躲不开阅读源码和深入了解安卓的底层,这是一篇入门级别的文章,可以对安卓架构有个大体的认识。 首先要理解andriod和AOSP的关系,我们日常开发的安卓跟市面上的安卓不是一个意思,我们开发的是安卓app,市面上的安卓其实是安卓系统。 维基百科: Android 是基于

    2023年04月12日
    浏览(42)
  • Android studio 设置安卓手机

    参考这个链接 ghttps://developer.android.com/studio/debug/dev-options 列出常用手机的设置,但是我的手机不在此列 Google Pixel Settings    About phone    Build number Samsung Galaxy S8 and later Settings    About phone    Software information    Build number LG G6 and later Settings    About phone    Software info    Bui

    2024年02月13日
    浏览(41)
  • Android Studio连接安卓手机

    点击右上角红框的【SDK Manager】-【SDK Tools】。 也可以在 【tools】-【SDK Manager】-【SDK Tools】下进入。 点击Google USB Driver,下载后点ok。 右键【我的电脑】-【高级系统设置】-【环境变量】,新建变量【Android_Home】; 值为:C:UsersHuoAppDataLocalAndroidSdk;C:UsersHuoAppDataLocalAnd

    2024年02月16日
    浏览(38)
  • Android APP开机启动,安卓APP开发自启动,安卓启动后APP自动启动 Android让程序开机自动运行APP

    第一步设置获取广播后的业务 第二查权限给APP 理论以上两步做完就可以了。APP也能收到广播信息了, 但是APP没有在桌面启动。 经过再研究,发现要在手机再设置自动开启等业务,以下是小米、魅族的系统设置的一些内容,其它平台自己研究。 这里已经显示收到广播信息  

    2024年02月06日
    浏览(57)
  • Android安卓安全和隐私保护

             Android是一款由Google开发的移动操作系统,它的发展历史可追溯到2003年,至今经历20年发展成一款广泛使用的智能手机操作系统,并延伸到其他移动领域,如平板电脑、智能电视等。         2003年,安迪·鲁宾(Andy Rubin)和他的团队创立了Android公司。该公司

    2024年02月10日
    浏览(41)
  • Android 13 关于安卓判断通知权限

    判断权限是否开启: public class NoticePermissionUtil {       private static final String CHECK_OP_NO_THROW = \\\"checkOpNoThrow\\\";     private static final String OP_POST_NOTIFICATION = \\\"OP_POST_NOTIFICATION\\\";       //调用该方法获取是否开启通知栏权限     public static boolean isNotifyEnabled(Context context) {        

    2023年04月13日
    浏览(43)
  • 浅析Android OTA机制

    OTA 全称 Over-the-Air Technology,这种在线升级,无需刷机升级的方式,叫做OTA升级,OTA升级可以借助Wifi无线网络或者手机移动网络完成升级,相当于借助空中无线网络完成升级; 项目中需要OTA的功能,因此有了此文,参考下Android的OTA实现机制,可以看到Android的OTA机制随着版本

    2024年01月25日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包