上篇介紹了RoboGuice的接入及基本使用,其中涉及到了一個@Singleton和@ContextSingleton的註解,這些都是作用域的註解,這篇我們先說明有關作用域的問題。 一.作用域 Scope Scope指的是作用域,指的就是註入的對象的生命周期,RoboGuice提供了預設的幾個作用域
上篇介紹了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