1.引言 属性动画作为Android动画功能的一个重要组成部分,可以实现很多有趣的动画效果,理解属性动画的执行过程有助于我们更好地使用属性动画去实现需求。本文将从源码的角度去探索属性动画的实现过程,加深大家对其的认知和理解。 2.属性动画相关的类 2.1 ValueAnimator 这个类是实现属性动画的一个重要的类,通过ValueAnimator.ofFloat()、ValueAnimator.ofInt()、ValueAnimator.ofObject()、ValueAnimator.ofArgb()、ValueAnimator.ofPropertyValuesHolder()等方法可以获得ValueAnimator的对象,然后可以通过对这个对象的操作去实现动画。使用ValueAnimator实现属性动画,需要实现ValueAnimator.AnimatorUpdateListener()接口,并在onAnimationUpdate()方法内为要添加动画的对象设置属性值。 2.2 ObjectAnimator ObjectAnimator是ValueAnimator的子类,可以操作目标对象的动画属性,这个类的构造函数支持采用参数的形式传入要使用动画的目标对象和属性名。 3.属性动画的实现过程 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(iv, "alpha", 1.0f, 0f); objectAnimator.setDuration(3000); objectAnimator.start(); 这是一段简单的代码,它使用属性动画实现了一张图片的透明度渐变的效果,我们从这一段代码入手,去分析属性动画的实现过程。 3.1 创建属性动画 /** * target:添加动画效果的目标对象 * propertyName:动画效果的属性名 * values:动画将会在这个时间之间执行的数值集合 */ public static ObjectAnimator ofFloat(Object target, String propertyName, float... values){ ObjectAnimator anim = new ObjectAnimator(target, propertyName); anim.setFloatValues(values); return anim; } 这个方法返回了一个属性动画对象,第一个参数是产生动画效果的目标对象,第二个参数是属性名,目标对象的属性名应该有与之对应的set()方法,例如我们传入属性名"alpha",那么这个目标对象也应该有setAlpha()方法。参数values传一个值的时候,这个值是动画的结束值,传两个数值的时候,第一个值是开始值,第二个值是结束值,多于两个值的时候,第一个值是开始值,最后一个值是结束值。 private ObjectAnimator(Object target, String propertyName) { setTarget(target); setPropertyName(propertyName); } 这个是属性动画的构造函数,里面执行了两个方法setTarget(target)和setPropertyName(propertyName)。 @Override public void setTarget(@Nullable Object target) { final Object oldTarget = getTarget(); if (oldTarget != target) { if (isStarted()) { cancel(); } mTarget = target == null ? null : new WeakReference(target); // New target should cause re-initialization prior to starting mInitialized = false; } } public void setPropertyName(@NonNull String propertyName) { // mValues could be null if this is being constructed piecemeal. Just record the // propertyName to be used later when setValues() is called if so. if (mValues != null) { PropertyValuesHolder valuesHolder = mValues[0]; String oldName = valuesHolder.getPropertyName(); valuesHolder.setPropertyName(propertyName); mValuesMap.remove(oldName); mValuesMap.put(propertyName, valuesHolder); } mPropertyName = propertyName; // New property/values/target should cause re-initialization prior to starting mInitialized = false; } mValues是一个PropertyValuesHolder数组,PropertyValuesHolder持有动画的属性名和属性值信息,mValuesMap是一个hashmap数组,用来管理PropertyValuesHolder对象,在调用getAnimatedValue(String)方法的时候,这个map通过属性名去查找动画执行的数值。当mValues不为空的时候,将属性名信息放入mValuesMap。 //ObjectAnimator @Override public void setFloatValues(float... values) { if (mValues == null || mValues.length == 0) { // No values yet - this animator is being constructed piecemeal. Init the values with // whatever the current propertyName is if (mProperty != null) { setValues(PropertyValuesHolder.ofFloat(mProperty, values)); } else { setValues(PropertyValuesHolder.ofFloat(mPropertyName, values)); } } else { super.setFloatValues(values); } } mValues为null或者数组元素个数为0的时候,调用其父类ValueAnimator的setValues()方法,在setValues()内执行了初始化mValues和mValuesMap的操作,并将PropertyValuesHolder放入mValuesMap。当mValues不为null且元素个数不为0的时候,调用其父类ValueAnimator的setFloatValues()方法,在setFloatValues()方法内满足条件又会调用到PropertyValuesHolder的setFloatValues()方法。 //PropertyValuesHolder public void setFloatValues(float... values) { mValueType = float.class; mKeyframes = KeyframeSet.ofFloat(values); } 这里的mValueType指的是提供的值的类型,mKeyframes是定义这个动画的关键帧集合。 //KeyframeSet public static KeyframeSet ofFloat(float... values) { boolean badValue = false; int numKeyframes = values.length; FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)]; if (numKeyframes == 1) { keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f); keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]); if (Float.isNaN(values[0])) { badValue = true; } } else { keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]); for (int i = 1; i < numKeyframes; ++i) { keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]); if (Float.isNaN(values[i])) { badValue = true; } } } if (badValue) { Log.w("Animator", "Bad value (NaN) in float animator"); } return new FloatKeyframeSet(keyframes); } 在这个方法内新建了一个FloatKeyframe数组,数组的元素至少为2个,FloatKeyframe是Keyframe的内部子类,持有这个动画的时间值对,Keyframe类被ValueAnimator用来定义整个动画过程中动画目标的数值,当时间从一帧到另一帧,目标对象的值也会从上一帧的值运动到下一帧的值。 /** * fraction:取值范围0到1之间,表示全部动画时长中已经执行的时间部分 * value:关键帧中与时间相对应的数值 */ public static Keyframe ofFloat(float fraction, float value) { return new FloatKeyframe(fraction, value); } 此方法使用给定的时间和数值创建一个关键帧对象,到这里,属性动画的创建过程基本完成。 3.2 属性动画执行过程 //ObjectAnimator @Override public void start() { AnimationHandler.getInstance().autoCancelBasedOn(this); if (DBG) { Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration()); for (int i = 0; i < mValues.length; ++i) { PropertyValuesHolder pvh = mValues[i]; Log.d(LOG_TAG, " Values[" + i + "]: " + pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " + pvh.mKeyframes.getValue(1)); } } super.start(); } 在代码中调用objectAnimator.start()的时候动画开始执行,内部调用了其父类ValueAnimator的start()方法。 //ValueAnimator private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; mSelfPulse = !mSuppressSelfPulseRequested; // Special case: reversing from seek-to-0 should act as if not seeked at all. if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) { if (mRepeatCount == INFINITE) { // Calculate the fraction of the current iteration. float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction)); mSeekFraction = 1 - fraction; } else { mSeekFraction = 1 + mRepeatCount - mSeekFraction; } } mStarted = true; mPaused = false; mRunning = false; mAnimationEndRequested = false; // Resets mLastFrameTime when start() is called, so that if the animation was running, // calling start() would put the animation in the // started-but-not-yet-reached-the-first-frame phase. mLastFrameTime = -1; mFirstFrameTime = -1; mStartTime = -1; addAnimationCallback(0); if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) { // If there's no start delay, init the animation and notify start listeners right away // to be consistent with the previous behavior. Otherwise, postpone this until the first // frame after the start delay. startAnimation(); if (mSeekFraction == -1) { // No seek, start at play time 0. Note that the reason we are not using fraction 0 // is because for animations with 0 duration, we want to be consistent with pre-N // behavior: skip to the final value immediately. setCurrentPlayTime(0); } else { setCurrentFraction(mSeekFraction); } } } 在这个方法内进行了一些赋值操作,addAnimationCallback(0)和startAnimation()是比较重要的操作。 //ValueAnimator private void addAnimationCallback(long delay) { if (!mSelfPulse) { return; } getAnimationHandler().addAnimationFrameCallback(this, delay); } 这个方法内执行了AnimationHandler的addAnimationFrameCallback()方法注册回调,我们继续看看addAnimationFrameCallback()方法。 //AnimationHandler public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) { if (mAnimationCallbacks.size() == 0) { getProvider().postFrameCallback(mFrameCallback); } if (!mAnimationCallbacks.contains(callback)) { mAnimationCallbacks.add(callback); } if (delay > 0) { mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay)); } } 这个方法添加了一个AnimationFrameCallback回调,AnimationFrameCallback是AnimationHandler的一个内部接口,其中有两个重要的方法doAnimationFrame()和commitAnimationFrame()。 //AnimationHandler interface AnimationFrameCallback { boolean doAnimationFrame(long frameTime); void commitAnimationFrame(long frameTime); } AnimationFrameCallback是可以收到动画执行时间和帧提交时间通知的回调,内有两个方法,doAnimationFrame()和commitAnimationFrame()。 //AnimationHandler private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider { final Choreographer mChoreographer = Choreographer.getInstance(); @Override public void postFrameCallback(Choreographer.FrameCallback callback) { mChoreographer.postFrameCallback(callback); } @Override public void postCommitCallback(Runnable runnable) { mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null); } @Override public long getFrameTime() { return mChoreographer.getFrameTime(); } @Override public long getFrameDelay() { return Choreographer.getFrameDelay(); } @Override public void setFrameDelay(long delay) { Choreographer.setFrameDelay(delay); } } 前面的getProvider()方法获得了MyFrameCallbackProvider的一个实例,MyFrameCallbackProvider是AnimationHandler的一个内部类,实现了AnimationFrameCallbackProvider接口,使用Choreographer作为计时脉冲的提供者,去发送帧回调。Choreographer从显示器子系统获得时间脉冲,postFrameCallback()方法发送帧回调。 //AnimationHandler public interface AnimationFrameCallbackProvider { void postFrameCallback(Choreographer.FrameCallback callback); void postCommitCallback(Runnable runnable); long getFrameTime(); long getFrameDelay(); void setFrameDelay(long delay); } //AnimationHandler private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); if (mAnimationCallbacks.size() > 0) { getProvider().postFrameCallback(this); } } }; 在这个回调内执行了doAnimationFrame()方法,如果mAnimationCallbacks的个数大于0,AnimationFrameCallbackProvider就继续发送帧回调,继续重复执行doAnimationFrame()。 //AnimationHandler private void doAnimationFrame(long frameTime) { long currentTime = SystemClock.uptimeMillis(); final int size = mAnimationCallbacks.size(); for (int i = 0; i < size; i++) { final AnimationFrameCallback callback = mAnimationCallbacks.get(i); if (callback == null) { continue; } if (isCallbackDue(callback, currentTime)) { callback.doAnimationFrame(frameTime); if (mCommitCallbacks.contains(callback)) { getProvider().postCommitCallback(new Runnable() { @Override public void run() { commitAnimationFrame(callback, getProvider().getFrameTime()); } }); } } } cleanUpList(); } 在这个方法内开启了一个循环,里面执行了callback.doAnimationFrame(),这个操作会触发ValueAnimator类中的doAnimationFrame()。 //ValueAnimator private void startAnimation() { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), System.identityHashCode(this)); } mAnimationEndRequested = false; initAnimation(); mRunning = true; if (mSeekFraction >= 0) { mOverallFraction = mSeekFraction; } else { mOverallFraction = 0f; } if (mListeners != null) { notifyStartListeners(); } } startAnimation()方法内调用了initAnimation()初始化动画。 //ValueAnimator public final boolean doAnimationFrame(long frameTime) { //省略部分代码 ... final long currentTime = Math.max(frameTime, mStartTime); boolean finished = animateBasedOnTime(currentTime); if (finished) { endAnimation(); } return finished; } 这个方法在执行动画的过程中会被多次调用,其中重要的操作是animateBasedOnTime(currentTime)。 //ValueAnimator boolean animateBasedOnTime(long currentTime) { boolean done = false; if (mRunning) { final long scaledDuration = getScaledDuration(); final float fraction = scaledDuration > 0 ? (float)(currentTime - mStartTime) / scaledDuration : 1f; final float lastFraction = mOverallFraction; final boolean newIteration = (int) fraction > (int) lastFraction; final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) && (mRepeatCount != INFINITE); if (scaledDuration == 0) { // 0 duration animator, ignore the repeat count and skip to the end done = true; } else if (newIteration && !lastIterationFinished) { // Time to repeat if (mListeners != null) { int numListeners = mListeners.size(); for (int i = 0; i < numListeners; ++i) { mListeners.get(i).onAnimationRepeat(this); } } } else if (lastIterationFinished) { done = true; } mOverallFraction = clampFraction(fraction); float currentIterationFraction = getCurrentIterationFraction( mOverallFraction, mReversing); animateValue(currentIterationFraction); } return done; } animateBasedOnTime()方法计算了已经执行的动画时长和动画分数,并调用animateValue()方法计算动画值。 //ValueAnimator void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } } } ValueAnimator的animateValue()方法内部首先根据动画分数得到插值分数,再根据插值分数计算动画值,并调用了AnimatorUpdateListener的onAnimationUpdate()方法通知更新。 //ObjectAnimator @Override void animateValue(float fraction) { final Object target = getTarget(); if (mTarget != null && target == null) { // We lost the target reference, cancel and clean up. Note: we allow null target if the /// target has never been set. cancel(); return; } super.animateValue(fraction); int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].setAnimatedValue(target); } } ObjectAnimator的animateValue()方法不仅调用了父类的animateValue()方法,还在循环内调用了PropertyValuesHolder的setAnimatedValue()方法,传入的参数是产生动画效果的目标对象。 //PropertyValuesHolder @Override void setAnimatedValue(Object target) { if (mFloatProperty != null) { mFloatProperty.setValue(target, mFloatAnimatedValue); return; } if (mProperty != null) { mProperty.set(target, mFloatAnimatedValue); return; } if (mJniSetter != 0) { nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue); return; } if (mSetter != null) { try { mTmpValueArray[0] = mFloatAnimatedValue; mSetter.invoke(target, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyValuesHolder", e.toString()); } catch (IllegalAccessException e) { Log.e("PropertyValuesHolder", e.toString()); } } } 在PropertyValuesHolder的setAnimatedValue()方法内部,先通过JNI去修改目标对象的属性值,如果通过JNI找不到对应的方法,则通过使用反射机制修改目标对象的属性值。 4.总结 属性动画的功能相当强大,可以为视图对象和非视图对象添加动画效果,属性动画是通过改变要添加动画的目标对象的属性值实现的,ValueAnimator基于动画时长和已经执行的时长计算得出动画分数,然后根据设置的时间插值器TimeInterpolator计算得出动画的插值分数,再调用对应的估值器TypeEvaluator根据插值分数、起始值和结束值计算得出对象的属性值,ObjectAnimator类在计算出动画的新值后自动地更新对象的属性值,ValueAnimator类则需要手动地去设置对象的属性值。 你可能感兴趣的:(探究Android属性动画执行过程) Vulkan hanpfei Android图形系统 Android7.0添加了对Vulkan的支持,一个高性能3D图形的低开销跨平台API。像OpenGLES一样,Vulkan提供了在应用中创建高质量,实时图形的工具。Vulkan的优势包括CPU开销降低及支持SPIR-VBinaryIntermediate语言。片上系统生产商(SoCs)比如GPU独立硬件供应商(IHVs)可以为Android编写Vulkan驱动;OEMs简单地需要为特定的硬件集成 深度学习模型中的知识蒸馏是如何工作的? c++服务器开发 深度学习人工智能 深度学习模型在多个领域,特别是计算机视觉和自然语言处理中,已经取得了革命性的进展。然而,随着模型复杂性和资源需求的不断攀升,如何将这些庞大模型的知识浓缩为更紧凑、更高效的形式,成为了当前研究的热点。知识蒸馏,作为一种将知识从复杂模型转移到更简单模型的策略,已经成为实现这一目标的有效工具。在本文中,我们将深入探究深度学习模型中知识蒸馏的概念、原理及其在各领域的应用,以期为读者提供一个全面而严谨的视角 autojs使用nodejs调用sqlite数据库 牙叔教程 nodejsautojssqlite数据库 牙叔教程简单易懂依赖"nodejs";require("rhino").install();const{device}=require("device");constpath=require("path");constfs=require("fs");constutil=require("util");constSQLiteDatabase=android.database.sqlite.SQLi 探索AutoJS:一款强大的Android自动化工具 夏庭彭Maxine 探索AutoJS:一款强大的Android自动化工具去发现同类优质开源项目:https://gitcode.com/在如今高度数字化的世界中,自动化扮演着越来越重要的角色。尤其在移动端,AutoJS是一个专为Android设计的强大自动化工具,它允许用户编写JavaScript代码来实现各种自动化任务,极大地提高了效率。这篇推荐文章将带你了解AutoJS的核心技术、应用场景以及其独特之处。项目简介 AGP 8.0 适配 - 问题列表 zhuzhumouse gradleandroid-studioandroid 不管是基于编译速度,包体积,新特性等那个方面的考虑,大家都应该开始AGP8.0的适配了,下面使我适配过程中遇到的各种问题,这里列出来,给大家的适配做一个参考。问题一:编译失败@TaskAction解决方案:在根目录的setting.gradle或者build.gradle里面添加如下代码:classpath'com.android.tools.build:gradle:8.0.0'原因说明:AGP 一篇文章搞定《Android布局优化》 小孟来码 Android一篇文章搞定Androidandroid性能优化 ------《一篇文章搞定Android布局优化》前言为什么要进行布局优化?Android绘制原理双缓冲机制布局加载原理布局加载优化的一些方法介绍AsyncLayoutInflater方案X2C方案Compose方案一些常规优化手段优化布局层级及复杂度mergeViewStub避免过度绘制前言在使用ViewPager时,如果我们的适配器使用的是Fragment。Android的绘制优化其实可以分为 Android 第四十二章 Fragment 漂泊的蚂蚁 Androidandroid 一、简介Fragment(碎片)主要用于对ActivityUI模块化,依托Activity存在,当Activity销毁时,Fragment随之销毁二、生命周期onAttach()Fragment和Activity绑定onCreate()初始化onCreateView()创建视图onActivityCreated()Activity已初始化完成onStart()界面可见onResume()获得焦点o Android 第四十四章 Menu 漂泊的蚂蚁 Androidandroid publicclassMainActivityextendsAppCompatActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublicboole Android中Handler的post(Runnable)用法 莫空0000 Android privateHandlermhandler=newHandler();privatevoidabc(){newThread(newRunnable(){@Overridepublicvoidrun(){try{/*dosomething*///里面执行的事件是被handler放在主线程中执行的,与Run所在线程无关。//.post方法一般用于更新UImhandler.post(newRunnab gms认证流程_Android P(9.0)GMS认证新要求 weixin_39879651 gms认证流程 本文档主要初步整理AndroidP对GMS认证的新要求一、基本环境主机要求:Java8Python2.7ADB1.0.39VTS环境$sudoapt-getinstallpython-dev$sudoapt-getinstallpython-protobuf$sudoapt-getinstallprotobuf-compiler$sudoapt-getinstallpython-virtualen Android14 AOSP 允许system分区和vendor分区应用进行AIDL通信 冬瓜神君 androidbinder 在Android14上,出于种种原因,system分区的应用无法和vendor分区的应用直接通过AIDL的方法进行通信,但是项目的某个功能又需要如此。好在Binder底层其实是支持的,只是在上层进行了屏蔽。修改frameworks/native/libs/binder/BpBinder.cpp文件,找到status_tBpBinder::transact方法,屏蔽相关判断语句或者错误返回即可。如下 Android从源码分析handler.post(runnable),view.post(runnable),runOnUiThread(runnable)执行时机 听者110 Android高级开发系列笔记Android线程 大家好,我是听者,耳听心受的听,孙行者的者,感谢大家阅读我的文章。废话不说直接进入主题,不管是Android还是其他语言,线程之间通信都是一个比较“头疼”问题,开发Android的码农应该都知道回到主线程的方式有handler.post(runnable),view.post(runnable),runOnUiThread(runnable)。但是这三种方式的区别以及其执行的时机如何呢?今天就给大 哔哩哔哩APP的 AGP8 升级之旅 锐湃 Gradleandroidgitee 前言是的,2024年我还在做Android原生开发,没有kmp,没有遥遥领先。本次Android大仓的AGP8升级涵盖多个APP多个业务方,持续3个月;分各个三大步,若干小步完成升级迁移,以下为本次升级踩坑经历。升级与踩坑本次AGP升级计划从7.2.2升级到8.2.2,AGP中最大的变动点是TransformApi接口的废弃、以及默认编译特性的开启。同时也要顺带升级Gradle版本,部分Gradl Flutter‘s app_plugin_loader Gradle plugin imperatively using the apply script错误解决 江上清风山间明月 Flutterflutterplugin_loaderGradleapplyplugin 文章目录问题描述解决方法1.查看当前的`android/build.gradle`2.迁移到`plugins`声明式方法修改前:修改后:3.同步Flutter工具更新4.参考Flutter官方文档补充说明问题描述用Androidstudio开发flutter的andoridapk时编译报如下警告:YouareapplyingFlutter’sapp_plugin_loaderGradleplugi gradle设置build执行命令_Android开发Gradle高级技巧、自定义Gradle Task、打包完成自动签名、执行Shell脚本... 一张卷纸 先说下为什么有这篇文章吧,最近在写一个应用需要进行系统签名;每次我都需要先将apk生成然后使用sign.jar和系统证书进行签名,非常的麻烦而且tmd还是搞两个系统也就是每次都需要用两个系统证书分别进行签名…一:每次我都需要两个不同的设备的安装包,所以我使用多渠道打包这样每次进行打包都会给我生成两个apk。如下代码:app.gradle,这里需要注意的是多渠道打包,需要指定一个flavorDime Mac环境jenkins多渠道配置打包Flutter、Android应用 Super-Bin Flutterandroidflutterjenkins移动端 这里写目录标题前言Jenkins安装启动安装插件项目配置General配置源码管理构建构建后操作执行解决问题找不到./gradlew命令解决方法1解决方法2只显示代码,没有正常显示二维码局域网ip无法访问jenkins修改配置可能与nginx的端口占用有关参考前言关于Flutter、Android多渠道配置打包,源码Jenkins安装两种安装方式:各平台官网安装Jenkins步骤mac官网安装je 创建全局异常处理器(Global Exception Processor) 易安杰 java开发语言jvmspringboot 先来了解一下什么是异常?在Java程序中,异常(Exception)是指在程序执行过程中发生的非正常情况,它打断了正常的指令流;Java中的异常处理是一种用于处理程序中错误和异常情况的一种机制。如上图所示异常主要分为两类,包括Error和Exception,两种异常有一个共同的父类是Throwable;在Exception中又分为RuntimeException(运行时异常)和CheckedExc AGP 8.0 适配 ---- jvm target compatibility zhuzhumouse androidjavagradle 基础知识科普Android工程的GradleJDK,以及代码里面配置的jvmTarget有什么区别?这是两个完全不同的概念:GradleJDK:指的是用于运行Gradle构建系统的JavaDevelopmentKit版本。Gradle是Android项目的构建工具,它负责处理项目的编译、打包、依赖管理等任务。GradleJDK的选择决定了在构建过程中使用的Java版本。jvmTarget:是jav 认识 ADB(Android Debug Bridge,Android SDK 中的一个工具) 我命由我12345 运维-ADBadbandroidandroid-studioandroidstudioandroidjetpack安卓androidruntime 一、ADB概述ADB,全称AndroidDebugBridge,是AndroidSDK中的一个工具ADB位于AndroidSDK下platform-tools目录中ADB起到调试桥的作用,ADB可以让开发者通过USB连接安卓设备,并在电脑上执行各种命令,从而方便开发和测试安卓应用程序ADB使开发者能够在电脑上对安卓设备进行全面的操作,包括安装卸载APK、拷贝文件、查看设备硬件信息、查看应用程序占用 android+8.0对应版本,Android各版本适配之8.0 chinhoyoo android+8.0对应版本 Android8.0透明Activity报错"Onlyfullscreenactivitiescanrequestorientation"1、分析问题首先,我的代码是这样的:style.xmltruetrueadjustPanfalse@color/app_transparent_colortrue//透明true//悬浮AndroidManifest.xml从上面可以看出,我的activity是 android8.0自适应图标,适配8.0+的自适应图标 贫民窟的大富翁 安卓8.0自适应图标适配教程图标背景XML文件 工具:mt管理器自适应图标是安卓8.0引入的新特性,可以让图标随系统主题变化,保持高度整齐,这个特性其实适配并不难准备工作:抠取图标前景(这个不用我讲了把),提取背景色步骤1:添加图标前景把图标前景文件丢进安装包,丢哪里都行,记住路径就行图片发自简书App打开arsc,在drawable/drawable下新建资源,类型为文本,把图标前景路径输进去,并记下这个资源的arscID比如7F020006 android代码跳转通知权限界面,Android 8.0+通知栏(Notification)适配详解,判断通知权限是否打开,并跳转至通知管理页面... 我是索吹 前不久由于API26(Android8.0)以上的设备没法显示通知栏,因此网上查阅了比较多了资料,得出结论,API26之后引入了通知渠道这么个东西,而后就写了一篇问题对应的博文:Android8.0通知栏(Notification)适配,在模拟上是完美运行,但是当我前两天安装到真机上测试时,咦?怎么又无效了?而后我就想着,是否是通知权限没打开?由于模拟器上不少权限的控制和真机上差异很大,我打开设置 深入理解 Android 混淆规则 nukix androidandroid 在Android开发中,混淆(Obfuscation)是一种保护代码安全的重要手段,通常通过ProGuard或R8工具来实现。本文将详细介绍Android混淆规则的基本原理、配置方法以及最佳实践,帮助开发者更好地保护应用代码。博主博客https://blog.uso6.comhttps://blog.csdn.net/dxk539687357什么是混淆?混淆是一种通过对代码进行重命名、删除无用代码 Android Studio gradle配置 nukix androidandroid settings.gradle配置指定路径module博主博客https://blog.uso6.comhttps://blog.csdn.net/dxk539687357一、正常情况,导入本项目的模块只需要使用include':app',':library'即可。二、但是当需要导入其他项目的模块,可以使用相对路径指定include':app',':library'project(':thirdl Android 接收 App 安装卸载更新 nukix androidjavaandroid 本篇文章主要介绍如何监听APP的安装、卸载和更新的广播。博主博客https://blog.uso6.comhttps://blog.csdn.net/dxk539687357Android8.0以前静态注册在AndroidManifest.xml中加入静态注册即可。Android8.0及以后需要动态注册InstallReceiverinstallReceiver=newInstallReceive Android中获取so文件来源于哪个库 火龙映天 Android相关android Androidapp中可能有很多的.so文件,有时我们不确定这些.so文件都是来源于哪些库的,可以通过在build.gradle中添加代码来统计。具体方法如下:1.在com.android.application模块的build.gradle文件最后添加如下代码://获取所有的.so文件的打包路径tasks.whenTaskAdded{task->if(task.name=='mergeDebug Android Java创建ViewModel新api debug_cat Android应用层开发androidjavaleetcode 背景项目使用Java,创建ViewModel发现之前旧api不管用了。不要问为什么项目还要用Java,别问。老项目不让升级。ViewModel创建新方式新方式是因为依赖新版本库,其实用旧版本库就回到旧方式了。依赖:deflifecycle_version="2.5.0"//ViewModelimplementation"androidx.lifecycle:lifecycle-viewmodel: agent和android怎么结合:健康助手,旅游助手,学习助手 ZhangJiQun&MXP 教学2021论文2024大模型以及算力android旅游学习人工智能语言模型自然语言处理prompt agent和android怎么结合:健康助手,旅游助手,学习助手创新点智能交互创新:提出全新的agent-Android交互模式,如基于手势、语音、眼动等多模态融合的交互方式。例如让agent能够同时理解用户的语音指令和手势动作,在Android设备上提供更加自然和高效的交互体验,比如在观看视频时,用户可以通过语音和手势结合的方式让agent调整视频播放进度、音量等。个性化服务创新:利用agent Android15音频进阶之焦点仲裁矩阵(一百零七) Android系统攻城狮 AndroidAudio工程师进阶系列音视频矩阵python 简介:CSDN博客专家、《Android系统多媒体进阶实战》一书作者新书发布:《Android系统多媒体进阶实战》优质专栏:Audio工程师进阶系列【原创干货持续更新中……】优质专栏:多媒体系统工程师系列【原创干货持续更新中……】优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 揭秘Linux:从服务器到智能家居,一个操作系统如何改变世界 寒水馨 Linux问题解决方案服务器linux智能家居 揭秘Linux:从服务器到智能家居,一个操作系统如何改变世界文章目录揭秘Linux:从服务器到智能家居,一个操作系统如何改变世界引言:Linux,不仅仅是一个操作系统Linux在企业级应用中的霸主地位云计算与虚拟化:Linux的天下大数据与人工智能:Linux的助推器Web服务器:Apache和Nginx的最佳搭档Linux在移动和嵌入式设备中的崛起Android:Linux的最成功衍生品物联网( 分享100个最新免费的高匿HTTP代理IP mcj8089 代理IP代理服务器匿名代理免费代理IP最新代理IP 推荐两个代理IP网站: 1. 全网代理IP:http://proxy.goubanjia.com/ 2. 敲代码免费IP:http://ip.qiaodm.com/ 120.198.243.130:80,中国/广东省 58.251.78.71:8088,中国/广东省 183.207.228.22:83,中国/ mysql高级特性之数据分区 annan211 java数据结构mongodb分区mysql mysql高级特性 1 以存储引擎的角度分析,分区表和物理表没有区别。是按照一定的规则将数据分别存储的逻辑设计。器底层是由多个物理字表组成。 2 分区的原理 分区表由多个相关的底层表实现,这些底层表也是由句柄对象表示,所以我们可以直接访问各个分区。存储引擎管理分区的各个底层 表和管理普通表一样(所有底层表都必须使用相同的存储引擎),分区表的索引只是 JS采用正则表达式简单获取URL地址栏参数 chiangfai js地址栏参数获取 GetUrlParam:function GetUrlParam(param){ var reg = new RegExp("(^|&)"+ param +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null 怎样将数据表拷贝到powerdesigner (本地数据库表) Array_06 powerDesigner ================================================== 1、打开PowerDesigner12,在菜单中按照如下方式进行操作 file->Reverse Engineer->DataBase 点击后,弹出 New Physical Data Model 的对话框 2、在General选项卡中 Model name:模板名字,自 logbackのhelloworld 飞翔的马甲 日志logback 一、概述 1.日志是啥? 当我是个逗比的时候我是这么理解的:log.debug()代替了system.out.print(); 当我项目工作时,以为是一堆得.log文件。 这两天项目发布新版本,比较轻松,决定好好地研究下日志以及logback。 传送门1:日志的作用与方法: http://www.infoq.com/cn/articles/why-and-how-log 上面的作 新浪微博爬虫模拟登陆 随意而生 新浪微博 转载自:http://hi.baidu.com/erliang20088/item/251db4b040b8ce58ba0e1235 近来由于毕设需要,重新修改了新浪微博爬虫废了不少劲,希望下边的总结能够帮助后来的同学们。 现行版的模拟登陆与以前相比,最大的改动在于cookie获取时候的模拟url的请求 synchronized 香水浓 javathread Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍然 maven 简单实用教程 AdyZhang maven 1. Maven介绍 1.1. 简介 java编写的用于构建系统的自动化工具。目前版本是2.0.9,注意maven2和maven1有很大区别,阅读第三方文档时需要区分版本。 1.2. Maven资源 见官方网站;The 5 minute test,官方简易入门文档;Getting Started Tutorial,官方入门文档;Build Coo Android 通过 intent传值获得null aijuans android 我在通过intent 获得传递兑现过的时候报错,空指针,我是getMap方法进行传值,代码如下 1 2 3 4 5 6 7 8 9 public void getMap(View view){ Intent i = apache 做代理 报如下错误:The proxy server received an invalid response from an upstream baalwolf response 网站配置是apache+tomcat,tomcat没有报错,apache报错是: The proxy server received an invalid response from an upstream server. The proxy server could not handle the request GET /. Reason: Error reading fr Tomcat6 内存和线程配置 BigBird2012 tomcat6 1、修改启动时内存参数、并指定JVM时区 (在windows server 2008 下时间少了8个小时) 在Tomcat上运行j2ee项目代码时,经常会出现内存溢出的情况,解决办法是在系统参数中增加系统参数: window下, 在catalina.bat最前面 set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m -Xms5 Karam与TDD bijian1013 KaramTDD 一.TDD 测试驱动开发(Test-Driven Development,TDD)是一种敏捷(AGILE)开发方法论,它把开发流程倒转了过来,在进行代码实现之前,首先保证编写测试用例,从而用测试来驱动开发(而不是把测试作为一项验证工具来使用)。 TDD的原则很简单: a.只有当某个 [Zookeeper学习笔记之七]Zookeeper源代码分析之Zookeeper.States bit1129 zookeeper public enum States { CONNECTING, //Zookeeper服务器不可用,客户端处于尝试链接状态 ASSOCIATING, //??? CONNECTED, //链接建立,可以与Zookeeper服务器正常通信 CONNECTEDREADONLY, //处于只读状态的链接状态,只读模式可以在 【Scala十四】Scala核心八:闭包 bit1129 scala Free variable A free variable of an expression is a variable that’s used inside the expression but not defined inside the expression. For instance, in the function literal expression (x: Int) => (x android发送json并解析返回json ronin47 android package com.http.test; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import 一份IT实习生的总结 brotherlamp PHPphp资料php教程php培训php视频 今天突然发现在不知不觉中自己已经实习了 3 个月了,现在可能不算是真正意义上的实习吧,因为现在自己才大三,在这边撸代码的同时还要考虑到学校的功课跟期末考试。让我震惊的是,我完全想不到在这 3 个月里我到底学到了什么,这是一件多么悲催的事情啊。同时我对我应该 get 到什么新技能也很迷茫。所以今晚还是总结下把,让自己在接下来的实习生活有更加明确的方向。最后感谢工作室给我们几个人这个机会让我们提前出来 据说是2012年10月人人网校招的一道笔试题-给出一个重物重量为X,另外提供的小砝码重量分别为1,3,9。。。3^N。 将重物放到天平左侧,问在两边如何添加砝码 bylijinnan java public class ScalesBalance { /** * 题目: * 给出一个重物重量为X,另外提供的小砝码重量分别为1,3,9。。。3^N。 (假设N无限大,但一种重量的砝码只有一个) * 将重物放到天平左侧,问在两边如何添加砝码使两边平衡 * * 分析: * 三进制 * 我们约定括号表示里面的数是三进制,例如 47=(1202 dom4j最常用最简单的方法 chiangfai dom4j 要使用dom4j读写XML文档,需要先下载dom4j包,dom4j官方网站在 http://www.dom4j.org/目前最新dom4j包下载地址:http://nchc.dl.sourceforge.net/sourceforge/dom4j/dom4j-1.6.1.zip 解开后有两个包,仅操作XML文档的话把dom4j-1.6.1.jar加入工程就可以了,如果需要使用XPath的话还需要 简单HBase笔记 chenchao051 hbase 一、Client-side write buffer 客户端缓存请求 描述:可以缓存客户端的请求,以此来减少RPC的次数,但是缓存只是被存在一个ArrayList中,所以多线程访问时不安全的。 可以使用getWriteBuffer()方法来取得客户端缓存中的数据。 默认关闭。 二、Scan的Caching 描述: next( )方法请求一行就要使用一次RPC,即使 mysqldump导出时出现when doing LOCK TABLES daizj mysqlmysqdump导数据 执行 mysqldump -uxxx -pxxx -hxxx -Pxxxx database tablename > tablename.sql 导出表时,会报 mysqldump: Got error: 1044: Access denied for user 'xxx'@'xxx' to database 'xxx' when doing LOCK TABLES 解决 CSS渲染原理 dcj3sjt126com Web 从事Web前端开发的人都与CSS打交道很多,有的人也许不知道css是怎么去工作的,写出来的css浏览器是怎么样去解析的呢?当这个成为我们提高css水平的一个瓶颈时,是否应该多了解一下呢? 一、浏览器的发展与CSS 《阿甘正传》台词 dcj3sjt126com Part Ⅰ: 《阿甘正传》Forrest Gump经典中英文对白 Forrest: Hello! My names Forrest. Forrest Gump. You wanna Chocolate? I could eat about a million and a half othese. My momma always said life was like a box ochocol Java处理JSON dyy_gusi json Json在数据传输中很好用,原因是JSON 比 XML 更小、更快,更易解析。 在Java程序中,如何使用处理JSON,现在有很多工具可以处理,比较流行常用的是google的gson和alibaba的fastjson,具体使用如下: 1、读取json然后处理 class ReadJSON { public static void main(String[] args) win7下nginx和php的配置 geeksun nginx 1. 安装包准备 nginx : 从nginx.org下载nginx-1.8.0.zip php: 从php.net下载php-5.6.10-Win32-VC11-x64.zip, php是免安装文件。 RunHiddenConsole: 用于隐藏命令行窗口 2. 配置 # java用8080端口做应用服务器,nginx反向代理到这个端口即可 p 基于2.8版本redis配置文件中文解释 hongtoushizi redis 转载自: http://wangwei007.blog.51cto.com/68019/1548167 在Redis中直接启动redis-server服务时, 采用的是默认的配置文件。采用redis-server xxx.conf 这样的方式可以按照指定的配置文件来运行Redis服务。下面是Redis2.8.9的配置文 第五章 常用Lua开发库3-模板渲染 jinnianshilongnian nginxlua 动态web网页开发是Web开发中一个常见的场景,比如像京东商品详情页,其页面逻辑是非常复杂的,需要使用模板技术来实现。而Lua中也有许多模板引擎,如目前我在使用的lua-resty-template,可以渲染很复杂的页面,借助LuaJIT其性能也是可以接受的。 如果学习过JavaEE中的servlet和JSP的话,应该知道JSP模板最终会被翻译成Servlet来执行;而lua-r JZSearch大数据搜索引擎 颠覆者 JavaScript 系统简介: 大数据的特点有四个层面:第一,数据体量巨大。从TB级别,跃升到PB级别;第二,数据类型繁多。网络日志、视频、图片、地理位置信息等等。第三,价值密度低。以视频为例,连续不间断监控过程中,可能有用的数据仅仅有一两秒。第四,处理速度快。最后这一点也是和传统的数据挖掘技术有着本质的不同。业界将其归纳为4个“V”——Volume,Variety,Value,Velocity。大数据搜索引 10招让你成为杰出的Java程序员 pda158 java编程框架 如果你是一个热衷于技术的 Java 程序员, 那么下面的 10 个要点可以让你在众多 Java 开发人员中脱颖而出。 1. 拥有扎实的基础和深刻理解 OO 原则 对于 Java 程序员,深刻理解 Object Oriented Programming(面向对象编程)这一概念是必须的。没有 OOPS 的坚实基础,就领会不了像 Java 这些面向对象编程语言 tomcat之oracle连接池配置 小网客 oracle tomcat版本7.0 配置oracle连接池方式: 修改tomcat的server.xml配置文件: <GlobalNamingResources> <Resource name="utermdatasource" auth="Container" type="javax.sql.DataSou Oracle 分页算法汇总 vipbooks oraclesql算法.net 这是我找到的一些关于Oracle分页的算法,大家那里还有没有其他好的算法没?我们大家一起分享一下! -- Oracle 分页算法一 select * from ( select page.*,rownum rn from (select * from help) page -- 20 = (currentPag 按字母分类: ABCDEFGHIJKLMNOPQRSTUVWXYZ其他