spring for android

从服务端转到Android,最大的遗憾是不能用绿色健康的Spring了。

虽然依稀记得有spring for android,但是看了官网才发现根本没有IOC 和 AOP这些功能,只有http客户端功能

大失所望之下,自己动手搞了个最简单的demo,遂有创建个开源项目android下的spring的念头...

诸君可能会大大拍砖 性能啦之类的  确实值得考虑,但是随着Android项目日益庞大,代码量稳步上升,结构越来越混乱,

维护成本也越来越高,相比之下使用Spring的服务端好很多。有兴趣的看看demo代码吧


android-spring 项目:(作为android lib 项目)

项目地址: https://code.google.com/p/spring-android/


package android.springframework;

/**
 * Bean上下文
 */
public interface BeanContext {
	String tag = "springframework";

	<T> T getBean(Class<T> clazz);

	<T> T getBean(String name, Class<T> clazz);

	Object getBean(String name);
}

package android.springframework;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
 * 组件注解
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {


	String name() default "";


	boolean prototype() default true;
}

package android.springframework;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Resource 注入注解<p>
 * 目前只支持字段
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Resource {

	String name() default "";
}
/**
 * 
 */
package android.springframework;

import static org.ow2.asmdex.Opcodes.*;

import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.ow2.asmdex.AnnotationVisitor;
import org.ow2.asmdex.ApplicationReader;
import org.ow2.asmdex.ApplicationVisitor;
import org.ow2.asmdex.ClassVisitor;
import org.ow2.asmdex.FieldVisitor;

import android.content.Context;
import android.util.Log;

/**
 * 基于注解的BeanContext
 *
 */
public class AnnotaionBeanContext implements BeanContext {
	/**
	 * 类扫描的基础包
	 */
	private final String basePkgToScan;
	private HashMap<String, Object> namedBeans = new HashMap<String, Object>();
	private HashMap<Class, Object> classBeans = new HashMap<Class, Object>();
	private HashMap<String, Class> descToClass = new HashMap<String, Class>();
	/**
	 * Android Application Context - 供所有使用Context的非Android组件类公共使用
	 */
	private final Context context;

	private static final String COMPONNET_ANNO_DESC = "Landroid/springframework/Component;";
	private static final String RESOURCE_ANNO_DESC = "Landroid/springframework/Resource;";
	private static final int API_ASM = ASM4;

	public AnnotaionBeanContext(String basePkgToScan, Context context) {
		Log.d(tag, "AnnotaionBeanContext 初始化 ");

		this.basePkgToScan = basePkgToScan;
		this.context = context;

		try {
			initApplication();
		} catch (Exception e) {
			Log.e(tag, Log.getStackTraceString(e));
		}
	}

	private void initApplication() throws Exception {
		URL moduleResUrl = AnnotaionBeanContext.class.getClassLoader()
				.getResource("AndroidManifest.xml");
		String path = moduleResUrl.getFile();
		String apkPath = path.substring(path.indexOf('/'), path.indexOf("!/"));
		Log.d(tag, "apkPath = " + apkPath);

		ZipFile zipFile = new ZipFile(apkPath);
		ZipEntry classesEntry = zipFile.getEntry("classes.dex");
		InputStream classesStream = zipFile.getInputStream(classesEntry);
		ApplicationReader ar = new ApplicationReader(API_ASM, classesStream);
		AppVisitor appVistor = new AppVisitor();
		appVistor.setModulePackage(basePkgToScan);
		ar.accept(appVistor, 0);
		classesStream.close();
		zipFile.close();
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getBean(Class<T> clazz) {
		return (T) classBeans.get(clazz);
	}

	@Override
	public <T> T getBean(String name, Class<T> clazz) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Object getBean(String name) {
		// TODO Auto-generated method stub
		return null;
	}

	class AppVisitor extends ApplicationVisitor {
		private String modulePackage;
		private BeanClassVisitor beanClassVisitor = new BeanClassVisitor(api);

		public AppVisitor() {
			super(API_ASM);
		}

		public void setModulePackage(String modulePackage) {
			if (modulePackage.startsWith("L")) {
				this.modulePackage = modulePackage;
			} else {
				this.modulePackage = 'L' + modulePackage.replace('.', '/');
			}
		}

		@Override
		public ClassVisitor visitClass(int access, String name,
				String[] signature, String superName, String[] interfaces) {
			if (name.startsWith(modulePackage)) {
				return beanClassVisitor;
			}
			return null;
		}

	}

	class BeanClassVisitor extends ClassVisitor {

		public BeanClassVisitor(int api) {
			super(api);
		}

		String className;
		BeanFieldVistor beanFieldVistor = new BeanFieldVistor();
		boolean isComponent;
		Object bean;

		@Override
		public void visit(int version, int access, String name,
				String[] signature, String superName, String[] interfaces) {
			Log.i(tag, "*** className = " + name);
			className = name;
			beanFieldVistor.reset();
		}

		@Override
		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
			Log.i(tag, "@Annotation : desc = " + desc + ", visible = "
					+ visible);
			if (COMPONNET_ANNO_DESC.equals(desc)) {
				isComponent = true;
				bean = initBean(className);
				// for (Field f : bean.getClass().getDeclaredFields()) {
				// Resource resAnn = f.getAnnotation(Resource.class);
				// if (resAnn == null) {
				// continue;
				// }
				// f.setAccessible(true);
				// Object fval = initBean(f.getType());
				// if (fval != null) {
				// try {
				// f.set(bean, fval);
				// } catch (Exception e) {
				// e.printStackTrace();
				// }
				// }
				// }
			}
			return null;
		}

		@Override
		public FieldVisitor visitField(int access, String name, String desc,
				String[] signature, Object value) {
			if (isComponent) {
				beanFieldVistor.set(bean, access, name, desc, signature);
				return beanFieldVistor;
			}
			return null;
		}

	}

	class BeanFieldVistor extends FieldVisitor {

		public BeanFieldVistor() {
			super(API_ASM);
		}

		void reset() {
			access = 0;
			fieldName = "";
			fieldDesc = "";
			signature = null;
			ownerBean = null;
		}

		int access;
		String fieldName;
		String fieldDesc;
		String[] signature;
		Object ownerBean;

		void set(Object bean, int access, String name, String desc,
				String[] signature) {
			this.ownerBean = bean;
			this.access = access;
			this.fieldName = name;
			this.fieldDesc = desc;
			this.signature = signature;
		}

		@Override
		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
			if (RESOURCE_ANNO_DESC.equals(desc)) {
				try {
					Field field = ownerBean.getClass().getDeclaredField(
							fieldName);
					field.setAccessible(true);
					Object fval = initBean(field.getType());
					if (fval != null) {
						field.set(ownerBean, fval);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return super.visitAnnotation(desc, visible);
		}

	}

	private Object initBean(String className) {
		if (className.endsWith(";")) {
			className = className.substring(1, className.length() - 1);
		}
		if (className.contains("/")) {
			className = className.replace('/', '.');
		}
		Object ins = null;
		Class<?> clazz = descToClass.get(className);
		if (clazz == null) {
			try {
				return initBean(clazz = Class.forName(className));
			} catch (Exception e) {
				Log.e(tag, Log.getStackTraceString(e));
			}
		} else {
			ins = classBeans.get(clazz);
		}
		return ins;
	}

	private Object initBean(Class<?> clazz) {
		if (clazz == null) {
			return null;
		}
		Object ins = classBeans.get(clazz);
		if (ins != null) {
			return ins;
		}
		descToClass.put(clazz.getName(), clazz);
		try {
			Constructor<?> defCons = clazz.getConstructor();
			ins = defCons.newInstance();
			classBeans.put(clazz, ins);
		} catch (NoSuchMethodException e) {
			try {
				Constructor<?> cons = clazz.getConstructor(Context.class);
				ins = cons.newInstance(context);
				classBeans.put(clazz, ins);
			} catch (Exception e1) {
				Log.e(tag, Log.getStackTraceString(e1));
			}
		} catch (Exception e) {
			Log.e(tag, Log.getStackTraceString(e));
		}
		return ins;
	}
}
项目依赖了ASMdex, 下载地址为:http://download.forge.ow2.org/asm/asmdex-1.0.jar

清单文件为空即可(AndroidManifest.xml):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.springframework"
    android:versionCode="1"
    android:versionName="1.0" >
</manifest>
project.properties:
# Project target.
target=android-19
android.library=true

一个简单的Spring android 实现完事,下面是一个demo项目,依赖spring-android项目


package springdemo.modules.app;

import android.content.Context;
import android.springframework.Component;

/**
 *
 */
@Component
public class AppManager {
	private Context context;

	public AppManager(Context context) {
		this.context = context;
	}

	public String getApps() {
		String apps = "apps:" + context.getPackageName() + ";com.facebook";
		return apps;
	}

}
package springdemo.modules.push;

import springdemo.modules.app.AppManager;
import android.springframework.Component;
import android.springframework.Resource;

/**
 *
 */
@Component
public class PushManager {

	@Resource
	private AppManager appManager;

	public String push() {
		return "push: appManager.getApps()=" + appManager.getApps();
	}
}


package springdemo;

import android.content.Context;
import android.springframework.AnnotaionBeanContext;
import android.springframework.BeanContext;

/**
 *
 */
public class SpringApplicationHelper {
	private BeanContext beanContext;
	private Context context;

	private SpringApplicationHelper(Context context) {
		Context applicationCtx = context.getApplicationContext();
		beanContext = new AnnotaionBeanContext(getClass().getPackage()
				.getName() + ".modules", applicationCtx);
		this.context = applicationCtx;
	}

	private static SpringApplicationHelper instance;

	public static SpringApplicationHelper getInstance(Context context) {
		if (instance == null && context != null) {
			instance = new SpringApplicationHelper(context);
		}
		return instance;
	}

	public BeanContext getBeanContext() {
		return beanContext;
	}

}

package spring.android.demo;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import spring.android.demo.R;
import springdemo.SpringApplicationHelper;
import springdemo.modules.push.PushManager;

public class MainActivity extends Activity {

	private static final String tag = "spring-test";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		findViewById(R.id.btn_spring_test).setOnClickListener(
				new View.OnClickListener() {
					@Override
					public void onClick(View v) {
						try {
							testSpringBean_pushMgr();
						} catch (Throwable e) {
							Log.e(tag, Log.getStackTraceString(e));
						}
					}
				});
	}
    private void testSpringBean_pushMgr(){
    	SpringApplicationHelper springHelper = SpringApplicationHelper
				.getInstance(MainActivity.this);
		PushManager pushManager = springHelper
				.getBeanContext()
				.getBean(PushManager.class);
		String msg = "pushManager.push() = "
				+ pushManager.push();
		Log.i(tag, msg);
    }
}

布局文件(main/layout.xml):

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

</LinearLayout>

project.properties文件:

# Project target.
target=android-19
android.library.reference.1=../spring-android

AndroidManifest.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="spring.android.demo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        >
        <activity
            android:name="spring.android.demo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


界面效果:


日志输出结果:

11-09 20:14:46.566: I/springframework(12149): *** className = Lspringdemo/modules/app/AppManager;
11-09 20:14:46.566: I/springframework(12149): @Annotation : desc = Landroid/springframework/Component;, visible = true
11-09 20:14:46.566: I/springframework(12149): *** className = Lspringdemo/modules/push/PushManager;
11-09 20:14:46.566: I/springframework(12149): @Annotation : desc = Landroid/springframework/Component;, visible = true
11-09 20:14:46.576: I/spring-test(12149): pushManager.push() = push: appManager.getApps()=apps:spring.android.demo;com.facebook

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值