关于android 获取sdk版本L最小的sdk版本是什么解决方法

VLC media player
VLC is a free and open source cross-platform multimedia player and framework that plays most multimedia files as well as DVDs, Audio CDs, VCDs, and various streaming protocols.
VLC is a free and open source cross-platform multimedia player and framework that plays most multimedia files, and various streaming protocols.
2.2.4&•&Windows&•&28&MB
Simple, fast and powerful
Simple, fast and powerful
Plays everything - Files, Discs, Webcams, Devices and Streams.
Plays most codecs with no codec packs needed - MPEG-2, MPEG-4, H.264, MKV, WebM, WMV, MP3...
Runs on all platforms - Windows, Linux, Mac OS X, Unix, iOS, Android ...
Completely Free - no spyware, no ads and no user tracking.
Screenshots
Get VLC for
Get VLC for
Get VLC for
Apple Platforms
Get VLC for Get VLC for Get VLC for
You can also directly get the .
Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for
Other Systems
Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for Get VLC for
VideoLAN, VLC, VLC media player and x264 are trademarks internationally registered by the
VideoLAN software is licensed under various open-source licenses: use and distribution are defined by each software license.
Design by . Some icons are licensed under the .
The VLC cone icon was designed by Richard ?iestad. Icons for VLMC, DVBlast and x264 designed by .技术效果卓越的专业移动广告平台
让您的广告遍布每个角落
提供专业优质的广告服务
在线电话:400-070-2010
运用HTML5技术结合优质媒体,In-Apps情景式植入带来深度体验,领先智能投放手段使营销互动更出色
以弹出框或全屏形式展现,更加吸引用户眼球,广告的弹出时机和页面场景均可由开发者设定,与应用更和谐地结合,兼顾了用户体验
新版积分墙 简化流程 优化体验
当金币不足时,玩家可以选择“获取免费积分”打开积分墙并通过下载激活应用赚取积分(金币)
新版Banner广告
支持动画等多种新型展现形式,同时用精美广告画面最大限度吸引用户眼球,打动潜在用户,转化率成倍增长,收入果断增加
友情链接:10:04 提问
android studio 中布局无法预览 ,总是出现无法找到SDK
按赞数排序
----------------------biu~biu~biu~~~在下问答机器人小D,这是我依靠自己的聪明才智给出的答案,如果不正确,你来咬我啊!
SDK路径设置了没,SDK下载了没。
一般是用View-&ToolWindow-&preview,这个预览方便,能和代码一起看
楼上说的方法就能解决了
你的机器人那是是“null”,说明没有API呀!你将SDK 配置好,在预览一下布局文件。
File/Project Structrue/SDK Location
你设置的api版本你可能没有,换下API版本
7630关注|1435收录
320关注|177收录
3378关注|199收录
其他相似问题Open Tech Pub
那些年关注的技术
由于GitCafe与Coding合并,并且需要将GitCafe的代码迁移出来. 博客使用Octopress,通过以下几步即可完成迁移.
修改部署的Git库地址
rake setup_github_pages
rake会询问新的Git库地址,例如: 这样再使用rake deploy后就会自动部署到Coding上.
修改source分支Git库地址
git remote set-url origin xxx.git
从这篇文章开始将分析Android四大组件的工作过程,Activity作为最常用的组件,我们最先来分析.(源码使用API_LEVEL_15)
通常我们编写以下代码来创建一个Activity:
Intent i = new Intent(this, TestActivity.class);
startActivity(i);
1.Activity
然后Activity通过一系列的生命周期展示在我们眼前,就以Activity的startActivity这个方法为入口,发现实际上都调用了startActivityForResult方法.
public void startActivityForResult(Intent intent, int requestCode) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
if (requestCode &= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received.
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
mParent.startActivityFromChild(this, intent, requestCode);
这里的mParent代表一个ActivityGroup,已经被弃用了,所以我们只需要关系 mParent == null 这段逻辑。Instrumentation没有图形界面,拥有启动能力并可以监控其他类的工具类,通过execStartActivity方法启动一个Activity。mMainThread.getApplicationThread()可以获得一个ApplicationThread对象,它是ActivityThread的内部类,这两个类对Activity的启动非常重要,后面会继续分析。
2.Instrumentation
现在分析一下execStartActivity方法。
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i&N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode &= 0 ? am.getResult() : null;
intent.setAllowFds(false);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
null, 0, token, target != null ? target.mEmbeddedID : null,
requestCode, false, false, null, null, false);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
return null;
这里我们只需要关心两个地方,一个是ActivityManagerNative.getDefault()的startActivity方法,另一个是checkStartActivityResult(result, intent)方法。
从方法名我们就可以看出startActivity是真实的实现,但是ActivityManagerNative是一个抽象类,ActivityManagerNative.getDefault()只返回一个继承自IInterface的IActivityManager接口。
static public IActivityManager getDefault() {
return gDefault.get();
checkStartActivityResult用于检查Activity的合法性等操作。
3.ActivityManagerService
ActivityManagerNative实现了IActivityManager接口,继承Binder,很明显这是一个Binder结构,gDefault是一个单例对象,在create初始化方法中调用asInterface进行绑定。ActivityManagerService则继承自ActivityManagerNative,拥有具体的实现。我们继续分析ActivityManagerService中的startActivity方法。
public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded, boolean debug,
String profileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,
grantedUriPermissions, grantedMode, resultTo, resultWho,
requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler,
null, null);
mMainStack是一个ActivityStack对象。
4.ActivityStack
在startActivityMayWait方法中的调用顺序是:
startActivityLocked
startActivityUncheckedLocked
resumeTopActivityLocked
startSpecificActivityLocked
realStartActivityLocked
在realStartActivityLocked方法中有一句
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
new Configuration(mService.mConfiguration),
r.compat, r.icicle, results, newIntents, !andResume,
mService.isNextTransitionForward(), profileFile, profileFd,
profileAutoStop);
其中app.thread为IApplicationThread类型,这个接口的实现类为ActivityThread的内部类ApplicationThread。
5.ApplicationThread
ApplicationThread继承自ApplicationThreadNative,ApplicationThreadNative继承自Binder并实现IApplicationThread接口。和ActivityManagerService一样,都是IPC的实现方式。
现在来看一下ApplicationThread中scheduleLaunchActivity的实现
// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
Bundle state, List&ResultInfo& pendingResults,
List&Intent& pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profileFile = profileName;
r.profileFd = profileFd;
r.autoStopProfiler = autoStopProfiler;
updatePendingConfiguration(curConfig);
queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
很简单只是发送了一个名字为H.LAUNCH_ACTIVITY的消息给H这个Handler做处理。在Handler中调用了handleLaunchActivity方法。
6.ActivityThread
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
if (r.profileFd != null) {
mProfiler.setProfiler(r.profileFile, r.profileFd);
mProfiler.startProfiling();
mProfiler.autoStopProfiler = r.autoStopProfiler;
// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);
if (localLOGV) Slog.v(
TAG, &Handling launch of & + r);
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward);
if (!r.activity.mFinished && r.startsNotResumed) {
// The activity manager actually wants this one to start out
// paused, because it needs to be visible but isn't in the
// foreground.
We accomplish this by going through the
// normal startup (because activities expect to go through
// onResume() the first time they run, before their window
// is displayed), and then pausing it.
However, in this case
// we do -not- need to do the full pause cycle (of freezing
// and such) because the activity manager assumes it can just
// retain the current state it has.
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
// We need to keep around the original state, in case
// we need to be created again.
r.state = oldState;
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
&Activity & + r.intent.getComponent().toShortString() +
& did not call through to super.onPause()&);
} catch (SuperNotCalledException e) {
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
&Unable to pause activity &
+ r.intent.getComponent().toShortString()
+ &: & + e.toString(), e);
r.paused = true;
// If there was an error, for any reason, tell the activity
// manager to stop us.
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null);
} catch (RemoteException ex) {
从源码可以看出performLaunchActivity这个方法完成了最终的启动过程。我们来分析一下方法中的几个操作步骤。
1.通过ActivityClientRecord获取Activity的基本信息
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
2.通过Instrumentation的newActivity通过ClassLoader创建Activity对象。
Activity activity = null;
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
if (r.state != null) {
r.state.setClassLoader(cl);
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
&Unable to instantiate activity & + component
+ &: & + e.toString(), e);
Instrumentation的newActivity方法只有一句。
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
3.通过LoadedApk中makeApplication方法获取Application对象,如果Application对象存在就不创建,不存在就调用Instrumentation的callApplicationOnCreate创建。
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
4.创建ContextImpl对象,并通过Activity的attach方法来完成数据初始化。
ContextImpl appContext = new ContextImpl();
appContext.init(r.packageInfo, r.token, this);
appContext.setOuterContext(activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, &Launching activity &
+ r.activityInfo.name + & with config & + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config);
5.Activity生命周期逻辑,意味着Activity已经完成了启动过程。
if (customIntent != null) {
activity.mIntent = customIntent;
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
activity.mCalled = false;
mInstrumentation.callActivityOnCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
&Activity & + r.intent.getComponent().toShortString() +
& did not call through to super.onCreate()&);
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
if (!r.activity.mFinished) {
if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
if (!r.activity.mFinished) {
activity.mCalled = false;
mInstrumentation.callActivityOnPostCreate(activity, r.state);
if (!activity.mCalled) {
throw new SuperNotCalledException(
&Activity & + r.intent.getComponent().toShortString() +
& did not call through to super.onPostCreate()&);
在Android中动画分为以下4种:
Tween Animation
Frame Animation
Layout Animation 布局动画
Property Animation 属性动画
动画实现流程:
创建Animation
设置相关属性
View调用startAnimation方法启动动画
Animation类是Android系统的一个动画抽象类,所有其他一些动画类都要继承该类中的实现方法。Animation类主要用于补间动画效果,提供了动画启动、停止、重复、持续时间等方法。Animation类中的方法适用于任何一种补间动画对象。
常用方法:
start() 启动
startNow() 立即启动
cancel() 取消动画
setDuration(long) 设置持续时间
setRepeatMode(int) 设置重复模式
setRepeatCount(int) 设置重复次数
setFillEnabled(boolean) 能否填充位置
setFillBefore(boolean) 回到起始填充位置
setFillAfter(boolean) 回到结束填充位置
setStartOffset(long) 设置延时启动时间
setInterpolator(Interpolator) 设置加速曲线
setAnimationListener(AnimationListener) 设置动画的回调
Tween Animation
该类Animations提供了旋转、移动、伸展和淡出等效果。Alpha——淡入淡出,Scale——缩放效果,Rotate——旋转,Translate——移动效果。
TranslateAnimation
TranslateAnimation类是Android系统中的位置变化动画类,用于控制View对象的位置变化,该类继承于Animation类。TranslateAnimation类中的很多方法都与Animation类一致,该类中最常用的方法便是TranslateAnimation构造方法。
public TranslateAnimation (float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
fromXDelta:位置变化的起始点X坐标。
toXDelta:位置变化的结束点X坐标。
fromYDelta:位置变化的起始点Y坐标。
toYDelta:位置变化的结束点Y坐标。
RotateAnimation
RotateAnimation类是Android系统中的旋转变化动画类,用于控制View对象的旋转动作,该类继承于Animation类。RotateAnimation类中的很多方法都与Animation类一致,该类中最常用的方法便是RotateAnimation构造方法。
public RotateAnimation (float fromDegrees, float toDegrees, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
fromDegrees:旋转的开始角度。
toDegrees:旋转的结束角度。
pivotXType:X轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
pivotXValue:X坐标的伸缩值。
pivotYType:Y轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
pivotYValue:Y坐标的伸缩值。
ScaleAnimation
ScaleAnimation类是Android系统中的尺寸变化动画类,用于控制View对象的尺寸变化,该类继承于Animation类。ScaleAnimation类中的很多方法都与Animation类一致,该类中最常用的方法便是ScaleAnimation构造方法。
public ScaleAnimation (float fromX, float toX, float fromY, float toY, int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
fromX:起始X坐标上的伸缩尺寸。
toX:结束X坐标上的伸缩尺寸。
fromY:起始Y坐标上的伸缩尺寸。
toY:结束Y坐标上的伸缩尺寸。
pivotXType:X轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
pivotXValue:X坐标的伸缩值。
pivotYType:Y轴的伸缩模式,可以取值为ABSOLUTE、RELATIVE_TO_SELF、RELATIVE_TO_PARENT。
pivotYValue:Y坐标的伸缩值。
AlphaAnimation
AlphaAnimation类是Android系统中的透明度变化动画类,用于控制View对象的透明度变化,该类继承于Animation类。AlphaAnimation类中的很多方法都与Animation类一致,该类中最常用的方法便是AlphaAnimation构造方法。
public AlphaAnimation (float fromAlpha, float toAlpha)
fromAlpha:开始时刻的透明度,取值范围0~1。
toAlpha:结束时刻的透明度,取值范围0~1。
AnimationSet
AnimationSet类是Android系统中的动画集合类,用于控制View对象进行多个动作的组合,该类继承于Animation类。AnimationSet类中的很多方法都与Animation类一致,该类中最常用的方法便是addAnimation方法,该方法用于为动画集合对象添加动画对象。
public void addAnimation (Animation a)
其中,参数a为Animation动画对象,可以是前述任何一种补间动作。
AnimationUtils
AnimationUtils类是Android系统中的动画工具类,提供了控制View对象的一些工具。该类中最常用的方法便是loadAnimation方法,该方法用于加载XML格式的动画配置文件。在Android系统中,除了在代码中设置动画效果外,还可以在XML配置文件中设置动画的组合动作,这种方式适用性更好。
public static Animation loadAnimation (Context context, int id)
context:上下文对象。
id:动画配置文件的ID。
Frame Animation
AnimationDrawable类:帧动画类
AnimationDrawable类是Android系统中的帧动画类。帧动画方式类似于放电影的原理,是通过顺序播放多张图片来实现动画效果的,图片之间有一定的动作连贯性,这样人眼看来就像对象真正在运动一样。AnimationDrawable类位于android.graphics.drawable软件包中,本节将介绍帧动画类中的主要编程方法。
&?xml version=&1.0& encoding=&utf-8&?&
&animation-list xmlns:android=&/apk/res/android&
android:oneshot=&true&&
&item android:drawable=&@drawable/p0& android:duration=&50&/&
&item android:drawable=&@drawable/p1& android:duration=&50&/&
&item android:drawable=&@drawable/p2& android:duration=&50&/&
&item android:drawable=&@drawable/p3& android:duration=&50&/&
&item android:drawable=&@drawable/p4& android:duration=&50&/&
&/animation-list&
通过动画配置文件,将其加载到ImageView的背景中,再start启动。
Layout Animations
LayoutAnimationsController可以用于实现使多个控件按顺序一个一个的显示。
LayoutAnimationsController用于为一个layout里面的控件,或者是一个ViewGroup里面的控件设置统一的动画效果。
每一个控件都有相同的动画效果。
控件的动画效果可以在不同的时间显示出来。
LayoutAnimationsController可以在xml文件当中设置,也可以在代码当中进行设置。
LayoutAnimations1
//1.加载动画set的XML文件
Animation animation = (Animation) AnimationUtils.loadAnimation(Animation2Activity.this, R.anim.list_anim);
//2.初始化LayoutAnimationController
LayoutAnimationController controller = new LayoutAnimationController(animation);
//3.设置子View动画顺序
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
//4.设置动画延迟
controller.setDelay(0.5f);
//5.将LayoutAnimation设置给ViewGroup
listView.setLayoutAnimation(controller);
Property Animation
属性动画要求对象必须实现对应的get和set方法,属性动画根据传递的初始值和最终值,以动画的效果多次调用对应的set方法,根据时间的推移越来越接近最终值。
如果需要对属性xxx做动画,必须满足两个条件:
object必须提供set方法,如果不指定初始状态则必须提供get方法(系统会通过get方法拿初始属性)。
object的set方法必须通过UI反应出来,不然动画无效(这点比较好理解,使UI布局更新)。
Google给出了3个解决方法:
给对象添加set和get方法,前提是拥有权限
用一个包装类,实现对应的set和get方法
采用ValueAnimator,监听动画过程,自己实现属性的改变
对象直接添加set和get方法
这个方式很简单,但是局限性较大。假如你想对Android SDK中的对象添加方法,正常是无法实现的。
使用包装类间接实现set和get方法
ViewWrapper1
private void performAnimate() {
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper, &width&, 500).setDuration(5000).start();
public void onClick(View v) {
if (v == mButton) {
performAnimate();
private static class ViewWrapper {
private View mTarget;
public ViewWrapper(View target) {
mTarget = target;
public int getWidth() {
return mTarget.getLayoutParams().width;
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
上述代码5s内让View的宽度增加到500px,为了达到这个效果,我们提供了ViewWrapper类专门用于包装View,具体到本例是包装View,然后我们对ViewWrapper的width熟悉做动画,并且在setWidth方法中修改其内部的target的宽度,而target实际上就是我们包装的Button,这样一个间接属性动画就搞定了。上述代码同样适用于一个对象的其他属性。
采用ValueAnimator,监听动画过程,自己实现属性的改变
ValueAnimator本身不作用于任何对象,也就是说直接使用它没有任何动画效果。它可以对一个值做动画,然后我们可以监听其动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的对象做了动画。
ValueAnimators1
private void performAnimate(final View target, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
//持有一个IntEvaluator对象,方便下面估值的时候使用
private IntEvaluator mEvaluator = new IntEvaluator();
public void onAnimationUpdate(ValueAnimator animator) {
//获得当前动画的进度值,整型,1-100之间
int currentValue = (Integer)animator.getAnimatedValue();
Log.d(TAG, &current value: & + currentValue);
//计算当前进度占整个动画过程的比例,浮点型,0-1之间
float fraction = currentValue / 100f;
//这里我偷懒了,不过有现成的干吗不用呢
//直接调用整型估值器通过比例计算出宽度,然后再设给Button
target.getLayoutParams().width = mEvaluator.evaluate(fraction, start, end);
target.requestLayout();
valueAnimator.setDuration(5000).start();
public void onClick(View v) {
if (v == mButton) {
performAnimate(mButton, mButton.getWidth(), 500);
关于这个ValueAnimator我要再说一下,拿上例来说,它会在5000ms内将一个数从1变到100,然后动画的每一帧会回调onAnimationUpdate方法,在这个方法里,我们可以获取当前的值(1-100),根据当前值所占的比例(当前值/100),我们可以计算出Button现在的宽度应该是多少,比如时间过了一半,当前值是50,比例为0.5,假设Button的起始宽度是100px,最终宽度是500px,那么Button增加的宽度也应该占总增加宽度的一半,总增加宽度是500-100=400,所以这个时候Button应该增加宽度400*0.5=200,那么当前Button的宽度应该为初始宽度+ 增加宽度(100+200=300)。上述计算过程很简单,其实它就是整型估值器IntEvaluator的内部实现。
View动画(渐变动画)的功能是有限的,大家可以尝试使用属性动画
为了在各种安卓版本上使用属性动画,你需要采用nineoldandroids,它是GitHub开源项目,jar包和源码都可以在网上下到,如果下不到jar包,我可以发给大家
再复杂的动画都是简单动画的合理组合,再加上本文介绍的方法,可以对任何属性作用动画效果,也就是说你几乎可以做出任何动画
属性动画中的插值器(Interpolator)和估值器(TypeEvaluator)很重要,它是实现非匀速动画的重要手段,你应该试着搞懂它,最好你还能够自定义它们
参考资料:
最近群里面有些小伙伴问我,希望可以解释一下View间的绘制关系,所以简单的写一下。
Android是如何绘制View的
一旦Activity获得焦点之后,将会触发绘制布局的流程。
绘制过程是从布局的根节点(root node)开始的,根据布局的树状结构测量(measure)并绘制(draw)。
这个过程用于规定每一个View的区域,然后绘制对应的界面。
ViewGroup用于负责请求每一个它的子View进行绘制,当子View接到请求则开始绘制自己的界面。
注意树的遍历是有序的,意味着父View将在子View绘制之前被调用。
Measure与Layout
绘制布局分为两个过程:measure和layout。
measure(int, int)方法用来自上而下的遍历View,每个View都是递归的测量自己所占的大小。
layout(int, int, int, int)方法同样是自上而下的,它的功能是将子View放置在合适的位置。
当View对象调用了measure方法之后,通过getMeasuredWidth()和getMeasuredHeight()方法就可以获取到测量后的值。
注意一点,子View的宽和高不得超过在父View。这可以确保所有的子View都被父View所包含。
ViewGroup.LayoutParams
父类如何得知子类想要如何布局呢,ViewGroup.LayoutParams类用于告诉父类它们的布局预期。
我们可以使用3种配置方式:
MATCH_PARENT,子View想与父View一样大小
WRAP_CONTENT,子View的大小足以包括它的内容
MeasureSpec
MeasureSpec用于父View向下要求子View的绘制模式,MeasureSpec有3种模式:
UNSPECIFIED,表示大小并不明确
EXACTLY,表示父类给定一个准确的大小
AT MOST,表示父类限制了子类的最大值
上面的逻辑是不是太抽象了?简单来说就是一个View如果想要展示出来需要两步:1、计算自己所占空间的大小。2、子View摆放的位置。(所有过程都是从父View发起,直到子View计算完成向它的父View返回结果)
自定义一个ViewGroup
我们自定义一个ViewGroup通常分为以下几步:
1.继承ViewGroup并且重写父类的三个构造函数
public class CustomViewGroup extends ViewGroup {
public CustomViewGroup(Context context) {
super(context);
public CustomViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
public CustomViewGroup(Context context, AttributeSet attrs, intdefStyle) {
super(context, attrs, defStyle);
2.重载onMeasure()方法
自定义ViewGroup的onMeasure()方法中,除了计算自身的尺寸外,还需要调用measureChildren()函数来计算子控件的尺寸。
onMeasure()的定义不是本文的讨论重点,因此这里我直接使用默认的onMeasure()定义,当然measureChildren()是必须得加的,或者我们针对性的调用子View的measure方法。
通常我们使用MeasureSpec.makeMeasureSpec(int size, int mode)来产生一个规格。
使用getChildCount()获取子View个数。使用getChildAt(i)获得对应的子View。在最后不要忘记使用setMeasuredDimension方法设置当前View的规格。
3.实现onLayout()方法
通常我们在这个方法里面调用getMeasuredWidth()和getMeasuredHeight()获取已经测量过的View大小,
然后根据这些数据来计算每个View对应的位置。通过layout(int l, int t, int r, int b)设置子View在当前View中的位置。
4.添加LayoutParams
generateLayoutParams()用于返回一个LayoutParams给子View,这样子View就可以将对应layout布局的参数传入,
我们自定义的父View可以使用getLayoutParams()获取我们传入的LayoutParams。
你可以跟踪源码看看,其实XML文件中View的layout_xxx参数都是被传递到了各种自定义ViewGroup.LayoutParams派生类对象中。例如LinearLayout的LayoutParams定义的关键部分如下:
public class LinearLayout extends ViewGroup {
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public float weight;
public int gravity = -1;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
a.recycle();
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LinearLayout.LayoutParams(getContext(), attrs);
这样你大概就可以理解为什么LinearLayout的子控件支持weight和gravity的设置了吧,当然我们也可以这样自定义一些属于我们ViewGroup特有的params。
这样修改之后,我们就可以在onLayout()函数中获取子控件的layout_xxx值了。
Android官方主页不再提供Eclipse ADT Bundle的下载,并且声明放弃维护。
以下是对应操作系统的下载地址:
windows 32:
windows 64:
一、JVM的生命周期
1.JVM实例对应了一个独立运行的java程序它是进程级别
a)启动。启动一个Java程序时,一个JVM实例就产生了,任何一个拥有public static void main(String[] args)函数的class都可以作为JVM实例运行的起点
b)运行。main()作为该程序初始线程的起点,任何其他线程均由该线程启动。JVM内部有两种线程:守护线程和非守护线程,main()属于非守护线程,守护线程通常由JVM自己使用,java程序也可以标明自己创建的线程是守护线程
c)消亡。当程序中的所有非守护线程都终止时,JVM才退出;若安全管理器允许,程序也可以使用Runtime类或者System.exit()来退出
2.JVM执行引擎实例则对应了属于用户运行程序的线程它是线程级别的
二、JVM的体系结构
1.类装载器(ClassLoader)(用来装载.class文件)
2.执行引擎(执行字节码,或者执行本地方法)
3.运行时数据区(方法区、堆、java栈、PC寄存器、本地方法栈)
三、JVM类加载器
JVM整个类加载过程的步骤:
装载过程负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名通过ClassLoader来完成类的加载,
同样,也采用以上三个元素来标识一个被加载了的类:类名+包名+ClassLoader实例ID。
链接过程负责对二进制字节码的格式进行校验、初始化装载类中的静态变量以及解析类中调用的接口、类。
完成校验后,JVM初始化类中的静态变量,并将其值赋为默认值。
最后对类中的所有属性、方法进行验证,以确保其需要调用的属性、方法存在,以及具备应的权限(例如public、private域权限等),会造成NoSuchMethodError、NoSuchFieldError等错误信息。
初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化,在四种情况下初始化过程会被触发执行:
调用了new;
反射调用了类中的方法;
子类调用了初始化;
JVM启动过程中指定的初始化类。
JVM类加载顺序:
JVM两种类装载器包括:启动类装载器和用户自定义类装载器。
启动类装载器是JVM实现的一部分;
用户自定义类装载器则是Java程序的一部分,必须是ClassLoader类的子类。
JVM装载顺序:
Jvm启动时,由Bootstrap向User-Defined方向加载类;
应用进行ClassLoader时,由User-Defined向Bootstrap方向查找并加载类;
1.Bootstrap ClassLoader
这是JVM的根ClassLoader,它是用C++实现的,JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME中jre/lib/rt.jar(Sun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。
2.Extension ClassLoader
JVM用此classloader来加载扩展功能的一些jar包。
3.System ClassLoader
JVM用此classloader来加载启动参数中指定的Classpath中的jar包以及目录,在Sun JDK中ClassLoader对应的类名为AppClassLoader。
4.User-Defined ClassLoader
User-DefinedClassLoader是Java开发人员继承ClassLoader抽象类自行实现的ClassLoader,基于自定义的ClassLoader可用于加载非Classpath中的jar以及目录。
ClassLoader抽象类的几个关键方法:
(1)loadClass
此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从parent ClassLoader中寻找,如仍然没找到,则从System ClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法
(2)findLoadedClass
此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native的方法。
(3)findClass
此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
(4)findSystemClass
此方法负责从System ClassLoader中寻找类,如未找到,则继续从Bootstrap ClassLoader中寻找,如仍然为找到,则返回null。
(5)defineClass
此方法负责将二进制的字节码转换为Class对象
(6)resolveClass
此方法负责完成Class对象的链接,如已链接过,则会直接返回。
四、JVM执行引擎
在执行方法时JVM提供了四种指令来执行:
invokestatic:调用类的static方法
invokevirtual:调用对象实例的方法
invokeinterface:将属性定义为接口来进行调用
invokespecial:JVM对于初始化对象(Java构造器的方法为:)以及调用对象实例中的私有方法时。
主要的执行技术有:
解释,即时编译,自适应优化、芯片级直接执行
解释属于第一代JVM,
即时编译JIT属于第二代JVM,
自适应优化(目前Sun的HotspotJVM采用这种技术)则吸取第一代JVM和第二代
JVM的经验,采用两者结合的方式
开始对所有的代码都采取解释执行的方式,并监视代码执行情况,然后对那些经常调用的方法启动一个后台线程,将其编译为本地代码,并进行优化。若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行。
五、JVM运行时数据区
第一块:PC寄存器
PC寄存器是用于存储每个线程下一步将执行的JVM指令,如该方法为native的,则PC寄存器中不存储任何信息。
第二块:JVM栈
JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址
第三块:堆(Heap)
它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。
堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的
Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配
TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。
第四块:方法区域(Method Area)
在Sun JDK中这块区域对应的为PermanetGeneration,又称为持久代。
方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class
对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。
第五块:运行时常量池(Runtime Constant Pool)
存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。
第六块:本地方法堆栈(Native Method Stacks)
JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。
六、JVM垃圾回收
JVM分别对新生代和旧生代采用的两种垃圾回收机制?
新生代的GC:新生代通常存活时间较短,因此基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和FromSpace或ToSpace之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代。
旧生代的GC:旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。
如何判断对象是否可以被回收?
引用计数法
根搜索算法
垃圾收集算法:
标记-清除(Mark-Sweep)算法
标记-整理算法
分代收集算法
GC的基本原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为收集器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停
对新生代的对象的收集称为minor GC;
对旧生代的对象的收集称为Full GC;
程序中主动调用System.gc()强制执行的GC为Full GC。
不同的对象引用类型, GC会采用不同的方法进行回收,JVM对象的引用分为了四种类型:
强引用:默认情况下,对象采用的均为强引用(这个对象的实例没有其他对象引用,GC时才会被回收)
软引用:软引用是Java中提供的一种比较适合于缓存场景的应用(只有在内存不够用的情况下才会被GC)
弱引用:在GC时一定会被GC回收
虚引用:由于虚引用只是用来得知对象是否被GC
JVM和DVM的不同点
1. Dalvik 和标准 Java 虚拟机(JVM)的首要差别
Dalvik 基于寄存器,而 JVM 基于栈。基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花费的时间更短。
2. Dalvik 和 Java 字节码的区别
Dalvik执行.dex格式的字节码,而JVM执行.class格式的字节码。android程序编译完之后生产.class文件,还有通过aapt工具生成的R.class等,然后dx工具会把.class文件处理成.dex文件,最终资源文件和.dex文件等打包成.apk文件。
3. Dalvik和Java运行环境的区别
Dalvik主要是完成对象生命周期管理,堆栈管理,线程管理,安全和异常管理,以及垃圾回收等等重要功能。
Dalvik负责进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。
你将从这篇文章中了解到什么是Stream,并且如何在Android开发中使用它。
不幸的是Android还不支持Java 8,Kitkat(Android 4.4+)以后的版本可以支持Java 7。
那么只能和新特性说再见了吗?当然不是,一些聪明人想出了一个解决办法:
如何安装RETROLAMBDA
作为热身,让我们来看看如何快速的在项目中使用Retrolambda。
(假设你已经了解Android Studio的gradle构建系统以及它是如何工作的)
1.在./build.gradle文件中添加新的classpath:
2.在./app/build.gradle文件中添加Retrolambda插件:
3.还需要添加:
4.编译gradle
A stream is an abstraction for specifying aggregate computations on a DataSet
Java 8 Stream API引入的目的在于弥补Java函数式编程的缺陷。对于很多支持函数式编程的语言,map()、reduce()基本上都内置到语言的标准库中了,不过,Java 8的Stream API总体来讲仍然是非常完善和强大,足以用很少的代码完成许多复杂的功能。
创建一个Stream有很多方法,最简单的方法是把一个Collection变成Stream。我们来看最基本的几个操作:
public static void main(String[] args) {
List&Integer& numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream&Integer& stream = numbers.stream();
stream.filter((x) -& {
return x % 2 == 0;
}).map((x) -& {
return x *
}).forEach(System.out::println);
集合类新增的stream()方法用于把一个集合变成Stream,然后,通过filter()、map()等实现Stream的变换。Stream还有一个forEach()来完成每个元素的迭代。
使用Stream的两个原因:
1.集合类会持有所有元素在内存中,大集合会占用大量内存。而Stream的元素是在访问的时候被计算出来,内存占用小。
2.二是集合类的迭代逻辑是调用者负责,通常是for循环,而Stream的迭代是隐含在对Stream的各种操作中,例如map()。
更多特性参考InfoQ的文章
Android中使用轻量级Stream API
上一部分介绍了Java 8 Stream的使用,但是我们的目的是讨论Android如何去使用这个特性。我们可以通过
,只需要增加它的classpath。
compile 'com.annimon:stream:1.0.1'
JAVA 8 vs Lightweight-Stream-API(LSA)
虽然Java 8的Stream和LSA工作方式一样,但他们间仍有少量的区别。例如使用LSA创建Stream时使用Stream.of(YourCollection),而Java 8中使用Stream。
另一个区别为排序操作,Java 8中使用‘sort()’,而LSA中使用‘sorted()’。
参加了几个面试都涉及到HTTP协议的问题,虽然可以简述出来,但是有些细节记不清了,整理一些关键点。
HTTP 1.1与HTTP 1.0的比较
HTTP 1.0规定浏览器与服务器只保持短暂的连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。但是,这也造成了一些性能上的缺陷。
上图为一个网页请求模型,它包含网页文档和几个图片元素。加载整个网页需要进行4次请求和响应。
为了克服HTTP 1.0的这个缺陷,HTTP 1.1支持持久连接,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。
一个包含有许多图像的网页文件的多个请求和应答可以在一个连接中传输,但每个单独的网页文件的请求和应答仍然需要使用各自的连接。HTTP 1.1还允许客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能够区分出每次请求的响应内容,这样也显著地减少了整个下载过程所需要的时间。
上图为基于HTTP 1.1协议的客户机与服务器的信息交换过程
可见,HTTP 1.1在继承了HTTP 1.0优点的基础上,也克服了HTTP 1.0的性能问题。不仅如此,HTTP 1.1还通过增加更多的请求头和响应头来改进和扩充HTTP 1.0的功能。例如,由于HTTP 1.0不支持Host请求头字段,WEB浏览器无法使用主机头名来明确表示要访问服务器上的哪个WEB站点,这样就无法使用WEB服务器在同一个IP地址和端口号上配置多个虚拟WEB站点。在HTTP 1.1中增加Host请求头字段后,WEB浏览器可以使用主机头名来明确表示要访问服务器上的哪个WEB站点,这才实现了在一台WEB服务器上可以在同一个IP地址和端口号上使用不同的主机名来创建多个虚拟WEB站点。HTTP 1.1的持续连接,也需要增加新的请求头来帮助实现,例如,Connection请求头的值为Keep-Alive时,客户端通知服务器返回本次请求结果后保持连接;Connection请求头的值为close时,客户端通知服务器返回本次请求结果后关闭连接。HTTP 1.1还提供了与身份认证、状态管理和Cache缓存等机制相关的请求头和响应头。
HTTP/1.0不支持文件断点续传,目前的Web服务器绝大多数都采用了HTTP/1.1。
RANGE:bytes是HTTP/1.1新增内容,HTTP/1.0每次传送文件都是从文件头开始,即0字节处开始。RANGE:bytes=XXXX表示要求服务器从文件XXXX字节处开始传送。
HTTP协议状态码
初始的请求已经接受,客户应当继续发送请求的其余部分。(HTTP 1.1新)
Switching Protocols
服务器将遵从客户的请求转换到另外一种协议(HTTP 1.1新)
一切正常,对GET和POST请求的应答文档跟在后面。
服务器已经创建了文档,Location头给出了它的URL。
已经接受请求,但处理尚未完成。
Non-Authoritative Information
文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP 1.1新)。
No Content
没有新文档,浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。
Reset Content
没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。
Partial Content
客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP 1.1新)。
Multiple Choices
客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。
Moved Permanently
客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。
类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”。
出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。
注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。
严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。
类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP 1.1新)。
Not Modified
客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。
客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。
Temporary Redirect
和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时 才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只能跟随对GET请求的重定向。(HTTP 1.1新)
Bad Request
请求出现语法错误。
Unauthorized
客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。
资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。
无法找到指定位置的资源。这也是一个常用的应答。
Method Not Allowed
请求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)对指定的资源不适用。(HTTP 1.1新)
Not Acceptable
指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP 1.1新)。
Proxy Authentication Required
类似于401,表示客户必须先经过代理服务器的授权。(HTTP 1.1新)
Request Timeout
在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP 1.1新)
通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP 1.1新)
所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP 1.1新)
Length Required
服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP 1.1新)
Precondition Failed
请求头中指定的一些前提条件失败(HTTP 1.1新)。
Request Entity Too Large
目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP 1.1新)。
Request URI Too Long
URI太长(HTTP 1.1新)。
Requested Range Not Satisfiable
服务器不能满足客户在请求中指定的Range头。(HTTP 1.1新)
Internal Server Error
服务器遇到了意料不到的情况,不能完成客户的请求。
Not Implemented
服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。
Bad Gateway
服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。
Service Unavailable
服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。
Gateway Timeout
由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP 1.1新)
HTTP Version Not Supported
服务器不支持请求中所指明的HTTP版本。(HTTP 1.1新)
官方文档给出的解释为。Service是一个无界面,长时间在后台运行的应用组件。
其他的应用组件可以启动一个Service,即使用户切换到其他应用后Service任然在后台运行。
另外,组件可以通过绑定(Bind)的方式与Service进行交互,甚至使用Interprocess Communication(IPC)的方式。
Service基本上可以采取两种形式:
当一个组件调用startService()方法后,Service将启动。一旦被启动,无论启动它的组件是否被销毁,Service都会在后台运行。
这种方式通常为了执行一个单独的操作,并且我们不需要Service返回状态。
当一个组件调用bindService()方法后,Service提供了一个接口使得可以与组件进行交互。包括发送请求,获得结果,甚至进程间通信IPC。
一般会分开讨论这两种Service的启动类型,但我们可以使Service在这两种方式下运行。这取决于你是否实现了一对回调方法:onStartCommand()允许组件启动;onBind()允许组件绑定。
值得注意的是Service是运行在主线程中的,这意味着Service不能创建它自己的线程,也不能运行在其他进程中(除非特别指定)。一旦你需要做一些消耗CPU的工作或者阻塞操作,你应该在Service中创建一个新的线程去完成。
何时使用Service或者Thread:Service因为是Android中的一个组件,会一直在后台运行,请确认是否需要一个常驻的功能。Thread通常是处理一个异步的任务,任务执行完就不再需要,它避免阻塞主线程。
Service常用基类
这是所有服务类的基类,继承该类,对于在服务中创建新线程很重要。因为默认服务使用应用的主线程,可能会降低程序的性能。
IntentService
这是一个Service的子类,该子类使用线程处理所有启动请求,一次一个。这是不使用服务处理多任务请求的最佳选择。你需要做的只是实现onHandleIntent()方法即可。可以为每个启动请求接收到intent,放到后台工作即可。
内部使用一个Handler和Looper来实现子线程处理.
Service生命周期
未绑定的服务
startService() -> onCreate() -> onStartCommand() -> 运行服务 -> 停止 -> onDestroy() -> 服务关闭
绑定的服务
bindService() -> onCreate() -> onBind() -> 客户端绑定到服务 -> 客户端调用unbindService() -> onUnbind() -> onDestroy() -> 服务关闭
自动启动Service
通常的办法是实现一个BroadcastReceiver,监听ACTION_BOOT_COMPLETED即可,并在接收完该广播后通过AlarmManager轮询发送自定义广播,再通过另一个BroadcastReceiver启动Service。
如果通过某种方式将整个进程杀死,所有的服务也会被杀死,此时将无法定期启动服务了。要想达到即使杀死了也可以自动启动服务,需要注册一个系统级别的BroadcastReceiver。
每年我们都在期待Google I/O大会中Android相关的新东西。然而,在线看视频简直太花时间了(拜GFW所赐还得使用一些小手段观看)。
这篇文章对整个视频做了总结,希望可以帮到大家。
Easing Design
可以帮你遵循最新的Material Design风格。这个库包含了一系列Material Design组件,
例如Navigation Drawer、Floating Labels for Editing Text、Floating Action Buttons和Snackbar,
所有的组件都兼容Android 2.1以上版本。
Android L已经介绍了Vector Drawables。随着Android Studio 1.3更改了Android Gradle插件,我们可以使用编译系统通过SVG和Vector Drawables生成不同dp的raster图像。
最终,开发工具团队开始重写了整个可视化设计编辑器、它帮你实现更多所见即所得的方式。
Improving the Grade Plugin & Build System
Android Gradle插件有时会靠不住,特别是它作为依赖管理,相关问题已经得到了修复。
Android Gradle插件最令人纠结的当然是超长的编译时间。工具团队从多个层面去解决这个问题。
,Java Android Compiler Kit的缩写,
它将Java源码直接编译成Android的Dex文件格式。它是基于Eclispse Java编译器的,这个过程减少了一步。换句话说就是,不需要在转成Dex前编译成JVM字节码。另外,它还支持增量编译。
压缩处理PNG图片同样花费了巨大的时间。工具团队已经提升了这方面的性能,将500张PNG和.9图从4秒减低到400毫秒。
aapt(Android Asset Packaging Tool),负责打包所有的Dex和资源文件,同样也得到了优化。
另外一个开销是因为Gradle自己造成的,当Gradle开始编译Android项目的时候,它不得不创建一个模块去描述variants(flavor + build type),
即使只打算构建一个,它也会解析所有variants的依赖。并且它会执行自定义的逻辑。开发团队使用Gradle Ware优化了这些步骤。这是结果:
当然,还没有说完。开发工具团队正在致力于新的Android Gradle插件,它基于Gradle Ware新的API。
新的API允许Gradle直接管理模块,并且可以让它做一些事情,比如缓存、并行和增量构建。这是下一代插件的结果:
这些数字并不包括缓存的优化,因为它还没开发完成。但它有一个小缺点,就是新插件使用新的DSL,而且还不能向下兼容。预览版将在几周后放出,但是正式版可能会在年末。
开发工具团队也介绍了一个Data Binding Library。它需要构建系统的支持,因为它会从XML文件声明中生成Java源文件。老版和新版的Android Gradle插件都可以支持。Android Studio还开始支持C/C++进行NDK开发。
今年Android测试新发布了。
它允许你使用Google测试云上的虚拟设备和物理设备进行app测试。并支持自动抓取,不需求自己写用例,当然如果你愿意也是支持的。
模拟器上并没有什么太多的变化,开发工具团队主要致力于稳定、正确性和可配置。Android Studio将下载并安装HAXM,性能上有很大的提升。
Android Auto模拟器中将会提供指纹识别的支持。
New Support Annotations
Java注解可以在编译和运行时进行很多神奇的事情。新增了13种注解可以帮助你避免一些Bug。
例如,@WorkerThread注解。方法中声明此注解会自动检查代码是否在UI线程。Android Studio会高亮显示错误。
另一个例子是,@RequiresPermission。一旦你使用的API没有在manifest文件中声明权限,Android Studio将会提醒你插入权限。
在Android M中权限控制有了一些变化,用户可以选择同意和拒绝某种权限,这意味着你的代码不得不去处理拒绝后的逻辑。
Android Studio将自动产生一个代码块帮助你完成这件事。
Data Binding
这个可能是给开发者印象最深的变化。当你开发Android的UI时,通常使用findViewByID()查找XML文件中的布局,并将Java POJO填充到里面。
Data Binding库可以让这个操作变简单。你可以声明POJO类型,变量表达式引用POJO,以及监听XML文件的布局,用来代替原来手动的操作。
在编译时期,构建系统会生成绑定的Java类,关联你的布局和POJO。
使用它只需要两步:POJO实现android.databindings.Observable接口,改变POJO则会反射到UI,反之亦然。
Data Binding库当前还属于beta阶段,需要Android Studio 1.3版本和最新的Gradle插件。更多内容请参考
Profiling Tools
这个内存和性能分析工具做了一些优化。你现在可以查看在Android Studio中堆和方法路径的快照,通过一个下拉的界面,你可以发现问题在哪。
它还能可视化的查看和跟踪,你不需要手动的生成文件。
现在内存快照是下拉显示的,看起来非常简洁。通过调试器可以查看当前的对象。它也可以让你去追踪引用链直到GC的根节点,这样你就可以知道谁持有了垃圾的引用。
New Features in Upcoming Releases
这个新的视觉设计器暂时还没加入到Android Studio 1.3版本。令人兴奋的是,它减轻了创建UI的负担。上图展示了一个新的主题编辑器,让你通过可视化查看和修改主题文件。
并且可以预览该主题的UI控件。
布局编辑器也加入了一些新的功能,上图蓝色的部分可以让你只关注UI的布局。它还提供通过拖拽的方式修改组件。
XML预览模式已经被扩展到可以显示系统参数,但是最重要的特点是通过所见即所得的方式直接在预览窗口进行编辑,包括从工具面板拖拽控件。
Copyright & 2016 - Wu Di -
Powered by
Thanks for

我要回帖

更多关于 android sdk版本 的文章

 

随机推荐