呃...我不是故意要凑篇幅写个什么上下篇,实在是因为Drawable源码有点长,一篇写不下啦O(∩_∩)O~

鉴于源码一般较长,以后所有源码分析的部分,英文注释非必要情况都不再保留!

2:Drawable源码分析/翻译

继续上Drawable源码:

package android.graphics.drawable;

public abstract class Drawable {

****

****

/**

这个方法很重要,故保留英文注释!

调用mutate(),使当前Drawable实例mutable,这个操作不可逆。

一个mutable的Drawable实例不会和其他Drawable实例共享它的状态。

当你需要修改一个从资源文件加载的Drawable实例时,mutate()方法尤其有用。

默认情况下,所有加载同一资源文件生成的Drawable实例都共享一个通用的状态,

如果你修改了其中一个Drawable实例,所有的相关Drawable实例都会发生同样的变化。

这个方法在[其实你不懂:Drawable着色(tint)的兼容方案 源码解析]

这篇文章里有过介绍,就是为了限定Drawable实例的编辑生效范围仅限于自身。

* Make this drawable mutable. This operation cannot be reversed. A mutable

* drawable is guaranteed to not share its state with any other drawable.

* This is especially useful when you need to modify properties of drawables

* loaded from resources. By default, all drawables instances loaded from

* the same resource share a common state; if you modify the state of one

* instance, all the other instances will receive the same modification.

*

* Calling this method on a mutable Drawable will have no effect.

*

* @return This drawable.

* @see ConstantState

* @see #getConstantState()

*/

public @NonNull Drawable mutate() {

return this;

}

/**

被隐匿

* @hide

*/

public void clearMutated() {

// Default implementation is no-op.

}

//下面几个方法介绍了通过不同的方式创建Drawable实例:

//流、XML、文件地址

public static Drawable createFromStream(InputStream is, String srcName) {

Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");

try {

return createFromResourceStream(null, null, is, srcName);

} finally {

Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

}

}

public static Drawable createFromResourceStream(Resources res, TypedValue value,

InputStream is, String srcName) {

Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");

try {

return createFromResourceStream(res, value, is, srcName, null);

} finally {

Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

}

}

public static Drawable createFromResourceStream(Resources res, TypedValue value,

InputStream is, String srcName, BitmapFactory.Options opts) {

if (is == null) {

return null;

}

Rect pad = new Rect();

if (opts == null) opts = new BitmapFactory.Options();

opts.inScreenDensity = Drawable.resolveDensity(res, 0);

Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);

if (bm != null) {

byte[] np = bm.getNinePatchChunk();

if (np == null || !NinePatch.isNinePatchChunk(np)) {

np = null;

pad = null;

}

final Rect opticalInsets = new Rect();

bm.getOpticalInsets(opticalInsets);

return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName);

}

return null;

}

public static Drawable createFromXml(Resources r, XmlPullParser parser)

throws XmlPullParserException, IOException {

return createFromXml(r, parser, null);

}

public static Drawable createFromXml(Resources r, XmlPullParser parser, Theme theme)

throws XmlPullParserException, IOException {

AttributeSet attrs = Xml.asAttributeSet(parser);

int type;

//noinspection StatementWithEmptyBody

while ((type=parser.next()) != XmlPullParser.START_TAG

&& type != XmlPullParser.END_DOCUMENT) {

// Empty loop.

}

if (type != XmlPullParser.START_TAG) {

throw new XmlPullParserException("No start tag found");

}

Drawable drawable = createFromXmlInner(r, parser, attrs, theme);

if (drawable == null) {

throw new RuntimeException("Unknown initial tag: " + parser.getName());

}

return drawable;

}

public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs)

throws XmlPullParserException, IOException {

return createFromXmlInner(r, parser, attrs, null);

}

public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs,

Theme theme) throws XmlPullParserException, IOException {

return r.getDrawableInflater().inflateFromXml(parser.getName(), parser, attrs, theme);

}

public static Drawable createFromPath(String pathName) {

if (pathName == null) {

return null;

}

Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);

try {

Bitmap bm = BitmapFactory.decodeFile(pathName);

if (bm != null) {

return drawableFromBitmap(null, bm, null, null, null, pathName);

}

} finally {

Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

}

return null;

}

public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,

@NonNull AttributeSet attrs) throws XmlPullParserException, IOException {

inflate(r, parser, attrs, null);

}

/**

从XML文件中加载Drawable实例,Drawable实例接受主题设置的风格

*/

public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,

@NonNull AttributeSet attrs, @Nullable Theme theme)

throws XmlPullParserException, IOException {

final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.Drawable);

mVisible = a.getBoolean(R.styleable.Drawable_visible, mVisible);

a.recycle();

}

/**

从XML文件中加载Drawable实例

*/

void inflateWithAttributes(@NonNull @SuppressWarnings("unused") Resources r,

@NonNull @SuppressWarnings("unused") XmlPullParser parser, @NonNull TypedArray attrs,

@AttrRes int visibleAttr) throws XmlPullParserException, IOException {

mVisible = attrs.getBoolean(visibleAttr, mVisible);

}

/**

这段注释很重要,故保留英文注释!

ConstantState这个抽象类被用于存储 多个Drawable实例间 共享的 常量状态值及数据。

如从同一个图片资源创建的多个BitmapDrawable实例,它们将共享

同一个存储在它们的ConstantState中的Bitmap。

* This abstract class is used by {@link Drawable}s to store shared constant state and data

* between Drawables. {@link BitmapDrawable}s created from the same resource will for instance

* share a unique bitmap stored in their ConstantState.

*

newDrawable可以运用ConstantState创建一个新的Drawable实例

*

* {@link #newDrawable(Resources)} can be used as a factory to create new Drawable instances

* from this ConstantState.

*

*

Drawable#getConstantState可以获取一个Drawable关联的ConstantState。

调用Drawable#mutate(),则将为新创建的Drawable实例单独关联一个ConstantState。

* Use {@link Drawable#getConstantState()} to retrieve the ConstantState of a Drawable. Calling

* {@link Drawable#mutate()} on a Drawable should typically create a new ConstantState for that

* Drawable.

*/

public static abstract class ConstantState {

/**

运用ConstantState创建一个新的Drawable实例

*/

public abstract @NonNull Drawable newDrawable();

/**

运用ConstantState创建一个新的Drawable实例

*/

public @NonNull Drawable newDrawable(@Nullable Resources res) {

return newDrawable();

}

/**

运用ConstantState创建一个新的Drawable实例

*/

public @NonNull Drawable newDrawable(@Nullable Resources res,

@Nullable @SuppressWarnings("unused") Theme theme) {

return newDrawable(res);

}

/**

返回会影响Drawable实例的一个bit掩码变化设置

*/

public abstract @Config int getChangingConfigurations();

/**

返回所有的像素数

public int addAtlasableBitmaps(@NonNull Collection atlasList) {

return 0;

}

/** @hide */

protected final boolean isAtlasable(@Nullable Bitmap bitmap) {

return bitmap != null && bitmap.getConfig() == Bitmap.Config.ARGB_8888;

}

/**

返回当前共享状态是否可以设置主题

*/

public boolean canApplyTheme() {

return false;

}

}

/**

返回当前Drawable的用于存储共享状态值的ConstantState实例

*/

public @Nullable ConstantState getConstantState() {

return null;

}

//通过Bitmap实例创建Drawable实例

private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,

Rect pad, Rect layoutBounds, String srcName) {

if (np != null) {

return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);

}

return new BitmapDrawable(res, bm);

}

/**

确保色彩过滤器和当前色彩与色彩模式一致

*/

@Nullable PorterDuffColorFilter updateTintFilter(@Nullable PorterDuffColorFilter tintFilter,

@Nullable ColorStateList tint, @Nullable PorterDuff.Mode tintMode) {

if (tint == null || tintMode == null) {

return null;

}

final int color = tint.getColorForState(getState(), Color.TRANSPARENT);

if (tintFilter == null) {

return new PorterDuffColorFilter(color, tintMode);

}

tintFilter.setColor(color);

tintFilter.setMode(tintMode);

return tintFilter;

}

/**

如果主题有效,则从中获取样式属性,

如果主题无效,则返回没有样式的资源。

*/

static @NonNull TypedArray obtainAttributes(@NonNull Resources res, @Nullable Theme theme,

@NonNull AttributeSet set, @NonNull int[] attrs) {

if (theme == null) {

return res.obtainAttributes(set, attrs);

}

return theme.obtainStyledAttributes(set, attrs, 0, 0);

}

/**

根据 原始像素值,资源单位密度和目标设备单位密度 获得一个float像素值

*/

static float scaleFromDensity(float pixels, int sourceDensity, int targetDensity) {

return pixels * targetDensity / sourceDensity;

}

static int scaleFromDensity(

int pixels, int sourceDensity, int targetDensity, boolean isSize) {

if (pixels == 0 || sourceDensity == targetDensity) {

return pixels;

}

final float result = pixels * targetDensity / (float) sourceDensity;

if (!isSize) {

return (int) result;

}

final int rounded = Math.round(result);

if (rounded != 0) {

return rounded;

} else if (pixels > 0) {

return 1;

} else {

return -1;

}

}

//获取单位密度

static int resolveDensity(@Nullable Resources r, int parentDensity) {

final int densityDpi = r == null ? parentDensity : r.getDisplayMetrics().densityDpi;

return densityDpi == 0 ? DisplayMetrics.DENSITY_DEFAULT : densityDpi;

}

static void rethrowAsRuntimeException(@NonNull Exception cause) throws RuntimeException {

final RuntimeException e = new RuntimeException(cause);

e.setStackTrace(new StackTraceElement[0]);

throw e;

}

/**

通过解析tintMode属性枚举值获得一个PorterDuff.Mode

被隐匿

* @hide

*/

public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {

switch (value) {

case 3: return Mode.SRC_OVER;

case 5: return Mode.SRC_IN;

case 9: return Mode.SRC_ATOP;

case 14: return Mode.MULTIPLY;

case 15: return Mode.SCREEN;

case 16: return Mode.ADD;

default: return defaultMode;

}

}

}

Drawable类本身源码先写到这儿,接着往下看。

3:Drawable绘制流程

看过Drawable源码,其实我们还是不清楚:

Drawable实例到底是如何被绘制到屏幕上面?

Drawable源码中的那些方法又是什么时候被谁调用的?

我们回想一下,使用Drawable最通常的步骤:

通过Resource获取Drawable实例

将获取的Drawable实例当做背景设置给View或者作为ImageView的src进行显示:

下面就逐步分析理解Drawable的绘制流程。

3.1:通过Resource获取Drawable实例

最常用写法:getResources().getDrawable(int id),看下关键代码:

public class Resources {

****

public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {

final Drawable d = getDrawable(id, null);

*****

return d;

}

public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)

throws NotFoundException {

final TypedValue value = obtainTempTypedValue();

try {

final ResourcesImpl impl = mResourcesImpl;

//

impl.getValue(id, value, true);

//将获取到的Drawable实例返回

return impl.loadDrawable(this, value, id, theme, true);

} ****

}

}

一路追踪下去:

public class ResourcesImpl {

//Resource实例,TypedValue,资源ID,Theme实例,true

@Nullable

Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,

boolean useCache) throws NotFoundException {

try {

********

//是否属于ColorDrawable

final boolean isColorDrawable;

//Drawable缓存

final DrawableCache caches;

final long key;

//判断资源是否属于颜色资源

if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT

&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {

isColorDrawable = true;

caches = mColorDrawableCache;

key = value.data;

} else {

//如果是加载一张普通的图片,不属于颜色资源

isColorDrawable = false;

caches = mDrawableCache;

key = (((long) value.assetCookie) << 32) | value.data;

}

if (!mPreloading && useCache) {

final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);

if (cachedDrawable != null) {

return cachedDrawable;

}

}

//如果在Drawable缓存里面未找到资源ID对应的Drawable实例,继续

final Drawable.ConstantState cs;

if (isColorDrawable) {

cs = sPreloadedColorDrawables.get(key);

} else {

//如果不属于颜色资源,则从sPreloadedDrawables中查询

//sPreloadedDrawables只有在执行cacheDrawable方法时

//才会进行数据添加:而第一次加载图片时候还未执行cacheDrawable

//所以此时cs = null.

cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);

}

Drawable dr;

if (cs != null) {

dr = cs.newDrawable(wrapper);

} else if (isColorDrawable) {

dr = new ColorDrawable(value.data);

} else {

//当第一次加载图片资源时候,cs=null且不属于颜色资源,

//实际是通过loadDrawableForCookie来获取Drawable实例

dr = loadDrawableForCookie(wrapper, value, id, null);

}

*********

}

private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,

Resources.Theme theme) {

****

final String file = value.string.toString();

****

final Drawable dr;

Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);

try {

if (file.endsWith(".xml")) {

//如果是从xml文件加载Drawable

final XmlResourceParser rp = loadXmlResourceParser(

file, id, value.assetCookie, "drawable");

dr = Drawable.createFromXml(wrapper, rp, theme);

rp.close();

} else {

//从图片资源加载Drawable,执行Drawable.createFromResourceStream

//获取Drawable实例

final InputStream is = mAssets.openNonAsset(

value.assetCookie, file, AssetManager.ACCESS_STREAMING);

dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);

is.close();

}

} catch (Exception e) {

****

}

****

return dr;

}

}

一路追踪下去:

public abstract class Drawable {

public static Drawable createFromResourceStream(Resources res, TypedValue value,

InputStream is, String srcName, BitmapFactory.Options opts) {

****

return drawableFromBitmap(res, bm, np, pad, opticalInsets, srcName);

****

}

private static Drawable drawableFromBitmap(Resources res, Bitmap bm, byte[] np,

Rect pad, Rect layoutBounds, String srcName) {

if (np != null) {

//如果加载的图片资源是.9 PNG,返回NinePatchDrawable实例

return new NinePatchDrawable(res, bm, np, pad, layoutBounds, srcName);

}

//对于普通图片资源,返回BitmapDrawable

return new BitmapDrawable(res, bm);

}

}

由此可见,通过Resource实例加载一张资源图片:

.9图返回1个NinePatchDrawable实例;

普通图片返回1个BitmapDrawable实例。

3.2:将获取的Drawable实例当做背景设置给View

最常用写法:targetView.setBackgroundDrawable(Drawable bg),

同样看一下关键代码

public class View implements Drawable.Callback, KeyEvent.Callback,

AccessibilityEventSource {

****

public void setBackgroundDrawable(Drawable background) {

****

if (background == mBackground) {

//如果当前背景和background相同,直接return

return;

}

boolean requestLayout = false;

mBackgroundResource = 0;

if (mBackground != null) {

if (isAttachedToWindow()) {

//如果当前View实例已经被绘制到屏幕上,则首先取消

//该View实例原始背景Drawable的动画

mBackground.setVisible(false, false);

}

//移除该View实例原始背景Drawable的动画监听接口

mBackground.setCallback(null);

//取消该View实例原始背景Drawable的所有事件

unscheduleDrawable(mBackground);

}

if (background != null) {

****

//设置background的布局方向和View实例一致,

//Drawable.setLayoutDirection见上一篇文章

background.setLayoutDirection(getLayoutDirection());

if (background.getPadding(padding)) {

//如果Drawable实例background有padding

resetResolvedPaddingInternal();

switch (background.getLayoutDirection()) {

case LAYOUT_DIRECTION_RTL:

//布局方向从右至左

mUserPaddingLeftInitial = padding.right;

mUserPaddingRightInitial = padding.left;

internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);

break;

case LAYOUT_DIRECTION_LTR:

default:

//布局方向从左至右

mUserPaddingLeftInitial = padding.left;

mUserPaddingRightInitial = padding.right;

//internalSetPadding会将四个参数值和View实例的padding进行比对,若不同则会重新布局+重建View的外部轮廓

internalSetPadding(padding.left, padding.top, padding.right, padding.bottom);

}

mLeftPaddingDefined = false;

mRightPaddingDefined = false;

}

if (mBackground == null

|| mBackground.getMinimumHeight() != background.getMinimumHeight()

|| mBackground.getMinimumWidth() != background.getMinimumWidth()) {

requestLayout = true;

}

//设置当前View实例的背景为传入的Drawable实例 background

mBackground = background;

if (background.isStateful()) {

//如果background会根据状态值变更外观,则设置其状态为

//当前View实例的state

background.setState(getDrawableState());

}

if (isAttachedToWindow()) {

//如果当前View实例已经被绘制到屏幕上

//且实例和实例的父控件及递归获得的根布局都处于可见状态,

//则设置background开启动画效果

background.setVisible(getWindowVisibility() == VISIBLE && isShown(), false);

}

applyBackgroundTint();

//设置background动画接口监听为View实例本身(View实现了 Drawable.Callback):

//public class View implements Drawable.Callback

background.setCallback(this);

if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {

mPrivateFlags &= ~PFLAG_SKIP_DRAW;

//需要重新布局

requestLayout = true;

}

} else {

mBackground = null;

if ((mViewFlags & WILL_NOT_DRAW) != 0

&& (mForegroundInfo == null || mForegroundInfo.mDrawable == null)) {

mPrivateFlags |= PFLAG_SKIP_DRAW;

}

requestLayout = true;

}

computeOpaqueFlags();

if (requestLayout) {

//重新布局

requestLayout();

}

mBackgroundSizeChanged = true;

//重绘View实例

invalidate(true);

//重建View实例的外部轮廓

invalidateOutline();

}

}

由此可见,setBackgroundDrawable方法,调用了Drawable实例的一系列方法,最终引发了View实例的重新布局(requestLayout())重绘(invalidate(true))及重建View实例的外部轮廓(invalidateOutline())。

invalidate会触发draw方法,我们继续看View.draw方法的关键代码:

public void draw(Canvas canvas) {

****

/*

翻译可能不甚准确,欢迎英语好的同学留言指正O(∩_∩)O~

Draw方法会执行以下几个步骤,且必须按顺序执行:

1:绘制View实例的背景

2:如有必要,保存画布图层以备褪色

3:绘制View实例的内容

4:绘制View实例的中的子控件

5:如有必要,绘制边缘并恢复图层

6:绘制滚动条

* 1. Draw the background

* 2. If necessary, save the canvas' layers to prepare for fading

* 3. Draw view's content

* 4. Draw children

* 5. If necessary, draw the fading edges and restore layers

* 6. Draw decorations (scrollbars for instance)

*/

//从步骤顺序上看,和Drawable相关的就是第1步,只看第1步代码

// Step 1, draw the background, if needed

int saveCount;

if (!dirtyOpaque) {

//绘制背景

drawBackground(canvas);

}

****

}

一路追踪下去:

private void drawBackground(Canvas canvas) {

//mBackground就是之前setBackgroundDrawable传入的Drawable实例

final Drawable background = mBackground;

if (background == null) {

return;

}

//设置background绘制范围为View实例的所在范围

setBackgroundBounds();

****

final int scrollX = mScrollX;

final int scrollY = mScrollY;

if ((scrollX | scrollY) == 0) {

//最终调用Drawable.draw(Canvas canvas)将Drawable实例

//绘制到屏幕上

background.draw(canvas);

} else {

canvas.translate(scrollX, scrollY);

//最终调用Drawable.draw(Canvas canvas)将Drawable实例

//绘制到屏幕上

background.draw(canvas);

canvas.translate(-scrollX, -scrollY);

}

}

由此可见,View实例的背景Drawable实例最终还是调用自身的Drawable.draw(@NonNull Canvas canvas)方法绘制到屏幕上。

继续查看Drawable.draw方法:

public abstract class Drawable {

//Drawable中的draw是一个抽象方法,应该是为了众多的

//子类Drawable拥有自定义的绘制逻辑进行重写

public abstract void draw(@NonNull Canvas canvas);

}

在分析Resource.getDrawable时候已经知道,

对于普通的图片资源,获取到的是一个BitmapDrawable实例,

我们就来看看BitmapDrawable的draw具体的绘制逻辑:

public class BitmapDrawable extends Drawable {

@Override

public void draw(Canvas canvas) {

****

if (shader == null) {

****

//最终调用了Canvas.drawBitmap方法,将Drawable实例中的bitmap绘制到View实例关联的画布上

canvas.drawBitmap(bitmap, null, mDstRect, paint);

if (needMirroring) {

canvas.restore();

}

} ****

}

}

至此,将获取的Drawable实例当做背景设置给View,和Drawable相关的一系列逻辑就分析完了,大致如下:

1:setBackgroundDrawable方法,调用了Drawable的一系列方法,设置了Drawable实例一系列属性值,最终引发了View实例的重新布局(requestLayout()),重绘(invalidate(true))及重建View实例的外部轮廓(invalidateOutline())

2:在View实例重绘过程的第一步,将得到的Drawable实例(View实例的背景)绘制到屏幕上,实质是调用了Drawable.draw(@NonNull Canvas canvas)

3:Drawable.draw本身是个抽象方法,绘制具体逻辑由其子类实现。

我们以之前获得的BitmapDrawable为例进行分析:

最终调用了Canvas.drawBitmap方法,将Drawable实例中的bitmap绘制到View实例关联的画布上

Drawable绘制流程今天先写到这儿,现在是2017/03/07 20:41,加班码字到现在有点累了,明后天继续把ImageView和Drawable关联的部分写完吧!

未完待续...

以上就是个人分析的一点结果,若有错误,请各位同学留言告知!

That's all !

android 代码 drawable,Android Drawable完全解析(一):Drawable源码分析(中)相关推荐

  1. Android四大组件之ContentProvider 全面解析,ContentResolver源码解析如何调用其它APP的ContentProvider

    今天来总结下Android中的ContentProvider(以下简称CP),具体代码请见https://github.com/Mangosir/ContentProviderReview/tree/ ...

  2. Android 12 新APP启动画面(SplashScreen API)简介源码分析

    以往的启动画面 默认情况下刚启动APP时会显示一会白色背景 如果把这个启动背景设置为null,则一闪而过的白色会变成黑色 如果把启动Activity设置为背景透明[< item name=&qu ...

  3. scroller类的用法完全解析以及带源码分析

    上一篇:scrollTo与scrollBy用法以及TouchSlop与VelocityTracker解析 通过上一篇内容对scrollTo与scrollBy用法以及TouchSlop与Velocity ...

  4. 鸿蒙系统源代码解析,鸿蒙内核源码分析(系统调用篇) | 图解系统调用全貌

    本篇说清楚系统调用 读本篇之前建议先读鸿蒙内核源码分析(总目录)工作模式篇. 本篇通过一张图和七段代码详细说明系统调用的整个过程,代码一捅到底,直到汇编层再也捅不下去. 先看图,这里的模式可以理解为空 ...

  5. Android ViewManger解析 从ViewRoot 源码分析invalidate

    转载请标明出处:http://blog.csdn.net/sk719887916/article/details/48443429,作者:skay     通过学习了AndroidUI之绘图机基础 知 ...

  6. Android 蓝牙hfp协议连接slc连接rfcomm连接源码分析(2)- HF侧发送和接受数据流程

    android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙hfp初始化.rfcomm连接.slc连接.sco连接源代码分析大全 - 点击下载 ...

  7. mysql驱动源码解析_mysql驱动源码分析

    注: 本系列文章使用JDK1.5 数据库驱动版本 mysql-connector-java-5.1.8b JAVA连接数据库是其众多功能中的一部分,主要有两种方式连接DataBase: 一种是采用JD ...

  8. 文件解析库doctotext源码分析

    doctotext中没有make install选项,make后生成可执行文件 在buile目录下面有.so动态库和头文件,需要的可以从这里面拷贝 build/doctotext就是可执行程序. do ...

  9. java log 配置,java日志系统--log4j配置解析过程,源码分析

    log4j 可以看成是非常类似jdk logger 结构 ,有个logger 与logManger 都是在logManger的静态块中初始化类,加载配置文件 Logger.getLogger(Test ...

  10. 【Android源码】源码分析深度好文+精编内核解析分享

    阅读Android源码的好处有很多,比如:可以加深我们对系统的了解:可以参考牛人优雅的代码实现:可以从根本上找出一些bug的原因-我们应该庆幸Android是开源的,所有的功能都可以看到实现,所有的b ...

最新文章

  1. 关于java使用javacomm20-win32实践总结 (转)
  2. [WPF]WPF中材质制作——图片和矢量图之争
  3. EDGE X Kubernetes Meetup·杭州站:云原生在边缘的实践与应用
  4. 对KVM虚拟机进行cpu pinning配置的方法
  5. 如何计算一只鸡的表面积?
  6. 理想中的那些智能家居!
  7. fx-1s可以用c语言,AX-1S 系列微型可编程控制器100兼容三菱FX-1S系列PLC)
  8. 一招win7 c盘瘦身
  9. 玩客云折腾记录(一):编译 ArmBian 系统
  10. dosbox基础使用
  11. 如何用C#做一个投票小demo
  12. 撑不住了,豆瓣用户需要实名制了
  13. 机器视觉系列(五)——镜头部分
  14. 如何给PDF文件去水印,10秒轻松搞定
  15. pythonQQ机器人系列:使用requests实现QQ机器人聊天(1-0)
  16. Centos7 下搭建小幺鸡接口管理工具
  17. 球形罩铆接机械臂设计(lunwen+开题报告+指导记录+设计图纸+PLC控制程序)
  18. HTML Tab 选项卡
  19. 利用matlab实现三体问题(双星、3星、多星运动)
  20. 英特尔:做「真男人」的代价

热门文章

  1. 前端JavaScript+HTML
  2. 设计模式--有道笔记的整理
  3. html2canvas解决图片空白,网络图片跨域
  4. 什么叫克隆人_【语文阅读理解】到底什么是克隆人
  5. MCS-51的中断系统
  6. Laurent(洛朗或者劳伦)多项式,泰勒展开式
  7. matlab画时间 频率图,在matlab中画频数图和累积频率图
  8. 计算机应用技能实验报告实验五,实验五excel应用—制作学生成绩表
  9. 工程流体力学笔记暂记12(总流伯努利方程)
  10. 发现电脑屏幕总是不自动关闭?看看你是否打开了这些程序……