Android 依賴注入框架RoboGuice

xbynet發表於2015-05-01

概述

在開發應用時一個基本原則是模組化,並且盡最大可能性地降低模組之間的耦合性。Dependency injection 大大降低了類之間的依賴性,可以通過annotation描述類之間的依賴性,避免了直接呼叫類似的建構函式或是使用Factory來參加所需的類,從而降低類或模組之間的耦合性,以提高程式碼重用並增強程式碼的可維護性。
Google Guice提供了Java平臺上一個輕量級的 Dependency injection 框架,並可以支援開發Android應用。本指南將使用Android平臺來說明Google Guice的用法。(其他類似框架有Dagger,Buffernife)
RoboGuice 為Android平臺上基於Google Guice開發的一個庫,可以大大簡化Android應用開發的程式碼和一些繁瑣重複的程式碼。比如程式碼中可能需要大量使用findViewById在XML中查詢一個View,並將其強制轉換到所需型別,onCreate 中可能有大量的類似程式碼。RoboGuice 允許使用annotation 的方式來描述id於View之間的關係,其餘的工作由roboGuice庫來完成。

RoboGuice主要功能:
  • 控制元件注入:用@InjectViews方法初始化控制元件,例如:@InjectView(R.id.textview1)TextView textView1。
  • 資源注入:用@InjectResources方法初始化資源,例如:@InjectResource(R.string.app_name)String name。
  • 系統服務注入:用@Inject方法初始化並獲取系統服務,例如:@Inject LayoutInflater inflater。
  • POJO物件注入:用@Inject方法注入並初始化POJO物件,例如:@Inject Foo foo。
以下示例對比看看RoboGuice的作用:
不使用RoboGuice:
    public void onCreate(Bundle savedInstanceState) { 
            super.onCreate(savedInstanceState); 
            setContentView(R.layout.main);
            name      = (TextView) findViewById(R.id.name); 
            thumbnail = (ImageView) findViewById(R.id.thumbnail); 
            loc       = (LocationManager) getSystemService(Activity.LOCATION_SERVICE); 
            icon      = getResources().getDrawable(R.drawable.icon); 
            myName    = getString(R.string.app_name); 
            name.setText( "Hello, " + myName ); 
        } 
    } 
使用RoboGuice:
 @ContentView(R.layout.main)
    class RoboWay extends RoboActivity { 
        @InjectView(R.id.name)             TextView name; 
        @InjectView(R.id.thumbnail)        ImageView thumbnail; 
        @InjectResource(R.drawable.icon)   Drawable icon; 
        @InjectResource(R.string.app_name) String myName; 
        @Inject                            LocationManager loc; 

        public void onCreate(Bundle savedInstanceState) { 
            super.onCreate(savedInstanceState); 
            name.setText( "Hello, " + myName ); 
        } 
    } 

使用前準備工作

RoboGuice官方Demo: https://github.com/roboguice/roboguice/tree/master/astroboy

安裝

方式一:新增依賴庫:(不推薦,在androidStudio上報錯,很詭異。不知道為什麼)

方式二:在build.gradle中的dependecies片段中新增:(推薦)
dependencies {
    provided 'org.roboguice:roboblender:3.0'
    compile 'org.roboguice:roboguice:3.0'
}



參考https://github.com/roboguice/roboguice/wiki/InstallationNonMaven

繼承類:

activities, services, and fragments都需要繼承自RoboGuice提供的下列類:
  • RoboActivity
  • RoboListActivity
  • RoboExpandableListActivity
  • RoboMapActivity
  • RoboPreferenceActivity
  • RoboAccountAuthenticatorActivity
  • RoboActivityGroup
  • RoboTabActivity
  • RoboFragmentActivity
  • RoboLauncherActivity
  • RoboService
  • RoboIntentService
  • RoboFragment
  • RoboListFragment
  • RoboDialogFragment
  • etc.

 Injection

  • Inherit from RoboActivity
  • Set your content view
  • Annotate your views with @InjectView
public class MyActivity extends RoboActivity {
        @InjectView(R.id.text1) TextView textView;

        @Override
        protected void onCreate( Bundle savedState ) {
            setContentView(R.layout.myactivity_layout);
            textView.setText("Hello!");
        }
    }
@ContentView註解:
  @ContentView(R.layout.myactivity_layout)
    public class MyActivity extends RoboActivity {
        @InjectView(R.id.text1) TextView textView;

        @Override
        protected void onCreate( Bundle savedState ) {
            textView.setText("Hello!");
        }
    }

將View注入Fragment@InjectView

由於Fragment中的依賴注入發生在onViewCreated()中,所以需要在onViewCreated中呼叫super.onViewCreate()之後才能使用注入的View.
注意這裡的:onViewCreated是在onCreateView呼叫之後恢復儲存的狀態到View之前立馬呼叫,
@InjectView TextView commentEditText;

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    commentEditText.setText("Some comment");
}

注入Resource到Activity:@InjectResource

public class MyActivity extends RoboActivity {
    @InjectResource(R.anim.my_animation) Animation myAnimation;
}

注入System Service:@Inject

class MyActivity extends RoboActivity {
    @Inject Vibrator vibrator;
    @Inject NotificationManager notificationManager;

注入POJO:@Inject

注意:需要提供一個無引數構造器
class MyActivity extends RoboActivity {
    @Inject Foo foo; // this will basically call new Foo();
}
當然如果不想使用預設構造器,可以註解構造器:
class Foo {
    Bar bar;
    @Inject
    public Foo(Bar bar) {
        this.bar = bar;
    }
}
也可以註解POJO的Field:
class Foo {
    @Inject Bar bar;

    // Roboguice doesn't need a constructor, but you might...
}

注入Singleton

前提:對單例類使用@Singleton
@Singleton //a single instance of Foo is now used though the whole app
class Foo {
}
class MyActivity extends RoboActivity {
    @Inject Foo foo; // this will basically call new Foo();
}
注意:這個單例一旦建立會一直駐留記憶體,其他類似的還有@ContextSingleton,@FragmentSingletion,他們的作用範圍比較小,利於垃圾回收。

自定義Binding

public interface IFoo {}

public class Foo implements IFoo {}
public class MyActivity extends RoboActivity {
    //How to tell RoboGuice to inject an instance of Foo ?
    @Inject IFoo foo;
}
要想注入時繫結介面IFoo到Foo,例項化Foo.需要進行繫結。
步驟:
1.向AndroidManifest.xml中新增meta-data
<application ...>
    <meta-data android:name="roboguice.modules"
               android:value="com.example.MyModule,com.example.MyModule2" />                
</application>
2.繼承AbstractModule
public class MyModule extends AbstractModule { 
    //a default constructor is fine for a Module

    public void bind() {
        bind(IFoo.class).to(Foo.class);
    }
}
public class MyModule2 extends AbstractModule {
    //if your module requires a context, add a constructor that will be passed a context.
    private Context context;

    //with RoboGuice 3.0, the constructor for AbstractModule will use an `Application`, not a `Context`
    public MyModule( Context context ) {
        this.context = context;
    }

    public void bind() {
        bind(IFoo.class).toInstance( new Foo(context));
    }
}

注入到Service與BroadcastReceiver:

public class MyService extends RoboService {

   @Inject ComputeFooModule computeFooModule;
public class MyBroadcastReceiver extends RoboBroadcastReceiver {

   @Inject ComputeFooModule computeFooModule;

注入到自定義View

class MyView extends View {
    @Inject Foo foo; 
    @InjectView(R.id.my_view) TextView myView;

    public MyView(Context context) {
        inflate(context,R.layout.my_layout, this);
        RoboGuice.getInjector(getContext()).injectMembers(this);//注意這一行,為了使其正常工作。
    }

    public MyView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        inflate(context,R.layout.my_layout, this);
        RoboGuice.getInjector(getContext()).injectMembers(this);
    }

    public MyView(Context context, AttributeSet attrs) {
    super(context, attrs);
        inflate(context,R.layout.my_layout, this);
        RoboGuice.getInjector(getContext()).injectMembers(this);
    }

    @Override
    public void onFinishInflate() {
        super.onFinishInflate();
        //All injections are available from here
        //in both cases of XML and programmatic creations (see below)
        myView.setText(foo.computeFoo());
    }
}

使用RoboGuice Event

public class MyActivity extends RoboActivity {

    // Any method with void return type and a single parameter with @Observes annotation
    // can be used as an event listener.  This one listens to onResume.    
    public void doSomethingOnResume( @Observes OnResumeEvent onResume ) {
        Ln.d("Called doSomethingOnResume in onResume");
    }

    // As you might expect, some events can have parameters.  The OnCreate event
    // has the savedInstanceState parameter that Android passes to onCreate(Bundle)
    public void doSomethingElseOnCreate( @Observes OnCreateEvent onCreate ) {
        Ln.d("onCreate savedInstanceState is %s", onCreate.getSavedInstanceState())
   }

    // And of course, you can have multiple listeners for a given event.
    // Note that ordering of listener execution is indeterminate!
    public void xxx( @Observes OnCreateEvent onCreate ) {
        Ln.d("Hello, world!")
    }
}

本文還有很多特性沒有介紹到,後續可能會進行補充。
參考:
http://www.oschina.net/p/roboguice
https://github.com/roboguice/roboguice/wiki
http://mobile.51cto.com/abased-426620.htm

相關文章