强行来一波Dagger2使用介绍

从H2O到OmniH2O:人形机器人遥操作背后的数据魔法与工程哲学 本文深入剖析了从H2O到OmniH2O的人形机器人遥操作技术演进。通过对比两代方案,揭示了如何利用数据重定向、仿真筛选及教师-学生蒸馏等关键技术,将人类动作数据转化为机器人可执行的鲁棒指令,解决了仿真到现实迁移的核心难题,展现了从原理验证到工程实用的系统性工程哲学。 阅读详情

安卓基础开发库,让开发简单点。
DevRing & Demo地址:https://github.com/LJYcoder/DevRing

学习/参考地址:
https://www.jianshu.com/p/cd2c1c9f68d4
https://blog.youkuaiyun.com/lisdye2/article/details/51942511
https://www.jianshu.com/p/24af4c102f62

前言

Dagger2已经出来挺久了,网上相关的教程也很多,但我还是坚持要写这篇,做事要有始有终嘛(这应该是本系列介绍的最后一个框架了,也是本系列最难上手的一个框架)
由于接触Dagger2的时间不长,有些理解可能也不到位,但我还是会尽我所能把它讲清楚些,不当之处也请大家指出。

什么是Dagger2

Dagger2是一个依赖注入(Dependency Injection)框架。

什么又是依赖注入呢?

借别人的话来说,就是“目标类中所依赖的其他类的初始化过程,不是通过在目标类中编码的方式完成,而是通过其他手段把已经初始化好的实例自动注入到目标类中”。

再换种方式来说,就是把类实例的初始化过程都挪至一个地方统一管理,具体需要哪个实例时,就从这个地方取出(注入到目标类中)。

使用Dagger2有什么好处

知其然,然后要知其所以然。

1. 解耦

假设有一个A类,项目中很多地方都使用到它(在很多地方通过new A()对A实例进行了初始化)。然后由于需求变动,A的构造函数增加了一个参数。
好了,牵一发而动全身,你需要把各个new A()的地方都进行修改。
但如果是使用Dagger2进行管理,你只需在类实例的供应端进行修改即可。

2. 让功能实现更专注于功能实现

假设现在你需要调用A类的x()方法来实现某功能,但是A类的构造过程相当的复杂(这样的例子可以参考GreenDao中获取XXXDao、Retrofit中获取Observable请求)

public void xxx(){
    E e = new E();
    D d = new D(e);
    C c = new C();
    B b = new B(c,d);
    A a = new A(b);
    a.x();
}

结果6行代码中,构造实例a占了5行,调用x()方法实现功能却只占了1行。
但如果使用Dagger2进行管理,将a实例的构造过程移至实例供应端,则功能实现模块的代码会变成这样

@Injcet
A a;
public void xxx(){
    a.x();
}

这就是所说的让功能实现更专注于功能实现,而不必去管a实例的构造过程。

3. 更好地管理类实例

通常我们开发中会有两种类实例:
一种是全局实例(单例),它们的生命周期与app保持一致。
一种是页面实例,它们的生命周期与页面保持一致。
通过Dagger2,我们可以使用一个组件专门管理全局类实例(也免去了单例的写法,不用考虑饿汉懒汉什么的);然后各个页面也有各自组件去管理它们的页面实例。
这样不管是对于实例的管理,还是项目的结构,都会变得更加的清晰明了。

4. 逼格高

(这点可以略过…)
当你不认识Dagger2却看着使用Dagger2的项目代码,很可能会感觉到一种茫然与高大上:
各种@Inject,@Provides,@Singleton,Lazy<>,Provider<>等是什么鬼?
为什么没有实例化的代码?
为什么明明是null却不会报空指针?
等你学会使用Dagger2之后,你也可以来一波”高逼格”的代码。

当然,这里也不得不提一下,使用Dagger2会增加代码量。所以如果是小项目/独立开发,你也可以考虑不用,因为你可能有种失大于得的感觉。如果是大项目/团队开发,使用后就得大于失了。


角色介绍

在讲用法前,先对几个重要角色进行了解。

实例需求端

一个类中,它包含了实例成员,在使用这些实例前,需要对它们进行初始化,但前面已经说了初始化的过程挪至其他地方。所以这个类的需求是,已经完成初始化的实例对象。
暂且把这样的类称为“实例需求端”。

实例供应端

进行实例初始化的地方,并对外供应所需的实例。

Dagger2中,供应端有两种方式可以供应实例:(后面会介绍)
第一种:使用Module
第二种:使用@Inject标注构造函数

桥梁

实例供应端并不知道它要供应的实例交给谁。
这个时候就需要通过桥梁,将实例需求端与实例供应端联系在一起。

Dagger2中,Component就负责扮演桥梁的角色。

使用介绍

1. 初步使用

1.1 添加依赖

compile 'com.google.dagger:dagger:2.14.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'

1.2 处理实例需求端

在实例需求端中,使用@Inject标注需要注入的实例变量。

public class UploadActivity extends AppCompatActivity{
    @Inject
    UploadPresenter mPresenter;
}

1.3 处理实例供应端

前面说了,供应端有两种方式可以供应实例。

方式一 使用Module
  • 使用@Module标注类,表示它为供应端
  • 在类中使用@Provides标注方法,表示它为提供实例的方法。
    在该方法中对实例进行初始化并返回该实例。
@Module
public class UploadActivityModule {
    @Provides
    UploadPresenter uploadPresenter() {
        return new UploadPresenter();
    }
}
方式二 使用@Inject标注构造函数
public class UploadPresenter{
    @Inject
    public UploadPresenter() {
    }
}
注意

方式一的优先级高于方式二,意思就是:
- 在供应某实例时,会先通过方式一查找是否存在返回该实例的的方法
- 如果存在,则获取实例并对外供应
- 如果不存在,再通过方式二查找是否存在@Inject标注的构造函数
- 如果存在,则将通过该构造函数构造实例并对外供应
- 如果不存在,那将报错,因为无法供应所需的实例

1.4 搭建桥梁

  • 使用@Component标注接口,表示它为桥梁。
    (如果使用1.3.1的方式供应实例,则需在@Component(modules=xxx.class)中指明module。)
    一个Component可以没有module,也可以同时有多个module。
  • 添加 void inject(实例需求端) 方法,表明实例供应端中的实例将交给该实例需求端。
    通过这个方法,查找实例需求端中需要注入的实例有哪些(使用@Inject标注的那些实例),然后在实例供应端中获取所需的实例,最后注入到实例需求端中
  • 用来注入的方法,它的方法名不一定要是inject,可以随便取,一般都取inject。但该方法的返回类型必须为void
@Component(modules = UploadActivityModule.class)
public interface UploadActivityComponent {
    void inject(UploadActivity uploadActivity);
}

1.5 编译,注入

  • 完成以上几步后,ReBuild一下项目以生成DaggerUploadActivityComponent类。
  • 在实例需求端中调用inject完成实例的注入
public class UploadActivity extends AppCompatActivity{
    @Inject
    UploadPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //注入实例
        DaggerUploadActivityComponent.builder()
            .build()
            .inject(this);

        //注入后即可调用mPresenter中的方法了
        mPresenter.xxx();
    }
}


2. 接收外部参数

有些时候,实例的初始化需要接收外部参数才能完成,比如MVP中的Presenter往往需要传入IView接口以便完成数据回调。

现在UploadPresenter的构造函数发生了变动,需传入IUploadView。

public class UploadPresenter{
    IUploadView mIView;
    public UploadPresenter(IUploadView iview) {
          mIView = iview;
    }
}

下面介绍两种方式来接收外部参数

2.1 方式一 通过Module的构造函数传入

对实例供应端的module进行改造
@Module
public class UploadActivityModule {
    IUploadView mIView;
    public UploadActivityModule(IUploadView iview) {
        mIView = iview;
    }

    @Provides
    IUploadView iUploadView(){
         return mIView;
    }

    @Provides
    UploadPresenter uploadPresenter(IUploadView iview) {
        return new UploadPresenter(iview);
    }
}
  • 添加了构造函数以便获取外部参数IUploadView
  • uploadPresenter()方法添加了IUploadView参数
    • 到时构建UploadPresenter实例时,会在这个Module或者同个Component下的其他Module中查找是否存在返回IUploadView的的方法
    • 如果存在,则通过该方法获取IUploadView以构造UploadPresenter
    • 如果不存在,则通过1.3.2方式查找是否存在@Inject标注的构造函数(当然不会存在,因为IUploadView是接口)
  • 不直接使用mIView来构造UploadPresenter是为了降低耦合度。uploadPresenter()方法只管获取构造Presenter所需的参数,而该参数从哪来、以后会有哪些变动,交给iUploadView()方法处理即可。

利用Module构造函数传入外部参数

public class UploadActivity implements IUploadView{
    @Inject
    UploadPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //注入实例
        DaggerUploadActivityComponent.builder()
            .uploadActivityModule(new UploadActivityModule(this))//通过构造函数传入外部参数IUploadView
            .build()
            .inject(this);

        //注入后即可调用mPresenter中的方法了
        mPresenter.xxx();
    }

    //实现IUploadView接口的方法
    @Override
    public void onUploadSuccess() {
        //上传成功
    }
    @Override
    public void onUploadFail() {
        //上传失败
    }
}

2.2 方式二 通过Component传入

对桥梁Component进行改造
@Component(modules = UploadActivityModule.class)
public interface UploadActivityComponent {
    void inject(UploadActivity uploadActivity);

    IUploadView iUploadView();

    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder iUploadView(IUploadView iUploadView);

        UploadActivityComponent build();
    }
}
  • 加入iUploadView()方法返回IUploadView
  • 加入了Builder来接收IUploadView
构建Component时传入外部参数
public class UploadActivity extends AppCompatActivity implements IUploadView{
    @Inject
    UploadPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //注入实例
        DaggerUploadActivityComponent.builder()
            .iUploadView(this)
            .build()
            .inject(this);

        //注入后即可调用mPresenter中的方法了
        mPresenter.xxx();
    }

    //实现IUploadView接口的方法
    @Override
    public void onUploadSuccess() {
        //上传成功
    }
    @Override
    public void onUploadFail() {
        //上传失败
    }
}


3. 限定符注解 @Qualifier

@Qualifier主要是用于解决,因供应端存在多个类型相同的实例而引起歧义的问题。

3.1 使用@Named注解

Dagger2默认提供了一个@Named注解,从代码可以看出属于@Qualifier的一种实现。

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
    String value() default "";
}

下面举个例子,现在页面需有两个Dialog,一个用于登录,一个用于注册。

@Module
public class TestActivityModule {
    private Context mContext;

    public TestActivityModule(Context context){
          mContext = context;
    }

    @Provides
    Context context(){
          return mContext;
    }

    @Named("login")
    @Provides
    Dialog loginDialog(Context context){
         Dialog dialog = new Dialog(context);
         dialog.setTitle("登录提示");
         ....
         return dialog;
    }

    @Named("register")
    @Provides
    Dialog registerDialog(Context context){
         Dialog dialog = new Dialog(context);
         dialog.setTitle("注册提示");
         ....
         return dialog;
    }
}
  • 可以看到,在实例供应端中,需在提供Dialog实例的方法上面加@Named注解以区分。如果不加则会报错,因为Dagger2不知道要使用哪个方法来获取Dialog实例。
@Component(modules = TestActivityModule.class)
public interface TestActivityComponent {
    void inject(TestActivity testActivity);
}
public class TestActivity extends AppCompatActivity{
    @Named("login")
    @Inject
    Dialog mDialogLogin;

    @Named("register")
    @Inject
    Dialog mDialogRegister;      

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //注入实例
        DaggerTestActivityComponent.builder()
            .testActivityModule(new TestActivityModule(this))
            .build()
            .inject(this);
    }    
}
  • 在实例需求端中,同样要使用@Named注解标注要注入的实例。让Dagger2知道,
    @Named(“login”)标注的mDialogLogin,需要通过@Named(“login”)标注的供应方法来获取。
    @Named(“register”)标注的mDialogRegister,需要通过@Named(“register”)标注的供应方法来获取。

3.2 自定义@Qualifier注解

使用@Named注解的话,需要加入字符串来区分,这样比较麻烦也容易出错。所以我们可以使用自定义的限定符注解。

@Qualifier
public @interface DialogLogin {
}
@Qualifier
public @interface DialogRegister {
}

然后把前面涉及的@Named(“login”)换成@DialogLogin,@Named(“register”)换成@DialogRegister即可~


4. 作用域注解 @Scope

@Scope的作用是使同一个Component中供应的实例保持唯一。

4.1 使用作用域注解实现局部单例

举例说明:

public class UploadActivity extends AppCompatActivity{
    @Inject
    UploadPresenter mPresenter1;
    @Inject
    UploadPresenter mPresenter2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //注入实例
        DaggerUploadActivityComponent.builder()
            .build()
            .inject(this);
    }
}
  • 如果不使用作用域注解,则代码中的mPresenter1,mPresenter2将会是两个不一样的实例,可通过打印内存地址查看。
  • 而如下使用作用域注解后,则两者将会是同一个实例,可通过打印内存地址查看。
步骤1 自定义@Scope注解
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScope {}
步骤2 桥梁Component添加作用域注解
@ActivityScope 
@Component(modules = UploadActivityModule.class)
public interface UploadActivityComponent {
    void inject(UploadActivity uploadActivity);
}
步骤3 供应端中提供实例的方法添加作用域注解
@Module
public class UploadActivityModule {
    @ActivityScope
    @Provides
    UploadPresenter uploadPresenter() {
        return new UploadPresenter();
    }
}

如果是使用1.3.2方式提供实例,则在类上方添加作用域注解

@ActivityScope
public class UploadPresenter{
    @Inject
    public UploadPresenter() {
    }
}

经过@Scope处理后,UploadActivity中的UploadPresenter实例将保持唯一。

4.2 使用作用域注解实现全局单例

全局单例,相信大家就很熟悉了,就是平时用饿汉懒汉等等写的那种单例模式。
前面已经说过@Scope作用是使同一个Component中供应的实例保持唯一。
也就是说,如果我在另一个Activity中再创建了一个新的Component,那么它所提供的UploadPresenter实例也将是新的。这和我们理解的全局单例并不一样。
所以,要想实现全局单例,那就要确保获取实例的Component一直都是同一个。
如何实现呢?答案是创建一个Component用于提供全局单例的实例,然后在Application中对该Component进行初始化,以后要获取单例时,都统一通过它来获取。

全局性的实例供应端

@Module
public class AppModule {
    private Application mApplication;

    public AppModule (Application application){
          mApplication = application;
    }

    @Singleton
    @Provides
    Application application(){
          return mApplication;
    }

    @Singleton
    @Provides
    ActivityStackManager activityStackManager() {
        return new ActivityStackManager();
    }
}

全局性的桥梁

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    ActivityStackManager activityStackManager();
    Application application();
}

Application中初始化

public class MyApplication extends Application {

    public static AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();

        mAppComponent= DaggerAppComponent.builder()
              .appModule(new AppModule(this))
              .build();
    }
}

每次都通过Application中的AppComponent获取某实例,即可保证全局单例

public class UploadActivity extends AppCompatActivity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MyApplication.mAppComponent.activityStackManager().pushOneActivity(this);     
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        MyApplication.mAppComponent.activityStackManager().popOneActivity(this);   
    }
}
  • 使用了Dagger2默认提供的作用域注解@Singleton,通过源码可以发现它的实现其实和前面的@ActivityScope是一样的。
  • 所以真正实现全局单例的不是并@Singleton,而是使用每次获取实例都通过同一个Component。
  • 但由于它的字面意思为单例,所以我们通常把它应用在全局性的桥梁和实例供应端中。
  • 全局性的Component中没有加入inject方法来自动注入(当然你也可以这么做,但全局性的比较少这么做),而是加入了activityStackManager()方法,供外部调用来获取实例。


5. Lazy 和 Provider

假如你并不希望在调用inject()方法后,就对@Inject标注的实例进行初始化注入,而是希望在用到该实例的时候再进行初始化,那么我们就可以使用Lazy和Provider来实现。

举例说明(省略实例供应端和桥梁的代码)

public class UploadActivity extends AppCompatActivity{
    @Inject
    Lazy<UploadPresenter> mPresenter1;
    @Inject
    Provider<UploadPresenter> mPresenter2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //注入实例
        DaggerUploadActivityComponent.builder()
            .build()
            .inject(this);
    }

    public void xxx(){
         //调用Lazy的get()方法后才开始初始化Presenter并得到该实例
         //并且后面每次调用get()方法得到的实例都将是同一个。
         mPresenter1.get().xxx();

         //调用Provider的get()方法后才开始初始化Presenter并得到该实例
         //并且后面每次调用get()方法,都会重新调用供应端的供应方法来获取新实例。
         mPresenter2.get().xxx();
    }
}

注意: 如果使用了前面介绍的作用域注解@Scope控制了实例的唯一性,那么即使多次调用Provider的get()方法,得到的依然是同一个实例。


6. 依赖 和 包含

假设供应端某个实例的初始化过程,需要用到全局的Context(即Application),那么我们可以通过桥梁间的依赖或包含,从全局的AppComponent中获取。

@Module
public class TestActivityModule {
    //需要使用到Application参数
    @Provides
    DbHelper dbHelper(Application application){
          return new DbHelper(application);
    }
}

6.1 通过依赖实现(TestActivityComponent依赖AppComponent)

通过dependencies = xxx.classs指定要依赖的Component :
“` java
@Component(modules = TestActivityModule.class, dependencies = AppComponent.class)
public interface TestActivityComponent {
void inject(TestActivity testActivity);
}

被依赖的Component中需定义相关实例的获取方法 :
``` java
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {

    Application application();
    ...
}




<div class="se-preview-section-delimiter"></div>

初始化TestActivityComponent时需传入依赖的Component

public class TestActivity extends AppCompatActivity{

    @Inject
    DbHelper mDbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerTestActivityComponent.builder()
            .appComponent(MyApplication.mAppComponent) //传入依赖的Component
            .build()
            .inject(this);
    }    
}




<div class="se-preview-section-delimiter"></div>

6.2 通过包含实现(AppComponent包含TestActivityComponent)

TestActivityComponent使用@Subcomponent注解而不是@Component

@Subcomponent(modules = TestActivityModule.class)
public interface TestActivityComponent {
    void inject(TestActivity testActivity);
}




<div class="se-preview-section-delimiter"></div>

AppComponent中定义相关方法,用来包含和获取SubComponent

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {

    TestActivityComponent addSub(TestActivityModule testActivityModule);
    ...
}




<div class="se-preview-section-delimiter"></div>

通过AppComponent来获取SubComponent,然后注入

public class TestActivity extends AppCompatActivity{

    @Inject
    DbHelper mDbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        TestActivityComponent testActivityComponent = MyApplication.mAppComponent.addSub(new TestActivityModule());
        testActivityComponent .inject(this);

    }    
}

6.3 依赖和包含使用小结

  • 通过桥梁Component之间的依赖或包含,可以获取到其他桥梁所连接的供应端提供的实例。
  • 使用依赖实现的话(假设A依赖B),A需通过dependencies指定依赖B,B中需定义A所需的相关实例的获取方法,A构造时需传入B。
  • 使用包含实现的话(假设B包含A),A需使用@Subcomponent标注,B中需定义方法来包含/获取A,A是通过调用B的方法来获取的。
  • 具有依赖关系的两个Component,它们的作用域注解@Scope必须不同,否则会引起歧义。


7. 一些Tips

7.1
在1.3.1和1.3.2介绍了两种实例供应方式:使用Module(方式一)、使用@Inject标注构造函数(方式二)。

那什么时候应该使用哪种方式呢? 假设现在供应端需要提供A类的实例
- 当无法在A类的构造函数上加入@Inject时(比如一些第三方库里的类),则使用方式一提供A实例。
- 当你希望在A类实例初始化时,A类中被@Inject标注的变量也被自动注入,则使用方式二提供A实例。

7.2
Module类可以声明为abstract抽象,但相关的供应方法需声明为static静态方法。

7.3
如果module中的供应方法声明了@Scope,那么它所属的component必须也声明相同的@Scope。
但如果component声明了@Scope,它的module的供应方法并不一定全都要声明@Scope。

7.4
@inject标注的实例变量不能声明为private,也不能为static,否则会编译报错。


安卓基础开发库,包含各常用模块,让开发简单点 DevRing安卓基础开发库,包含各常用模块,让开发简单点 立即下载

相关推荐

MOOS-ivp 实验三 MOOS简介(1)

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 例如:第一章 Python 机器学习入门之pandas的使用 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录系列文章目录前言一、pandas是什么?二、使用步骤1.引入库2.读入数据总结 前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是本篇文章正文内容,下面案例

weixin_44151170的博客 7434

Dagger 使用入门

Dagger ,一款让我们能省去无用代码的依赖注入框架!

wangzp的博客 3269

GaN制备micro-led(一)

随着第三代半导体 GaN 材料的快速发展,GaN 基 LED 发展迅速并广泛商业化,这 为可见光通信的实现及应用提供了基础。然而,普通商用 LED 的调制带宽只有几十兆 赫兹,并不能很好的满足可见光通信系统高传播速率的需求。为增大调制带宽,针对发 射端 LED 光源部分的改进仍需提高。将光子晶体结构引入 LED 芯片量子阱内部,能够 影响光子寿命和光子传播,进而改变 LED 芯片的调制带宽和光提取效率。结合倒装微 尺寸(Micro)LED 结构,使其封装体积最小化,这使得可见光通信能够更加灵活地.

weixin_45852211的博客 895

使用 antd 的 form 中的 Upload.dragger 组件实现 文件上传

目录背景需求参考文献 背景 负责后端的我要解决前端的问题真是太难了…… 需求 参考文献 https://blog.youkuaiyun.com/ThisEqualThis/article/details/103633911 https://blog.youkuaiyun.com/qq_36753153/article/details/108243179 https://blog.youkuaiyun.com/qq8241994/article/details/82857730 ...

lizhengze1117的博客 6904

低速信号设计之 ESPI 篇

ESPI 是 Intel 推出的增强型串行外设接口,基于 SPI 发展而来,新增 Reset# 和 Alert# 信号,采用点对点主从通信模型,最高速率达 33MHz,支持半双工与 DMA 传输。 其物理接口含 10 条线路,通信协议分物理层、数据链路层等四层。布局布线需关注信号完整性、电源完整性,还得考虑与 SPI 的兼容性。 ESPI 广泛应用于计算机系统(连接 BIOS、电源管理器等)和工业控制领域,具有引脚少、功耗低等优势,但设计复杂且存在兼容性挑战。

qq_16237389的博客 1379

5分钟搞定JY901S与STM32通信:硬件I2C+DMA配置全流程(附OLED显示教程)

本文详细介绍了如何通过STM32硬件I2C接口配合DMA高效读取JY901S姿态传感器数据,并实现OLED实时显示。文章从硬件连接、I2C与DMA配置、通信协议解析到数据可视化,提供了完整的实战流程和避坑指南,帮助开发者快速构建高精度、低CPU占用的姿态感知系统。

weixin_29185167的博客 480

这可能是本年度最好用的 Dagger 使用教程 一(基本使用)

本文从一个例子入手,解释了依赖注入,和 Dagger 这个库的初步使用方式。

李斯维的博客空间 4672

45个经典Linux面试题(非常详细)零基础入门到精通,收藏这一篇就够了

(文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如 i 节点,磁盘分布图,间接块,超级块等。$ chmod 751 file 给 file 的属主分配读、写、执行(7)的权限,给 file 的所在组分配读、执行(5)的权限,给其他用户分配执行(1)的权限。linux命令’hash’管理着一个内置的哈希表,记录了已执行过的命令的完整路径, 用该命令可以打印出你所使用过的命令以及执行的次数。创建文件:典型的如 touch,vi 也可以创建文件,其实只要向一个不存在的文件输出,都会创建文件。

小陈的博客 2070

关于Antd Pro在ProForm里面使用Upload.Dragger(拖拽上传)校验不生效的问题

如果你想通过Form.Item给添加rules校验,Form.Item和之间不能包含div或者是其他dom,否则你的的拖拽上传rules校验会失效。

RookieYoung的博客 1289

Android So加载机制分析----基于Android14源码

本文深入分析Android 14系统中SO(共享库)的两种加载机制:System.loadLibrary()和System.load()。前者通过ClassLoader查找标准路径加载,后者直接加载指定路径的SO文件。文档详细追踪了从Java层到动态链接器的完整加载链路,包括ClassLoader路径查找、JNI桥接、ART虚拟机处理以及底层linker的符号解析和重定位过程。同时还分析了Namespace隔离、JNI_OnLoad机制等核心内容,为开发者提供全面的SO加载技术解析和问题排查指南。

保持专注,保持热爱 349

mjlab 入门教程(一)MuJoCo 人形机器人强化学习训练 sim-to-real 部署框架

摘要:mjlab是一个结合IsaacLab API与MuJoCo简洁性的GPU加速机器人仿真框架,专为强化学习和机器人研究设计。它提供快速安装(支持uv、pip、conda和Docker)、核心功能(域随机化、NaN守护、观测历史/延迟、执行器/传感器系统)以及从IsaacLab的迁移指南。相比IsaacLab,mjlab更轻量、启动更快,直接构建在MuJoCo Warp上,避免了Omniverse开销。其哲学强调裸机性能、开发者体验和核心领域专注,适合需要快速迭代和直接MuJoCo控制的用户,而Isaac

2506_90492529的博客 2724

使用Dagger快速入门

其实就是声明了作用域的一个名字,把哪个对象的声明周期,绑定在了一个component上@Scope//相当于IOC容器 有一些对象的实例//把模块装载到这个组件上@MyScope//2.将对象注入到MainActivity这个类里//表明这个类是一个dagger模块@Module@MyScope@Provides.build();//相当于Springboot @Bean 第二种方式告知Dagger 可以调用该方法来获取到要注入对象的实力。

qq_53374893的博客 1000

【OpenClaw从入门到精通】第19篇:OpenClaw电商实战:从零搭建比价+库存监控+自动调价全自动化系统(2026实测附完整代码)

2026年OpenClaw成为全球最火开源项目,电商行业因其场景高频、数据标准化成为其核心落地领域。本文针对电商从业者面临的比价繁琐、库存混乱、调价滞后三大痛点,基于OpenClaw的“接入层-执行层-决策层”三层架构,从零拆解智能比价、多平台库存监控、动态自动调价三大核心场景的实现逻辑。文中包含完整的环境配置、技能安装、代码实现(含Python脚本与YAML配置)、执行结果验证,同时补充关键算法原理与避坑指南,兼顾新手入门与进阶提升。读者可直接参考本文实操流程,快速搭建属于自己的电商自动化系统,实现7×2

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 2238

【Android】Dagger2

1,简介 不像Spring框架那样通过反射完成IOC、DI、AOP功能,Dagger仅仅完成IOC,DI过程,实现原理是动态生成代码;Dagger通过注解完成注入,常用注解有以下: @Module @Component @Inject @Singleton @Provider @Scope 2,简单实例 (1)创建依赖提供方,对象A和B; package com.zjw.myblog.dagger; import com.zjw.myblog.dagger.domain.A; .

qq_40900926的博客 1312

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Dagger从入门到放弃

Dagger的好处: 使用Dagger2 的好处比较明显的就是,可以方便的修改,构建对象。 比如很多对象的构建都需要Context,你只要有一个可以提供Context的moudule就可以了,还可以比较方便的控制对象的生命周期,简单来讲就是可以很方便的构建和修改对象的构造函数(只需要维护好Component,module,当然这些对于新手并不简单)。 Dagger的缺点: 1.移动类到不同的...

ithouse的专栏 912

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

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

Android Dagger2中的Scope到底是干什么用的

凡是用@Scope标注的标注,比如@SIngleton这种的,不论是他提供的,还是你自己自定义的,除了名字不一样之外,效果都是一样的,也就是:只要用这种标注标记了你module中的某个@providers标记的方法,那么这个方法的返回值就会由一个DoubleCheck包装,对外提供一个单例,同一个DaggerCcomponent不论注入几个新的实体,都是用同一个单例,如果不标记,那么多次注入的就是...

u011856790的博客 765

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

安卓Dagger框架:依赖注入实践与应用

Dagger是适用于Android和Java生态系统的强大依赖注入(Dependency Injection, DI)工具,以其编译时生成代码和高效的运行时性能而著称。本文旨在深入探讨Dagger的核心概念、工作流程、优缺点以及实际代码示例,以便开发者更好地理解并有效利用这一框架来优化安卓应用的设计与开发。

wlred1980的博客 1436

为什么说dagger.js 是最适合后端开发者使用的前端框架

后端开发者常面临前端框架复杂、构建工具繁琐的问题。轻量级框架dagger.js提供零配置方案,仅需引入一个脚本即可运行,采用指令驱动DOM方式,无需记忆复杂API,天然适合管理后台等场景。其20KB体积、无依赖特性可嵌入现有后端页面,实现渐进式增强,完美契合后端开发者模板引擎思维,让前端开发回归HTML+JS的简洁时代。

2404_89388058的博客 845

WM-DAgger:用世界模型生成错误恢复数据突破VLA模仿学习瓶颈

视觉-语言-动作(VLA)模型是具身智能的核心架构,其性能高度依赖高质量动作演示数据。然而传统模仿学习受限于人类演示的覆盖广度与数据效率,尤其在错误恢复、物理异常、长尾场景等关键环节严重不足。世界模型凭借对物理因果、多模态状态和任务语义的联合建模能力,可主动合成符合力学约束、语义连贯的高价值恢复轨迹,显著提升VLA在真实环境中的鲁棒性与泛化性。该技术已在仓储分拣、厨房操作等复杂具身任务中验证有效性,成为连接仿真训练与真实部署的关键桥梁。本文聚焦WM-DAgger方法论,解析其如何以世界模型为‘数据炼金术士’

dianwei5413的博客 457

以无厚入有间:浅谈dagger.js的无组件设计

本文探讨了现代前端框架过度组件化的问题,并提出了一种基于指令的解决方案。Dagger.js摒弃组件概念,以DOM元素为最小逻辑单元,通过指令(如+load、*exist)直接控制元素行为。相比Vue/React的组件级抽象,它能实现更细粒度的控制,天然支持Web Components,无需构建工具或虚拟DOM。这种设计让HTML重获运行时能力,使开发者能更自由地在任意层级操作DOM,实现从完整应用到局部交互的无缝衔接。Dagger.js不是对抗组件化,而是回归Web本质,让HTML、CSS、JS重新融为一体

2404_89388058的博客 467

android开发框架总结 (一)从java基本原则细数MVP的“七宗罪”

前言 我们都知道,MVP是在MVC的基础上做了一次升级,相比MVC,MVP中P层与V层隔离,V层只负责UI,业务逻辑由抽象出来的P层负责,真正意义上的隔离View的细节和复杂性的模式....... 好了好了,上面是网上MVP“鼓吹”的基本套路。“欲想让其灭亡,必先使其膨胀”今天,反其道而行之,我们来罗列MVP的“七宗罪”   MVP的“七宗罪” 有一定经验的工程师都知道,我们的jav...

qq_39037047的博客 716

WM-DAgger:用世界模型在latent space生成物理合理的恢复轨迹

世界模型(World Model)作为具身智能的核心组件,正从像素级预测转向可微分、物理一致的潜空间动力学建模;其核心价值在于将抽象的环境理解转化为可执行的动作策略。latent space建模不仅规避了3D仿真的计算开销与Sim2Real鸿沟,更实现了与VLA多模态表征的原生对齐,显著提升动作泛化性与失败恢复能力。在模仿学习场景中,该范式通过自动合成高可信度‘临界失败-恢复’轨迹,有效缓解演示数据稀疏、物理不合理及鲁棒性不足等工程痛点,已成功应用于机械臂抓取、家庭服务机器人等真实VLA系统。

weixin_29169505的博客 240

一文讲透Android依赖注入,简洁全面

有很多介绍依赖注入的文章,看完以后觉得好像懂了,又好像没什么收获。我结合自己多年开发经验,在基本原理的基础上,说一些自己的理解和体会,帮助读者理解、应用这一常用的模式工具,提升编程能能力。

yinminsumeng的博客 4114

【VLN】VLN Paradigm Alg:模仿学习及其细节(3)

3. 模仿学习3.1 Teacher-forcing 与 Student-forcing 的异同点与搭配使用3.1.1 核心定义(一句话区分)3.1.2 Teacher-forcing(教师强制 · 离线演示)3.1.3 使用方式(训练流程)3.1.4 本质3.1.5 优点3.1.6 致命缺陷:暴露偏差(Exposure Bias)3.2 Student-forcing(学生强制 · 交互式演示)3.2.1 使用方式(训练流程)3.2.2 本质3.2.3 优点3.2.4 缺陷3.3 能否搭配使用?

格物致知 817
上一篇: 让你快速上手的Glide4.x教程
下一篇: 安卓基础开发库,DevRing使用说明
丶Jy
博客等级 码龄11年 24粉丝 12原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值