RoboGuice 3.0 (二)進階篇

来源:http://www.cnblogs.com/pedro-neer/archive/2016/03/08/5253649.html
-Advertisement-
Play Games

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


上篇介紹了RoboGuice的接入及基本使用,其中涉及到了一個@Singleton和@ContextSingleton的註解,這些都是作用域的註解,這篇我們先說明有關作用域的問題。

一.作用域 Scope

Scope指的是作用域,指的就是註入的對象的生命周期,RoboGuice提供了預設的幾個作用域:

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • EventBus是一個Android上用的消息分發的類庫,非常靈活好用,主要的原理是利用了反射註冊以及調用. 本文是在閱讀EventBus的源碼過程中所記錄的東西, 遇到不懂的去查了,然後留下了鏈接. 有點流水賬,講得也不是很深入,如果有錯請幫忙指正.
  • 當Activity 處於Android 應用中運行時,它的活動狀態由 Android 以 Activity 棧的形式管理。當前活動的Activity位於棧頂。隨著不同應用的運行,每個Activity都有可能從活動狀態轉入非活動狀態,也可能從非活動狀態轉入活動狀態。 Activity 的生命周期 歸納
  • 所謂的單例就是:一個應用程式(app)只有一個實例化對象,這個對象就是單例,一般用於音樂播放器和工具類 在這裡教大家如何手動創建單例,一種是利用互斥鎖的方式,另一種是利用dispatch的一次性執行. 1//通過互斥鎖創建單例 2 +(instancetype)sharedNetWorkTools
  • 概述 我們平時也使用了很多的xcode插件,雖然官方對於插件製作沒有提供任何支持,但是載入三方的插件,預設還是被允許的.第三方的插件,需要存放在 ~/Library/Application Support/Developer/Shared/Xcode/Plug-ins文件夾中,尾碼名必須是.xcpl
  • 用eclipse新建Android工程時,自動生成的appcompat_v7出錯,有個紅色交叉,而且新建的Android工程有一個紅色感嘆號。 這時你去看看你新建的Android工程是不是沒有生成R文件。當你Refresh和Clean都毫無成效後,不妨更新sdk試試。 更新sdk的時候只需要安裝SD
  • Android的UI線程主要負責處理用戶的按鍵事件、用戶觸屏事件及屏幕繪圖事件等,因此開發者的其他操作不應該、也不能阻塞UI線程,否則UI界面將會變得停止響應——用戶感覺非常糟糕。(總之,開發者需要牢記:不要再UI線程中執行一些耗時的操作)。 為了避免UI線程失去響應的問題,Android建議將耗時
  • 在iOS系統,使用Url Scheme框架在APP間互相跳轉和傳遞數據,本文只介紹如果檢測和跳轉。 Url Scheme框架 如果你想知道ios設備中是否安裝QQ這個軟體,我們可以通過一個簡單方法判斷到 if ([[UIApplication sharedApplication] canOpenUR
  • 代碼示例:https://github.com/johnlui/Swift-On-iOS/tree/master/ControlOrientation/ControlOrientation 環境要求:Xcode 7 / Swift2.0 前兩天遇到了一個 “使用指定的不同屏幕方向打開新頁面” 的需求
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...