原创文章,转载请注明
请先阅读《 android——框架的实现系列(依赖注入的实现) 》
由于接触android不久,对于每个界面必须要写一个activity和fragment的方式深恶痛绝。不知道有什么好的办法,结合开发web的经验,希望能把这个控制层解脱出来。
之前的web开发中,通过jersey加反射实现了一个控制层类,前端只需要在url中表明后端需要处理业务的类的类名,方法名和json参数即可自动转向业务逻辑层进行处理,通过该控制层类直接返回结果。从此再也不用写控制层了,从而把精力放到业务逻辑的实现上。
基于这个思路进行改造。虽然目标暂时不可能制定的和web一样,但是初步还是希望能更简洁,逻辑更清晰一些。
实现目标
1、整个app中只有一个MainActivity。当然,如果能把这个类也去掉就再好不过了,貌似不现实。该类提供主界面的样式和其他界面的跳转。
2、页面转向只需要调用一个方法 ,比如route(String position,Map<String,Object> args),position表示需要跳转的页面的类名,args表示需要传递的方法
3、页面的展现通过实现一个称为AALayout的抽象类来实现
这样做的好处在于,整个app的开发只需要写一次MainActivity,以后就不需要再写新的activity或fragment了。这样精力就集中在实现界面AALayout中了。
比如在MainActivity中进行如下调用
worldTime.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
Map<String,Object> args=new HashMap<String,Object>();
args.put("cityData", new ZoneCityData());
routeFromRoot(ZoneCityLayout.class.getName(),args);
}
});
worldTime为主界面的一个资源view,调用routeFromRoot即可跳转到ZoneCityLayout界面。
需要被管理的界面为了能被框架识别,需要加上@Componet注释,如下
@Component
@Layout(value=R.layout.common_time)
public class CommonTimeLayout extends AALayout{
}
实现方法
1、BaseActivity类
MainActivity需要继承该类。该类提供跳转的逻辑,思路是这样的
a、通过fragment进行跳转,这里把跳转任务转交给BaseFragment,这里有几个问题
a.1、getFragmentManager().beginTransaction().replace需要MainActivity的layout文件id,为了区别别的layout,因此使用@Root注释进行依赖注入
回顾上一篇文章可以知道,依赖注入在void onCreate(Bundle savedInstanceState)方法中实现即可
a.2、fragment界面不会取消它所在的activity的监听,这样的话监听会混乱。为了不至于混乱,通过标志位实现,通过BackHandledInterface接口来提供额外的方法来实时判断标志位。并提供了两个方法,一个是routeFromRoot一个是routeFormOthers,我也想把两个方法合成一个,但是没想到好的方法
a.3、由于fragment的出栈需要其他的操作,比如root标志位的获取,因此需要实现额外的接口BackHandledInterface,这里有两个操作,一个是退栈,一个是root标志的变更。
2、BaseFragment类
由该接到BaseActivity的跳转需求,由它来完成跳转任务,思路是这样的
a、由LayoutFactory类通过position来获得需要转向的界面AALayout类的对象,前面已经说过,所有的界面需要实现AALayout这个抽象类。
b、资源依赖注入到界面对象中,请参考上一篇文章
c、调用界面对象的getLayout(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState, Context context, Bundle argBundle)方法展示界面。该方法为AALayout的抽象方法,所有界面需要在此方法中实现界面的展示。
d、按了回退键时,通过BackHandledFragment类进行退栈管理,把当前fragment返回
3、BackHandledFragment类
该类为fragment的堆栈管理,通过getActivity()获得当前BaseActivity,再通过BackHandledInterface接口,BaseActivity实现了该接口进行退栈操作。
4、BackHandledInterface接口
该接口提供了退栈操作和root标志位处理的方法
5、AALayout类
该类为抽象类,所有界面需要继承它,实现它的抽象方法void getLayout(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState,Context context,Bundle argBundle),该方法用来展示界面
6、LayoutFactory类
该类通过需要跳转的界面类名返回界面对象。实现的方法通过BeanHelper获得实例即可。这也是为什么界面需要使用@Component进行标注,对于BeanHelper和@Component可以通过《 android——框架的实现系列(依赖注入的实现) 》这篇文章了解
以上就是所有的实现思路
下面来看一下代码吧
BaseActivity类
public class BaseActivity extends FragmentActivity implements BackHandledInterface{
protected BackHandledFragment mBackHandedFragment;
public static DBHelper helper;
protected boolean isRoot=true;
private int xmlId;
private int layoutId;
@Override
protected void onCreate(Bundle savedInstanceState) {
//框架入口,进行类容器,实例容器,数据库映射工具初始化
LoadHelper.init();
Class<?> cls=getClass();
String childClassName=cls.getSimpleName();
if(childClassName!=null)
//解析root注释,获得入口资源id
try {
if(cls.isAnnotationPresent(Root.class)){
Root root=cls.getAnnotation(Root.class);
xmlId=root.xml_id();
}
//获得布局资源
if(cls.isAnnotationPresent(Layout.class)){
Layout layout=cls.getAnnotation(Layout.class);
layoutId=layout.value();
setContentView(layoutId);
}
//依赖注入资源
Field[] fields=cls.getDeclaredFields();
for(Field field : fields){
Class<?> fcls=field.getType();
if(View.class.isAssignableFrom(fcls)){
if(field.isAnnotationPresent(Resource.class) && !Modifier.isStatic(field.getModifiers())){
field.setAccessible(true);
Resource resource=field.getAnnotation(Resource.class);
int rid=resource.value();
Method m=cls.getMethod("findViewById", int.class);
View v=(View) m.invoke(this, rid);
field.set(this, v);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
super.onCreate(savedInstanceState);
}
@Override
public void onDestroy(){
super.onDestroy();
if(helper!=null){
helper.close();
}
}
@Override
public void setSelectedFragment(BackHandledFragment selectedFragment) {
this.mBackHandedFragment = selectedFragment;
}
@Override
public void setIsRoot(boolean flag) {
this.isRoot=flag;
}
public void setmBackHandedFragment(BackHandledFragment mBackHandedFragment) {
this.mBackHandedFragment = mBackHandedFragment;
}
public void routeFromRoot(String position,Map<String,Object> args){
if(isRoot){
if(getFragmentManager().getBackStackEntryCount() == 1)
isRoot=true;
else
isRoot=false;
route(position,args);
}
}
public void routeFromOthers(String position,Map<String,Object> args){
isRoot=false;
route(position,args);
}
private void route(String position,Map<String,Object> args){
BaseFragment baseFragment = new BaseFragment();
/*
if(this instanceof Activity)
baseFragment.setFieldMap(getExtraField());
*/
mBackHandedFragment=baseFragment;
Bundle bundle = new Bundle();
bundle.putString("position", position);
if(args!=null){
for(Map.Entry<String, Object> argEntry : args.entrySet()){
Object arg=argEntry.getValue();
if(arg instanceof String){
bundle.putString(argEntry.getKey(), (String) arg);
}
else if(arg instanceof Integer){
bundle.putInt(argEntry.getKey(), (Integer) arg);
}
else if(arg instanceof Long){
bundle.putLong(argEntry.getKey(), (Long) arg);
}
else if(arg instanceof Float){
bundle.putFloat(argEntry.getKey(), (Float) arg);
}
else if(arg instanceof Double){
bundle.putDouble(argEntry.getKey(), (Double) arg);
}
else if(arg instanceof Boolean){
bundle.putBoolean(argEntry.getKey(), (Boolean) arg);
}
else if(arg instanceof Serializable){
bundle.putSerializable(argEntry.getKey(), (Serializable) arg);
}
else if(arg instanceof IBinder){
bundle.putBinder(argEntry.getKey(), (IBinder) arg);
}
else if(arg instanceof Bundle){
bundle.putAll((Bundle) arg);
}
}
}
baseFragment.setArguments(bundle);
getFragmentManager().beginTransaction().replace(xmlId,baseFragment)
.addToBackStack(null)
.commit();
}
@Override
public void onBackPressed(){
if(mBackHandedFragment == null || !mBackHandedFragment.onBackPressed()){
if(getFragmentManager().getBackStackEntryCount() == 0){
super.onBackPressed();
}else{
if(getFragmentManager().getBackStackEntryCount() == 1)
isRoot=true;
else
isRoot=false;
getFragmentManager().popBackStack();
}
}
}
@SuppressWarnings("rawtypes")
protected void startService(Class service) {
Intent i = new Intent(this, service);
i.setFlags(32);
//i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startService(i);
}
}
BaseFragment类
public class BaseFragment extends BackHandledFragment {
private Map<String,Object> fieldMap;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
Context context = getActivity();
Bundle bundle = this.getArguments();
//通过position获得指定的界面
position = (String) bundle.get("position");
View layout = null;
try {
Class<?> cls=Class.forName(position);
AALayout aalayout;
if(fieldMap!=null){
aalayout = LayoutFactory.create(cls,fieldMap);
}
else{
aalayout = LayoutFactory.create(cls);
}
//获取布局资源,依赖注入资源
Class<?> acls=aalayout.getClass();
//获取layout资源
if(acls.isAnnotationPresent(Layout.class)){
Layout layoutAnno=acls.getAnnotation(Layout.class);
int layoutXmlId=layoutAnno.value();
layout = inflater.inflate(layoutXmlId, container, false);
Field[] fields=cls.getDeclaredFields();
for(Field field : fields){
Class<?> fcls=field.getType();
if(View.class.isAssignableFrom(fcls)){
//获取Resource资源
if(field.isAnnotationPresent(Resource.class) && !Modifier.isStatic(field.getModifiers())){
field.setAccessible(true);
Resource resource=field.getAnnotation(Resource.class);
int rid=resource.value();
View v=layout.findViewById(rid);
field.set(aalayout, v);
}
}
}
}
//相当于页面跳转
aalayout.getLayout(inflater,container,savedInstanceState,context,bundle);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return layout;
}
@Override
public boolean onBackPressed() {
return false;
}
public void setFieldMap(Map<String, Object> fieldMap) {
this.fieldMap = fieldMap;
}
}
BackHandledFragment类
public abstract class BackHandledFragment extends Fragment{
protected String position;
protected BackHandledInterface mBackHandledInterface;
public abstract boolean onBackPressed();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(getActivity() instanceof BackHandledInterface)){
throw new ClassCastException("Hosting Activity must implement BackHandledInterface");
}else{
this.mBackHandledInterface = (BackHandledInterface)getActivity();
}
}
@Override
public void onStart() {
super.onStart();
mBackHandledInterface.setSelectedFragment(this);
}
}
BackHandledInterface接口
public interface BackHandledInterface {
public abstract void setSelectedFragment(BackHandledFragment selectedFragment);
public abstract void setIsRoot(boolean flag);
}
AALayout类
public abstract class AALayout {
public abstract void getLayout(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState,Context context,Bundle argBundle);
}
LayoutFactory类
public class LayoutFactory {
@SuppressWarnings("unchecked")
public static AALayout create(Class<?> cls){
Object bean=BeanHelper.getBean(cls);
Method m;
try {
m = cls.getDeclaredMethod("getExtraField");
Map<String,Object> map=(Map<String, Object>) m.invoke(bean);
bean=create(cls,map);
} catch (Exception e) {
return (AALayout) bean;
}
return (AALayout) bean;
}
public static AALayout create(Class<?> cls,Map<String,Object> map){
Object bean=BeanHelper.getBean(cls);
try{
for(Map.Entry<String, Object> filedEntry : map.entrySet()){
String fieldName=filedEntry.getKey();
Field field=cls.getDeclaredField(fieldName);
field.setAccessible(true);
Object fieldBean=filedEntry.getValue();
field.set(bean, fieldBean);
}
}catch(NoSuchFieldException e){
return (AALayout) bean;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
return (AALayout) bean;
}
}
Root注释类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Root {
int xml_id();
}