Zhujiaqqq Blog


  • 首页

  • 关于

  • 标签

  • 分类

撸LayoutInflater源码

发表于 2019-11-24 | 分类于 Android

本文使用android-28源码,路径:/frameworks/base/core/java/android/view/LayoutInflater.java

入口:
Activity.setContentView(@LayoutRes int layoutResID)做了什么?

每个Activity都要设置一个布局:

1
2
3
4
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}

这里调用了getWindow().setContentView(layoutResID);,Window 类的实现类PhoneWindow。查看PhoneWindow的setContentView方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void setContentView(int layoutResID) {
...
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}

这里最终使用LayoutInflater加载布局。


LayoutInflater的使用只有一句话:

1
LayoutInflater.from(mContext).inflate(resId, contentParent);

可以分为两个部分:

  • LayoutInflater.from(mContext)通过mContext获取LayoutInflater对象
  • inflate(resId, contentParent)将布局resId转化层View,加入contentParent中

获取LayoutInflater对象

1
2
3
4
5
6
7
8
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}

这里使用了getSystemService方法去获取LayoutInflater对象,所以进入ContextImpl类的getSystemService()方法:

1
2
3
4
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}

这里获取的是SystemServiceRegistry中的service,去SystemServiceRegistry中查找:

1
2
3
4
5
6
7
8
9
static {
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
...
}

可以看到,在SystemServiceRegistry类被加载的时候,在static静态块中注册了LAYOUT_INFLATER_SERVICE。这里直接返回了PhoneLayoutInflater类的对象,PhoneLayoutInflater是LayoutInflater的子类,在LayoutInflater的基础上重写了onCreateView方法,为系统自带的View添加前缀:

1
2
3
4
5
6
7
8
9
10
11
12
private static final String[] sClassPrefixList = {
"android.widget.",
"android.webkit.",
"android.app."
};
@Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
for (String prefix : sClassPrefixList) {
View view = createView(name, prefix, attrs);
...
}
return super.onCreateView(name, attrs);
}

所以,LayoutInflater.from(mContext)最终返回一个LayoutInflater的子类PhoneLayoutInflater的对象。

加载布局文件

LayoutInflater加载布局依靠inflate方法:

1
2
3
4
5
6
7
8
9
10
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
// 获取布局文件到解析器
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}

这里调用了inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;

// 找到START_TAG标签,即整个布局的开头
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty
}
final String name = parser.getName();
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
// 如果是布局开头是一个merge标签,那就直接递归去解析标签里面的view
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// 这里先创建根布局的View,例如LinearLayout\RelativeLayout等
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
// 将parser中获取的布局属性转换层LayoutParams,然后放入根布局的view中
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
// 递归解析根View下的所有布局,转换成View
rInflateChildren(parser, temp, attrs, true);
// attachToRoot在入参中设置为false,不会走下面的代码,
//如果attachToRoot为true的话,就会将解析得到的布局View加入传入的根View
if (root != null && attachToRoot) {
root.addView(temp, params);
}
}
return result;
}
}

inflate方法的主体流程就是这些,这里主要看以下几个方法:

  • 创建根布局的View:createViewFromTag(root, name, inflaterContext, attrs);
  • 遍历创建子布局:rInflateChildren(parser, temp, attrs, true);
  • 最终创建View的方法:view = createView(name, prefix, attrs);

创建根布局的View

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
try {
View view;
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
// 如何标签的名字没有“.”,那么就是系统自带的View,如TextView等
// 使用onCreateView(parent, name, attrs)方法
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
// 如果是有“.”,说明是自定义的View,或者扩展库的View
// 使用createView(name, null, attrs);方法
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}

这里的onCreateView(parent, name, attrs)和createView(name, null, attrs)的区别在于onCreateView使用了PhoneLayoutInflater重写的onCreateView方法,将系统View添加了前缀然后再使用createView方法进行创建View。

遍历创建子布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}

void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
// 获取当前深度
final int depth = parser.getDepth();
int type;
// 遍历整个View树
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
final String name = parser.getName();
// 获取通过标签获取View
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
// 递归遍历
rInflateChildren(parser, view, attrs, true);
// 将获取的View插入父View中
viewGroup.addView(view, params);
}
}

这根布局下创建子布局的View通过在While循环下进行递归,一层一层的实现

最终创建View的方法

最终创建View的方法在createView中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
// 获取当前标签对于的view的构造器
Constructor<? extends View> constructor = sConstructorMap.get(name);
// 如果构造器无效,则从缓存中删除
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;

if (constructor == null) {
// 如果构造器为空,通过类名去获取构造器,并加入缓存中
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
// 这里限定构造器的入参是:Context.class, AttributeSet.class两个类型的对象
constructor = clazz.getConstructor(mConstructorSignature);
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
}

Object lastContext = mConstructorArgs[0];
if (mConstructorArgs[0] == null) {
mConstructorArgs[0] = mContext;
}
Object[] args = mConstructorArgs;
args[1] = attrs;
// 通过反射创建View对象
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
//对ViewStub进行异步加载处理
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
}
mConstructorArgs[0] = lastContext;
return view;
}

从代码中可以看出,最终创建View是通过反射的方式创建,并且构造方法必须是两个入参的那种。


总结

  • LayoutInflater的创建最终是一个PhoneLayoutInflater对象,只是通过SystemService在类加载的时候创建,并没有跨进程的操作。
  • LayoutInflater在解析布局文件的时候,是通过XmlResourceParser完成的。布局文件是一个树形结构,xml的解析就是一层层的解析,所以View的创建也是一层层创建,创建的时候使用递归的形式完成。
  • 在LayoutInflater中,View的创建是通过反射完成的,效率并不高;View反射创建的时候是通过调用View的两个入参的构造方法,所以在写自定义View的时候如果你的View要在布局文件中插入,就必须要重写View的两个入参的构造方法。

撸Handler-Message-MessageQueue-Looper源码

发表于 2019-11-23 | 分类于 Android

Handler-MessageQueue-Looper是Android特有的线程间通信机制。

Handler

Handler作为面向开发者的类,每种功能都复写了很多不同的方法,以达到方便开发者调用的目的:

Handler构建

Hanlder的创建方法

Handler的构造最主要方法如下:

1
2
3
4
5
6
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

需要获取一下四个成员变量:

  • looper 指定的looper,与线程有关
  • messageQueue looper的messageQueue
  • callback 可空
  • mAsynchronous 是否异步

Handler发送消息

发送runnable
发送message

上面的这些方法最终会调用一下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
// 将发送的消息加入队列
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// 将消息的target设为handler对象自己,表示这个消息是handler自己发送的,后面也会根据target的不同,由不同的handler处理消息
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 这里调用messageQueue的enqueue方法将消息加入消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}

这里调用messageQueue的enqueue方法将消息加入消息队列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 如果新加入的message立即需要处理或是比队首的消息先处理,那么将消息插入到最前面
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 遍历待处理消息队列,将入队的消息按处理时间顺序插入消息队列中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

可以看出,将消息插入消息队列分为两种情况:

  • 如果当前消息最先需要处理,那么就插入队首
  • 如果不是需要最先处理,那么遍历整个队列,按消息需要处理的时间先后顺序插入消息

Handler拿一个未发送的message

obtainMessage

obtainMessage最终调用如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Hanlder.class
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;

return m;
}

Message.class

public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

最终调用Message类的obtain()方法,Message的obtain()方法通过从Message类中维护的消息池(sPool)中获取之前已经使用过的消息,如果消息池中没有消息,那就返回一个新的消息,这样中可以减少消息对象的创建。

Handler处理消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

public void handleMessage(Message msg) {
}

这里如果在构造Handler的时候加入了mCallback对象,那会就会执行mCallback对象的handleMessage()方法,否则执行Handler对象的handleMessage()方法。所以在我们写handler代码的时候都回去重写handleMessage(Message msg)方法,来处理handler发送消息。

开发中使用API主要就是以上的部分,下面开始分析,消息的处理机制。


Looper

Looper类在普通的开发中很难使用到,但是它缺少消息机制中重要的一个部分:负责轮询,并分发消息给相应的handler进行处理。
Looper是与线程紧密联系的一个类,Looper类中维护了一个ThreadLocal对象,用于存放不同线程的Looper对象:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper对象的构造是通过Looper.prepare()方法实现的:

1
2
3
4
5
6
7
8
9
10
11
12
public static void prepare() {
// 默认情况下,quitAllowed为true,即looper对象可以退出循环并结束,但是在主线程中的looper不能退出循环,因为主线程所有的事件逻辑都在主线程的looper中进行轮询处理,如果退出loop循环,那么意味着主线程执行完了,应用结束。
prepare(true);
}

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
// 每个线程的looper只可以创建一次
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

looper的执行只有一个方法,这个方法会进入一个死循环,在循环中不断获取消息队列中的消息然后分发给相应的handler进行处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
for (;;) {
// 1 拿消息
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
try {
// 2 分发给handler处理
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
// 3 消息的回收
msg.recycleUnchecked();
}
}

Looper.loop在循环中主要做了三件事:

  1. 从消息队列MessageQueue中获取消息
  2. 将获取的消息给相应的handler进行处理
  3. 将处理完的消息进行回收

想看第一点,Message msg = queue.next(); // might block
源码的注释上写到:might block(有可能阻塞)。进入MessageQueue的next()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Message next() {
// 这段与消息队列退出有关,可以对照quit()方法看
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 在native层阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 得到message,将message队列的对首给message.next,并返回message对象
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

// 与Looper退出相关
if (mQuitting) {
dispose();
return null;
}

// 这里处理idle handlers.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
// 如果idle handler 的queueIdle方法返回false那执行完就移除idleHandler队列
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
}

next()使用nativePollOnce(ptr, nextPollTimeoutMillis);方法,在没有消息的时候进行阻塞,当有消息的时候再从mMessages中获取消息,然后判断是否到到执行消息的时间,如果到了就返回消息,否则就计算要等待的时间,然后下一次循环再调用nativePollOnce()方法进行阻塞,直到消息需要执行了,然后返回消息。

nativePollOnce()方法是一个native的方法:

1
2
3
4
5
6
7
/frameworks/base/core/jni/android_os_MessageQueue.cpp

static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}

long ptr是java层线程创建Looper的时候创建MessageQueue时从native层获取的,对应的就是native 层的MessageQueue。android_os_MessageQueue_nativePollOnce方法会通过ptr先获取nativeMessageQueue对象,然后调用nativeMessageQueue的pollOnce方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
//阻塞在这里 epoll_wait
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;

if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}

最终调用mLooper->pollOnce(timeoutMillis);方法,当消息队列中没有消息,timeoutMillis(Java层的nextPollTimeoutMillis)= -1,就会阻塞。只有当新加入消息的时候会调用nativeWake(mPtr);native方法:

1
2
3
4
5
6
7
8
9
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->wake();
}

void NativeMessageQueue::wake() {
// epoll_ctl
mLooper->wake();
}

最终调用mLooper->wake();唤醒mLoop,此时nativePollOnce方法往后执行,messageQueue.next()才能获取message并返回。

至此,

  1. 从handler发送消息
  2. messageQueue将消息入队列
  3. looper轮询调用messageQueue的 next方法获取下一个需要处理的消息
  4. looper将消息分发给handler进行处理

整个流程完毕


总结

handler消息处理有一下几个大块:

  • looper使用死循环轮询消息,使线程一直运行并监控发来的消息然后处理
  • 在轮询的时候核心是通过messageQueue的next方法获取下一个要处理的消息,这里使用native层调用epoll_wait和epoll_clt进行线程的阻塞和唤醒
  • handler的obtain方法最终使用的是message的obtain方法,message类中维护类一个message池,为handler提供空白消息,池化的做法可以避免频繁创建对象,导致频繁GC

撸Retrofit2源码

发表于 2019-11-21 | 分类于 Android

本文使用Retrofit-2.6.2源码

切入点:

  • retrofit对象构造

  • 1
    2
    3
    4
    5
    6
    new Retrofit.Builder()
    .baseUrl(Constants.BASE_URL)
    .client(getOkHttpClient())
    .addConverterFactory(GsonConverterFactory.create())
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build();
  • 调用请求接口retrofit.create(final Class<T> service)


Retrofit对象构造

Retrofit对象构造使用的是构造者模式,主要看retrofit2.Retrofit.Builder类:

1
2
3
4
5
6
7
8
9
10
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
...
}

Builder主要构造的几个成员变量:

  • platform:Android平台还是非Android平台(Java8)
  • callFactory:实现newCall方法的类,一般是OkHttpClient
  • baseUrl:hostUrl
  • converterFactories:对网络请求对响应进行转化
  • callAdapterFactories;对请求的封装
  • callbackExecutor:Android平台下默认为主线程handler执行
  • validateEagerly:默认为false,为true的时候提前加载请求方法

retrofit.create()

整个Retrofit的使用就是从create方法开始的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public <T> T create(final Class<T> service) {
// 1 监测service请求接口是不是有效的
Utils.validateServiceInterface(service);
if (validateEagerly) {
// 2 如果validateEagerly=true,那么预加载请求的方法
eagerlyValidateMethods(service);
}
//3 使用动态代理处理service请求接口
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];

@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
  1. 注释1处,监测service请求接口是不是有效的:

    • 条件一:service类是一个接口

    • 条件二:service接口中不包含其他子接口

      则认为有效,否则直接抛异常

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    static <T> void validateServiceInterface(Class<T> service) {
    if (!service.isInterface()) {
    //如果不是接口直接抛出异常
    throw new IllegalArgumentException("API declarations must be interfaces.");
    }

    if (service.getInterfaces().length > 0) {
    // 如果service中包含其他子接口,抛出异常
    throw new IllegalArgumentException("API interfaces must not extend other interfaces.");
    }
    }
  1. 注释2处,如果validateEagerly=true,那么预加载请求的方法(默认validateEagerly=false,不会执行这个方法):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private void eagerlyValidateMethods(Class<?> service) {
    //获取当前的平台,Android端开发的时候是Android()
    Platform platform = Platform.get();
    //遍历接口中的所有方法,挑选不是默认、静态的方法
    for (Method method : service.getDeclaredMethods()) {
    if (!platform.isDefaultMethod(method) && !Modifier.isStatic(method.getModifiers())) {
    // 将挑选出的方法进行包装,加入缓存
    loadServiceMethod(method);
    }
    }
    }
  2. 注释3处,使用动态代理处理service请求接口,在动态代理的invoke()方法中实现了请求接口的封装:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override public @Nullable Object invoke(Object proxy, Method method,
    @Nullable Object[] args) throws Throwable {
    // If the method is a method from Object then defer to normal invocation.
    if (method.getDeclaringClass() == Object.class) {
    // 如果这个方法是继承自object,那么这个方法不做处理,直接返回被代理的方法
    return method.invoke(this, args);
    }
    if (platform.isDefaultMethod(method)) {
    // 默认方法只会在Java8中出现,Android平台上直接会抛异常
    return platform.invokeDefaultMethod(method, service, proxy, args);
    }
    // 4 这里将请求进行封装、加入缓存,最后invoke调用
    return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   
4. 注释4处,将请求进行封装、加入缓存,最后invoke调用:

```java
ServiceMethod<?> loadServiceMethod(Method method) {
// 在缓存中获取请求方法,如果存在直接返回
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;

synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
// 5 将请求方法进行封装得到一个ServiceMethod对象
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
  1. 注释5处,通过ServiceMethod的静态方法parseAnnotations()将请求方法进行封装得到一个ServiceMethod对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
    // 将请求方法的注解、参数、参数的注解等提取并生成requestFactory对象
    RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
    // 获取请求方法的返回类型
    Type returnType = method.getGenericReturnType();
    if (Utils.hasUnresolvableType(returnType)) {
    // 如果是不是一个Class的类型或者不是参数化的Class类型,你就抛出异常
    throw methodError(method,
    "Method return type must not include a type variable or wildcard: %s", returnType);
    }
    if (returnType == void.class) {
    // 如果返回类型为空,抛出异常
    throw methodError(method, "Service methods cannot return void.");
    }

    return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
    }

    parseAnnotations()的RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);这句话将请求方法的注解、参数、参数的注解等提取并生成requestFactory对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
    return new Builder(retrofit, method).build();
    }

    RequestFactory build() {
    for (Annotation annotation : methodAnnotations) {
    // 这里解析请求方法的注解:请求类型、是否有请求体、请求的url、请求头
    parseMethodAnnotation(annotation);
    }
    ...
    int parameterCount = parameterAnnotationsArray.length;
    parameterHandlers = new ParameterHandler<?>[parameterCount];
    // 逐一解析参数
    for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
    parameterHandlers[p] =
    parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
    }
    ...
    return new RequestFactory(this);
    }

    到此,完成了请求方法的request部分的封装。但是ServiceMethod对象还缺少返回参数的封装,所有ServiceMethod.parseAnnotations()方法最终调用return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);做后续处理,返回一个完整的ServiceMethod对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
    Retrofit retrofit, Method method, RequestFactory requestFactory) {
    // 6 获取请求方法的返回类型
    adapterType = method.getGenericReturnType();
    // 7 通过方法的返回类型去创建callAdapter
    CallAdapter<ResponseT, ReturnT> callAdapter =
    createCallAdapter(retrofit, method, adapterType, annotations);
    // 8 响应的类型
    Type responseType = callAdapter.responseType();
    // 9 构建响应的转换器
    Converter<ResponseBody, ResponseT> responseConverter =
    createResponseConverter(retrofit, method, responseType);

    okhttp3.Call.Factory callFactory = retrofit.callFactory;

    return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
    }

    注释6处,获取请求方法的返回类型,一般情况下是:Call<xxxBean>或Observable<xxxBean>这样的封装类型。通过这个返回类型,注释7处得到了callAdapter:

    1
    2
    3
    4
    private static <ResponseT, ReturnT> CallAdapter<ResponseT, ReturnT> createCallAdapter(
    Retrofit retrofit, Method method, Type returnType, Annotation[] annotations) {
    return (CallAdapter<ResponseT, ReturnT>) retrofit.callAdapter(returnType, annotations);
    }

    这里调用哦那个retrofit类的callAdapter()方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public CallAdapter<?, ?> callAdapter(Type returnType, Annotation[] annotations) {
    // 调用nextCallAdapter()方法
    return nextCallAdapter(null, returnType, annotations);
    }
    public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
    Annotation[] annotations) {
    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
    // 遍历callAdapterFactories找到合适的callAdapter返回
    CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
    if (adapter != null) {
    return adapter;
    }
    }
    }

    callAdapterFactories.get(i).get(returnType, annotations, this)因为callAdapterFactories中可能有多个Factory,且它们的实现都不一样,一下对比两个:

    • DefaultCallAdapterFactory.class

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      @Override public @Nullable CallAdapter<?, ?> get(
      Type returnType, Annotation[] annotations, Retrofit retrofit) {
      if (getRawType(returnType) != Call.class) {
      return null;
      }
      final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);

      return new CallAdapter<Object, Call<?>>() {
      @Override public Type responseType() {
      return responseType;
      }
      @Override public Call<Object> adapt(Call<Object> call) {
      return executor == null
      ? call
      : new ExecutorCallbackCall<>(executor, call);
      }
      };
      }
如果返回类型不是Call的包装类型,那么直接返回null。
  • CompletableFutureCallAdapterFactory.class (Java8)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Override public @Nullable CallAdapter<?, ?> get(
    Type returnType, Annotation[] annotations, Retrofit retrofit) {
    if (getRawType(returnType) != CompletableFuture.class) {
    return null;
    }

    Type innerType = getParameterUpperBound(0, (ParameterizedType) returnType);
    if (getRawType(innerType) != Response.class) {
    // Generic type is not Response<T>. Use it for body-only adapter.
    return new BodyCallAdapter<>(innerType);
    }
    Type responseType = getParameterUpperBound(0, (ParameterizedType) innerType);
    return new ResponseCallAdapter<>(responseType);
    }
如果返回类型的被包装类不是CompletableFuture,那么直接返回null

最终从callAdapterFactories筛选出合适的Fractory。

注释8处,获取响应的类型:

1
return Utils.getParameterUpperBound(index, type);

注释9处,构建响应的转换器:

1
2
3
4
5
private static <ResponseT> Converter<ResponseBody, ResponseT> createResponseConverter(
Retrofit retrofit, Method method, Type responseType) {
Annotation[] annotations = method.getAnnotations();
return retrofit.responseBodyConverter(responseType, annotations);
}

使用的retrofit的responseBodyConverter()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public <T> Converter<ResponseBody, T> responseBodyConverter(Type type, Annotation[] annotations) {
return nextResponseBodyConverter(null, type, annotations);
}

public <T> Converter<ResponseBody, T> nextResponseBodyConverter(
@Nullable Converter.Factory skipPast, Type type, Annotation[] annotations) {
checkNotNull(type, "type == null");
checkNotNull(annotations, "annotations == null");

int start = converterFactories.indexOf(skipPast) + 1;
for (int i = start, count = converterFactories.size(); i < count; i++) {
Converter<ResponseBody, ?> converter =
converterFactories.get(i).responseBodyConverter(type, annotations, this);
if (converter != null) {
//noinspection unchecked
return (Converter<ResponseBody, T>) converter;
}
}
}

responseBodyConverter()方法调用了nextResponseBodyConverter()方法,与之前callAdapter的创建类似,也是从factories中获取合适的Factory然后创建出converter对象。

最终通过responseConverter 、callAdapter、入参传入的requestFactory以及retrofit.callFactory构造出请求方法独有的HttpServiceMethod对象。

至此,请求方法的封装过程已经完毕。


请求调用:ServiceMethod.invoke()

ServiceMethod是一个抽象类,invoke()方法的实现在它的子类HttpServiceMethod中:

1
2
3
4
5
6
@Override final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}

protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

从上面创建HttpServiceMethod的代码可以看出,HttpServiceMethod的实现类为CallAdapted,所以adapt()也是在CallAdapted中实现的:

1
2
3
@Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}

adapt的最终实现有回到了HttpServiceMethod对象的成员变量callAdapter的adapt()方法中了,callAdapter

接口有多个实现类,这里看两个实现类的adapt:

  • D efaultCallAdapterFactory.call

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    new CallAdapter<Object, Call<?>>() {
    public Type responseType() {
    return responseType;
    }

    public Call<Object> adapt(Call<Object> call) {
    return (Call)(executor == null ? call : new DefaultCallAdapterFactory.ExecutorCallbackCall(executor, call));
    }
    }

    static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
    this.callbackExecutor = callbackExecutor;
    this.delegate = delegate;
    }

    public void enqueue(final Callback<T> callback) {
    Utils.checkNotNull(callback, "callback == null");
    this.delegate.enqueue(new Callback<T>() {
    public void onResponse(Call<T> call, final Response<T> response) {
    ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() {
    public void run() {
    if (ExecutorCallbackCall.this.delegate.isCanceled()) {
    callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
    } else {
    callback.onResponse(ExecutorCallbackCall.this, response);
    }

    }
    });
    }

    public void onFailure(Call<T> call, final Throwable t) {
    ExecutorCallbackCall.this.callbackExecutor.execute(new Runnable() {
    public void run() {
    callback.onFailure(ExecutorCallbackCall.this, t);
    }
    });
    }
    });
    }
    }
这是默认情况的callAdapter,直接在ExecutorCallbackCall中执行call.callback(主线程)。
  • RxJava2CallAdapter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    @Override public Object adapt(Call<R> call) {
    Observable<Response<R>> responseObservable = isAsync
    ? new CallEnqueueObservable<>(call)
    : new CallExecuteObservable<>(call);

    Observable<?> observable;
    if (isResult) {
    observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
    observable = new BodyObservable<>(responseObservable);
    } else {
    observable = responseObservable;
    }
    //添加调度器
    if (scheduler != null) {
    observable = observable.subscribeOn(scheduler);
    }

    if (isFlowable) {
    return observable.toFlowable(BackpressureStrategy.LATEST);
    }
    if (isSingle) {
    return observable.singleOrError();
    }
    if (isMaybe) {
    return observable.singleElement();
    }
    if (isCompletable) {
    return observable.ignoreElements();
    }
    return RxJavaPlugins.onAssembly(observable);
    }
通过判断是同步还是异步,创建对应的Observable对象,然后根据类型不同返回不同的Observable对象。

至此,整个retrofit调用请求的过程全部完成。

总结

  • Retrofit 库主要使用了运行时注解+动态代理实现对网络请求接口的封装
  • 使用适配器模式对请求的结果进行转换,以实现与不同框架的搭配使用

UI原理总结

发表于 2019-10-21

UI原理总结

  • Activity的attach 方法里创建PhoneWindow。
  • onCreate方法里的 setContentView 会调用PhoneWindow的setContentView方法,创建DecorView并且把xml布局解析然后添加到DecorView中。
  • 在onResume方法执行后,会创建ViewRootImpl,它是最顶级的View,是DecorView的parent,创建之后会调用setView方法。
  • ViewRootImpl 的 setView方法,会将PhoneWindow添加到WMS中,通过 Session作为媒介。 setView方法里面会调用requestLayout,发起绘制请求。
  • requestLayout 一旦发起,最终会调用 performTraversals 方法,里面将会调用View的三个measure、layout、draw方法,其中View的draw 方法需要一个传一个Canvas参数。
  • 最后分析了软件绘制的原理,通过relayoutWindow 方法将Surface跟当前Window绑定,通过Surface的lockCanvas方法获取Surface的的Canvas,然后View的绘制就通过这个Canvas,最后通过Surface的unlockCanvasAndPost 方法提交绘制的数据,最终将绘制的数据交给SurfaceFlinger去提交给屏幕显示。

kotlin数组和集合

发表于 2019-10-21

Kotlin数组

对于基本类型和String,可以是用xxxArray()创建数组:

1
2
3
val intArray = IntArray(10)
val longArray = LongArray(10)
val stringArray = StringArray()

也可以使用arrayOf()、arrayOfNulls()、emptyArray()进行类型推测:

1
2
3
4
val intArray = arrayOf(1, 2, 3, 4)
val stringArray = arrayOf("a", "b", "c")
val longArray = arrayOfNulls<Long>(10)
val emptyArray = emptyArray<String>()

使用Array(size: Int, init:(Int) -> T),第二个参数可以通过闭包的形式创建数组对象

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main(args: Array<String>) {
val stringArray = Array(10) {
it.toString()
}

val intArray = Array(10) {
it * it
}

val arr = Array(10, arrInit())
}

fun arrInit(): (Int) -> Int = { it * 2 }

Kotlin集合

1
2
3
4
5
6
7
8
9
//不可变
val set = setOf<Int>(1, 2, 3)
//可变
val mutableSetOf = mutableSetOf<Int>(1, 2, 3)
val hashSetOf = hashSetOf<Int>(1, 2, 3)
val linkedSetOf = linkedSetOf<Int>(1, 2, 3)
val sortedSetOf = sortedSetOf(1, 2, 3)
val hashMapOf = hashMapOf<String, Int>()
val arrayListOf = arrayListOf<Int>()

xxxMapOf<T>()等价于Java的new xxxMap<T>(),List和Set也一样。


集合数组操作类

  • 无元素操作符

    1
    2
    3
    4
    5
    6
    7
    8
    val list = listOf<Int>(1, 2, 3, 4, 5, 6, 7)
    if (list.contains(1)) {
    }
    list.first()
    list.last()
    println(list.indexOf(1))
    println(list.elementAt(1))
    list.single()
  • 顺序操作符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    val list = listOf<Int>(1, 2, 3, 4, 5, 6, 7)
    list.reversed()
    list.sorted()
    list.sortedDescending()
    list.sortedBy {
    it
    }
    list.sortedByDescending {
    it
    }
  • 映射操作符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    val list = listOf<Int>(1, 2, 3, 4, 5, 6, 7)
    list.map {
    }
    list.flatMap { it ->
    List(it) {
    it * it
    }
    }
    list.groupBy {
    it
    }
  • 过滤操作符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    val list = listOf<Int>(1, 2, 3, 4, 5, 6, 7)
    // 过滤
    list.filter {
    it%2==0
    }.forEach {
    println(it)
    }
    //获取前三个
    list.take(3).forEach { println(it) }
    //抛弃前三个
    list.drop(3).forEach { println(it) }
  • 生产操作符

    1
    2
    3
    4
    5
    6
    val list = listOf<Int>(1, 2, 3, 4, 5, 6, 7)
    list.zip(list).forEach { println(it) }
    list.partition {
    it%3==0
    }.first.forEach { println(it) }
    list.plus(100).forEach { print(it) }
  • 统计操作符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    val list = listOf<Int>(1, 2, 3, 4, 5, 6, 7)
    if (list.any { it==3 }) {
    println("yes")
    }
    if (list.all { it > 0 }) {
    println("yes")
    }
    if (list.none { it < 0 }) {
    println("yes")
    }
    list.max()
    list.min()
    println(list.sum())

Java集合类

发表于 2019-10-17

Java的集合主要分为三大块:List、Map、Set

List

常用的List类有:ArrayList、LinkedList

List接口需要实现一下方法:

List接口

ArrayList

  • ArrayList的默认容量为10

  • 每次扩容会变为之前的1.5倍

  • ArrayList中的数据减少时,容量不会减少

  • ArrayList存储元素使用数组:

    transient Object[] elementData;

  • ArrayList中的size指的是集合中元素的个数:

    private int size;

  • ArrayList通过索引访问元素很快,时间复杂度为O(1):

    public E get(int index)

  • ArrayList在尾部插入元素,时间复杂度为O(1):

    public boolean add(E e)

  • ArrayList在尾部删除元素,时间复杂度为O(1):

    public E remove(int index) index为size-1时

  • ArrayList从中间插入元素,需要搬移元素,平均时间复杂度为O(n):

    public void add(int index, E element)

  • ArrayList从中间删除元素,需要搬移元素,平均时间复杂度为O(n):

    public E remove(int index)

  • ArrayList可以求并集、交集、单向差集

    • public boolean addAll(Collection<? extends E> c)
  • public boolean retainAll(Collection<?> c)

  • public boolean removeAll(Collection<?> c)

  • 为什么ArrayList中存储元素的数组elementData是被transient的?

    因为ArrayList中的elementData是可以扩容的,但是它的长度可能大于或等于实际元素的个数,如果,在序列化的时候将elementData自动序列化,那么会有多个空元素被序列化到磁盘中,所有源码中使用transient禁止序列化,并且在private void writeObject(java.io.ObjectOutputStream s)中通过size的个数对元素进行序列化


LinkedList

  • LinkedList继承了Queue接口,实现队列的功能
  • LinkedList继承了Deque接口,实现双端队列的功能(队列、栈)
  • LinkedList内部使用双向链表实现List的功能
  • LinkedList没有实现RandomAccess接口,所以访问非队列首尾的元素比较低效
  • LinkedList中用size记录元素个数,并有链表首节点first和链表尾节点last
  • LinkedList在队列首尾添加、删除元素效率高,时间复杂度为O(1):
    • private void linkFirst(E e)
    • void linkLast(E e)
    • private E unlinkFirst(Node<E> f)
    • private E unlinkLast(Node<E> l)
  • LinkedList在中间添加、删除元素效率低,时间复杂度为O(n):
    • public void add(int index, E element)
    • public E remove(int index)

Map

常用的Map有:

  • HashMap
  • LinkedHashMap
  • ConcurrentHashMap/HashTable
  • TreeMap

Map接口

HashMap

  1. HashMap的内部结构是数组+链表(jdk1.7)。链表长度大于8时,且扩容达到64长度数组时,链表会转化为红黑树(jdk1.8新增)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    transient Node<K,V>[] table;//数组
    //链表的结构
    static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;
    }
    //红黑树结构
    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
    TreeNode<K,V> parent; // red-black tree links
    TreeNode<K,V> left;
    TreeNode<K,V> right;
    TreeNode<K,V> prev; // needed to unlink next upon deletion
    boolean red;
    }
  2. 为什么用数组+链表

    当新插入的元素放入数组中,数组当前位置为空时,直接插入;如何当前位置已经存在元素,则对当前链表进行搜索,找到最后一个链表元素,将新插入的元素放在最后一个链表元素的后面。这样的做法是为了解决Hash冲突的问题。

  3. HashMap的hash()如何设计,为什么?

    将Key的hashCode与hashCode的高16位进行异或得到。

    因为在插入数据的时候是通过2的幂次进行散列操作,如果不与高16位进行异或,那么会有更高的hash冲突。

  4. hash冲突你还知道哪些解决办法?

    比较出名的有四种(1)开放定址法(2)链地址法(3)再哈希法(4)公共溢出区域法

  5. HashMap在什么条件下扩容?

    数组中所有元素大于数组长度*扩容阈值

  6. 为什么数组长度/扩容的容量为2的幂次?

    为了存取高效、减少碰撞、让数据均匀分布在每个链表中,所以元素对于数组的位置计数使用元素key的hash对数组长度进行取模操作,并使用位移操作进行计算。

    1
    2
    hash%length
    hash&(length-1)

    当length为2的幂次($2^n$)时,length的二进制位1000…00,那么length-1位111…11,这样在&hash的时候可以减小碰撞。

  7. 为什么在解决hash冲突的时候,不直接用红黑树?而选择先用链表,再转红黑树?

    因为红黑树需要进行左旋,右旋,变色这些操作来保持平衡,而单链表不需要。

    当元素小于8个当时候,此时做查询操作,链表结构已经能保证查询性能。当元素大于8个的时候,此时需要红黑树来加快查询速度,但是新增节点的效率变慢了。

    因此,如果一开始就用红黑树结构,元素太少,新增效率又比较慢,无疑这是浪费性能的。


ConcurrentHashMap

  • 是HashMap的线程安全版本,内部结构相同
  • 效率高于HashTable

Java的GC机制

发表于 2019-10-15 | 分类于 Java

判断哪些对象需要被GC

  • 堆
  • 方法区
  • JVM栈
  • 本地方法栈

    可达性分析方法(Java使用):通过判断对象是否被GC Root 直接或间接引用,进而判断对象是否可用,如果对象不可以就可以对这个对象进行GC

引用计数方法(python使用):每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,但无法解决对象相互循环引用的问题。

如何触发GC

  • 程序调用System.gc()
  • 根据Eden区和FromSpace区的内存大小来决定,如果内存不足,则会启动GC(此时应用线程停止)

GC又分为 minor GC 和 Full GC (也称为 Major GC )

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:

a.调用System.gc时,系统建议执行Full GC,但是不必然执行

b.老年代空间不足

c.方法去空间不足

d.通过Minor GC后进入老年代的平均大小大于老年代的可用内存

e.由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

GC算法

GC常用算法有:标记-清除算法,标记-压缩算法,复制算法,分代收集算法。

新生代、老年代的转化过程

具体过程:新生代(Young)分为Eden区,From区与To区

当系统创建一个对象的时候,总是在Eden区操作,当这个区满了,那么就会触发一次YoungGC,也就是年轻代的垃圾回收。一般来说这时候不是所有的对象都没用了,所以就会把还能用的对象复制到From区。

这样整个Eden区就被清理干净了,可以继续创建新的对象,当Eden区再次被用完,就再触发一次YoungGC,然后呢,注意,这个时候跟刚才稍稍有点区别。这次触发YoungGC后,会将Eden区与From区还在被使用的对象复制到To区,

再下一次YoungGC的时候,则是将Eden区与To区中的还在被使用的对象复制到From区。

经过若干次YoungGC后,有些对象在From与To之间来回游荡,这时候From区与To区亮出了底线(阈值),这些家伙要是到现在还没挂掉,对不起,一起滚到(复制)老年代吧。

老年代经过这么几次折腾,也就扛不住了(空间被用完),好,那就来次集体大扫除(Full GC),也就是全量回收。如果Full GC使用太频繁的话,无疑会对系统性能产生很大的影响。所以要合理设置年轻代与老年代的大小,尽量减少Full GC的操作。

使用APT实现Android组件化路由功能

发表于 2019-10-06 | 分类于 Android

组件化后不同模块之间Activity的跳转

组件化后,只有主工程模块依赖其他业务模块,而各个业务模块之间没有互相依赖关系。一个模块可以调用被依赖模块的类和方法,而被依赖的模块不能引用依赖模块的类(依赖只能单向传递)。所以,单个业务模块无法调用主模块和其他业务模块的类和方法,那么业务间Activity的跳转就需要使用隐式跳转:

  • 直接跳转包名:startActivity(new Intent(“com.example.boost”))
  • 使用manifest中的action、category、data进行隐式跳转:startActivity(new Intent(Intent.ACTION_VIEW, Uri.prase("xxxx")))
  • 利用反射

以上这些方法,虽然都能实现不同模块间的Activity跳转。但是,隐式跳转容易被非法应用劫持,反射最大的弊端在于代码结构发生变化后,就需要修改相应的反射路径。

目前比较推荐的方法就是使用一套统一的路由框架,所有的业务模块、主模块都依赖于这个路由模块,路由模块中包含所有Activity的引用,这样,每个模块都可以互相调用不同的模块的Activity。

路由框架


搭建路由模块

路由模块需要包含所有Activity类的引用,并能进行跳转,所有需要建立一个Android Library。

20191006191058

Route路由类的设计:

  • Route类作为路由需要在每个模块中可以直接调用,那最方便的方法就是使用单例模式
  • Route中要包含所有Activity的引用,那么就需要使用一个map将Activity和对应的名称关联起来
  • 在使用路由进行页面跳转的时候需要有一个方法,方法要传入待跳转的Activity的名称和需要的参数
  • Route需要一个方法,将Activity的引用和对应的名称传入路由中

具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
package com.example.route;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import dalvik.system.DexFile;

/**
* @Description: 路由
* @Author: jiazhu
* @Date: 2019-09-19 09:29
* @ClassName: Route
*/
public class Route {
private static Route route = new Route();

private Context mContext;
private Map<String, Class<? extends Activity>> activityList;

private Route() {
activityList = new HashMap<>();
}

public static Route getInstance() {
return route;
}

public void init(Application application) {
mContext = application;
List<String> classNames = getClassName("com.example.util");
for (String s : classNames) {
try {
Class<?> aClass = Class.forName(s);
if (IRoute.class.isAssignableFrom(aClass)) {
IRoute iRoute = (IRoute) aClass.newInstance();
iRoute.putActivity();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

private List<String> getClassName(String packageName) {
ArrayList<String> classList = new ArrayList<>();
String path = null;
try {
path = mContext.getPackageManager().getApplicationInfo(mContext.getPackageName(), 0).sourceDir;
DexFile dexFile = new DexFile(path);
Enumeration<String> entries = dexFile.entries();
while (entries.hasMoreElements()) {
String name = entries.nextElement();
if (name.contains(packageName)) {
classList.add(name);
}
}
return classList;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

public void putActivity(String path, Class<? extends Activity> clazz) {
if (TextUtils.isEmpty(path)) {
return;
}
if (clazz == null) {
return;
}
activityList.put(path, clazz);
}

public void jumpActivity(String path, Bundle bundle) {
Class<? extends Activity> aClass = activityList.get(path);
if (aClass == null) {
return;
}
Intent intent = new Intent().setClass(mContext, aClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (bundle != null) {
intent.putExtra("bundle", bundle);
}
mContext.startActivity(intent);
}
}

对于init方法,在整个应用启动的时候需要被调用,这样可以获取Application作为以后跳转的context上下文;除此之外,还需要将每一个模块中的Activity获取到,传入Route中。

Route中有一个putActivity()的方法,这个方法可以将Activity的引用和名称传入Route中,那么在每一个模块中如何才能调用这个方法,将本模块的Activity传递给Route呢?


使用APT(注解处理工具)将每一个模块的Activity传入Route路由

APT全名:Annotation Processiong Tool。

使用APT需要有两个部分组成,一个是注解,另一个是处理注解。

1. 注解

这里不需要Android相关的内容,所有新建一个Java Library:

20191006193602

这个模块中只需要创建注解类。

一个注解类主要有以下几个部分构成:

  • @Target()指定这个注解是针对哪类数据进行注解:TYPE:类;FIELD:元素;METHOD:方法;PARAMETER:参数…
  • @Retention()指定这个注解的生效阶段:SOURCE:在源码阶段有效;CLASS:在编译期有效;RUNTIME:在运行时有效
  • @interface xxx{}注解类名称
  • 注解的方法

针对路由框架,实现的注解如下:

1
2
3
4
5
6
7
8
/**
* @author jiazhu
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface BindPath {
String value();
}

可以看出:

  1. 这个注解类是针对类进行注解的
  2. 注解在编译期有效
  3. 注解BindPath中有一个方法:value(),这个方法获取一个String类型值

BindPath获取的值作为Activity的名称,Activity的引用可以在后面通过注解处理器进行获取。

2.处理注解

处理注解也是不需要Android相关的内容,所有新建一个Java Library。

做一个注解处理器的类,需要集成AbstractProcessor注解处理器抽象类。

在处理注解之前,需要实现三个方法:

  • 初始化这个注解处理器,并拿到一个创建文件的对象:

    1
    2
    3
    4
    5
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
    super.init(processingEnvironment);
    mFiler = processingEnvironment.getFiler();
    }
  • 配置需要处理的注解类

    1
    2
    3
    4
    5
    6
    @Override
    public Set<String> getSupportedAnnotationTypes() {
    Set<String> types = new HashSet<>();
    types.add(BindPath.class.getCanonicalName());
    return types;
    }
  • 设置支持的代码版本

    1
    2
    3
    4
    @Override
    public SourceVersion getSupportedSourceVersion() {
    return processingEnv.getSourceVersion();
    }

然后需要重写父类的process方法,实现注解的处理。处理的步骤如下:

  1. 获取需要处理的被注解标注的对象,并获取对象的注解
  2. 将对象的名称和注解方法返回的值提取出来(对象名称就是Activity,注解方法的返回值就是Activity的名称)
  3. 通过Filer创建每个模块对应的添加路由的类,并用Writer写入刚才通过注解获取的Activity和名称。

最终代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* @author jiazhu
*/
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
Filer mFiler;

@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
}

@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindPath.class.getCanonicalName());
return types;
}

@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}

@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindPath.class);
HashMap<String, String> map = new HashMap<>();
String packageName = null;
for (Element element : elements) {
if (element instanceof TypeElement) {
TypeElement typeElement = (TypeElement) element;
BindPath annotation = typeElement.getAnnotation(BindPath.class);
String path = annotation.value();
String activityName = typeElement.getQualifiedName().toString();
if (packageName == null) {
packageName = activityName.substring(0, activityName.lastIndexOf("."));
packageName = packageName.replace('.', '_');
}
map.put(path, activityName);
}
}

if (map.size() > 0) {

Writer writer = null;
String utilName = "ActivityUtil_" + packageName;
try {
JavaFileObject javaFileObject = mFiler.createSourceFile("com.example.util." + utilName);
writer = javaFileObject.openWriter();
Iterator<String> iterator = map.keySet().iterator();
writer.write("package com.example.util;\n" +
"\n" +
"import com.example.route.IRoute;\n" +
"import com.example.route.Route;\n" +
"\n" +
"public class " + utilName + " implements IRoute {\n" +
" @Override\n" +
" public void putActivity() {\n");
while (iterator.hasNext()) {
String path = iterator.next();
String value = map.get(path);
writer.write("Route.getInstance().putActivity(\"" + path + "\"," + value + ".class);\n");
}

writer.write(" }\n" +
"}\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

return false;
}
}

通过注解,将各个模块的Activity添加注解,然后对代码进行一次编译。

在每一个模块生成的代码中会多出来一个类,这个类会将这个模块中的所有已注解的Activity和它们的名称传入Route类中。

当我们跳转某个模块的Activity的时候,只需要如下代码:

1
Route.getInstance().jumpActivity( "mapp/main", null);

JNI层动态注册Native方法

发表于 2019-10-02 | 分类于 Android

Android中,通常创建native的方法是在java层写一个native的方法声明,然后通过Alt+enter进行自动生成jni的方法,或者在命令行中使用javah生成头问题然后对照头问题的函数名,在c文件中创建函数。这种属于静态注册方法,虽然在AS中使用非常方法,但是有一下几个弊端:

  1. 在调用该方法时,有额外的查找方法的性能开销
  2. 在后期需要重构代码时,jni中的函数名需要手动修改,比较麻烦
  3. 在程序运行时,无法动态更换

所以,在NDK开发时,可以使用动态注册Native方法来规避上面的问题。

动态注册方法是通过jni中的JNI_OnLoad方法实现的:

1
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);

JNI_OnLoad()方法在虚拟机加载C库是被执行(当System.loadLibrary("native-lib")时)。

在JNI_OnLoad()中使用

1
2
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
jint nMethods)

对native方法进行注册。RegisterNatives()中需要三个参数:

  • clazz指的是native方法所在的类,可以使用env->FindClass()获取;

    1
    jclass clazz = env->FindClass("com/example/jni/MainActivity");
  • methods指的是需要动态注册的方法(这是一个指向数组的指针),JNINativeMethod是一个结构体:

    1
    2
    3
    4
    5
    typedef struct {
    const char* name;
    const char* signature;
    void* fnPtr;
    } JNINativeMethod;
    • name是java层的方法名;
    • signature是方法的出参入参的类型
    • fnPtr是jni层的函数指针
  • nMethods指的是需要动态注册的方法的个数,就是methods的长度:sizeof(g_methods) / sizeof(g_methods[0])


最终的实现:

Java层:

1
public native String _test();

jni层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
extern "C" JNIEXPORT jstring JNICALL
my_jni_test(JNIEnv *env, jobject instance) {
return env->NewStringUTF("this is a method");
}

static JNINativeMethod g_methods[] = {
{
"_test",
"()Ljava/lang/String;",
(void *) my_jni_test
},
};

jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
vm->GetEnv((void **) &env, JNI_VERSION_1_6);

jclass clazz = env->FindClass("com/example/jni/MainActivity");

env->RegisterNatives(clazz, g_methods, sizeof(g_methods) / sizeof(g_methods[0]));
return JNI_VERSION_1_6;
}

FFmpeg抽取视频数据

发表于 2019-09-22 | 分类于 音视频

SPS/PPS在ffmpeg的codec->extradata中获取

  1. 加载ffmpeg的日志模块

    1
    2
    3
    #include <libavutil/log.h>
    ...
    av_log_set_level(AV_LOG_DEBUG);
  2. 注册所有编解码器

    1
    2
    /*register all formats and codec*/
    av_register_all();
  3. 打开视频文件并获取视频文件上下文

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    AVFormatContext *fmt_ctx = NULL;

    /*open input media file, and allocate format context*/
    if((err_code = avformat_open_input(&fmt_ctx, src_filename, NULL, NULL)) < 0){
    av_strerror(err_code, errors, 1024);
    av_log(NULL, AV_LOG_DEBUG, "Could not open source file: %s, %d(%s)\n",
    src_filename,
    err_code,
    errors);
    return -1;
    }
  4. 打印视频详细信息

    1
    2
    /*dump input information*/
    av_dump_format(fmt_ctx, 0, src_filename, 0);
  5. 生成一个空的数据包,用于接收视频包

    1
    2
    3
    4
    /*initialize packet*/
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
  6. 获取视频流

    1
    2
    3
    4
    5
    6
    7
    /*find best video stream*/
    video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_stream_index < 0)
    {
    av_log(NULL, AV_LOG_ERROR, "Could not find %s stream in input file %s\n", av_get_media_type_string(AVMEDIA_TYPE_VIDEO),src_filename);
    return AVERROR(EINVAL);
    }
  7. 循环读取视频流中的包,进行处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /*read frames from media file*/
    while (av_read_frame(fmt_ctx, &pkt) >= 0)
    {
    if (pkt.stream_index == video_stream_index)
    {
    h264_mp4toannexb(fmt_ctx, &pkt, dst_fd);
    }
    //release pkt->data
    av_packet_unref(&pkt);
    }
    1. 对包进行处理,提取帧数据进行写文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      int h264_mp4toannexb(AVFormatContext *fmt_ctx, AVPacket *in, FILE *dst_fd)
      {

      AVPacket *out = NULL;
      AVPacket spspps_pkt;

      int len;
      uint8_t unit_type;
      int32_t nal_size;
      uint32_t cumul_size = 0;
      const uint8_t *buf;
      const uint8_t *buf_end;
      int buf_size;
      int ret = 0, i;

      out = av_packet_alloc();

      buf = in->data;
      buf_size = in->size;
      buf_end = in->data + in->size;

      do
      {
      ret = AVERROR(EINVAL);
      if (buf + 4 /*s->length_size*/ > buf_end)
      goto fail;
      //一个AVPacket中存的可能是一帧也可能是多帧
      // nal_size是一个帧的具体大小,在帧的头部的前4个字节
      for (nal_size = 0, i = 0; i < 4 /*s->length_size*/; i++)
      nal_size = (nal_size << 8) | buf[i];

      buf += 4; /*s->length_size;*/
      //帧数据的第一个字节的后5位是这个帧的nal单元
      /* nal: 7 -> sps
      8 -> pps
      key frame -> 5
      normal frame -> 1 */
      unit_type = *buf & 0x1f;

      if (nal_size > buf_end - buf || nal_size < 0)
      goto fail;
      /* prepend only to the first type 5 NAL unit of an IDR picture, if no sps/pps are already present */
      if (/*s->new_idr && */ unit_type == 5 /*&& !s->idr_sps_seen && !s->idr_pps_seen*/)
      {
      // 关键帧,需要获取sps/pps数据,并重新修改视频宽高比等参数
      h264_extradata_to_annexb(fmt_ctx->streams[in->stream_index]->codec->extradata,
      fmt_ctx->streams[in->stream_index]->codec->extradata_size,
      &spspps_pkt,
      AV_INPUT_BUFFER_PADDING_SIZE);
      // 为数据增加特征码
      if ((ret = alloc_and_copy(out,
      spspps_pkt.data, spspps_pkt.size,
      buf, nal_size)) < 0)
      goto fail;
      /*s->new_idr = 0;*/
      /* if only SPS has been seen, also insert PPS */
      }
      else
      {
      if ((ret = alloc_and_copy(out, NULL, 0, buf, nal_size)) < 0)
      goto fail;
      }

      len = fwrite(out->data, 1, out->size, dst_fd);
      if (len != out->size)
      {
      av_log(NULL, AV_LOG_DEBUG, "warning, length of writed data isn't equal pkt.size(%d, %d)\n",
      len,
      out->size);
      }
      fflush(dst_fd);

      next_nal:
      buf += nal_size;
      cumul_size += nal_size + 4; //s->length_size;
      } while (cumul_size < buf_size);
      fail:
      av_packet_free(&out);

      return ret;
      }
  1. 获取SPS/PPS数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    int h264_extradata_to_annexb(const uint8_t *codec_extradata, const int codec_extradata_size, AVPacket *out_extradata, int padding)
    {
    uint16_t unit_size;
    uint64_t total_size = 0;
    uint8_t *out = NULL, unit_nb, sps_done = 0,
    sps_seen = 0, pps_seen = 0, sps_offset = 0, pps_offset = 0;
    const uint8_t *extradata = codec_extradata + 4;
    static const uint8_t nalu_header[4] = {0, 0, 0, 1};
    int length_size = (*extradata++ & 0x3) + 1; // retrieve length coded size, 用于指示表示编码数据长度所需字节数

    sps_offset = pps_offset = -1;

    /* retrieve sps and pps unit(s) */
    unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
    if (!unit_nb)
    {
    goto pps;
    }
    else
    {
    sps_offset = 0;
    sps_seen = 1;
    }

    while (unit_nb--)
    {
    int err;

    unit_size = AV_RB16(extradata);
    total_size += unit_size + 4;
    if (total_size > INT_MAX - padding)
    {
    av_log(NULL, AV_LOG_ERROR,
    "Too big extradata size, corrupted stream or invalid MP4/AVCC bitstream\n");
    av_free(out);
    return AVERROR(EINVAL);
    }
    if (extradata + 2 + unit_size > codec_extradata + codec_extradata_size)
    {
    av_log(NULL, AV_LOG_ERROR, "Packet header is not contained in global extradata, "
    "corrupted stream or invalid MP4/AVCC bitstream\n");
    av_free(out);
    return AVERROR(EINVAL);
    }
    if ((err = av_reallocp(&out, total_size + padding)) < 0)
    return err;
    memcpy(out + total_size - unit_size - 4, nalu_header, 4);
    memcpy(out + total_size - unit_size, extradata + 2, unit_size);
    extradata += 2 + unit_size;
    pps:
    if (!unit_nb && !sps_done++)
    {
    unit_nb = *extradata++; /* number of pps unit(s) */
    if (unit_nb)
    {
    pps_offset = total_size;
    pps_seen = 1;
    }
    }
    }

    if (out)
    memset(out + total_size, 0, padding);

    if (!sps_seen)
    av_log(NULL, AV_LOG_WARNING,
    "Warning: SPS NALU missing or invalid. "
    "The resulting stream may not play.\n");

    if (!pps_seen)
    av_log(NULL, AV_LOG_WARNING,
    "Warning: PPS NALU missing or invalid. "
    "The resulting stream may not play.\n");

    out_extradata->data = out;
    out_extradata->size = total_size;

    return length_size;
    }
  1. 为每一帧数据增加特征码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    static int alloc_and_copy(AVPacket *out,
    const uint8_t *sps_pps, uint32_t sps_pps_size,
    const uint8_t *in, uint32_t in_size)
    {
    uint32_t offset = out->size;
    uint8_t nal_header_size = offset ? 3 : 4;
    int err;

    err = av_grow_packet(out, sps_pps_size + in_size + nal_header_size);
    if (err < 0)
    return err;

    if (sps_pps)
    memcpy(out->data + offset, sps_pps, sps_pps_size);
    memcpy(out->data + sps_pps_size + nal_header_size + offset, in, in_size);
    if (!offset)
    {
    AV_WB32(out->data + sps_pps_size, 1);
    }
    else
    {
    (out->data + offset + sps_pps_size)[0] =
    (out->data + offset + sps_pps_size)[1] = 0;
    (out->data + offset + sps_pps_size)[2] = 1;
    }

    return 0;
    }
12…5

Zhujiaqqq

Java Android Python Algorithm and Machine learning

43 日志
6 分类
55 标签
© 2019 Zhujiaqqq
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4