spring for android

来源:互联网 发布:估值建模 知乎 编辑:程序博客网 时间:2024/05/16 09:16

从服务端转到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")@Overridepublic <T> T getBean(Class<T> clazz) {return (T) classBeans.get(clazz);}@Overridepublic <T> T getBean(String name, Class<T> clazz) {// TODO Auto-generated method stubreturn null;}@Overridepublic Object getBean(String name) {// TODO Auto-generated method stubreturn 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('.', '/');}}@Overridepublic 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;@Overridepublic void visit(int version, int access, String name,String[] signature, String superName, String[] interfaces) {Log.i(tag, "*** className = " + name);className = name;beanFieldVistor.reset();}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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-19android.library=true

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


package springdemo.modules.app;import android.content.Context;import android.springframework.Component;/** * */@Componentpublic 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;/** * */@Componentpublic class PushManager {@Resourceprivate 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";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);findViewById(R.id.btn_spring_test).setOnClickListener(new View.OnClickListener() {@Overridepublic 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-19android.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 0