otto 源码浅析

Android 事件总线OTTO使用说明和源码解析 OTTO是Square推出的库,地址:https://github.com/square/otto OTTO基于Guava项目的Android支持库,如果你在Android程序开发的过程中想要不同的组件之间进行有效的通信可以使用这个库。通过otto库可以。 otto的官方介绍 An enhanced Guava-based event bus with emphasis on Android support.Otto is an event bus designed to decouple differen 阅读详情

目录

otto简单使用说明

Bus的构造方法

Bus的register方法

findAllProducers

dispatchProducerResultToHandler

findAllSubscribers

Bus的post方法

Bus的unregister方法


otto简单使用说明

单例创建Bus类

register用于注册,unregister用于取消注册

post用于发送事件

@Subscribe注解修饰的方法用于接收事件

@Produce注解修饰的方法用于创建并发送事件

 

Bus的构造方法

先看Bus类的成员变量,如下代码,根据给的注释大致能猜测这些成员变量的含义,具体的作用还是等到下面具体代码执行的流程再来看看。

  private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType =
          new ConcurrentHashMap<Class<?>, Set<EventHandler>>();

  private final ConcurrentMap<Class<?>, EventProducer> producersByType =
          new ConcurrentHashMap<Class<?>, EventProducer>();

  private final String identifier;

  private final ThreadEnforcer enforcer;

  private final HandlerFinder handlerFinder;

  private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> eventsToDispatch =
      new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() {
        @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() {
          return new ConcurrentLinkedQueue<EventWithHandler>();
        }
      };

  private final ThreadLocal<Boolean> isDispatching = new ThreadLocal<Boolean>() {
    @Override protected Boolean initialValue() {
      return false;
    }
  };
  public Bus() {
    this(DEFAULT_IDENTIFIER);
  }

  public Bus(String identifier) {
    this(ThreadEnforcer.MAIN, identifier);
  }

  public Bus(ThreadEnforcer enforcer) {
    this(enforcer, DEFAULT_IDENTIFIER);
  }

  public Bus(ThreadEnforcer enforcer, String identifier) {
    this(enforcer, identifier, HandlerFinder.ANNOTATED);
  }

  Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {
    this.enforcer =  enforcer;
    this.identifier = identifier;
    this.handlerFinder = handlerFinder;
  }

可以看到,使用代码配置好的参数,enforcer为ThreadEnforcer.MAIN,handlerFinder为HandlerFinder.ANNOTATED。

先看ThreadEnforcer.MAIN,这应该是判断是否在主线程执行的判断,如果不是,就报出异常。

  ThreadEnforcer MAIN = new ThreadEnforcer() {
    @Override public void enforce(Bus bus) {
      if (Looper.myLooper() != Looper.getMainLooper()) {
        throw new IllegalStateException("Event bus " + bus + " accessed from non-main thread " + Looper.myLooper());
      }
    }
  };

再看HandlerFinder.ANNOTATED,从HandlerFinder这个单词字面意思理解,就是一个用来查找某样事务的工具类。从下面的代码也可以看到,ANNOTATED的findAllProducers和findAllSubscribers方法都交给了AnnotatedHandlerFinder类去处理。

  HandlerFinder ANNOTATED = new HandlerFinder() {
    @Override
    public Map<Class<?>, EventProducer> findAllProducers(Object listener) {
      return AnnotatedHandlerFinder.findAllProducers(listener);
    }

    @Override
    public Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
      return AnnotatedHandlerFinder.findAllSubscribers(listener);
    }
  };

到此为止,Bus类已经创建好了。

 

Bus的register方法

  public void register(Object object) {
    if (object == null) {
      throw new NullPointerException("Object to register must not be null.");
    }
    enforcer.enforce(this);

    // findAllProducers从类中找到所有发送消息的方法
    Map<Class<?>, EventProducer> foundProducers = handlerFinder.findAllProducers(object);
    for (Class<?> type : foundProducers.keySet()) {
      // 此处注意,在使用Produce注解时,在应用里一个返回事件只能出现一次,否则就会报错
      final EventProducer producer = foundProducers.get(type);
      // 据此可知,producersByType是一个key为事件class对象,value为EventProducer的并发集合
      EventProducer previousProducer = producersByType.putIfAbsent(type, producer);
      //checking if the previous producer existed
      if (previousProducer != null) {
        throw new IllegalArgumentException("Producer method for type " + type
          + " found on type " + producer.target.getClass()
          + ", but already registered by type " + previousProducer.target.getClass() + ".");
      }
      // 对于类里面包含了Produce注解的方法,一旦执行register方法,就会发送事件给订阅事件的实例对象
      // 根据事件class对象,找到订阅此对象的Set集合
      Set<EventHandler> handlers = handlersByType.get(type);
      if (handlers != null && !handlers.isEmpty()) {
        for (EventHandler handler : handlers) {
          // 分发事件给每个EventHandler
          dispatchProducerResultToHandler(handler, producer);
        }
      }
    }

    // findAllSubscribers找到所有的订阅者
    // handlersByType是一个key为事件class对象,value为EventHandler集合的并发集合
    Map<Class<?>, Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object);
    for (Class<?> type : foundHandlersMap.keySet()) {
      Set<EventHandler> handlers = handlersByType.get(type);
      if (handlers == null) {
        //concurrent put if absent
        Set<EventHandler> handlersCreation = new CopyOnWriteArraySet<EventHandler>();
        handlers = handlersByType.putIfAbsent(type, handlersCreation);
        if (handlers == null) {
            handlers = handlersCreation;
        }
      }
      final Set<EventHandler> foundHandlers = foundHandlersMap.get(type);
      // 添加进handlersByType
      if (!handlers.addAll(foundHandlers)) {
        throw new IllegalArgumentException("Object already registered.");
      }
    }

    // 遍历foundHandlersMap
    for (Map.Entry<Class<?>, Set<EventHandler>> entry : foundHandlersMap.entrySet()) {
      Class<?> type = entry.getKey();
      EventProducer producer = producersByType.get(type);
      if (producer != null && producer.isValid()) {
        Set<EventHandler> foundHandlers = entry.getValue();
        for (EventHandler foundHandler : foundHandlers) {
          if (!producer.isValid()) {
            break;
          }
          if (foundHandler.isValid()) {
	    // 分发EventProducer的事件给EventHandler,因为它们发送和接收的事件类型相同
            dispatchProducerResultToHandler(foundHandler, producer);
          }
        }
      }
    }
  }

上面的步骤可以理解为三步:

1.findAllProducers -> dispatchProducerResultToHandler,一旦有

2.findAllSubscribers

3.更新订阅对象信息 -> dispatchProducerResultToHandler

其中,handlersByType和producersByType含义如下:

  // handlersByType是一个key为事件class对象,value为EventHandler集合的并发集合
  private final ConcurrentMap<Class<?>, Set<EventHandler>> handlersByType =
          new ConcurrentHashMap<Class<?>, Set<EventHandler>>();

  // producersByType是一个key为事件class对象,value为EventProducer的并发集合
  private final ConcurrentMap<Class<?>, EventProducer> producersByType =
          new ConcurrentHashMap<Class<?>, EventProducer>();

findAllProducers

findAllProducers是AnnotatedHandlerFinder的静态方法

  static Map<Class<?>, EventProducer> findAllProducers(Object listener) {
    final Class<?> listenerClass = listener.getClass();
    Map<Class<?>, EventProducer> handlersInMethod = new HashMap<Class<?>, EventProducer>();

    Map<Class<?>, Method> methods = PRODUCERS_CACHE.get(listenerClass);
    if (null == methods) {
      methods = new HashMap<Class<?>, Method>();
      // 找到包含Produce注解的方法
      loadAnnotatedProducerMethods(listenerClass, methods);
    }
    if (!methods.isEmpty()) {
      for (Map.Entry<Class<?>, Method> e : methods.entrySet()) {
	// 封装一个EventProducer,包含实例对象以及对应的Method方法对象,更新到handlersInMethod中,handlersInMethod的key为Produce注解方法的返回事件class对象
        EventProducer producer = new EventProducer(listener, e.getValue());
        handlersInMethod.put(e.getKey(), producer);
      }
    }

    return handlersInMethod;
  }

  private static void loadAnnotatedProducerMethods(Class<?> listenerClass,
      Map<Class<?>, Method> producerMethods) {
    Map<Class<?>, Set<Method>> subscriberMethods = new HashMap<Class<?>, Set<Method>>();
    // 最终调用loadAnnotatedMethods方法
    loadAnnotatedMethods(listenerClass, producerMethods, subscriberMethods);
  }

  private static void loadAnnotatedSubscriberMethods(Class<?> listenerClass,
      Map<Class<?>, Set<Method>> subscriberMethods) {
    Map<Class<?>, Method> producerMethods = new HashMap<Class<?>, Method>();
    // 最终调用loadAnnotatedMethods方法
    loadAnnotatedMethods(listenerClass, producerMethods, subscriberMethods);
  }

  private static void loadAnnotatedMethods(Class<?> listenerClass,
      Map<Class<?>, Method> producerMethods, Map<Class<?>, Set<Method>> subscriberMethods) {
    // 遍历Method 
    for (Method method : listenerClass.getDeclaredMethods()) {
      // The compiler sometimes creates synthetic bridge methods as part of the
      // type erasure process. As of JDK8 these methods now include the same
      // annotations as the original declarations. They should be ignored for
      // subscribe/produce.
      if (method.isBridge()) {
        continue;
      }
      // Method包含注解Subscribe
      if (method.isAnnotationPresent(Subscribe.class)) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length != 1) {
          throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires "
              + parameterTypes.length + " arguments.  Methods must require a single argument.");
        }

        Class<?> eventType = parameterTypes[0];
        if (eventType.isInterface()) {
          throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
              + " which is an interface.  Subscription must be on a concrete class type.");
        }

        if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
          throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
              + " but is not 'public'.");
        }
	// 保存找到的方法,subscriberMethods是key为事件class对象,value为Method集合的映射
        Set<Method> methods = subscriberMethods.get(eventType);
        if (methods == null) {
          methods = new HashSet<Method>();
          subscriberMethods.put(eventType, methods);
        }
        methods.add(method);
      } else if (method.isAnnotationPresent(Produce.class)) { // Method包含注解Produce
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (parameterTypes.length != 0) {
          throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires "
              + parameterTypes.length + " arguments.  Methods must require zero arguments.");
        }
        if (method.getReturnType() == Void.class) {
          throw new IllegalArgumentException("Method " + method
              + " has a return type of void.  Must declare a non-void type.");
        }

        Class<?> eventType = method.getReturnType();
        if (eventType.isInterface()) {
          throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
              + " which is an interface.  Producers must return a concrete class type.");
        }
        if (eventType.equals(Void.TYPE)) {
          throw new IllegalArgumentException("Method " + method + " has @Produce annotation but has no return type.");
        }

        if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
          throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
              + " but is not 'public'.");
        }

        if (producerMethods.containsKey(eventType)) {
          throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
        }
	// 保存方法
        producerMethods.put(eventType, method);
      }
    }

    // 保存到当前key为订阅类的class对象的并发集合映射上
    PRODUCERS_CACHE.put(listenerClass, producerMethods);
    SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
  }

上述过程主要完成以下工作:

1.找到注解Subscribe和Produce的方法, 更新PRODUCERS_CACHE和SUBSCRIBERS_CACHE

2.返回一个key为事件class对象,value为EventProducer的映射,EventProducer中包含了实例对象和包含注解Produce方法的信息

其中,PRODUCERS_CACHE和SUBSCRIBERS_CACHE含义如下:

  /** Cache event bus producer methods for each class. */
  // 缓存用并发集合,key为类的class对象,value为Map,跟具体的注册实例对象无关,跟类有关
  private static final ConcurrentMap<Class<?>, Map<Class<?>, Method>> PRODUCERS_CACHE =
    new ConcurrentHashMap<Class<?>, Map<Class<?>, Method>>();

  /** Cache event bus subscriber methods for each class. */
  // 缓存用并发集合,key为类的class对象,value为Map,跟具体的注册实例对象无关,跟类有关
  private static final ConcurrentMap<Class<?>, Map<Class<?>, Set<Method>>> SUBSCRIBERS_CACHE =
    new ConcurrentHashMap<Class<?>, Map<Class<?>, Set<Method>>>();

dispatchProducerResultToHandler

Producer产生的事件传给订阅者

  private void dispatchProducerResultToHandler(EventHandler handler, EventProducer producer) {
    Object event = null;
    try {
      // 反射获取需要发送的事件
      event = producer.produceEvent();
    } catch (InvocationTargetException e) {
      throwRuntimeException("Producer " + producer + " threw an exception.", e);
    }
    if (event == null) {
      return;
    }
    dispatch(event, handler);
  }

  protected void dispatch(Object event, EventHandler wrapper) {
    try {
      wrapper.handleEvent(event);
    } catch (InvocationTargetException e) {
      throwRuntimeException(
          "Could not dispatch event: " + event.getClass() + " to handler " + wrapper, e);
    }
  }

EventHandler的handleEvent方法

  public void handleEvent(Object event) throws InvocationTargetException {
    if (!valid) {
      throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events.");
    }
    try {
      method.invoke(target, event);
    } catch (IllegalAccessException e) {
      throw new AssertionError(e);
    } catch (InvocationTargetException e) {
      if (e.getCause() instanceof Error) {
        throw (Error) e.getCause();
      }
      throw e;
    }
  }

findAllSubscribers

findAllSubscribers是AnnotatedHandlerFinder的静态方法,该方法返回的是key为订阅事件class对象,value为EventHandler的集合,其中EventHandler是订阅者实例对象及其方法Method的包装类。

  static Map<Class<?>, Set<EventHandler>> findAllSubscribers(Object listener) {
    Class<?> listenerClass = listener.getClass();
    Map<Class<?>, Set<EventHandler>> handlersInMethod = new HashMap<Class<?>, Set<EventHandler>>();

    Map<Class<?>, Set<Method>> methods = SUBSCRIBERS_CACHE.get(listenerClass);
    if (null == methods) {
      methods = new HashMap<Class<?>, Set<Method>>();
      loadAnnotatedSubscriberMethods(listenerClass, methods);
    }
    if (!methods.isEmpty()) {
      for (Map.Entry<Class<?>, Set<Method>> e : methods.entrySet()) {
        Set<EventHandler> handlers = new HashSet<EventHandler>();
        for (Method m : e.getValue()) {
          // 封装订阅实例和Method对象为EventHandler
          handlers.add(new EventHandler(listener, m));
        }
	// handlersInMethod是key为订阅事件class对象,value为EventHandler的集合,由此,一个类里面可以订阅多个相同事件,这个好像没啥必要
        handlersInMethod.put(e.getKey(), handlers);
      }
    }

    return handlersInMethod;
  }

Bus的post方法

  public void post(Object event) {
    if (event == null) {
      throw new NullPointerException("Event to post must not be null.");
    }
    enforcer.enforce(this);

    // 找到event的class及其父类的class
    Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());

    boolean dispatched = false;
    // 遍历class
    for (Class<?> eventType : dispatchTypes) {
      // 根据事件class对象,找到对应的订阅者包装类集合
      Set<EventHandler> wrappers = getHandlersForEventType(eventType);

      if (wrappers != null && !wrappers.isEmpty()) {
        dispatched = true;
        for (EventHandler wrapper : wrappers) {
	  // 加入到队列中
          enqueueEvent(event, wrapper);
        }
      }
    }

    if (!dispatched && !(event instanceof DeadEvent)) {
      post(new DeadEvent(this, event));
    }
    // 执行队列中的事件任务
    dispatchQueuedEvents();
  }

post方法用于发送事件,具体执行流程大致如下:

1.根据事件Class对象,找到对应的EventHandler订阅者对象

2.将事件对象和EventHandler对象包装成EventWithHandler对象,加入到队列中

3.启动执行这个队列中的任务

 

其他详细代码如下:

flattenHierarchy方法

  Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
    Set<Class<?>> classes = flattenHierarchyCache.get(concreteClass);
    if (classes == null) {
      Set<Class<?>> classesCreation = getClassesFor(concreteClass);
      classes = flattenHierarchyCache.putIfAbsent(concreteClass, classesCreation);
      if (classes == null) {
        classes = classesCreation;
      }
    }

    return classes;
  }

  private final ConcurrentMap<Class<?>, Set<Class<?>>> flattenHierarchyCache =
      new ConcurrentHashMap<Class<?>, Set<Class<?>>>();

  private Set<Class<?>> getClassesFor(Class<?> concreteClass) {
    List<Class<?>> parents = new LinkedList<Class<?>>();
    Set<Class<?>> classes = new HashSet<Class<?>>();

    parents.add(concreteClass);

    while (!parents.isEmpty()) {
      Class<?> clazz = parents.remove(0);
      classes.add(clazz);

      Class<?> parent = clazz.getSuperclass();
      if (parent != null) {
        parents.add(parent);
      }
    }
    return classes;
  }

 getHandlersForEventType方法

  Set<EventHandler> getHandlersForEventType(Class<?> type) {
    return handlersByType.get(type);
  }

enqueueEvent方法

  protected void enqueueEvent(Object event, EventHandler handler) {
    eventsToDispatch.get().offer(new EventWithHandler(event, handler));
  }

  private final ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>> eventsToDispatch =
      new ThreadLocal<ConcurrentLinkedQueue<EventWithHandler>>() {
        @Override protected ConcurrentLinkedQueue<EventWithHandler> initialValue() {
          return new ConcurrentLinkedQueue<EventWithHandler>();
        }
      };

dispatchQueuedEvents方法

  protected void dispatchQueuedEvents() {
    // don't dispatch if we're already dispatching, that would allow reentrancy and out-of-order events. Instead, leave
    // the events to be dispatched after the in-progress dispatch is complete.
    if (isDispatching.get()) {
      return;
    }

    isDispatching.set(true);
    try {
      while (true) {
        EventWithHandler eventWithHandler = eventsToDispatch.get().poll();
        if (eventWithHandler == null) {
          break;
        }

        if (eventWithHandler.handler.isValid()) {
          dispatch(eventWithHandler.event, eventWithHandler.handler);
        }
      }
    } finally {
      isDispatching.set(false);
    }
  }

 

Bus的unregister方法

注销跟该对象相关的信息,防止内存泄漏。

  public void unregister(Object object) {
    if (object == null) {
      throw new NullPointerException("Object to unregister must not be null.");
    }
    enforcer.enforce(this);

    // 找到跟该对象相关的所有EventProducer
    Map<Class<?>, EventProducer> producersInListener = handlerFinder.findAllProducers(object);
    // 遍历EventProducer集合,从producersByType中删除并将删除的EventProducer置为无效
    for (Map.Entry<Class<?>, EventProducer> entry : producersInListener.entrySet()) {
      final Class<?> key = entry.getKey();
      EventProducer producer = getProducerForEventType(key);
      EventProducer value = entry.getValue();

      if (value == null || !value.equals(producer)) {
        throw new IllegalArgumentException(
            "Missing event producer for an annotated method. Is " + object.getClass()
                + " registered?");
      }
      producersByType.remove(key).invalidate();
    }

    // 找到跟该对象相关的所有EventHandler
    Map<Class<?>, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object);
    // 遍历EventHandler集合,从handlersByType中删除并将删除的EventHandler置为无效
    for (Map.Entry<Class<?>, Set<EventHandler>> entry : handlersInListener.entrySet()) {
      Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey());
      Collection<EventHandler> eventMethodsInListener = entry.getValue();

      if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) {
        throw new IllegalArgumentException(
            "Missing event handler for an annotated method. Is " + object.getClass()
                + " registered?");
      }

      for (EventHandler handler : currentHandlers) {
        if (eventMethodsInListener.contains(handler)) {
          handler.invalidate();
        }
      }
      currentHandlers.removeAll(eventMethodsInListener);
    }
  }

 

Otto的使用和源码分析 OttoOtto是square的一个event bus实现。Event bus是一款针对Android优化的发布/订阅事件总线,主要功能是替代Intent、Handler、BroadCast 在Activity、Fragment、Service、Thread之间传递消息。其优点是开销小,代码更优雅,以及 将发送者和接收者解耦。 阅读详情

相关推荐

otto源码分析

otto这个开源项目是一个event bus模式的消息框架,用于程序各个模块之间的通信,此消息框架可以使得各个 模块之间减少耦合性。 此项目是支付公司square一个开源项目,项目托管于github https://github.com/square/otto 基本模型是,Android的组件可以注册监听,然后发送消息,接收消息,模式就是观察者模式,但是有别于 java实现的

com360的专栏 4635

DIY制作otto机器人

机器人是自动控制机器(Robot)的俗称,自动控制机器包括一切模拟人类行为或思想与模拟其他生物的机械(如机器狗,机器猫等)

3580

Otto源码初探

Otto是面向Android平台的事件总线框架,Otto有利于程序组件之间解耦,简化组件通信逻辑。相关网站:otto主页,github地址。Otto框架使用简单,实例化Bus对象,发送事件对象。

白子的博客 673

铁托(Tito)

Tito 是一个 Biped 跳舞 DIY 机器人,衍生自 Zowi 和 Bob,使用 Arduino UNO 板,连接和支持更容易。

acktomas的博客 1631

Arduino-DIY,一小时内制造出你自己的机器人!(这是入门版“绿色”无蓝牙,有关最新更新,请点击此处:-Ottody/DIY.zip

Arduino-DIY.zip,build you own robot in one hour! (this is the entry version "green" NO Bluetooth, for latest updates please go here:Otto DIY,Arduino是一家开源软硬件公司和制造商社区。

Otto DIY + Arduino蓝牙机器人易于进行3D打印

“带类固醇的奥托DIY” +蓝牙+ APP +开关+触摸传感器+强度+声音检测...

开源了,一个会跳舞的机器人

一个会跳舞的小智AI,otto机器人

zsqlzjtu的专栏 2070

数据分析实战:如何用皮尔逊相关系数避免伪相关陷阱(附Python代码)

本文通过实战案例与Python代码,深入解析了皮尔逊相关系数在数据分析中的应用与常见陷阱。文章重点揭示了如何识别与避免由混杂变量导致的伪相关,并介绍了通过可视化、敏感性分析、分层分析与偏相关分析等方法来检验相关性的可靠性,帮助分析师建立严谨的相关性思维。

weixin_29051245的博客 410

52.otto 源码解析

otto 源码解析Otto的源码非常简单,一共就九个类。项目结构 AnnotatedHandlerFinder ( 查找并缓存所有注解方法 ) Bus ( otto核心业务类,包括注册、反注册、发送事件等等 ) DeadEvent ( 内置事件,没有订阅者,不能被传递 ) EventHandler ( 封装@Subscribe方法及其所在类 ) EventProducer ( 封装@Produce方

Save you from anything 1914

ComfyUI工作流换脸节点报错ReActorFaceSwap

找到自己ComfyUI安装目录的custom_nodes下是否有这个comfyui-reactor-node-main。大家在借鉴别人工作流进行AI换脸的时候,如果遇到ReActorFaceSwap报错提示,莫慌,看我的,两步解决问题。这里千万注意不要嵌套文件夹,保证点击去这个文件就可以看到内置的配置文件!找到自己ComfyUI安装目录的facerestore_models。如果有这个文件,直接跳过,去下一步。如果没有,可以去Git官网下载。

qq_70810809的博客 9015

【云迁移工具RDA】迁移常见问题

Agent无法正常启动,Linux系统提示“SMS.0201 Failed to start sms agent!”,Windows系统提示“SMS.0201 源端主机网络繁忙,无法连接api网关”。该问题通常是由于源端没有通公网,或者没有按照要求配置hosts,无法与iam或者sms通信导致。需要排查源端网络。1.登录源端服务器。2.检查源端是否能正常访问互联网,若无法访问,请处理后重新启动Agent。可参考弹性公网IP Ping不通检查源端的DNS,防火墙,安全组、本地网络等。

2404_89358774的博客 1485

【Arduino】OTTO机器人(做二次开发的一点点总结)

偶然一个机会得以接触一下Arduino,了解一下Arduino代码的编写模式,接触了C++函数封装,算是真正接触了一下项目开发,也意识到项目开发中的种种问题不是写代码能解决的,在阅读别人代码时也需要有科学的方法去读。做此贴,以作总结。 目录 OTTO指令解码: OTTO机器人驱动源码翻阅: OTTO机器人源码修改: 总结: OTTO指令解码: 首先这个项目是基于原先otto...

永远年轻 永远热泪盈眶 3936

【Power Compiler手册】4.saif文件

功耗计算的准确性取决于切换活动数据的准确性。这些数据是通过使用RTL仿真或门级仿真生成的,并存储在SAIF文件中。在执行功耗优化和分析之前,应该使用SAIF文件来反标设计对象上的切换活动信息。SAIF是由Synopsys支持的ASCII格式,用于促进不同Synopsys工具之间的信息交换(参见IEEE 1801标准,附录J)。使用`read_saif`命令读取SAIF文件,使用`write_saif`命令写出SAIF文件。有关更多信息,请参见`read_saif`和`write_saif`命令的手册页。

QRBQ 2186

Otto源码分析

Android event bus库Otto源码分析

大头鬼Bruce 4541

杰理外挂flash使用教程(四):制作fat32文件系统格式的资源文件

本文档提供了FAT文件系统镜像生成与资源打包的完整指南,包含两种实现方案:推荐的WSL/Ubuntu环境方案和Windows原生方案。详细说明了从创建空白镜像、格式化、资源文件复制到最终打包为设备可用的res.bin文件的完整流程,并对比了FAT32与FAT16的特性差异。文档还包含关键文件说明、使用注意事项和典型应用场景,适用于嵌入式系统开发、固件升级等场景。

qq_42014563的博客 1339

一文掌握 MongoDB 详细安装与配置(Windows / Linux / macOS 全平台)

本文详细介绍了MongoDB社区版7.0+在Windows、Linux和macOS三大操作系统上的完整安装、配置与安全加固流程。主要内容包括:各平台推荐安装方式(Windows使用MSI安装包、Linux通过官方仓库、macOS通过Homebrew)、基本配置参数说明、服务启动方法以及关键安全措施(如创建管理员用户和启用身份验证)。文章还提供了系统最低要求、下载地址、数据目录设置建议以及生产环境使用注意事项,适合开发、测试及生产环境部署参考。

数据知道的博客 6707

EventBus & Otto

一、基本介绍 EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递,这里的事件可以理解为消息,本文中统一称为事件。事件传递既可用于 Android 四大组件间通讯,也可以用户异步线程和主线程间通讯等等。EventBus EventBus3.0版本有较大的更新,性能上有很大提升。这里主要介绍新版本。 传统的事件传递方

韩梦飞沙_韩亚飞 1003

FCC 首个扫地机器人豁免落地,Matic 凭什么抢先?

Matic获FCC首个扫地机器人豁免,实为提前布局的合规姿态。其产品在美国组装,并采用已认证射频模组,虽未达65%美国成分门槛,但已具备“本土制造”优势。豁免本质是对既有合规状态的确认,而非解救。此举在商业上放大品牌价值,抢占“安全、合规、本土”叙事高地,凸显供应链安全正取代技术标准成为监管核心。未来合规成本将攀升,规则主导权仍在FCC手中。

致力于lidar应用开发 97

Android事件总线(四)源码解析otto

上一篇文章中讲到了otto的用法,这一篇我们来讲一下otto的源码。可能有人觉得otto过时了,但是通过源码我们学习的是高手设计otto时的设计理念,这种理念是不过时的。

Android进阶三部曲 - 刘望舒 4328

电力负荷预测 | 电力系统负荷预测模型(Python线性回归、随机森林、支持向量机、BP神经网络、GRU、LSTM)

电力负荷预测 | 电力系统负荷预测模型(Python线性回归、随机森林、支持向量机、BP神经网络、GRU、LSTM)

天天酷科研的博客 3185

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Otto介绍

Otto 是Android系统的一个Event Bus模式类库。用来简化应用组件间的通信。关于Event Bus模式的详细情况,请参考这里。 Otto的使用是比较简单的,先到项目主页下载源码:https://github.com/square/otto 下载后的源码目录中包含一个library和sample目录, library目录是类库源代码;sample目录是示例代码。 主要

wangjia55的专栏 1万+

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

otto android 使用,[Android] Otto源码简析

用例本文主要按照如下例子展开://1. 新建bus对象,默认仅能在主线程上对消息进行调度Bus bus = new Bus(); // maybe singleton//2. 新建类A(subscriber),answerAvailable()方法为事件回调,在主线程上运行class A {public A() {bus.register(this);}// 可见性为public,仅有一个Even...

weixin_39624360的博客 155

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

otto使用和源码解析

基本同EventBus 参考文件 使用的区别 1.otto的Bus类相当与EventBus中的EventBus类,它封装了otto的主要功能,但它不是个单例,每次都要用new创建出来,这样显然不是很方便,因此我们用单例模式将它封装起来(DCL) 2. 使用@Produce来发布事件 Produce注解用来生产发布事件,需要注意的是它生产事件前它需要注册,并且在生产完事件后需要取消注册。如果使用这种方法则在跳转到发布者所在的类中则会立即产生事件并触发订阅者 public class SecondActivit

Barryjacket的博客 695

[Android事件总线]otto介绍和源码解读

文章目录概念从监听者模式说起作用使用源码解读postregisterunregisterTips 概念 从监听者模式说起 在写代码的场景中,我们经常碰到“希望当发生某种情况能触发一段逻辑”的需求。拿Android举例,最常见的例子就是当某个按钮被点击时,做一段逻辑。 如果说让事件处理的一方决定触发的时机,那只能不断轮询看是否发生了点击事件来决定触发逻辑了,显然是行不通的。 while(true) ...

code code code 646

Otto源码解读

源码传送门:https://github.com/square/otto 最近工作不是很忙,就花了半天的时间阅读了奥托的源码,于是就有了这篇博文;自己写下的心得体会,算是个学习笔记。 奥托的原理并不难,其源码阅读起来也很容易,其思想原理就是订阅和发布事件,实质是注解+技术反射而实现的观察者模式,但是呢从其实现设计上来看还是可以琢磨出一些东西出来。 其使用也很简单: //事件生成的对象 cl...

菜鸟博客 2204

事件总线Otto源码解析

Otto 事件总线示例 Otto是发布订阅式框架。由Square开发,但是Square停止更新了,推荐用Rxjava来代替。还是来学习一下Otto的思想。 首先来看看示例,定义一个Otto管理类,封装Bus的注册、取消注册,向外提供这些方法: public class OttoBusManager { private static OttoBusManager instance; ...

qq_26984087的博客 263
上一篇: PriorityBlockingQueue简述
下一篇: Retrofit源码浅析
Sun_Jingjing
博客等级 码龄10年 1粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值