ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android Jetpack 之 Lifecycle - 2

2021-06-18 21:03:55  阅读:222  来源: 互联网

标签:return LifecycleOwner Jetpack event LifecycleObserver klass Android Lifecycle


原文:https://juejin.cn/post/6974737636330045454

源码分析

生命周期 (Lifecycle )

public abstract class Lifecycle {
    /**
     * Adds a LifecycleObserver that will be notified when the LifecycleOwner changes
     * state.
     * <p>
     * The given observer will be brought to the current state of the LifecycleOwner.
     * For example, if the LifecycleOwner is in {@link State#STARTED} state, the given observer
     * will receive {@link Event#ON_CREATE}, {@link Event#ON_START} events.
     *
     * @param observer The observer to notify.
     */
    @MainThread
    public abstract void addObserver(@NonNull LifecycleObserver observer);

    /**
     * Removes the given observer from the observers list.
     * <p>
     * If this method is called while a state change is being dispatched,
     * <ul>
     * <li>If the given observer has not yet received that event, it will not receive it.
     * <li>If the given observer has more than 1 method that observes the currently dispatched
     * event and at least one of them received the event, all of them will receive the event and
     * the removal will happen afterwards.
     * </ul>
     *
     * @param observer The observer to be removed.
     */
    @MainThread
    public abstract void removeObserver(@NonNull LifecycleObserver observer);

    /**
     * Returns the current state of the Lifecycle.
     *
     * @return The current state of the Lifecycle.
     */
    @MainThread
    @NonNull
    public abstract State getCurrentState();

    // Event 对应 activity 的各个生命周期
	//ON_ANY事件可以代表前面任意一种。
	//举个例子,当Activity的onCreate()生命周期方法被调用时会产生ON_CREATE事件,观察者可以监听该事件以便处理Activity此时的生命周期。
    @SuppressWarnings("WeakerAccess")
    public enum Event {
       ON_CREATE,    //用于匹配生命周期所有者的onCreate事件.
       ON_START,     //用于匹配生命周期所有者的onStart事件.
       ON_RESUME,    //用于匹配生命周期所有者的onResume事件.
       ON_PAUSE,     //用于匹配生命周期所有者的onCreate事件.
       ON_STOP,      //用于匹配生命周期所有者的onStop事件.
       ON_DESTROY,   //用于匹配生命周期所有者的onDestroy事件.
       ON_ANY        //用于匹配生命周期所有者的所有事件.

    }

    /**
     * Lifecycle states. You can consider the states as the nodes in a graph and
     * {@link Event}s as the edges between these nodes.
     */
    @SuppressWarnings("WeakerAccess")
    public enum State {
        /**
         * Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
         * any more events. For instance, for an {@link android.app.Activity}, this state is reached
         * <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
         */
        DESTROYED,

        /**
         * Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
         * the state when it is constructed but has not received
         * {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
         */
        INITIALIZED,

        /**
         * Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached in two cases:
         * <ul>
         *     <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
         *     <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
         * </ul>
         */
        CREATED,

        /**
         * Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached in two cases:
         * <ul>
         *     <li>after {@link android.app.Activity#onStart() onStart} call;
         *     <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
         * </ul>
         */
        STARTED,

        /**
         * Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
         * is reached after {@link android.app.Activity#onResume() onResume} is called.
         */
        RESUMED;

        /**
         * Compares if this State is greater or equal to the given {@code state}.
         *
         * @param state State to compare with
         * @return true if this State is greater or equal to the given {@code state}
         */
        public boolean isAtLeast(@NonNull State state) {
            return compareTo(state) >= 0;
        }
    }
}

Lifecycle 类表示Android应用组件的生命周期,是被观察者。这是一个抽象类,它的实现是 LifecycleRegistry 类。

Lifecycle 对象有3个方法:

  • 添加观察者:void addObserver(LifecycleObserver observer)
  • 删除观察者:void removeObserver(LifecycleObserver observer)。-
  • 获取当前状态:State getCurrentState()

Lifecycle的2个关键方法中参数都是LifecycleObserver

所以在分析 LifecycleRegistry之前我们先看下LifecycleObserver

生命周期观察者 (LifecycleObserver)

LifecycleObserver 基本介绍

标记接口 LifecycleObserver 表示生命周期观察者,是 lifecycle-aware 组件。

public interface LifecycleObserver {

}

这个接口用来声明它的实现类是生命周期观察者 ,我们要在这个类里接收到LifecycleOwner的生命周期变化事件,然后做自身逻辑。

但当我们看到LifecycleObserver这个接口时,第一感觉是比较奇怪:这个接口竟然是空的。那我们如何去接受生命周期变化的事件呢?(通常正常的是回调接口)

其实开头就介绍过用法了,google的做法是配合注解 OnLifecycleEvent 使用,LifecycleObserver 只是起一个声明和约束作用。其内部继承数如下图:

看到这么多类似的接口,我猜或许这是因为生命周期钩子比较多,如果全都放在接口里,那么实现者需要添加很多空实现方法,可阅读性降低。

比如FullLifecycleObserver

interface FullLifecycleObserver extends LifecycleObserver {

    void onCreate(LifecycleOwner owner);

    void onStart(LifecycleOwner owner);

    void onResume(LifecycleOwner owner);

    void onPause(LifecycleOwner owner);

    void onStop(LifecycleOwner owner);

    void onDestroy(LifecycleOwner owner);
}

这个接口就将所有的生命周期函数声明了,如果我们实现这个接口,就得重写所有接口,然而我们不一定要用这么多接口。(其实你想用也用不了,这个接口不是public,我们不能实现)

我们知道用注解的话大多数是反射解析的,这就涉及到性能问题,当然使用apt可以在编译期解决这个问题。这点后文会详细分析。

如果我们不想用注解,又不想一下子实现所有的方法,可以吗? 答案是肯定的。java8的接口default方法。而且google也鼓励我们用这种方式,那就是DefaultLifecycleObserver

DefaultLifecycleObserver

注意:在androidx.lifecycle.Lifecycle类的顶部注释里有一段

/**
 * If you use <b>Java 8 Language</b>, then observe events with {@link DefaultLifecycleObserver}.
 * To include it you should add {@code "androidx.lifecycle:common-java8:<version>"} to your
 * build.gradle file.
 * <pre>
 * class TestObserver implements DefaultLifecycleObserver {
 *     {@literal @}Override
 *     public void onCreate(LifecycleOwner owner) {
 *         // your code
 *     }
 * }
 * </pre>
 * If you use <b>Java 7 Language</b>, Lifecycle events are observed using annotations.
 * Once Java 8 Language becomes mainstream on Android, annotations will be deprecated, so between
 * {@link DefaultLifecycleObserver} and annotations,
 * you must always prefer {@code DefaultLifecycleObserver}.
 * <pre>
 * class TestObserver implements LifecycleObserver {
 *   {@literal @}OnLifecycleEvent(ON_STOP)
 *   void onStopped() {}
 * }
 * </pre>
 * <p>
 * Observer methods can receive zero or one argument.
 * If used, the first argument must be of type {@link LifecycleOwner}.
 * Methods annotated with {@link Event#ON_ANY} can receive the second argument, which must be
 * of type {@link Event}.
 * <pre>
 * class TestObserver implements LifecycleObserver {
 *   {@literal @}OnLifecycleEvent(ON_CREATE)
 *   void onCreated(LifecycleOwner source) {}
 *   {@literal @}OnLifecycleEvent(ON_ANY)
 *   void onAny(LifecycleOwner source, Event event) {}
 * }
 * </pre>
 * These additional parameters are provided to allow you to conveniently observe multiple providers
 * and events without tracking them manually.
 */
public abstract class Lifecycle {
}

意思就是一旦Java 8成为Android的主流,注解将被弃用,所以介于DefaultLifecycleObserver和注解两者之间,更推荐使用 DefaultLifecycleObserver

使用方式如下

implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"

这个库里其实就一个 DefaultLifecycleObserver.java 接口类。之后我们需要 LifecycleObserver 的时候,一般实现 DefaultLifecycleObserver 接口即可(不用再去直接实现 LifecycleObserver 接口),使用方式变成了下面这样:

class NormalActivity : Activity(), LifecycleOwner {

    private var lifecycleRegistry: LifecycleRegistry = LifecycleRegistry(this)

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_normal)

        lifecycle.addObserver(object : DefaultLifecycleObserver {
            //下面的方法视自身情况选择实现
            override fun onCreate(owner: LifecycleOwner) {
            }

            override fun onStart(owner: LifecycleOwner) {
            }

            override fun onResume(owner: LifecycleOwner) {
            }

            override fun onPause(owner: LifecycleOwner) {
            }

            override fun onStop(owner: LifecycleOwner) {
            }

            override fun onDestroy(owner: LifecycleOwner) {
            }
        })

    }
}

可以看到,这里我们直接在覆写我们需要的生命周期对应回调方法中写入我们的逻辑代码即可。更加简洁。

LifecycleObserver 之间的转换

我们在使用 LifecycleObserver时,框架内部会将其转换为GenericLifecycleObserver 或其子类。 其转换行为在类 androidx.lifecycle.Lifecycling#getCallback 中。至于这个方法何时触发,我们后续会分析。

现在先看下这个转换过程

@NonNull
    static GenericLifecycleObserver getCallback(Object object) {
         // 如果是 FullLifecycleObserver, 转换为 FullLifecycleObserverAdapter
        if (object instanceof FullLifecycleObserver) {
            return new FullLifecycleObserverAdapter((FullLifecycleObserver) object);
        }

        // 如果是 GenericLifecycleObserver, 不转换
        if (object instanceof GenericLifecycleObserver) {
            return (GenericLifecycleObserver) object;
        }

        final Class<?> klass = object.getClass();
         // 获取转换type, GENERATED_CALLBACK 为使用代码生成,REFLECTIVE_CALLBACK 为使用反射调用
        int type = getObserverConstructorType(klass);
        if (type == GENERATED_CALLBACK) {
            // 采用代码生成
            List<Constructor<? extends GeneratedAdapter>> constructors =
                    sClassToAdapters.get(klass);
            // 一个构造函数
            if (constructors.size() == 1) {
                GeneratedAdapter generatedAdapter = createGeneratedAdapter(
                        constructors.get(0), object);
                return new SingleGeneratedAdapterObserver(generatedAdapter);
            }
            // 多个构造函数
            GeneratedAdapter[] adapters = new GeneratedAdapter[constructors.size()];
            for (int i = 0; i < constructors.size(); i++) {
                adapters[i] = createGeneratedAdapter(constructors.get(i), object);
            }
            return new CompositeGeneratedAdaptersObserver(adapters);
        }
        // 默认采用反射调用
        return new ReflectiveGenericLifecycleObserver(object);
    }

这个方法的本质,其实就是根据传进来的一个LifecycleObserver 对象,构造出来一个 GenericLifecycleObserver 对象(目前有四个子类:FullLifecycleObserverAdapterSingleGeneratedAdapterObserverCompositeGeneratedAdaptersObserverReflectiveGenericLifecycleObserver),而最终构造出来的对象,就包含了我们创建的 LifecycleObserver 的所有信息,包括各种回调方法等。

当我们通过注解的方式来自定义LifecycleObserver 的时候,按照传统方式,必定要通过反射来对注解进行解析,这样就会对性能造成影响。那么如何优化呢?

  • 通过缓存,来避免每次都通过反射获取构造器。

  • 通过注解处理器,在编译时对那些被@OnLifecycleEvent注解标注的普通方法,进行预处理,生成以“类名_LifecycleAdapter”命名的类,将各种回调方法直接进行逻辑转换,避免反射,进而来提高性能。

我们先看下仅用反射的情况下google是如何优化的

不引入注解处理器时,Lifecycling.getCallback(observer)返回的是ReflectiveGenericLifecycleObserver

package androidx.lifecycle;
class ReflectiveGenericLifecycleObserver implements GenericLifecycleObserver {
    private final Object mWrapped;
    private final CallbackInfo mInfo;

    ReflectiveGenericLifecycleObserver(Object wrapped) {
        mWrapped = wrapped;
        mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Event event) {
        mInfo.invokeCallbacks(source, event, mWrapped);
    }
}

这里我们主要关注回调信息 CallbackInfo 的获取方式的代码:

mInfo = ClassesInfoCache.sInstance.getInfo(mWrapped.getClass());

因为反射的代价是比较大的,所以又通过 ClassesInfoCache.java这个单例类,为 ReflectiveGenericLifecycleObserver 类要调用的各种方法的相关信息进行了缓存。

点进去看下它的 getInfo(...) 方法内部,是如何获取方法信息的。

CallbackInfo getInfo(Class klass) {
     	// 先读取缓存
        CallbackInfo existing = mCallbackMap.get(klass);
        if (existing != null) {
            return existing;
        }
        existing = createInfo(klass, null);
        return existing;
    }
private CallbackInfo createInfo(Class klass, @Nullable Method[] declaredMethods) {
    	// 先提取父类的 CallbackInfo    
    	Class superclass = klass.getSuperclass();
        Map<MethodReference, Lifecycle.Event> handlerToEvent = new HashMap<>();
        if (superclass != null) {
            CallbackInfo superInfo = getInfo(superclass);
            if (superInfo != null) {
                handlerToEvent.putAll(superInfo.mHandlerToEvent);
            }
        }

    	// 再提取接口的 CallbackInfo
        Class[] interfaces = klass.getInterfaces();
        for (Class intrfc : interfaces) {
            for (Map.Entry<MethodReference, Lifecycle.Event> entry : getInfo(
                    intrfc).mHandlerToEvent.entrySet()) {
                // verifyAndPutHandler 的作用是实现了接口或者覆写了父类的方法,但是添加了不同的注解事件。
                verifyAndPutHandler(handlerToEvent, entry.getKey(), entry.getValue(), klass);
            }
        }

		// 最后处理类自身的注解
        Method[] methods = declaredMethods != null ? declaredMethods : getDeclaredMethods(klass);
        boolean hasLifecycleMethods = false;
    	// 遍历方法,寻找被 OnLifecycleEvent 注解的方法
        for (Method method : methods) {
            OnLifecycleEvent annotation = method.getAnnotation(OnLifecycleEvent.class);
            if (annotation == null) {
                continue;
            }
            hasLifecycleMethods = true;
            // 处理参数个数,最多两个参数
            Class<?>[] params = method.getParameterTypes();
            int callType = CALL_TYPE_NO_ARG;
            if (params.length > 0) {
                callType = CALL_TYPE_PROVIDER;
                if (!params[0].isAssignableFrom(LifecycleOwner.class)) {
                    throw new IllegalArgumentException(
                            "invalid parameter type. Must be one and instanceof LifecycleOwner");
                }
            }
            Lifecycle.Event event = annotation.value();

            if (params.length > 1) {
                callType = CALL_TYPE_PROVIDER_WITH_EVENT;
                if (!params[1].isAssignableFrom(Lifecycle.Event.class)) {
                    throw new IllegalArgumentException(
                            "invalid parameter type. second arg must be an event");
                }
                if (event != Lifecycle.Event.ON_ANY) {
                    throw new IllegalArgumentException(
                            "Second arg is supported only for ON_ANY value");
                }
            }
            if (params.length > 2) {
                throw new IllegalArgumentException("cannot have more than 2 params");
            }
            MethodReference methodReference = new MethodReference(callType, method);
            verifyAndPutHandler(handlerToEvent, methodReference, event, klass);
        }
        CallbackInfo info = new CallbackInfo(handlerToEvent);
    	// 放入缓存中
        mCallbackMap.put(klass, info);
        mHasLifecycleMethods.put(klass, hasLifecycleMethods);
        return info;
    }

这里,就能看到对注解进行处理的代码了。

我们再看下另一种优化(使用注解处理器)

首先需要在项目模块中引入

annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0"
复制代码

引入这个之后,会自动生成xxx_LifecycleAdapter的文件,路径为

/app/build/generated/source/apt/debug/package_name/LifeObserveCustomName_LifecycleAdapter.java

我们先看下效果:

public class LifecyclePresenter implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void onCreateInPresenter(@NotNull LifecycleOwner owner){
        System.out.println("on create");
    };

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    void onDestroyInPresenter(@NotNull LifecycleOwner owner){
        System.out.println("on destory");
    };

    @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
    void onLifecycleChangedInPresenter(@NotNull LifecycleOwner owner,
                            @NotNull Lifecycle.Event event){
        System.out.println("on any");
    }
}

上面的代码在引入注解处理器后,编译项目时就会在build目录下自动生成LifecyclePresenter_LifecycleAdapter

public class LifecyclePresenter_LifecycleAdapter implements GeneratedAdapter {
  final LifecyclePresenter mReceiver;

  LifecyclePresenter_LifecycleAdapter(LifecyclePresenter receiver) {
    this.mReceiver = receiver;
  }

  @Override
  public void callMethods(LifecycleOwner owner, Lifecycle.Event event, boolean onAny,
      MethodCallsLogger logger) {
    boolean hasLogger = logger != null;
    if (onAny) {
      if (!hasLogger || logger.approveCall("onLifecycleChangedInPresenter", 4)) {
        mReceiver.onLifecycleChangedInPresenter(owner,event);
      }
      return;
    }
    if (event == Lifecycle.Event.ON_CREATE) {
      if (!hasLogger || logger.approveCall("onCreateInPresenter", 2)) {
        mReceiver.onCreateInPresenter(owner);
      }
      return;
    }
    if (event == Lifecycle.Event.ON_DESTROY) {
      if (!hasLogger || logger.approveCall("onDestroyInPresenter", 2)) {
        mReceiver.onDestroyInPresenter(owner);
      }
      return;
    }
  }
}

这样通过该依赖库,在编译期把这些标注了的方法进行预处理,然后直接回调这些方法,避免反射,进行提高性能。

我们回头看下Lifecycling.getCallback(observer)方法中是如何判断 是代码生成还是反射调用的代码

 // 获取转换type, GENERATED_CALLBACK 为使用代码生成,REFLECTIVE_CALLBACK 为使用反射调用
 int type = getObserverConstructorType(klass);
 // todo 详细过程暂不分析 下面简要分析下

前面已经分析过,引入注解处理器后,会生成xxx_LifecycleAdapter类,因此如果我们找到这个生成类,则认为采用代码生成方式,否则采取反射调用。

我们看下关键方法

 private static int getObserverConstructorType(Class<?> klass) {
        if (sCallbackCache.containsKey(klass)) {
            return sCallbackCache.get(klass);
        }
        int type = resolveObserverCallbackType(klass);
        sCallbackCache.put(klass, type);
        return type;
    }
private static int resolveObserverCallbackType(Class<?> klass) {
        // anonymous class bug:35073837
        // 匿名内部类采用反射
        if (klass.getCanonicalName() == null) {
            return REFLECTIVE_CALLBACK;
        }
		// 寻找生成类
        Constructor<? extends GeneratedAdapter> constructor = generatedConstructor(klass);
        if (constructor != null) {
            // 找到生成类,则采用 GENERATED_CALLBACK
            sClassToAdapters.put(klass, Collections
                    .<Constructor<? extends GeneratedAdapter>>singletonList(constructor));
            return GENERATED_CALLBACK;
        }

   		// 是否有方法被 OnLifecycleEvent 注解
        boolean hasLifecycleMethods = ClassesInfoCache.sInstance.hasLifecycleMethods(klass);
        if (hasLifecycleMethods) {
            // 如果有方法被 OnLifecycleEvent 注解, 则采用反射
            return REFLECTIVE_CALLBACK;
        }

    // 如果没有找到生成类,也没有方法被 OnLifecycleEvent 注解。 我们需要去看其父类和接口,或许它们被注解,因此这里又递归调用
        Class<?> superclass = klass.getSuperclass();
        List<Constructor<? extends GeneratedAdapter>> adapterConstructors = null;
        // 如果父类实现了 LifecycleObserver
    	if (isLifecycleParent(superclass)) {
            if (getObserverConstructorType(superclass) == REFLECTIVE_CALLBACK) {
                return REFLECTIVE_CALLBACK;
            }
            adapterConstructors = new ArrayList<>(sClassToAdapters.get(superclass));
        }

   			// 如果有接口继承了 LifecycleObserver
        for (Class<?> intrface : klass.getInterfaces()) {
            if (!isLifecycleParent(intrface)) {
                continue;
            }
            if (getObserverConstructorType(intrface) == REFLECTIVE_CALLBACK) {
                return REFLECTIVE_CALLBACK;
            }
            if (adapterConstructors == null) {
                adapterConstructors = new ArrayList<>();
            }
            adapterConstructors.addAll(sClassToAdapters.get(intrface));
        }
        if (adapterConstructors != null) {
            sClassToAdapters.put(klass, adapterConstructors);
            return GENERATED_CALLBACK;
        }

        return REFLECTIVE_CALLBACK;
    }

因此当我们引入注解处理器后,这里将会返回GENERATED_CALLBACK

Lifecycling.getCallback(observer)方法就会把我们的LifecyclePresenter对象构建成一个 SingleGeneratedAdapterObserver对象返回(因为这里只有一个构造器,如果是多个构造器的话返回CompositeGeneratedAdaptersObserver),之后的 mLifecycleObserver.onStateChanged(owner, event);其实调用的就是SingleGeneratedAdapterObserveronStateChanged(owner, event)方法:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class SingleGeneratedAdapterObserver implements GenericLifecycleObserver {

    private final GeneratedAdapter mGeneratedAdapter;

    SingleGeneratedAdapterObserver(GeneratedAdapter generatedAdapter) {
        mGeneratedAdapter = generatedAdapter;
    }

    @Override
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        mGeneratedAdapter.callMethods(source, event, false, null);
        mGeneratedAdapter.callMethods(source, event, true, null);
    }
}

这里面就可以看到,它调用了内部包裹的类的callMethods(...)方法,也就是我们上面提到的LifecyclePresenter_LifecycleAdaptercallMethonds(...)方法。

综上,再看Lifecycling.getCallback(observer)方法就比较容易理解了。

  1. 如果传进来的的参数 object 是 FullLifecycleObserver 类型,就把它构造成FullLifecycleObserverAdapter 对象,并返回
  2. 如果传进来的的参数 object 是GenericLifecycleObserver类型,直接返回该对象
  3. 如果1,2都不满足,就解析该类的的构造器的Type(该类是反射获取的,还是通过注解处理器生成的)。如果是通过注解处理器生成的类来调用回调函数,就返回一个SingleGeneratedAdapterObserver/CompositeGeneratedAdaptersObserver 对象
  4. 如果以上条件都不满足,就通过反射来调用各回调函数。返回一个 ReflectiveGenericLifecycleObserver 对象

标签:return,LifecycleOwner,Jetpack,event,LifecycleObserver,klass,Android,Lifecycle
来源: https://blog.csdn.net/datian1234/article/details/118033744

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有