热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

javavmart2.1.0_ARTRuntime创建(二)启动参数

Android7.0aosp_shamu-userdebug一.启动时获取的参数AndroidRuntime::startVm(frameworkbaecprejniAndroid

Android 7.0 aosp_shamu-userdebug

一. 启动时获取的参数

AndroidRuntime::startVm(/framework/bae/cpre/jni/AndroidRuntime.cpp)方法会首先获取大量系统属性,并将这些系统属性转化为实际的启动参数,下面是获取的系统属性以及对应的启动参数:

dalvik.vm.checkjni(ro.kernel.android.checkjni)

真实值:无

默认为false, 如果为true, 则添加参数-Xcheck:jni

dalvik.vm.execution-mode

真实值:无

默认为KEMDefault, 有四个取值:KEMDefault, kEMIntPortable, kEMIntFast, kEMJitCompiler. 如果不是kEMDefault,则相应的参数为:-Xint:portable, -Xint:fast, -Xint:jit

dalvik.vm.stack-trace-file

真实值:/data/anr/traces.txt

参数的形式为:-Xstacktracefile:/data/anr/traces.txt

dalvik.vm.jniopts

真实值:没有设置

如果有设置的话,参数形式为:Xjnipots:...

{exit, runtime_exit}

Hook Runtime的exit()为runtime_exit(int code)函数

{vfprintf, runtime_vfprintf}

Hook Runtime的fprintf()为runtime_vfprintf(FILE* fp, const char* format, va_list ap)函数

{sensitiveThread, runtime_isSensitiveThread()}

Hook Runtime的sensitiveThread()为runtime_isSensitiveThread()函数

直接添加参数-verbose:gc

dalvik.vm.heapstartsize

真实值:8m

参数的形式为:-Xms8m

dalvik.vm.heapsize

真实值:512m

参数的形式为:-Xmx512m

dalvik.vm.heapgrowthlimit

真实值:256m

参数形式为:-XX:HeapGrowthLimit=256m

dalvik.vm.heapminfree

真实值:512k,

参数形式为:-XX:HeapMinFree=512k

dalvik.vm.heapmaxfree

真实值:8m

参数形式为:-XX:HeapMaxFree=8m

dalvik.vm.heaputilization

真实值:0.75

参数形式为:-XX:HeapTargetUtilization=0.75

dalvik.vm.usejit

真实值:true

参数形式为:-Xusejit:true

dalvik.vm.jitmaxsize

真实值是:无

如果设置则参数形式为:-Xjitmaxsize:${dalvik.vm.jitmaxsize}

dalvik.vm.jitinitialize

真实值:无,

如果设置参数形式为:-Xjitinitialize:${dalvik.vm.jitinitialize}

dalvik.vm.jitthreshold

真实值:无

如果设置参数形式为:-Xjitthreshold:..

dalvik.vm.usejitprofiles

真实值:true,

参数形式为:-Xjitsaveprofilinginfo

dalvik.vm.jitprithreadweight

真实值:无

如果设置,参数的形式为:-Xjitprithreadweight

dalvik.vm.jittransitionweight

真实值:无

如果设置,参数的形式为:-Xjittransitionweight:${dalvik.vm.jittransitionweight}

ro.config.low_ram

真实值:无

如果设置,参数形式为:-XXLowMemoryMode

dalvik.vm.gctype

真实值:无

如果设置,参数形式为:-Xgc:${dalvik.vm.gctype}

dalvik.vm.backgroundgctype

真实值:无

如果设置,参数形式为:-XX:BackgroundGC=${dalvik.vm.backgroundgctype}

直接添加参数-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y

dalvik.vm.lockprof.threshold

真实值:500

参数形式为:-Xlockprofthreshold:500

vold.decrypt

trigger_restart_framework

因为不等于trigfer_restart_min_framework和1,所以还要获取属性dalvik.vm.image-dex2oat-filter, 系统中并未设置该值,如果设置了,参数的形式为:--compiler-filter=${dalvik.vm.image-dex2oat-filter}, -Ximage-compiler-option

直接添加参数Ximage-compiler-option

直接添加参数--compiled-classes=/system/etc/preloaded-classes

如果文件/system/etc/compiled-classes存在(实际存在),则添加参数:-Ximage-compiler-option, --compiled-classes=/system/etc/compiled-classes

dalvik.vm.imgae-dex2oat-flags

真实值:无

如果设置,参数形式为:-Ximage-compile-option, ${image-dex2oat-flags}

dalvik.vm.dex2oat-Xms

真实值:64m

参数形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, -Xms64m

dalvik.vm.dex2oat-Xmx

真实值:512m

参数形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, -Xms512m

dalvik.vm.dex2oat-filter

真实值:无

如果设置了,参数的形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, --compiler-filter=${dalvik.vm.dex2oat-filter}

dalvik.vm.dex2oat-threads

真实值:无

如果设置了,参数形式为:-Xcompiler-option, --runtime-arg, -Xcompiler-option, -j${dalvik.vm.dex2oat-threads}

dalvik.vm.image-dex2oat-threads

真实值:无

如果设置了,参数形式为:-Ximage-compiler-option, --runtime-arg, -Ximage-compiler-option, -j${dalvik.vm.image-dex2oat-threads}

dalvik.vm.isa.arm.variant

真实值:krait

参数最终形式为:-Ximage-compiler-option, --runtime-arg, -Ximage-compiler-option, --instruction-set-variant=krait , -Xcompiler-option, --runtime-arg, -Xcompiler-option, --instruction-set-variant=krait

dalvik.vm.isa.arm.features

真实值:default

参数形式为:-Ximage-compiler-option, --runtime-arg, -Ximage-compiler-option, --instruction-set-features=default, -Xcompiler-option, --runtime-arg, -Xcompiler-option, instruction-set-variant-features=default

dalvik.vm.dex2oat-flags

真实值:无

如果设置,参数形式为:-Xcompiler-option, [dex2oat-flags]

dalvik.vm.extra-opts

真实值:无

如果设置,参数形式为:[extra-opts]

读取Locale,然后添加参数-Duser.locale=zh-Hans-CN

ro.debuggable

真实值:1,

如果ro.debuggable=1同时dalvik.vm.method-trace=true(实际为false),则添加参数-Xmethod-trace, -Xmethod-trace-file:[dalvik.vm.method-trace-file], -Xmethod-trace-file-size:[dalvik.vm.method-trace-file-siz], 如果dalvik.vm.method-trace-stream=true, 添加参数-Xmethod-trace-stream(实际都没有添加)

ro.dalvik.vm.native.bridge

真实值:0

如果值不为0, 则参数为:-XX:NativeBridge=[ro.dalvik.vm.native.bridge]

ro.product.cpu.abilist32

真实值:[armeabi-v7a,armeabi]

参数形式为:--cpu-abilist=[armeabi-v7a,armeabi]

dalvik.vm.zygote.max-boot-retry

真实值:无

如果设置, 最终形式为:-Xzygote-max-boot-retry=[dalvik.vm.zygote.max-boot-retry]

debug.generate-debug-info

真实值:无

如果值为true, 添加参数:-Xcompiler-option, --generate-debug-info, -Ximage-compiler-option, --generate-debug-info

ro.build.fingerprint

真实值:Android/aosp_shamu/shamu:7.0/NBD91U/xx12231922:userdebug/test-keys

参数形式为:-Xfingerprint:Android/aosp_shamu/shamu:7.0/NBD91U/xx12231922:userdebug/test-keys

二. 实际传入的参数

{

{`-Xstacktracefile:/data/anr/traces.txt`, NULL}, //{optionString, extraInfo}

{`exit`, runtime_exit},

{`vfprintf`, `runtime_vfprintf`},

{`sensitiveThread`, `runtime_isSensitiveThread`},

{`-verbose:gc`, NULL},

{`-Xms8m`, NULL},

{`-Xmx512m`, NULL},

{`-XX:HeapGrowthLimit=256m`,NULL},

{`-XX:HeapMinFree=512k`, NULL},

{`-XX:HeapMaxFree=8m`,NULL},

{`-XX:HeapTargetUtilization=0.75`,NULL},

{`-Xusejit:true`, NULL},

{`-Xjitsaveprofilinginfo`,NULL},

{`-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y`, NULL},

{`-Xlockprofthreshold:500`,NULL},

{`-Ximage-compiler-option`,NULL},

{`--compiled-classes=/system/etc/preloaded-classes`,NULL},

{`-Ximage-compiler-option`,NULL},

{`--compiled-classes=/system/etc/compiled-classes`,NULL},

{`-Xcompiler-option`,NULL},

{`--runtime-arg`,NULL},

{`-Xcompiler-option`,NULL},

{`-Xms64m`,NULL},

{`-Xcompiler-option`,NULL},

{`--runtime-arg`,NULL},

{`-Xcompiler-option`,NULL},

{`-Xmx512m`,NULL},

{`-Ximage-compiler-option`,NULL},

{`--runtime-arg`,NULL},

{`-Ximage-compiler-option`,NULL},

{`--instruction-set-variant=krait`,NULL},

{`-Xcompiler-option`,NULL},

{`--runtime-arg`,NULL},

{`-Xcompiler-option`,NULL},

{`--instruction-set-variant=krait`,NULL},

{`-Ximage-compiler-option`,NULL},

{`--runtime-arg`,NULL},

{`-Ximage-compiler-option`,NULL},

{`--instruction-set-features=default`,NULL},

{`-Xcompiler-option`,NULL},

{`--runtime-arg`,NULL},

{`-Xcompiler-option`,NULL},

{`--instruction-set-features=default`,NULL},

{`-Duser.locale=zh-Hans-CN`,NULL},

{`--cpu-abilist=[armeabi-v7a,armeabi]`,NULL},

{`-Xfingerprint:Android/aosp_shamu/shamu:7.0/NBD91U/xx12231922:userdebug/test-keys`, NULL}

}

三. 存储参数的数据结构和方法

3.1 存储参数的数据结构

typedef struct JavaVMInitArgs {

jint version;

jint nOptions;

JavaVMOption* options;

jboolean ignoreUnrecognized

}

typedef struct JavaVMOption {

const char* optionString;

void* extractInfo;

}

typedef std::vector> RuntimeOptions

3.2 最终传入的数据结构

JavaVMInitArgs initArgs;

initArgs.version = JNI_VERSION_1_4;

initArgs.options = mOptions.editArray();

initArgs.nOptions = mOptions.size();

initArgs.ignoreUnrecognized = JNI_FALSE;

3.3 添加参数的方法

void AndroidRuntime::addOption(const char* optionString, void* extraInfo)

{

JavaVMOption opt;

opt.optionString = optionString;

opt.extraInfo = extraInfo;

mOptions.add(opt);

}

四. 解析参数流程

4.1 JNI_CreateJavaVM

extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {

const JavaVMInitArgs* args = static_cast(vm_args);

...

RuntimeOptions options;

for (int i = 0; i nOptions; ++i) {

JavaVMOption* option = &args->options[i];

options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));

}

bool ignore_unrecognized = args->ignoreUnrecognized;

if (!Runtime::Create(options, ignore_unrecognized)) {

return JNI_ERR;

}

...

}

4.2 Runtime::Create()

bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) {

RuntimeArgumentMap runtime_options;

return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) &&

Create(std::move(runtime_options));

}

4.3 Runtime::ParseOptions

bool Runtime::ParseOptions(const RuntimeOptions& raw_options,

bool ignore_unrecognized,

RuntimeArgumentMap* runtime_options) {

InitLogging(/* argv */ nullptr);

bool parsed = ParsedOptions::Parse(raw_options, ignore_unrecognized, runtime_options);

if (!parsed) {

LOG(ERROR) <<"Failed to parse options";

return false;

}

return true;

}

4.4 ParsedOptions::Parse

位于/art/runtime/parsed_options.cc

bool ParsedOptions::Parse(const RuntimeOptions& options,

bool ignore_unrecognized,

RuntimeArgumentMap* runtime_options) {

CHECK(runtime_options !&#61; nullptr);

ParsedOptions parser;

return parser.DoParse(options, ignore_unrecognized, runtime_options);

}

4.5 ParsedOptions::DoParse

bool ParsedOptions::DoParse(const RuntimeOptions& options,

bool ignore_unrecognized,

RuntimeArgumentMap* runtime_options) {

...

//将字符串参数生成对应的M::key结构

auto parser &#61; MakeParser(ignore_unrecognized);

std::vectorargv_list;

//解析带有extraOption的参数, [5.2]

if (!ProcessSpecialOptions(options, nullptr, &argv_list)) {

return false;

}

CmdlineResult parse_result &#61; parser->Parse(argv_list);

// 处理parse errors

if (parse_result.IsError()) {

...

}

using M &#61; RuntimeArgumentMap;

RuntimeArgumentMap args &#61; parser->ReleaseArgumentsMap();

if (args.Exists(M::Help)) {

//-help

Usage(nullptr);

return false;

} else if (args.Exists(M::ShowVersion)) {

//-showversion

UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());

Exit(0);

} else if (args.Exists(M::BootClassPath)) {

//-Xbootclasspath

LOG(INFO) <<"setting boot class path to " <<*args.Get(M::BootClassPath);

}

//-Xusejit和-Xint不能同时使用

if (args.GetOrDefault(M::UseJitCompilation) && args.GetOrDefault(M::Interpret)) {

Usage("-Xusejit:true and -Xint cannot be specified together");

Exit(0);

}

//获取默认的bootclasspath

if (getenv("BOOTCLASSPATH") !&#61; nullptr) {

args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));

}

//获取默认的classpath

if (getenv("CLASSPATH") !&#61; nullptr) {

args.SetIfMissing(M::ClassPath, std::string(getenv("CLASSPATH")));

}

//设置参数-XX:ParallelGCThreads&#61;0u

//kDefaultEnableParallelGC&#61;false

args.SetIfMissing(M::ParallelGCThreads, gc::Heap::kDefaultEnableParallelGC ?

static_cast(sysconf(_SC_NPROCESSORS_CONF) - 1u) : 0u);

// -verbose:gc

{

LogVerbosity *log_verbosity &#61; args.Get(M::Verbose);

if (log_verbosity !&#61; nullptr) {

gLogVerbosity &#61; *log_verbosity;

}

}

MaybeOverrideVerbosity();

// 设置-Xprofile:,由于在启动时没有指定这个参数,所以使用默认值:

Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));

//再次检查extraInfo

if (!ProcessSpecialOptions(options, &args, nullptr)) {

return false;

}

{

// 如果没有设置,background回收器是默认的homogeneous compaction

// 如果foreground回收器是GSS, 则background也是GSS

// 如果是low memory模式, 则使用semispace

gc::CollectorType background_collector_type_;

gc::CollectorType collector_type_ &#61; (XGcOption{}).collector_type_; // NOLINT [whitespace/braces] [5]

//查看是否存在-XX:LowMemoryMode,实际上是没有的

bool low_memory_mode_ &#61; args.Exists(M::LowMemoryMode);

//获取参数-XX:BackgroundGC,实际上并未指定,所以使用默认值

background_collector_type_ &#61; args.GetOrDefault(M::BackgroundGc);

{

//获取参数-Xgc,实际上参数中并没有指定

...

}

if (background_collector_type_ &#61;&#61; gc::kCollectorTypeNone) {

if (collector_type_ !&#61; gc::kCollectorTypeGSS) {

//如果foreground回收器不是GSS,且当前是low_memory_mode,则使用SS, 否则指定为homogeneous compact

background_collector_type_ &#61; low_memory_mode_ ?

gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;

} else {

background_collector_type_ &#61; collector_type_;

}

}

args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });

}

#if defined(ART_TARGET)

std::string core_jar("/core.jar");

std::string core_libart_jar("/core-libart.jar");

#else

// The host uses hostdex files.

std::string core_jar("/core-hostdex.jar");

std::string core_libart_jar("/core-libart-hostdex.jar");

#endif

//获取-Xbootclasspath,之前已经通过getenv("BOOTCLASSPATH")设置

auto boot_class_path_string &#61; args.GetOrDefault(M::BootClassPath);

//在bootclasspath中查找/core.jar, 如果找到的话,则用/core-libart.jar替换

size_t core_jar_pos &#61; boot_class_path_string.find(core_jar);

if (core_jar_pos !&#61; std::string::npos) {

boot_class_path_string.replace(core_jar_pos, core_jar.size(), core_libart_jar);

args.Set(M::BootClassPath, boot_class_path_string);

}

{

//获取-Xbootclasspath和-Xbootclasspath-location:

auto&& boot_class_path &#61; args.GetOrDefault(M::BootClassPath);

auto&& boot_class_path_locations &#61; args.GetOrDefault(M::BootClassPathLocations);

//实际并未指定-Xbootclasspath-location

if (args.Exists(M::BootClassPathLocations)) {

//如果boot_class_path_locations的path数量不等于boot_class_path中的path路径,打印Usage并返回

...

}

}

//如果没有指定compilercallbacks回调函数(实际参数中并没有指定)以及没有指定-Ximage(实际参数也并未指定)

//指定image文件路径是/system/framework/boot.art

if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {

std::string image &#61; GetAndroidRoot();

image &#43;&#61; "/framework/boot.art";

args.Set(M::Image, image);

}

//获取-XX:HeapGrowthLimit(实际参数中为256m),判断是否小于0或者大于-Xmx(实际为512m)

//如果小于0或者大于-Xmx,则将-XX:HeapGrowthLimit设为-Xmx的值

if (args.GetOrDefault(M::HeapGrowthLimit) <&#61; 0u ||

args.GetOrDefault(M::HeapGrowthLimit) > args.GetOrDefault(M::MemoryMaximumSize)) {

args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));

}

//实际参数中并没有指定-Xexperimental

if (args.GetOrDefault(M::Experimental) & ExperimentalFlags::kLambdas) {

...

}

*runtime_options &#61; std::move(args);

return true;

}

DoParse方法主要做了以下几件事:

先将所有的字符串参数名称集中生成对应的RuntimeArgumentMap::[KEY]结构

从实际传入的参数中解析extra_info,extra_info用来hook函数,实际传入的参数中hook的是下面三个函数:exit,vfprintf,sensitiveThread

判断传入的参数中是否有-help,-showversion,如果存在,则打印对应的帮助信息并返回

确保-Xusejit和-Xint不能共同使用

getenv(BOOTCLASSPATH)并设置-Xbootclasspath

getenv(CLASSPATH)并设置-Xclasspath

设置-XX:ParallelGCThreads&#61;0u

根据实际传入的参数设置LogVerbosity

获取Xprofile,由于实际参数中没有设置,所以使用默认值,并设置Trace::SetDefaultClockSource

再次检索extra_info

根据kuseReadBarrier的值确定foreground GC, 如果kuseReadBarrier&#61;true,foregroundGC&#61;CC,否则foregroundGC&#61;default;查看实际参数中是否指定-Xgc,如果指定了则将参数指定的GC设为background GC(实际参数中并未指定);如果foreground GC是GSS,则background GC也是GSS;如果foreground GC不是GSS,则如果实际参数中指定了-XX:LowMemoryMode(实际参数中并未指定), 即低内存模式下backgroundGC&#61;GSS, 否则backgroundGC&#61;homogeneous Space Compact

如果bootclasspath中包含/core.jar,则用/core-libart.jar替换

如果指定了-Xbootclasspath-location,检查-Xbootclasspath-location锁指定的path数量是否和-Xbootclasspath中的path数量一样,不一样输出Usage并返回

由于实际参数中并没有指定-Ximage:和名为compilercallbacks的extra_info,指定Runtime的Image文件是/system/framework/boot.art

确保-XX:HeapGrowthLimit大于0同时小于-Xmx

4.5.1 ParsedOptions::ProcessSpecialOptions

bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options,

RuntimeArgumentMap* runtime_options,

std::vector* out_options) {

using M &#61; RuntimeArgumentMap;

for (size_t i &#61; 0; i

const std::string option(options[i].first);

if (option &#61;&#61; "bootclasspath") {

... //实际并没有设置

} else if (option &#61;&#61; "compilercallbacks") {

... //实际并没有设置

} else if (option &#61;&#61; "imageinstructionset") {

... //实际并没有设置

} else if (option &#61;&#61; "sensitiveThread") {

const void* hook &#61; options[i].second;

bool (*hook_is_sensitive_thread)() &#61; reinterpret_cast(const_cast(hook));

if (runtime_options !&#61; nullptr) {

runtime_options->Set(M::HookIsSensitiveThread, hook_is_sensitive_thread);

}

} else if (option &#61;&#61; "vfprintf") {

const void* hook &#61; options[i].second;

if (hook &#61;&#61; nullptr) {

Usage("vfprintf argument was nullptr");

return false;

}

int (*hook_vfprintf)(FILE *, const char*, va_list) &#61;

reinterpret_cast(const_cast(hook));

if (runtime_options !&#61; nullptr) {

runtime_options->Set(M::HookVfprintf, hook_vfprintf);

}

hook_vfprintf_ &#61; hook_vfprintf;

} else if (option &#61;&#61; "exit") {

const void* hook &#61; options[i].second;

if (hook &#61;&#61; nullptr) {

Usage("exit argument was nullptr");

return false;

}

void(*hook_exit)(jint) &#61; reinterpret_cast(const_cast(hook));

if (runtime_options !&#61; nullptr) {

runtime_options->Set(M::HookExit, hook_exit);

}

hook_exit_ &#61; hook_exit;

} else if (option &#61;&#61; "abort") {

... //实际并没有设置

} else {

... //实际并没有设置

}

}

return true;

}



推荐阅读
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
author-avatar
小麻雀yuuri
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有