高质量 Android 开发框架 LoonAndroid 详解

整个框架式不同于androidannotations,Roboguice等ioc框架,这是一个类似spring的实现方式。在整应用的生命周期中找到切入点,然后对activity的生命周期进行拦截,然后插入自己的功能。

框架的说明

如果你想看ui方面的东西,这里没有,想要看牛逼的效果这里也没有。这只是纯实现功能的框架,它的目标是节省代码量,降低耦合,让代码层次看起来更

清晰。整个框架一部分是网上的,一部分是我改的,为了适应我的编码习惯,还有一部分像orm完全是网上的组件。在此感谢那些朋友们。整个框架式的初衷是为

了偷懒,之前都是一个功能一个jar,做项目的时候拉进去,这样对于我来说依然还是比较麻烦。最后就导致我把所有的jar做成了一个工具集合包。有很多框

架都含有这个工具集合里的功能,这些不一定都好用,因为这是根据我个人使用喜欢来实现的,如果你们有自己的想法,可以自己把架包解压了以后,源码拉出来改
动下。目前很多框架都用到了注解,除了androidannotations没有入侵我们应用的代码以外,其他的基本上都有,要么是必须继承框架里面的
activity,要么是必须在activity的oncreat里面调用某个方法。整个框架式不同于
androidannotations,Roboguice等ioc框架,这是一个类似spring的实现方式。在整应用的生命周期中找到切入点,然后对
activity的生命周期进行拦截,然后插入自己的功能。

如果需要混淆 第一步 你要先引入你得架包 -libraryjars   libs/android-support-v4.jar
-libraryjars   libs/loonandroid.jar 第二步 你要保证注解在代码优化的时候不能被删除掉
-keepattributes Signature -keepattributes Annotation第三步 support4 要排除掉 -dontwarn android.support.v4.**
-keep class android.support.v4.** { ; }
-keep interface android.support.v4.app.
* { ; }
-keep public class * extends android.support.v4.
*
-keep public class * extends android.app.Fragment 第四步 只要使用了注解的包名 全部排除掉 -dontwarn xxx.**
-keep class xxx.** { ; }
其中XXX替换成你使用了注解的包名第五步 保证R不被混淆 -keep class *
.R$* {
*;
} 即OK

框架的主要功能

其中分为以下几种:

  • 自动注入框架(只需要继承框架内的application既可)
  • 图片加载框架(多重缓存,自动回收,最大限度保证内存的安全性)
  • 网络请求模块(继承了基本上现在所有的http请求)
  • eventbus(集成一个开源的框架)
  • 验证框架(集成开源框架)
  • json解析(支持解析成集合或者对象)
  • 数据库(不知道是哪位写的 忘记了)
  • 多线程断点下载(自动判断是否支持多线程,判断是否是重定向)
  • 自动更新模块
  • 一系列工具类

一 自动注入框架

1 无需继承任何BaseActivity

举例:普通activity


  1. public class FourActivity extends Activity { 
  2.  
  3.        View xx; 
  4.  
  5.        @Override 
  6.        protected void onCreate(Bundle savedInstanceState) { 
  7.            super.onCreate(savedInstanceState); 
  8.            setContentView(R.layout.activity_main4); 
  9.            xx = find......; 
  10.            //--------------------------------------------------------- 
  11.            组件的初始化 
  12.            //--------------------------------------------------------- 
  13.        } 
  14.    } 

这其中我们会耗费大量的代码或者重复性的去些一些代码。特别是布局比较复杂的情况下。

如果用框架


  1. @InjectLayer(R.layout.activity_main3) 
  2. public class ThirdActivity extends Activity { 
  3.     @InjectView 
  4.     View xx; 

即可

像软件的说明页面,就是单纯的展示一个布局,那么就是


  1. @InjectLayer(R.layout.activity_main3) 
  2. public class ThirdActivity extends Activity { 

即可

整个ioc框架不需要你继承任何的acitivity,这样就保证了不会在你的代码结构层次上造成影响,因为有的时候你需要自己的BaseActivity来实现你公用的功能。

2 支持子父布局

这种情况下,对于一般的框架来说,做法有以下几种:

  • ActivityGroup  一般的ioc框架都需要继承框架内的activity,activitygroup会让很多框架用不了,现在ActivityGroup也是不提倡的了。
  • BaseActivity 一般的Ioc框架会需要你的BaseActivity 去继承框架内的activity
  • 中间用fragment 这样的情况也一样,你的FragmentActivity必须继承它的activity才能实现ioc框架功能。对于这个框架来说很容易实现

1 ActivityGroup  你不需要继承任何activity 和普通activity 实现方式(如上面的例子)

2 BaseActivity

见代码:

首先是BaseActivity        @InjectPLayer(R.layout.activity_com)        public class BaseActivity extends Activity {}

其中R.layout.activity_com是包括上下导航的布局,中间是一个view子activity只需要这么写即可

@InjectLayer(value = R.layout.activity_main, parent = R.id.common)
    public class MainActivity extends BaseActivity {}

当然 又会有问题了,那么我上下导航里面的点击事件怎么绑定,怎么去初始化,难道要每一个子activity都要去写吗?当然不需要


  1. @InjectPLayer(R.layout.activity_com) 
  2.   public class BaseActivity extends Activity { 
  3.   @InjectInit 
  4.   private void init() { 
  5.       MeApplication.logger.s("公共类的初始化"); 
  6.   } 
  7.  
  8.   // 这里是第一种交互事件注入方式(单击) 
  9.   @InjectMethod(@InjectListener(ids = { R.id.top, R.id.bottom }, listeners = { OnClick.class })) 
  10.   private void click2(View view) { 
  11.       Handler_TextStyle handler_TextStyle = new Handler_TextStyle(); 
  12.       switch (view.getId()) { 
  13.           case R.id.top: 
  14.               handler_TextStyle.setString("点击了顶部按钮(在基类中统一注册,也可以单独注册)"); 
  15.               handler_TextStyle.setBackgroundColor(Color.RED, 3, 5); 
  16.               Toast.makeText(this, handler_TextStyle.getSpannableString(), Toast.LENGTH_LONG).show(); 
  17.               break; 
  18.           case R.id.bottom: 
  19.               handler_TextStyle.setString("点击了底部按钮(在基类中统一注册,也可以单独注册)"); 
  20.               handler_TextStyle.setBackgroundColor(Color.RED, 3, 5); 
  21.               Toast.makeText(this, handler_TextStyle.getSpannableString(), Toast.LENGTH_LONG).show(); 
  22.           break; 
  23.           } 
  24.       } 
  25.   } 

如上 其中@InjectInit注解表示不管是在子activity还是父activity 都是在布局初始化完成以后才会调用,其先后顺序是

父布局layout->子布局layout->父布局ioc和事件绑定->子布局事件绑定。

父activity
中可以对所有的公用组件和事件进行初始化和绑定还没完,又会有另一个问题,如果我某个页面下导航的a按钮和其他页面底部a按钮的功能不一样
要单独设置怎么办。那么我们可以在子布局进行@InjectMethod和@InjectView进行事件绑定和组件注入,它们会覆盖父类中相同id的组
件的操作以下是view注入的方法说明:

@InjectPLayer

表示是Activity的setContentView

@InjectLayer(value = R.layout.activity_main2, parent = R.id.common, isFull = true, isTitle = true)

其中需要哪个参数就用哪个,value 是必须的
如果只有layout可以这么写@InjectPLayer(R.layout.activity_com)。其中value
表示layout,parent表示它在父布局中所对应组件的id 如上图中
中间显示区域的view的id。Isfull是否全屏,默认为false.isTitle 是否有标题,默认false;

@InjectView

自动注入view注解。

基本写法:

@InjectView
    TextView test;

其中test表示它在xml中对应的Id为test

@InjectView(R.id.next2)
    TextView test;

表示它在xml中对应的Id为next2

高级写法:

@InjectView(binders = { @InjectBinder(method = "click", listeners = { OnClick.class, OnLongClick.class }) })
    Button next, next3, next4;

其中表示对id为next,next3,next4进行注解,其中binders 表示绑定了以下事件,binders
是个数组,也就是说可以用多个InjectBinder绑定多个事件,也可以用listeners = { OnClick.class,
OnLongClick.class }来表示对组件注入了点击事件和长按事件

@InjectView(value = R.id.next2, binders = { @InjectBinder(method = "click", listeners = { OnClick.class }) })
    Button button;

对于变量名和组件id不一致的view则需要设置value Click 表示那些注入的事件触发以后所调用的方法,其必须在当前类内。 //
支持由参数和无参数 即click(View view)或者click() 当然click名字必须对于变量注解中的method = “click”


  1.   private void click(View view) { 
  2.         switch (view.getId()) { 
  3.             case R.id.next: 
  4.             startActivity(new Intent(this, ThirdActivity.class)); 
  5.             break; 
  6.             ... 
  7.         } 
  8.     } 
  9.  
  10. @InjectResource 
  11.  
  12.     @InjectResource 
  13.     String action_settings; 
  14.  
  15.     @InjectResource 
  16.     Drawable ic_launcher; 
  17.  
  18. InjectResource支持string和drawable的注解 
  19.  
  20. @InjectMethod 

// 底部导航栏 子类覆盖父类


  1. @InjectMethod(@InjectListener(ids = { R.id.bottom }, listeners = { OnClick.class, OnLongClick.class })) 
  2. private void click3(View view) { 
  3.     Handler_TextStyle handler_TextStyle = new Handler_TextStyle(); 
  4.     handler_TextStyle.setString("点击了底部按钮 子类覆盖了父类"); 
  5.     handler_TextStyle.setBackgroundColor(Color.RED, 3, 5); 
  6.     Toast.makeText(this, handler_TextStyle.getSpannableString(), Toast.LENGTH_LONG).show(); 

@InjectMethod是当我们对一个组件只需要触发而不需要find出来的时候用到。 ids 表示绑定哪些id,listeners 表示绑定哪些事件 这两个参数都是数组

当然 如果嫌注解字段太长,可以自己修改。这个是整个view的注入。


  1. @InjectInit 
  2.  
  3.     @InjectInit 
  4.     void init() { 
  5.         MeApplication.logger.s("子类的初始化"); 
  6.         test.setText("初始化完成,第一个页面"); 
  7.     } 

这个注解你在activity中添加到任何一个方法名上,那么,当所有的layout和所有的view以及事件绑定完毕以后,会第一个调用含有这个注解的方法。它相当于oncreat

注意:框架注解了整个activity的生命周期, @InjectOnNewIntent,@InjectPause,@InjectResume,
    @InjectRestart,@InjectStart,@InjectStop 其中OnDestroy无注解。
    如果Activity中有含有这些注解的方法 那么不同生命周期下回自动调用这些方法

二:Fragment的自动注入


  1. @Override 
  2. public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
  3.     this.inflater = inflater; 
  4.     View rootView = inflater.inflate(R.layout.activity_left, container, false); 
  5.     Handler_Inject.injectView(this, rootView); 
  6.     return rootView; 

只需要在onCreateView里面调用Handler_Inject.injectView(this, rootView);
    即可

在fragment中除了activity的生命周期注解和@InjectLayer注解无法使用外,组件绑定和事件绑定都可以使用,@InjectBefore也可以使用

@InjectBefore 是在组件初始化之前调用

三:图片下载框架

这个是图片框架重写了好多次了,总是有点问题,里面基本上每一段代码都有注释,还有一些bug,
    因为项目中用的还是这次重写之前的。
    如果大家发现问题,记得告诉我框架中调用的方法名和参数基本上都不会变,避免替换jar导致需要改动大部分代码。

整个图片下载的逻辑是这样的:

  • 1 根据url和view去调用图片下载的方法
  • 2 从缓存去拿bitmap
  • 3 如果bitmap不为空 判断是否针对这个url有单独的配置 没有则使用全局配置加载图片
  • 4 如果bitmap为空 则开启线程,放到本地线程池中,然后从本地文件读取
  • 5 如果文件存在,则转为bitmap放到缓存,然后重复2,然后9
  • 6 如果文件不存在,则开启线程放到网络线程池中去下载文件
  • 7 下载成功则放到本地sdcard 然后把文件转为bitmap放到缓存,然后重复2,然后9
  • 8 下载不成功,然后重复2,然后9
  • 9 如果bitmap不为空 判断是否针对这个url有单独的配置 没有则使用全局配置加载这张图片 如果bitmap为空 则显示失败的默认图

具体的流程 可以参考源码

缓存分为三层

第一层是LruCache(原理去百度)
    第二层是LinkedHashMap
    第三层是 view标记

1 当LruCache中的图片超过了规定了内存,那么从LruCache移除一个使用最少的,放到LinkedHashMap中
    2 当每一张图片的url对应一个count,一旦加载一张图片,那么这个url的count加1
    3 自定义AsyImageView继承ImageView,重写了onDetachedFromWindow方法,一旦
        AsyImageView从当前视图移除掉会调用onDetachedFromWindow该方法,此刻该图片所对应的url数目count减1
    4 因为listview中的imageview如果用了ViewHolder那么第3条就不适合了,此刻每一个imagview的hashCode对应一个url,
        一旦imagview更换了一个新的url,那么该imagview的hashcode上一个的引用将被移除,
        那么上一次显示的url所对应的count将减1
    5 当LinkedHashMap超过了规定限制的时候,那么遍历所有的count一旦count为0 则移除回收

图片下载使用

一:必须条件

必须在配置文件中添加配置,来打开图片下载引擎的初始化,为了减少启动时间,默认关闭。
    #开启框架内置的图片下载 如果不设置 则无法使用框架类的图片下载
    imageload_open=true

    二:使用方法

1 普通图片下载

ImageDownloader.download("网络和本地图片链接",mAsyImageView);

如果需要配置bitmap的高宽

第一种方式:

在xml布局文件中对AsyImageView的高宽进行设置

第二种方式:

全局图片配置,所有图片显示默认用此配置

GlobalConfig globalConfig = GlobalConfig.getInstance();
    globalConfig.setMaxWidth(w);

来设置

第三种

SingleConfig config = new SingleConfig();
    config ....设置宽高
    ImageDownloader.download("网络和本地图片链接",mAsyImageView,config )

其中优先级

第三种 > 第一种 > 第二种

其中GlobalConfig  支持的设置有高宽的设置,内存缓存的大小,默认图片,
        下载失败的图片,最大缓存数目,线程池,缓存类型,显示控制,listview得滑动监听,图片加载动画
        其中SingleConfig 支持的设置有高宽的设置,默认图片,下载失败的图片,下载进度,显示控制,加载动画

其中SingleConfig 优先于GlobalConfig

支持配置文件配置:

mAsyImageView.setTemplate("one");
    ImageDownloader.download("url",mAsyImageView);

其中one在配置文件里面配置,这样 不管在任何地方,只要AsyImageView.setTemplate(“one”);就可以使用名称为one的配置了。

支持本地文件加载调用接口不变。

需要进度显示的:


  1. SingleConfig config = new SingleConfig(); 
  2.  config.setDisplayer(new DisplayerLister() { 
  3.      @Override 
  4.      public void startLoader(AsyImageView imageView) { 
  5.          super.startLoader(imageView); 
  6.      } 
  7.      @Override 
  8.      public Bitmap finishLoader(Bitmap bitmap, AsyImageView imageView) { 
  9.          pin_progress_1.setVisibility(View.GONE); 
  10.          return bitmap; 
  11.      } 
  12.  
  13.      @Override 
  14.      public void progressLoader(int progress, AsyImageView imageView) { 
  15.          pin_progress_1.setProgress(progress); 
  16.          super.progressLoader(progress, imageView); 
  17.      } 
  18.  }); 
  19.  ImageDownloader.download("url",photo,config); 

其中url的服务器必须支持获取文件长度

需要显示动画的:如果是单独某一个图片


  1. SingleConfig config = new SingleConfig(); 
  2.    config.setDisplayerAnimation(new FadeInAnimation()); 

如果是全局的


  1. GlobalConfig config = new GlobalConfig(); 
  2. config.setDisplayerAnimation(new FadeInAnimation()); 

其中FadeInAnimation是框架自带的一个渐变的动画如果需要自定义 实现DisplayerAnimation接口即可

2 listview中图片下载

只要在listview的注解@InjectView(isasy=true)中添加了isasy=true(默认为false)
    那么系统会自动给你注入OnScrollListener滚动事件,以便实现图片飞行停止才加载,缓慢拖动加载的功能。如果你要实现自己的OnScrollListener

如下


  1.     @InjectBefore 
  2.     void test(){ 
  3.         //@InjectView(isasy=true)表示这个listview里面有网络图片下载,并且需要实现滑动停止才加载的功能 
  4.         //@InjectView(isasy=true)框架会给listview自动注入OnScrollListener,如果你自己也要滚动监听 
  5.         //那么请在此配置,如下 
  6.         GlobalConfig config = GlobalConfig.getInstance(); 
  7.         config.setOnScrollLoaderListener(new MyOnScrollListener()); 
  8.         System.out.println("before"); 
  9.     } 
  10.     //必须继承框架内的滚动监听 
  11.     class MyOnScrollListener extends OnScrollLoaderListener{ 
  12.     @Override 
  13.     public void onScrollListener(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 
  14.         ApplicationBean.logger.s("滚动监听:"+firstVisibleItem); 
  15.     } 
  16.  
  17.     @Override 
  18.     public void onScrollStateChange(AbsListView view, int scrollState) { 
  19.         ApplicationBean.logger.s("滚动状态"); 
  20.         } 
  21.     } 
  22.  
  23. @InjectBefore 表示在组件初始化以前开始调用,因为滚动监听必须在listview被初始化之前赋值,否则无效 将默认使用框架内的滚动监听 

3 无需显示的图片下载


  1. ImageDownloader.download("url", new LoaderLister() { 
  2.      @Override 
  3.      public void finishLoader(String url, File file) { 
  4.          System.out.println("下载完成"+file.getPath()); 
  5.      } 
  6.      @Override 
  7.      public void failLoader(String url) { 
  8.          System.out.println("下载失败"); 
  9.      } 
  10.  }); 

如果需要下载进度


  1. ImageDownloader.download("url", new LoaderLister() { 
  2.  
  3.        @Override 
  4.        public void startLoader(String url) { 
  5.            System.out.println("开始下载"); 
  6.            super.startLoader(url); 
  7.        } 
  8.  
  9.        @Override 
  10.        public void finishLoader(String url, File file) { 
  11.            System.out.println("下载完成"+file.getPath()); 
  12.        } 
  13.  
  14.        @Override 
  15.        public void progressLoader(int progress) { 
  16.            System.out.println("下载进度"+progress); 
  17.            super.progressLoader(progress); 
  18.        } 
  19.    }); 

来源:51CTO

时间: 2024-11-02 07:01:18

高质量 Android 开发框架 LoonAndroid 详解的相关文章

《Java和Android开发实战详解》——1.1节编程语言基础知识

1.1编程语言基础知识 Java和Android开发实战详解 "编程语言"(Programming Language)是人类告诉计算机如何工作的一款语言,如同人与人之间沟通使用自然语言,编程语言被设计用于人类与计算机之间进行沟通.从技术角度来说,编程语言就是一款将执行指令传达给计算机的标准通信技术. 1.1.1 程序.软件与应用程序 在说明编程语言之前,我们需要了解什么是程序.软件与应用程序.简单地说来,编程语言提供了语法,可以让我们编写程序代码来建立程序,程序经编译建立成应用程序后,

Android 文件选择器详解及实例代码_Android

     本文给大家讲解下Android文件选择器的使用.实际上就是获取用户在SD卡中选择的文件或文件夹的路径,这很像C#中的OpenFileDialog控件.        此实例的实现过程很简单,这样可以让大家快速的熟悉Android文件选择器,提高开发效率.        网上曾经见到过一个关于文件选择器的实例,很多人都看过,本实例是根据它修改而成的,但更容易理解,效率也更高,另外,本实例有自己的特点:        1.监听了用户按下Back键的事件,使其返回上一层目录.       

Android 指纹识别详解及实现方法_Android

最近项目需要使用到指纹识别的功能,查阅了相关资料后,整理成此文. 指纹识别是在Android 6.0之后新增的功能,因此在使用的时候需要先判断用户手机的系统版本是否支持指纹识别.另外,实际开发场景中,使用指纹的主要场景有两种: 纯本地使用.即用户在本地完成指纹识别后,不需要将指纹的相关信息给后台. 与后台交互.用户在本地完成指纹识别后,需要将指纹相关的信息传给后台. 由于使用指纹识别功能需要一个加密对象(CryptoObject)该对象一般是由对称加密或者非对称加密获得.上述两种开发场景的实现大

《Android游戏开发详解》——第3章,第3.6节图形

3.6 图形Android游戏开发详解基于文本的程序很容易构建,但是基于文本的游戏已经过时了.在本节中,我们将介绍如何使用Java类库中的类(尤其是javax.swing包中的类),来创建一个图形用户界面(Graphical User Interface ,GUI).你会发现,尽管添加一个简单的用户界面很直接,但GUI是一个很大的主题.我将只是提供一个快速的介绍,完全只是创建一个窗口和显示一个基于Java的游戏所需要的基础知识.如果你想要学习Swing的更多知识,并且要创建专业的应用程序,请访问

Android Animation动画详解(二): 组合动画特效

前言     上一篇博客Android Animation动画详解(一): 补间动画 我已经为大家介绍了Android补间动画的四种形式,相信读过该博客的兄弟们一起都了解了.如果你还不了解,那点链接过去研读一番,然后再过来跟着我一起学习如何把简单的动画效果组合在一起,做出比较酷炫的动画特效吧. 一. 动画的续播     如题,大家想想,如果一个页面上包含了许多动画,这些动画要求按顺序播放,即一个动画播放完成后,继续播放另一个动画,使得这些动画具有连贯性.那该如何实现呢? 有开发经验或者是逻辑思维

android网络应用开发详解

Android网络应用开发,主要有两种方式,一种是socket(是对tcp/udp协议的封装),另外一种就是使用Http协议,Android中主要提供了两种方式,HttpURLConnection和Apache HttpClient.下面对Android网络应用开发进行具体的阐述. 一.基于socket的网络通信 socket是为了网络服务提供的一种机制.通信的两端都是socket. 网络通信其实就是socket间的通信. 数据在两个socket间进行io传输. 1.基于udp的socket编程

Android系统service详解

Android存在3大类service, 处于android系统的不同框架层,具体解读如下: 1) init.rc中的service //关键词"service"  name  二进制可执行程序路径 service servicemanager /system/bin/servicemanager class core user system group system critical onrestart restart healthd onrestart restart zygote

Android 混淆代码详解及实例

  Android 混淆代码详解及实例         为了防止自己的劳动成果被别人窃取,混淆代码能有效防止被反编译,下面来总结以下混淆代码的步骤: 1. 大家也许都注意到新建一个工程会看到项目下边有这样proguard-project.txt一个文件,这个对混淆代码很重要,如果你不小心删掉了,没关系,从其他地方拷贝一个过来 2. 最重要的就是在proguard-project.txt添加混淆的申明了: a. 把所有你的jar包都申明进来,例如: -libraryjars libs/apns_1

Android Socket通信详解_Android

一.Socket通信简介  Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是"请求-响应方式",即在请求时建立连接通道,当客户端向服务器发送请求后,服务器端才能向客户端返回数据.而Socket通信则是在双方建立起连接后就可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端想服务器发送请求. 那么,什么是socket?Socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信.通过