RoboGuice 3.0 (二)进阶篇

上篇介绍了RoboGuice的接入及基本使用,其中涉及到了一个@Singleton和@ContextSingleton的注解,这些都是作用域的注解,这篇我们先说明有关作用域的问题。

一.作用域 Scope

Scope指的是作用域,指的就是注入的对象的生命周期,RoboGuice提供了默认的几个作用域:

  • @Singleton,被标注@Singleton注解的对象生命周期将保持和Application一致,也就是和整个App的生命周期一致。
  • @ContextSingleton,被标注@ContextSingleton注解的对象生命周期将保持和Context提供者一致,这里主要指的是是Activity中,RoboGuice在Activity中提供了默认的Context容器,使我们初始化Activity成员的时候使用了这个容器中的Context,虽然注解

我们先看下RoboGuice的官方文档对@ContextSingleton的说明。

To the opposite of singletons created via @Singleton, the singletons created via@ContextSingleton are tied to a Context lifecycle and are garbage collected when this context gets destroyed.

大致就是,和@Singleton注解不同,@ContextSingleton注解是与Context的生命周期绑定的,当Context被销毁时,这种单例会随Context回收而回收。

When you use the annotation @ContextSingleton, you create an Object that will not be garbage collected within a given Context lifecycle. It will be destroyed when the context itself is destroyed, but will remain in memory even if your context doesn't use it anymore. This annotation can still create memory leaks if you don't use it properly, for instance when using fragments.

也就是当我们使用@ContextSingleton时,虽然是随生命周期联动的,但是当我们不使用这个单例对象时(当Context还存在时),这个对象会一直存于内存中,一个很明显的例子就是Fragment,在Fragment中标注一个@ContextSingleton属性的成员,当Fragment被回收而Activity没被回收时(因为Fragment中Context是从依附的Activity中获取的),还是会造成内存泄露。

RoboGuice官方文档声称,我们会加入一个@FragmentScope来解决这个问题,但是并没有,那个issue被关闭了,这一点还是很迷的,目前也没看到什么好的解决办法,只有从代码层面规范。

二.对象绑定

这里的对象绑定指的是将一个接口或类绑定到一个子类、对象、或对象提供容器上。当我们注入这个接口或类时,默认会根据绑定的类别初始化这个接口的实现。

对象绑定需要定义module,并且注册module到manifast文件。

欲练神功,需要两步:

  • Register modules in your AndroidManifest.xml file
  • Create classes that extend AbstractModule

翻译成代码就是这样:

<application
        android:allowBackup="true"
        android:name=".GuiceApplication"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data android:name="roboguice.modules"
            android:value="github.pedroneer.roboguice.GuiceModule" />
public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {

    }
}

好了,然后举个栗子。

定义一个GsonProvider的接口,

public interface GsonProvider {
    Gson get();
}

我们在另一个类中实现了这个接口。

public class GsonProviderImpl implements GsonProvider {
    @Override
    public Gson get() {
        return new GsonBuilder().
                serializeNulls().
                create();
    }
}

在module中绑定GsonProvider到GsonProviderImpl上。

public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(GsonProvider.class).to(GsonProviderImpl.class);
    }
}

这样我们在Activity中就可以使用@Inject去注入GsonProvider了,RoboGuice在初始化这个接口时会使用GsonProviderImpl定义的实现。

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboFragmentActivity {

    @Inject
    GsonProvider gsonProvider;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String demoString = gsonProvider.get().toJson("123");
    }
}

这时候你就会问了:"那我直接注入这个GsonProviderImpl不就可以了,你这是多此一举!"。

RoboGuice还提供了另一种实现方法,可能写起来会更简单一些。

这种方法就是在Module中定义Providers,简单理解就是容器提供这个对象的初始化服务,当你你需要使用这个对象时,容器会帮你初始化好。

public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {
        //bind(GsonProvider.class).to(GsonProviderImpl.class);
    }

    @Provides
    Gson provideGson(){
        return new GsonBuilder()
                .serializeNulls().
                        create();
    }
}

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboFragmentActivity {

    @Inject
    Gson gson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String demoString = gson.toJson("123");
    }
}

这样也是可以实现的,是不是觉得写起来更舒服一些,代码量也少了很多。

三.绑定类型

当我们需要注入不同属性的对象时,会用到类型绑定。

还是上面注入Gson的例子。当我需要属性不同的Gson时,上面的代码就实现不了了,比如上面提到的serializeNulls,是用来指定Gson在序列化时是否需要将null序列化。

例如下面这个model:

@SuppressWarnings("unused")
public class CommitData {
    private int commentNum;
    private int goodNum;
    private int badNum;
    private CommentReply commentReply;

    public int getCommentNum() {
        return commentNum;
    }

    public void setCommentNum(int commentNum) {
        this.commentNum = commentNum;
    }

    public int getGoodNum() {
        return goodNum;
    }

    public void setGoodNum(int goodNum) {
        this.goodNum = goodNum;
    }

    public int getBadNum() {
        return badNum;
    }

    public void setBadNum(int badNum) {
        this.badNum = badNum;
    }

    public CommentReply getCommentReply() {
        return commentReply;
    }

    public void setCommentReply(CommentReply commentReply) {
        this.commentReply = commentReply;
    }

    public static class CommentReply{
        private String reply;
        private String time;

        public String getReply() {
            return reply;
        }

        public void setReply(String reply) {
            this.reply = reply;
        }

        public String getTime() {
            return time;
        }

        public void setTime(String time) {
            this.time = time;
        }
    }
}
        CommitData data = new CommitData();
        data.setBadNum(20);
        data.setCommentNum(30);
        data.setGoodNum(49);

当我们不初始化CommentReply对象时,带serializeNulls属性的Gson对象和不带是有区别的:

  • 注明serializeNulls:{"badNum":20,"commentNum":30,"commentReply":null,"goodNum":49}
  • 不带serializeNulls:{"badNum":20,"commentNum":30,"goodNum":49}

为了实现上述功能,我们之前定义的provider是有问题的,RoboGuice提供了类型的概念,我们可以定义和选择初始化使用的类型。使用的就是@Named注解,使用方法如下,当注入时,注明@Named字段来标示需要使用哪种初始化方法。

public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {
        //bind(GsonProvider.class).to(GsonProviderImpl.class);
    }

    @Provides
    @Named("Serialize Nulls")
    Gson provideGson(){
        return new GsonBuilder()
                .serializeNulls().
                        create();
    }

    @Provides
    @Named("Custom")
    Gson provideCustomGson(){
        return new GsonBuilder().
                        create();
    }
}
@ContentView(R.layout.activity_main)
public class MainActivity extends RoboFragmentActivity {

    private static final String TAG = "MainActivity";

    @Inject
    @Named("Serialize Nulls")
    Gson gson;

    @Inject
    @Named("Custom")
    Gson customGson;


    @SuppressLint("SetTextI18n")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CommitData data = new CommitData();
        data.setBadNum(20);
        data.setCommentNum(30);
        data.setGoodNum(49);
        String demoString = gson.toJson(data);
        Log.d(TAG, "onCreate: " + demoString);

        demoString = customGson.toJson(data);
        Log.d(TAG, "onCreate: " + demoString);
    }
}

除了上面这种写法,我们还可以这样使用:效果是相同的。

public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {
      bind(GsonProvider.class).annotatedWith(Names.named("Custom")).to(GsonProviderImpl.class);
        bind(GsonProvider.class).annotatedWith(Names.named("Serialize Nulls")).to(GsonNotSNProviderImpl.class);
    }
}

public class GsonProviderImpl implements GsonProvider {
    @Override
    public Gson get() {
        return new GsonBuilder().
                serializeNulls().
                create();
    }
}
public class GsonNotSNProviderImpl implements GsonProvider{
    @Override
    public Gson get() {
        return new GsonBuilder().create();
    }
}

除此之外,RoboGuice还提供手动绑定实现Provider方法的方式,代码如下。

public class GuiceModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(GsonProvider.class).annotatedWith(Names.named("Serialize Nulls")).to(GsonNotSNProviderImpl.class);

        bind(GsonProvider.class).annotatedWith(Names.named("Custom")).toProvider(GsonImpl.class);
    }

    static class GsonImpl implements Provider<GsonProviderImpl>{

        @Override
        public GsonProviderImpl get() {
            return new GsonProviderImpl();
        }
    }
}
@ContentView(R.layout.activity_main)
public class MainActivity extends RoboFragmentActivity {

    private static final String TAG = "MainActivity";

    @Inject
    @Named("Serialize Nulls")
    GsonProvider gson;

    @Inject
    @Named("Custom")
    GsonProvider customGson;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CommitData data = new CommitData();
        data.setBadNum(20);
        data.setCommentNum(30);
        data.setGoodNum(49);
        String demoString = gson.get().toJson(data);
        Log.d(TAG, "onCreate: " + demoString);

        demoString = customGson.get().toJson(data);
        Log.d(TAG, "onCreate: " + demoString);
}

最后,还有一些比较小的点,比如Module可以存在默认的构造方法,传入的是Application,绑定时候的作用域、使用Provider注解的作用域(下图中in.(Singleton.class))等等。

public class GuiceModule extends AbstractModule {

    private Application application;

    public GuiceModule(Application application) {
        this.application = application;
    }

    @Override
    protected void configure() {
        bind(GsonProvider.class).annotatedWith(Names.named("Serialize Nulls")).to(GsonNotSNProviderImpl.class);

        bind(GsonProvider.class).annotatedWith(Names.named("Custom")).toProvider(GsonImpl.class).in(Singleton.class);
    }

    static class GsonImpl implements Provider<GsonProviderImpl>{

        @Override
        public GsonProviderImpl get() {
            return new GsonProviderImpl();
        }
    }
}

四.其他常用方法

RoboGuice还提供了IntentExtra的获取,但是注意,如果标注@InjectExtra的value没有找到对应的数据,则app会crash,如果允许获取不到extra,则必须将optional = true。

除此之外,RoboGuice提供了直接获取图中对象的方法,如下的Gson对象获取。

@ContentView(R.layout.activity_second)
public class SecondActivity extends RoboFragmentActivity {

    @InjectExtra(value = "pull", optional = true)
    String pull;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Gson gson = RoboGuice.getInjector(this).getInstance(Gson.class);
        String demoStr = gson.toJson(pull);
        Ln.d(demoStr);
    }
}

五.Events、Ln

RoboGuice提供了默认的观察者模式,我们可以接收Activity的生命周期事件。

Ln则是RoboGuice的log神器,使用比较方便。

@ContentView(R.layout.activity_main)
public class MainActivity extends RoboFragmentActivity {

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

    public void doSomethingOnResume(@Observes OnResumeEvent event) {
        if (event.getActivity() != null) {
            Ln.e("onResume");
        }
    }
}
03-03 18:40:20.053 12381-12381/github.pedroneer.roboguice E//MainActivity.java:72: main onResume

此外还可以自定义事件,可以在RoboGuice的Wiki查看,这里不赘述。

https://github.com/roboguice/roboguice/wiki/Using-Events-in-your-RoboGuice-application

posted @ 2016-03-08 12:53  pedro_neer  阅读(842)  评论(0编辑  收藏  举报