Android12系统环境变量设置

这篇具有很好参考价值的文章主要介绍了Android12系统环境变量设置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近在移植百度Apollo Cyber通信框架至安卓系统中,发现Cyber本身依赖于环境变量来实现服务的初始化配置。相应地,我也需要在安卓系统中引入这些环境变量,并确保在Native服务启动时这些环境变量已经准备就绪。

由于此前我对环境变量的了解并不多,于是研究学习了一下Android系统中关于环境变量的相关配置,这篇博文即对此做了一个记录。

背景简介

首先我们需要探究一下为什么我们需要环境变量。环境变量是一组动态的,可手动编辑、设置的值。一般而言,我们程序的运行往往会依赖于各种各样的配置,这些配置我们可以以配置文件的形式存放于本地文件系统中,再让程序在运行时进行解析从而获取配置,这是一种比较通用的做法;而另一种比较通用的做法是将这些配置写入环境变量,程序在运行时自动读取环境变量即可。前者适合配置比较多的情况,而后者则适合配置少且配置可能需要多进程共享的情况,比较灵活。

可以说环境变量是非常重要的,没有环境变量的支持,无论是Windows系统中的程序还是Linux系统中的程序都无法正常运行。

环境变量分为两类:全局环境变量和局部环境变量。全局环境变量是全局可见的,所有进程可见;而局部变量则针对部分进程可见。 

查看环境变量

 如何查看系统的环境变量呢,这里提供两个命令:env或者printenv命令,如下所示:

_=/system/bin/env
ANDROID_DATA=/data
HOME=/
ANDROID_TZDATA_ROOT=/apex/com.android.tzdata
ANDROID_STORAGE=/storage
ANDROID_ASSETS=/system/app
TERM=xterm-256color
ANDROID_SOCKET_adbd=19
ANDROID_ART_ROOT=/apex/com.android.art
CYBER_DOMAIN_ID=71
CYBER_PATH=/data/cyber
EXTERNAL_STORAGE=/sdcard
DOWNLOAD_CACHE=/data/cache
LOGNAME=root
SYSTEMSERVERCLASSPATH=/system/framework/com.android.location.provider.jar:/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/car-frameworks-service.jar:/apex/com.android.appsearch/javalib/service-appsearch.jar:/apex/com.android.media/javalib/service-media-s.jar:/apex/com.android.permission/javalib/service-permission.jar
DEX2OATBOOTCLASSPATH=/apex/com.android.art/javalib/core-oj.jar:/apex/com.android.art/javalib/core-libart.jar:/apex/com.android.art/javalib/okhttp.jar:/apex/com.android.art/javalib/bouncycastle.jar:/apex/com.android.art/javalib/apache-xml.jar:/system/framework/framework.jar:/system/framework/framework-graphics.jar:/system/framework/ext.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/apex/com.android.i18n/javalib/core-icu4j.jar:/system/framework/android.car.jar
BOOTCLASSPATH=/apex/com.android.art/javalib/core-oj.jar:/apex/com.android.art/javalib/core-libart.jar:/apex/com.android.art/javalib/okhttp.jar:/apex/com.android.art/javalib/bouncycastle.jar:/apex/com.android.art/javalib/apache-xml.jar:/system/framework/framework.jar:/system/framework/framework-graphics.jar:/system/framework/ext.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/apex/com.android.i18n/javalib/core-icu4j.jar:/system/framework/android.car.jar:/apex/com.android.appsearch/javalib/framework-appsearch.jar:/apex/com.android.conscrypt/javalib/conscrypt.jar:/apex/com.android.ipsec/javalib/android.net.ipsec.ike.jar:/apex/com.android.media/javalib/updatable-media.jar:/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar:/apex/com.android.os.statsd/javalib/framework-statsd.jar:/apex/com.android.permission/javalib/framework-permission.jar:/apex/com.android.permission/javalib/framework-permission-s.jar:/apex/com.android.scheduling/javalib/framework-scheduling.jar:/apex/com.android.sdkext/javalib/framework-sdkextensions.jar:/apex/com.android.tethering/javalib/framework-connectivity.jar:/apex/com.android.tethering/javalib/framework-tethering.jar:/apex/com.android.wifi/javalib/framework-wifi.jar
SHELL=/bin/sh
ANDROID_BOOTLOGO=1
ASEC_MOUNTPOINT=/mnt/asec
HOSTNAME=trout_x86
USER=root
TMPDIR=/data/local/tmp
PATH=/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin
ANDROID_ROOT=/system
ANDROID_I18N_ROOT=/apex/com.android.i18n
trout_x86:/ # 

 除了这个命令以外,我们还可以使用echo来获取某个具体环境变量的值,如下所示:

TMPDIR=/data/local/tmp
PATH=/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin
ANDROID_ROOT=/system
ANDROID_I18N_ROOT=/apex/com.android.i18n
trout_x86:/ # echo $USER
root
trout_x86:/ # echo $ANDROID_SOCKET_adbd
19

 需要说明的是,上述方法仅针对全局环境变量有效,如果是查看局部环境变量,我们可以使用set命令,如下所示:

trout_x86:/ $ set
DOWNLOAD_CACHE=/data/cache
EPOCHREALTIME=1682412013.723827
EXTERNAL_STORAGE=/sdcard
HOME=/
HOSTNAME=trout_x86
IFS=$' \t\n'
KSHEGID=2000
KSHGID=2000
KSHUID=2000
KSH_VERSION='@(#)MIRBSD KSH R59 2020/05/16 Android'
LINES
LOGNAME=shell
OPTIND=1
PATH=/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin
PATHSEP=:
PGRP=24010
PIPESTATUS[0]=1
PPID=574
PS1=$'${|\n\tlocal e=$?\n\n\t(( e )) && REPLY+="$e|"\n\n\treturn $e\n}$HOSTNAME:${PWD:-?} $ '
PS2='> '
PS3='#? '
PS4='[$EPOCHREALTIME] '
PWD=/
RANDOM=30514
SECONDS=3878
SHELL=/bin/sh
SYSTEMSERVERCLASSPATH=/system/framework/com.android.location.provider.jar:/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/car-frameworks-service.jar:/apex/com.android.appsearch/javalib/service-appsearch.jar:/apex/com.android.media/javalib/service-media-s.jar:/apex/com.android.permission/javalib/service-permission.jar
TERM=xterm-256color
TMOUT=0
TMPDIR=/data/local/tmp
USER=shell
USER_ID=
 环境变量加载

 那么Android系统是如何加载环境变量的呢,我们可以先看看/system/core/rootdir/Android.mk文件中的内容(节选):

$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
	@echo "Generate: $< -> $@"
	@mkdir -p $(dir $@)
	$(hide) cp $< $@
	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@

 Android.mk中这部分其实就引入了一个关键的文件:init.environ.rc.in,在Android12.1中其内容定义如下:

# set up the global environment
on early-init
    export ANDROID_BOOTLOGO 1
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data
    export ANDROID_STORAGE /storage
    export ANDROID_ART_ROOT /apex/com.android.art
    export ANDROID_I18N_ROOT /apex/com.android.i18n
    export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
    export EXTERNAL_STORAGE /sdcard
    export ASEC_MOUNTPOINT /mnt/asec
    %EXPORT_GLOBAL_ASAN_OPTIONS%
    %EXPORT_GLOBAL_GCOV_OPTIONS%
    %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
    %EXPORT_GLOBAL_HWASAN_OPTIONS%

 该文件内定义的内容其实就是全局的环境变量设置,通过在rc启动文件中使用export命令进行导入。但这只是初始定义的文件,是无法直接生效的。

在上面Android.mk展示的片段中,会将 init.environ.rc.in 中的中%EXPORT_GLOBAL_ASAN_OPTIONS%、%EXPORT_GLOBAL_GCOV_OPTIONS%、%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%、%EXPORT_GLOBAL_HWASAN_OPTIONS%使用sed命令进行替换,替换的值来源于Android.mk中获取到的EXPORT_GLOBAL_ASAN_OPTIONS、EXPORT_GLOBAL_GCOV_OPTIONS、EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS、EXPORT_GLOBAL_HWASAN_OPTIONS变量,这部分变量的赋值逻辑如下:

# init.environ.rc

include $(CLEAR_VARS)
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE := init.environ.rc
LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
LOCAL_LICENSE_CONDITIONS := notice
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)

EXPORT_GLOBAL_ASAN_OPTIONS :=
ifneq ($(filter address,$(SANITIZE_TARGET)),)
  EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS include=/system/asan.options
  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_OPTIONS_FILES) $(ASAN_EXTRACT_FILES)
endif

EXPORT_GLOBAL_HWASAN_OPTIONS :=
ifneq ($(filter hwaddress,$(SANITIZE_TARGET)),)
  ifneq ($(HWADDRESS_SANITIZER_GLOBAL_OPTIONS),)
    EXPORT_GLOBAL_HWASAN_OPTIONS := export HWASAN_OPTIONS $(HWADDRESS_SANITIZER_GLOBAL_OPTIONS)
  endif
endif

EXPORT_GLOBAL_GCOV_OPTIONS :=
ifeq ($(NATIVE_COVERAGE),true)
  EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
endif

EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
ifeq ($(CLANG_COVERAGE),true)
  EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
endif

 由此,在编译阶段对 init.environ.rc.in 进行处理,得到init.environ.rc,打包后位于根目录;init进程在启动时就会读取该rc启动文件,进行环境变量的设置。

在我的环境中, init.environ.rc 文件内容如下:

# set up the global environment
on early-init
    export ANDROID_BOOTLOGO 1
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data
    export ANDROID_STORAGE /storage
    export ANDROID_ART_ROOT /apex/com.android.art
    export ANDROID_I18N_ROOT /apex/com.android.i18n
    export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
    export EXTERNAL_STORAGE /sdcard
    export ASEC_MOUNTPOINT /mnt/asec

除了init.environ.rc.in中定义的全局环境变量,还有部分环境变量是在init.rc中进行导入的,如DOWNLOAD_CACHE环境变量。如下所示:


    # enable armv8_deprecated instruction hooks
    write /proc/sys/abi/swp 1

    # Linux's execveat() syscall may construct paths containing /dev/fd
    # expecting it to point to /proc/self/fd
    symlink /proc/self/fd /dev/fd

    export DOWNLOAD_CACHE /data/cache                                                                                                                                                                       

    # This allows the ledtrig-transient properties to be created here so
    # that they can be chown'd to system:system later on boot 
    write /sys/class/leds/vibrator/trigger "transient"

 那么环境变量具体是如何加载的呢,加载的过程其实就在init进程中进行。在rc文件中定义的export导入环境变量会被init进程以command的形式执行,从而导入到系统环境中,具体代码细节可以参考—— system/core/init/action_manager.cpp与system/core/init/action.cpp:

void ActionManager::ExecuteOneCommand() {
    {   
        auto lock = std::lock_guard{event_queue_lock_};
        // Loop through the event queue until we have an action to execute
        while (current_executing_actions_.empty() && !event_queue_.empty()) {
            for (const auto& action : actions_) {
                if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
                               event_queue_.front())) {
                    current_executing_actions_.emplace(action.get());
                }   
            }   
            event_queue_.pop();
        }   
    }   

    if (current_executing_actions_.empty()) {
        return;
    }   

    auto action = current_executing_actions_.front();

    if (current_command_ == 0) {
        std::string trigger_name = action->BuildTriggersString();
        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
                  << ":" << action->line() << ")";
    }   

    action->ExecuteOneCommand(current_command_);

    // If this was the last command in the current action, then remove
    // the action from the executing list.
    // If this action was oneshot, then also remove it from actions_.
    ++current_command_;
    if (current_command_ == action->NumCommands()) {
        current_executing_actions_.pop();
        current_command_ = 0;
        if (action->oneshot()) {
            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
                           actions_.end());
        }   
    }   
}    
void Action::ExecuteCommand(const Command& command) const {
    android::base::Timer t;
    auto result = command.InvokeFunc(subcontext_);
    auto duration = t.duration();

    // Any action longer than 50ms will be warned to user as slow operation
    if (!result.has_value() || duration > 50ms ||
        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
        std::string trigger_name = BuildTriggersString();
        std::string cmd_str = command.BuildCommandString();

        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
                  << ":" << command.line() << ") took " << duration.count() << "ms and "
                  << (result.ok() ? "succeeded" : "failed: " + result.error().message());
    }   
}     

除了export导入环境变量,rc启动文件中定义的action都会通过类似的方式执行。

现在我们可以明确知道的是,Android系统中的环境变量可以通过rc来进行配置,但若我们观察得更仔细一点,会发现还有一些环境变量是没有通过rc配置的,那么这部分环境变量是怎么加载的呢——其实是通过setenv系统调用进行配置的,这里我们以PATH环境变量为例子:

86_64:/ # echo $PATH
/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin

我们通过在rc中进行搜索,会发现没有地方去export该环境变量,但倘若我们在init进程源码中使用搜索setenv,我们就会有惊喜的发现:

// Update $PATH in the case the second stage init is newer than first stage init, where it is first set.
    if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
        PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
    }

 很明显,PATH环境变量是通过setenv去进行设置的,这属于在代码内硬编码;我们看看_PATH_DEFPATH宏定义:

/** Default shell search path. */
#define _PATH_DEFPATH "/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"

 这与我们观察到的环境变量是一致的。

添加环境变量

前面我们已经了解到环境变量是如何加载的了,现在假设我们需要在Android中添加自己的环境变量,需要怎么做呢,这里列举几种办法:

1.在 init.environ.rc.in中进行添加,这种方式适合全局环境变量;可以在不修改init.rc的情况下添加环境变量,比较推荐使用。此时可以参考rc启动文件的语法规则使用export命令导入即可。

2.在init.rc中进行添加,这种方式与上述第一种方式类似,适合添加全局环境变量,不过这种方式会直接修改init.rc,可能会导致其他问题,如果不小心修改错误,可能会导致系统无法开机。这里给一个AOSP Android12中的示例:

// system/etc/init/hw/init.rc

# Linux's execveat() syscall may construct paths containing /dev/fd

# expecting it to point to /proc/self/fd

symlink /proc/self/fd /dev/fd



export DOWNLOAD_CACHE /data/cache



# This allows the ledtrig-transient properties to be created here so

# that they can be chown'd to system:system later on boot

write /sys/class/leds/vibrator/trigger "transient"



# This is used by Bionic to select optimized routines.

write /dev/cpu_variant:${ro.bionic.arch} ${ro.bionic.cpu_variant}

chmod 0444 /dev/cpu_variant:${ro.bionic.arch}

write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}

 3.如果我们需要的并非全局环境变量,如只是某些进程需要使用的环境变量,可以在对应进程的rc启动文件内通过setenv进行导入,如下所示:

​
service myservice /system/bin/myservice

        user system

        group root

        setenv MY_VARIABLE  value

 其中MY_VARIABLE为环境变量名,value为对应的值。

 4.在init.rc中使用单独的服务来进行导入,示例如下:

service setenv /system/bin/sh -c "export FOO=bar; exec sleep 3600"

    class main

    oneshot

这部分定义了一个名为setenv的服务,其执行的内容为:通过sh终端export名为FOO,值为bar的环境变量,之后sleep 3600s;其中oneshot表示只在开机阶段执行一次。

 5.直接修改init进程源码,通过setenv系统调用设置环境变量

使用环境变量

在程序内使用环境变量,我们会用到两个函数:setenv与getenv.

在C/C++中使用,两者皆需要引用stdlib.h头文件。

getenv函数:

char *getenv(const char *name);

使用示例:

​const char* reject_kill_server = getenv("ADB_REJECT_KILL_SERVER");

if (reject_kill_server && strcmp(reject_kill_server, "1") == 0) {

        adb_set_reject_kill_server(true);

}

返回值为指针,空指针则表明没有匹配的环境变量存在。

setenv函数:

int setenv(const char name, const char value, int overwrite);

返回值0代表成功,返回-1代表失败,同时会设置errno标志位。

对于应用(Java)而言,我们则可以通过System.getenv与System.setenv进行环境变量的获取与设置。

需要注意的是setenv函数设置的环境变量不是全局的,只对对应进程以及子进程有效。这也是为什么Android系统所有的全局环境变量都是放在rc启动文件内,因为只有init进程会去解析这些rc启动文件导入环境变量,并传递给后续的所有进程,自然也就成为“全局环境变量”。文章来源地址https://www.toymoban.com/news/detail-830720.html

到了这里,关于Android12系统环境变量设置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • Android Studio开发环境搭建及本地Gradle设置方法

    Android Studio开发环境搭建及本地Gradle设置方法 在进行Android应用程序开发时,Android Studio是一款非常强大的集成开发环境(IDE)。它提供了丰富的工具和功能,帮助开发者轻松创建、测试和调试Android应用程序。本文将详细介绍如何搭建Android Studio开发环境,并配置本地Gradle设置

    2024年02月07日
    浏览(51)
  • Android Studio开发环境搭建及设置本地Gradle方法

    Android Studio开发环境搭建及设置本地Gradle方法 在Android应用程序开发中,Android Studio是一款非常受欢迎的集成开发环境(IDE)。本文将详细介绍如何搭建Android Studio开发环境,并设置本地Gradle方法。同时,我们也会提供相应的源代码示例。 下载和安装Android Studio 首先,我们需要

    2024年01月19日
    浏览(51)
  • Mac系统下Android studio配置环境变量(ADB、JDK、GRADLE、Flutter)

    mac os 启动台–终端 进入当前用户的home目录(默认) : cd 若.bash_profile文件不存在则创建:touch .bashrc(名字可以自己定义.bash_profile) 打开.bash_profile(文件不存在则创建则:touch file_name):open .bashrc 在.bashrc文件最后添加需要配置的环境变量 ####格式: export PATH_NAME= P A T H : PATH: P A T

    2023年04月11日
    浏览(69)
  • Android反射@hide API 方法、变量,支持Android11和Android12

    Android源码中现在有大量的方法和变量被@hide所修饰,而这些被hide修饰的方法和变量是不允许应用层进行反射获取的,所以富有探索精神的程序员们就开始想尽各种办法绕过系统hide限制来使用@hide修饰的方法和变量。 Android11之前 我们可以使用套娃的形式来欺骗系统,让系统误

    2024年02月11日
    浏览(60)
  • Mac系统配置java、Android_sdk、gradle、maven、ndk、flutter、tomcat环境变量

    搞了三天,终于搞定MAC系统下的各种环境变量了…… 旧版本10.13.6或者更老的MAC系统,只用在.bash_profile文件编辑就行了;新版本10.14.2、10.15.7或者更高的,还要去.zshrc文件加一句source ~/.bash_profile,才能使所有环境永久生效。 打开终端(相当于Windows系统的命令提示符),运行

    2024年02月05日
    浏览(49)
  • Android环境变量&macOS环境变量配置

    关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 我们继续总结学习 Android基础知识 ,温故知新。 今天我们讲讲Android环境变量的配置,这个还是比较

    2024年02月08日
    浏览(50)
  • 【北京迅为】《iTOP-3588开发板快速测试手册》第二章 Android12系统功能测试

    RK3588是一款低功耗、高性能的处理器,适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用,RK3588支持8K视频编解码,内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP,内置NPU,支持INT4/INT8/INT16/FP16混合运算能力

    2024年02月21日
    浏览(53)
  • Android12 获取音频输出列表&设置音频输出通道

    有个需求是APP端能够获取所有音频输出列表,研究了很长时间源码,发现只有这个API合适。 这个API能够获取到设备上所有可用的输出,且APP可以调用。 需要在framework层修改,找一个Manager或者自己写一个Manager,添加以下API方法。

    2024年02月16日
    浏览(59)
  • 保姆级别——Android Studio安装教程&环境变量配置

    1.1 官网下载地址: Download Android Studio and SDK tools  |  Android Developers 1.2 找到下载好的安装包,双击,再按以下步骤操作。 1.3 这里点击Browse更换安装路径(推荐),也可以安装在默认路径下。 1.4 安装完成后勾选Start Android Studio启动并点击finish。  1.5 启动完成选择不导入配置,然

    2024年02月16日
    浏览(51)
  • Android SDK环境变量配置及连接真机

    目录 一、SDK环境变量配置 二、Android设备连接调试 三、常用adb命令 一、SDK下载和环境变量配置 SDK下载:    1、 官网地址:https://www.androiddevtools.cn/ ···       2、本地安装SDK:                           🔺安装提示:需要安装JDK  SDK指得是Android专属的软件开发工

    2024年03月20日
    浏览(44)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包