Facebook Twitter LinkedIn E-mail
magnify
Home Posts tagged "Roboguice"

Android RoboGuice2 使用指南(4): 综合示例Astroboy

前面介绍了RogoGuice2.0的基本用法,其它使用可以参见RoboGuice1.1开发指南,2.0中提供了对Fragment,View(自定义View中使用注入)的支持,本博客不再一一介绍。

本例使用的是RoboGuice 开发包中的简单示例Astroboy(阿童木)。涉及的使用RoboGuice2.0 的一些常用方法。

本例下载(Eclipse项目)。

下面对项目中RoboGuice2的使用进行解释。因为本例没使用自定义绑定,所以无需使用res/values/roboguice.xml 定义Module. 如有自定义模块,可以参见Android RoboGuice2 使用指南(2): 第一个例子Hello World

1. 类Astroboy

// There's only one Astroboy, so make it a @Singleton.
// This means that there will be only one instance of Astroboy in the entire
// app.
// Any class that requires an instance of Astroboy will get the same instance.
// This also means this class needs to be thread safe, of course
@Singleton
public class Astroboy {

	// Because Astroboy is a Singleton, we can't directly inject the current
	// Context since the current context may change depending on what activity
	// is using Astroboy
	// at the time. Instead we use the application context.
	// Vibrator is bound to context.getSystemService(VIBRATOR_SERVICE) in
	// DefaultRoboModule.
	// Random has no special bindings, so Guice will create a new instance for
	// us.
	@Inject	Application application;
	@Inject	Vibrator vibrator;
	@Inject	Random random;

	public void say(String something) {
		// Make a Toast, using the current context as returned by the Context
		// Provider
		Toast.makeText(application, "Astroboy says, \"" + something + "\"",
				Toast.LENGTH_LONG).show();
	}

	public void brushTeeth() {
		vibrator.vibrate(
				new long[] { 0, 200, 50, 200, 50, 200, 50, 200, 50, 200, 50,
						200, 50, 200, 50, 200, 50, 200, 50, 200, 50, 200, 50, },
				-1);
	}

	public String punch() {
		final String expletives[] = new String[] { "POW!", "BANG!", "KERPOW!",
				"OOF!" };
		return expletives[random.nextInt(expletives.length)];
	}
}

程序中只希望使用一个Astroboy实例,因此可以使用@Singleton标注,此后任何使用

@Inject Astroboy astroboy;

注入的Astroboy都会指向同一个实例,这也是符合Singleton设计模式的。

@Inject Application application; 注入Application实例。参见Android RoboGuice 使用指南(15):Inject Context

@Inject Vibrator vibrator; 注入Android Vibrator实例,参见Android RoboGuice 使用指南(16):Standard Injection

@Inject Random random; 对于普通的Java 类型(POJO),如果该类具有缺省构造函数(不带参数的等),也可以使用RoboGuice自动注入实例。

因此当Astroboy创建时,RoboGuice 自动为application, vibrator, random 创建实例,无需使用new 或参数传入来构造它们。

2. 类AstroboyRemoteControl

/**
 * A class to control Astroboy remotely.
 *
 * This class uses the current context, so we must make it @ContextSingleton.
 * This means that there will be one AstroboyRemoteControl for every activity or
 * service that requires one. Note that we actually ask for the Activity, rather
 * than the Context (which is the same thing), because we need access to some
 * activity-related methods and this saves us from having to downcast to an
 * Activity manually.
 *
 * It also asks RoboGuice to inject the Astroboy instance so we can control him.
 *
 * What you'll learn in this class - What @ContextScope means and when to use it
 * - How to inject an Activity instead of a Context (which is really the same
 * thing) - How to use RoboGuice's convenient and flexible logging facility, Ln.
 */
@ContextSingleton
public class AstroboyRemoteControl {

	// The Astroboy class has been decorated with @Singleton, so this instance
	// of Astroboy will be the same instance used elsewhere in our app.
	// Injecting an Activity is basically equivalent to "@Inject Context context",
	// and thus also requires @ContextScope. If you wanted, you could also
	// @Inject Application, Service, etc. wherever appropriate.
	@Inject	Astroboy astroboy;
	@Inject	Activity activity;

	public void brushTeeth() {
		// More info about logging available here:
		// http://code.google.com/p/roboguice/wiki/Logging
		Ln.d("Sent brushTeeth command to Astroboy");
		astroboy.brushTeeth();
	}

	public void say(String something) {
		Ln.d("Sent say(%s) command to Astroboy", something);
		astroboy.say(something);
	}

	public void selfDestruct() {
		Toast.makeText(
				activity,
				"Your evil remote control has exploded! Now Astroboy is FREEEEEEEEEE!",
				Toast.LENGTH_LONG).show();
		activity.finish();
	}
}

与Singleton类似的一个Scope标注为@ContextSingleton ,它表示对于每个Activity实例有一个实例,不同的activity对应不同的实例。

@Inject Astroboy astroboy; 注入同一个Astroboy实例(Singleton)。

@Inject Astroboy astroboy; 注入对应的Activity实例。

3. 类AstroboyMasterConsole

/**
 * This activity uses an AstroboyRemoteControl to control Astroboy remotely!
 *
 * What you'll learn in this class: - How to use @InjectView as a typesafe
 * version of findViewById() - How to inject plain old java objects as well
 * (POJOs) - When injection happens - Some basics about injection, including
 * when injection results in a call to an object's default constructor, versus
 * when it does something "special" like call getSystemService()
 */
@ContentView(R.layout.main)
public class AstroboyMasterConsole extends RoboActivity {

	// Various views that we inject into the activity.
	// Equivalent to calling findViewById() in your onCreate(), except more
	// succinct
	@InjectView(R.id.self_destruct)	Button selfDestructButton;
	@InjectView(R.id.say_text)	EditText sayText;
	@InjectView(R.id.brush_teeth)	Button brushTeethButton;
	@InjectView(tag = "fightevil")	Button fightEvilButton; // we can also use tags if we want

	// Standard Guice injection of Plain Old Java Objects (POJOs)
	// Guice will find or create the appropriate instance of AstroboyRemoteControl for us
	// Since we haven't specified a special binding for AstroboyRemoteControl,
	// Guice will create a new instance for us using AstroboyRemoteControl's default constructor.
	// Contrast this with Vibrator, which is an Android service that is
	// pre-bound by RoboGuice.
	// Injecting a Vibrator will return a new instance of a Vibrator obtained by
	// calling
	// context.getSystemService(VIBRATOR_SERVICE). This is configured in
	// DefaultRoboModule, which is
	// used by default to configure every RoboGuice injector.
	@Inject	AstroboyRemoteControl remoteControl;
	@Inject	Vibrator vibrator;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState); // @Inject, @InjectResource, and
											// @InjectExtra injection happens
											// during super.onCreate()

		sayText.setOnEditorActionListener(new OnEditorActionListener() {
			public boolean onEditorAction(TextView textView, int i,
					KeyEvent keyEvent) {

				// Have the remoteControl tell Astroboy to say something
				remoteControl.say(textView.getText().toString());
				textView.setText(null);
				return true;
			}
		});

		brushTeethButton.setOnClickListener(new OnClickListener() {
			public void onClick(View view) {
				remoteControl.brushTeeth();
			}
		});

		selfDestructButton.setOnClickListener(new OnClickListener() {
			public void onClick(View view) {

				// Self destruct the remoteControl
				vibrator.vibrate(2000);
				remoteControl.selfDestruct();
			}
		});

		// Fighting the forces of evil deserves its own activity
		fightEvilButton.setOnClickListener(new OnClickListener() {
			public void onClick(View view) {
				startActivity(new Intent(AstroboyMasterConsole.this,
						FightForcesOfEvilActivity.class));
			}
		});

	}

}

AstroboyMasterConsole 为主Activity,要使用RoboGuice,则Activity需从RoboActivity派生,其它如Service,Fragment等可以参见Android RoboGuice 使用指南(13):RoboGuice 功能描述

@InjectView(R.id.self_destruct) Button selfDestructButton;  注入View实例,功能同findViewById。 它的另外一种方法是使用Tag,如

@InjectView(tag = “fightevil”) Button fightEvilButton ,功能一样。

这个类使用@ContentView(R.layout.main) 为Activity指明ContentView,无需再调用setContentView.

4. 类FightForcesOfEvilActivity

/**
 * Things you'll learn in this class: - How to inject Resources - How to use
 * RoboAsyncTask to do background tasks with injection - What it means to be a @Singleton
 */
public class FightForcesOfEvilActivity extends RoboActivity {

	@InjectView(R.id.expletive)	TextView expletiveText;

	// You can also inject resources such as Strings, Drawables, and Animations
	@InjectResource(R.anim.expletive_animation)	Animation expletiveAnimation;

	// AstroboyRemoteControl is annotated as @ContextSingleton, so the instance
	// we get in FightForcesOfEvilActivity will be a different instance than
	// the one we got in AstroboyMasterConsole
	// @Inject AstroboyRemoteControl remoteControl;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.fight_evil);

		expletiveText.setAnimation(expletiveAnimation);
		expletiveAnimation.start();

		// Throw some punches
		for (int i = 0; i < 10; ++i)
			new AsyncPunch(this) {
				@Override
				protected void onSuccess(String expletive) throws Exception {
					expletiveText.setText(expletive);
				}

				// We could also override onException() and onFinally() if we
				// wanted

			}.execute();

	}

	// This class will call Astroboy.punch() in the background
	public static class AsyncPunch extends RoboAsyncTask<String> {

		// Because Astroboy is a @Singleton, this will be the same
		// instance that we inject elsewhere in our app.
		// Random of course will be a new instance of java.util.Random, since
		// we haven't specified any special binding instructions anywhere
		@Inject	Astroboy astroboy;
		@Inject	Random random;

		public AsyncPunch(Context context) {
			super(context);
		}

		public String call() throws Exception {
			Thread.sleep(random.nextInt(5 * 1000));
			return astroboy.punch();
		}
	}
}

@InjectResource(R.anim.expletive_animation) Animation expletiveAnimation; 注入资源,可以参见Android RoboGuice 使用指南(18):Inject Resources

从代码中可以看出使用RoboGuice 注入可以简化程序,运行结果如下图:

 

Android RoboGuice2 使用指南(3): Inject 自定义View

前面介绍了Android RoboGuice2 的HelloWorld示例,并介绍了从RoboGuice 1.1 升级到RoboGuice2.0 的主要注意事项。其它的基本概念和RoboGuice1.1基本一样,可以参见

本例介绍如何Inject自定义的View,Inject自定义的View和Android自带的View(如TextView,Button)方法一样。

本例使用一个自定义的TextView,每隔1秒显示当前时间。前定义如下:

//--------------------------------- PACKAGE ------------------------------------
package com.pstreets.guice.customview;

//--------------------------------- IMPORTS ------------------------------------
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.TextView;

import java.util.Calendar;
import java.util.Date;

public final class TimeTextView extends TextView {

	public TimeTextView(Context context, AttributeSet attrs) {
		super(context, attrs);
		postDelayed(mUpdateView, mRepeatTimePeriod);

	}

	private void setTimeString() {
		Calendar c = Calendar.getInstance();
		Date currentTime = c.getTime();
		String timeString = formatTime(currentTime);
		setText(timeString);
	}

	private Handler mHandler = new Handler();
	/**
	 * one second.
	 */
	private int mRepeatTimePeriod = 1000;

	private Runnable mUpdateView = new Runnable() {

		@Override
		public void run() {
			TimeTextView.this.setTimeString();
			// Force toggle again in a second
			mHandler.postDelayed(this, mRepeatTimePeriod);
		}

	};

	private String formatTime(Date time) {
		int hours = time.getHours();
		int miniutes = time.getMinutes();
		int seconds = time.getSeconds();
		String ret = "";
		if (hours < 10) {
			ret += "0";
		}
		ret += hours + ":";
		if (miniutes < 10) {
			ret += "0";
		}
		ret += miniutes + ":";
		if (seconds < 10) {
			ret += "0";
		}
		ret += seconds;

		return ret;
	}

}

修改main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView
    android:id="@+id/hello"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
<TextView

    android:layout_width="fill_parent"
    android:layout_height="0dp"
    android:layout_weight="1.0"

    />
<com.pstreets.guice.customview.TimeTextView
    android:id="@+id/txtTime"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"

    />
</LinearLayout>

定义该TimeTextView的id为txtTime。

这样可以在对应的Activity中使用InjectView 来给对应的txtTime 自动赋值(注入)。

 

@ContentView(R.layout.main)
public class GuiceDemo extends RoboActivity  {

	@InjectView (R.id.txtTime) TimeTextView txtTime;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        txtTime.setTextColor(0xFFFF0000);

    }

}

由于GuiceDemo是从RoboActivity派生的,在onCreate 第一行的super.onCreate() 完成注入功能。如果你对RoboGuice1.1 熟悉的话,在使用注入的变量前如txtTime,需要执行setContentView。
在RoboGuice2.0中你可以使用同样的方法,2.0还提供了一个简单的标注方法@ContentView ,如本例,为Activity设置ContentView。

本例下载 

从本例可以看出,和RoboGuice1.1 相比,RoboGuice2.0在使用上要简洁的多。本例只需从RoboActivity派生,不在需要定义Application等。

 

Android RoboGuice2 使用指南(2): 第一个例子Hello World

本例实现的功能和例子Android RoboGuice 使用指南(2):第一个例子Hello World一样,所不同的是本例使用RoboGuice2.0 来实现。

  1. 下载新的RoboGuice库,Roboguice2.0 库有四个库组成,如下图所示:

库可以从 http://code.google.com/p/roboguice/下载,也可以从本站下载

2. 创建一个新Android项目,比如GuiceDemo,目标平台Android1.5以上。

3. 一般可以在该项目下添加一个libs目录,将两个jar文件拷到libs目录下,然后通过: Project > Properties > Java Build Path > Libraries > Add  JARs

注:从ADT17开始,添加的jar文件需放在libs 子目录下,可以参见升级到ADT 17 出现dalvikvm: Unable to resolve superclass的问题

添加了对应guice 和roboguice库的引用之后,就可以开始编写第一个使用roboguice2 的例子。

使用roboguice2 的步骤:

Roboguice2 中不在含有RoboApplication 类,因此无需也不可能派生RoboApplication的子类。这里重复一下HelloWorld 的Layout 和类说明

1.  在这个简单的例子中,它使用的Layout 定义如下:

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:orientation=”vertical”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
>
<TextView
android:id=”@+id/hello”
android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
android:text=”@string/hello”
/>
</LinearLayout>
我们定义了一个TextView ,它的id为hello.

假定这个应用使用一个IGreetingService ,它有一个方法getGreeting() 返回一个字符串,至于IGreetingService 如何实现,GuideDemo 不需要关心。

Dependency injection 设计模式的一个核心原则为: Separate behavior from dependency resolution. 也就说将应用需要实现的功能和其所依赖的服务或其它对象分离。 对本例来说GuiceDemo只要知道它依赖于IGreetingService 服务,至于IGreetingService有谁实现GuiceDemo并不需要知道。

在Roboguice 中使用@Inject 来表示这种依赖关系。

public class GuiceDemo extends RoboActivity  {

 @InjectView (R.id.hello) TextView helloLabel;
 @Inject IGreetingService greetingServce;

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 helloLabel.setText(greetingServce.getGreetings());
 }
}
  • 使用RoboGuice 的Activity需要从RoboActivity派生(RoboActivity为Activity的子类).
  • 使用@Inject标注greetingServce依赖于IGreetingService服务
  • 使用@InjectView表示helloLabel 依赖于R.id.hello (XML)

代码中没有创建greetingServce 对象的代码(如 new xxx()) 和为helloLabel 赋值的代码。这些值都可以Roboguice 自动创建和赋值注入(Inject)到变量中。

为了说明问题,我们在代码中添加两个对getGreetings的实现,一个为HelloWorld, 一个为HelloChina:

public class HelloChina implements IGreetingService{

 @Override
 public String getGreetings() {
 return "Hello,China";
 }

}

public class HelloWorld implements IGreetingService{

 @Override
 public String getGreetings() {
 return "Hello,World";
 }

}

2. 到这里,你可能有些困惑,RoboGuice怎么知道使用那个类(HelloWorld或是HelloChina)为GuiceDemo中的greetingServce 赋值呢?这是通过在Module 中定义binding 来实现的。

在项目中添加一个GreetingModule (从AbstractModule 派生而非AbstractAndroidModule类)重载configure方法:

public class GreetingModule extends AbstractAndroidModule{

@Override
protected void configure() {
bind(IGreetingService.class).to(HelloWorld.class);
//bind(IGreetingService.class).to(HelloChina.class);

}

}

将IGreetingService 绑定到HelloWorld 类。

3. 在res/values/roboguice.xml 定义Module

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="roboguice_modules">
        <item>com.pstreets.guice.demo.GreetingModule</item>
    </string-array>
</resources>

可以将GreetingModule 绑定改为HelloChina ,对比一下:

通过改变binding ,GuiceDemo 显示了不同的结果,GuiceDemo不依赖于具体的实现,可以非常方便的改变接口的实现而无需更改GuiceDemo的代码。大大降低了类于类之间的耦合性。

本例下载(包括RoboGuice2库)

 

Android RoboGuice2 使用指南(1): 概述

RoboGuice最近推出了2.0版本。和1.1相比具有:

  • 提高了稳定性
  • 支持Fragment
  • 更简洁易用

但由于RoboGuice2 不完全向下兼容RoboGuice1.1,因此原来使用RobuGuice1.1开发的项目需要对代码做些修改才可以使用RoboGuice2.0.

这里主要说明一下使用Eclipse IDE开发环境升级到RoboGuice2.0 的一些升级注意事项:

1. 下载新的RoboGuice库,Roboguice2.0 库有四个库组成,如下图所示:

       库可以从 http://code.google.com/p/roboguice/下载,也可以从本站下载

2. 原先1.1中的RoboApplication 在2.0 已经不存在了。2.0使用上更方便,通常的应用如果不是有自定义绑定的话,无需再派生Application。

3. 如果你使用了自定义的Module来定义Bindings,在2.0中可以通过XML来定义,比如在res/values/roboguice.xml 定义

<?xml version=”1.0″ encoding=”utf-8″?>

<resources>

<string-array name=”roboguice_modules”>

<item>com.pstreets.guice.demo.GreetingModule</item>

</string-array> </resources>

4. 原先由AbstractAndroidModule派生的类,在2.0中改为AbstractModule ,如:

ublic class GreetingModule extends AbstractModule {

@Override  protected void configure() {   bind(IGreetingService.class).to(HelloWorld.class);   //bind(IGreetingService.class).to(HelloChina.class);     }

}

5. 修改AndroidManifest.xml ,去除原来定义的RoboApplication派生类定义,并在代码中去除RoboApplication派生类定义。

以上是主要从RoboGuice1.1 升级到2.0的主要步骤。在学习2.0之前,可以参考一下1.1的介绍,基本概念还是一样的。

 

Android RoboGuice 使用指南(20):总结

前面介绍了Roboguice的基本用法,英文原文可以参考 和其Javadoc . 目前版本为1.1

Roboguice 支持Android平台上的Dependency Injection, 降低类或模块之间的耦合性,以提高代码重用并增强代码的可维护性。

  1. Android RoboGuice 使用指南(1):概述
  2. Android RoboGuice 使用指南(2):第一个例子Hello World
  3. Android RoboGuice 使用指南(3):Bindings 概述
  4. Android RoboGuice 使用指南(4):Linked Bindings
  5. Android RoboGuice 使用指南(5):Binding Annotations
  6. Android RoboGuice 使用指南(6):Instance Bindings
  7. Android RoboGuice 使用指南(7):@Provides Methods
  8. Android RoboGuice 使用指南(8):Provider Bindings
  9. Android RoboGuice 使用指南(9):Untargetted Bindings
  10. Android RoboGuice 使用指南(10):Just-in-time Bindings
  11. Android RoboGuice 使用指南(11):Scopes
  12. Android RoboGuice 使用指南(12):如何绑定generic类型
  13. Android RoboGuice 使用指南(13):RoboGuice 功能描述
  14. Android RoboGuice 使用指南(14):Inject View
  15. Android RoboGuice 使用指南(15):Inject Context
  16. Android RoboGuice 使用指南(16):Standard Injection
  17. Android RoboGuice 使用指南(17):Inject Extra
  18. Android RoboGuice 使用指南(18):Inject Resources
  19. Android RoboGuice 使用指南(19):发送接收Events
 

Android RoboGuice 使用指南(19):发送接收Events

Roboguice 提供了对Context 生命周期相关的事件的send 和receive ,系统缺省支持的事件为:

  • OnActivityResultEvent
  • OnConfigurationChangedEvent
  • OnContentChangedEvent
  • OnContentViewAvailableEvent
  • OnCreateEvent
  • OnDestroyEvent
  • OnNewIntentEvent
  • OnPauseEvent
  • OnRestartEvent
  • OnResumeEvent
  • OnStartEvent
  • OnStopEvent

一个简单的例子如下:

public class MyActivity extends RoboActivity {
 // You must "register" your listener in the current
 // context by injecting it.
 // Injection is commonly done here in the activity,
 //but can also be done anywhere really.
 @Inject protected MyListeners myListeners;

}


// In this example, all the listeners are in a
// MyListeners class, but really they could
// be anywhere as long as it's registered.
// You can even put the listeners directly into
// your activity classes if you like!
class MyListeners {

 // 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!")
 }
}

有关Events的注意事项如下:

  • 在Context中使用@Inject定义事件的Listener.
  • Event只能在某一特定的Context(Activity)中传送,不能跨Context发送,接受。
  • Event除了提供上面列出的Context相关事件外,也可以使用自定义的事件。
  • @observes 只能应用到方法上,而不能应用到构造函数上。

下面使用一个自定义事件MyEvent,通过observer 这个自定义事件来发送,接收自定义事件。

public class EventDemo extends RoboActivity {

 @Inject protected EventManager eventManager;
 @InjectView (R.id.button) Button button;

 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.eventdemo);
 button.setOnClickListener(mGoListener);
 }

 private OnClickListener mGoListener = new OnClickListener()
 {
 public void onClick(View v)
 {
 eventManager.fire(EventDemo.this,new MyEvent());
 }
 };


 protected void handleEvent(@Observes MyEvent event){
 Toast.makeText(this, "Custom event",
 Toast.LENGTH_LONG).show();
 }
}


class MyEvent{
 //put any memeber you want here.
}

本例下载