DroidParts 中文系列教程(基于官方教程)

DroidParts中文系列教程(基于官方教程)

(一)DroidParts框架概况

2014418星期五

11:36

他是一个精心构造的安卓框架,包括下面这些基本功能

  1. DI依赖注入,可以注入ViewFragmentServices,资源等
  2. ORM:高效简单的持久化工具
  3. EventBus:可以发送和接受事件(消息)
  4. 简单的JSON序列和反序列化工具,而且支持处理嵌套对象
  5. 改进类的AsyncTaskIntentService,包括一场处理和结果监听
  6. 日志封装:可以自定填充tag
  7. RESTClient:支持get, post, put, delete,支持读流,支持JSON
  8. ImageFetcher支持异步绑定图片到ImageView上面,支持缓存,淡入淡出,透明
  9. 其他的一些工具类
  10. 支持2.2+,默认的包不到300kb

 

 

 

注意:教程里面涉及到的方法都在官方提供的sample里面有,所以要想查看示例,可以仔细研究那两个Sample,尤其是DroidPartsGram,另外一个是一个比较老的示例了。

 另外,我在翻译时会把Activity翻译成活动,Fragment翻译成片段,不喜欢这种翻译的自己打算!

GitHub地址:https://github.com/yanchenko/droidparts(测试、源码、样例都在这里)

官网:http://droidparts.org/index.html(教程在这里)

 

 (二)开始

官方Github库的结构:

  1. droidparts - DroidParts库项目:安卓库项目
  2. droidparts-support - DroidParts Support Library.对应于安卓官方的低版本支持库
  3. droidparts-samples - 两个学习的示例:

    DroidPartsGram - a better Instagram alternative. (:

    droidparts-sample - an older fragment-free app.

  1. droidparts-test - unit tests.

 

 

给你的项目添加DroidParts

几种方法:

  1. 下载droidparts-x.y.z.jar,把他放在libs文件夹下。
  2. 通过Gradle或者Maven添加依赖的形式:如果是Maven的话:(这里我用的是当前的最新版2.0.3)
    <dependency>
      <groupId>org.droidparts</groupId>
      <artifactId>droidparts</artifactId>
      <version>2.0.3</version>
    </dependency>
    如果是Gradle的话:
    compile 'org.droidparts:droidparts:2.0.3'
  3. DroidParts作为一个库项目。

DroidParts本身的依赖

  1. 如果你用*.sherlokc.*包的话,需要添加ActionBarSherlock(这个类是官方的一个可以让Android3.0以下的版本支持Actionbar的类
  2. 如果你用*.support.*包的话,或者如果你想用ImageFetched类在Android3.0以下的版本做图片的内存缓存的话,你需要添加安卓支持库(Android Support Library
  3. 如果你想在Android3.0以下使用RESTClient进行POSTing多个文件的话,需要ApacheHttpMine支持。
  4. 如果要用OkHttpWorker的话,需要OkHttp的支持。

 

配置

  1. 如果你想使用ORM,你需要继承AbstractDBOpenHelper
  2. 如果你想支持依赖注入的话,需要在清单文件中指定一个AbstractDependencyProvider的子类,详见后面依赖注入章节。
  3. AbstractApplication:这个是可选的。
  4. 日志框架:Logging,可以在清单文件中进行配置。

ProGuard

记得在你的ProGuard配置中包含proguard-droidparts.cfg文件,否则可能导致框架失效。

 来自 <http://droidparts.org/getting_started.html>

(三)依赖注入

依赖注入概述:

  1. DroidParts可以注入视图,片段,服务,资源等,当然也可以自定义注入。
  1. 自定义注入通过DependencyProvider类来实现。
  2. 如果你继承了来自DroidParts的活动、服务、片段(Fragments),则会自动注入,如果要手动注入,可以调用:Injector.inject(…).我们可以自己看一下DroidParts提供的这些活动、服务、片段,其实实现都很简单,也是调用了上面那个语句实现的注入,还包括其他几条简单的语句。

DependencyProvider

  1. 自定义的DependencyProvider需要继承AbstractDependencyProvider类,然后返回对象来实现注入。比如,你要注入CustomObject类,你可以通过实现下面这样的方法:

public CustomObject getCustomObject();

//

public CustomObject getCustomObject(Context ctx);

    至于调用哪个看你的注入类的需要,如果注入类需要Context的话就调用第二个。

 

配置

  1. AbstractDependencyProvider的源码发现,他有一个特殊的方法需要实现:

public abstract AbstractDBOpenHelpergetDBOpenHelper()

  1. 如果需要自定义依赖注入,必须在清单文件(AndroidManifest.xml)中加入下面这样的元信息:

<application >

<meta-data
        android:name="droidparts_dependency_provider"
        android:value=".DependencyProvider" />

</application>

  1. 然后在你需要注入的对象上添加注解@InjectDependency表示注入自定义的依赖

 

 

注解:

  1. DroidParts提供了很多的注解,如:
  • @InjectView:

@InjectView
Button btn1;
//和这个语句是相同的作用: btn1 = (Button)findViewById(R.id.btn1)

@InjectView(id=R.id.view_btn, click=true)
Button btn2;
// btn2 = (Button)findViewById(R.id.view_btn)
// btn2.setOnClickListener(this);

如果添加了click=true,看下面的同义语句,我们就可以看出,你必须让你的类实现View.OnClickListener方法。

其他的一些注解:

@InjectBundleExtra:

Activity中等价于: getIntent().getExtras().getXX().

Fragment中等价于: getArguments().getXX().

@InjectDependency - 自定义注入

@InjectResource - 注入res中的下面这些类型的资源:String, String[], Drawable等。

@InjectSystemService 等价于: getSystemSerice(Context.SERVICE_NAME).

@InjectFragment - FragmentActivity中,通过指定布局文件来注入一个Fragment.

@InjectParentActivity -在一个Fragment类中,通过这个注入指定他所属的Activity

这一节的内容例子可以参见官方提供的例子:droidparts-sample

参考:http://droidparts.org/di.html

 (四)Activity

DroidParts包含了许多基本的活动类,都是在官方的Activity基础之上融合进了框架的内容(即继承了官方对应的Activity类)。主要包括:

 

org.droidparts.activity.* - Android 3.0+ with fragments support.

org.droidparts.activity.legacy.* - Android 2.2+ without Fragments.

org.droidparts.activity.support.v4.* - Android 2.2+ with Android Support Library Fragments.

org.droidparts.activity.support.v7.* - Android 2.2+ with Android Support Library Fragments & ActionBar.

org.droidparts.activity.sherlock.* - Android 2.2+ with ActionBarSherlock & Fragments.

你可以继承合适的Activity来进行你的开发,或者调用下面这句话:

Injector.inject(this);

效果差不多,看一下上面这些Activity发现,基本上就是实现了注入。(还有EventBus实现等)

如果使用上面的那些活动类的话,你会多一个额外的生命周期函数叫onPreInject(),这个方法的用处是设置一些数据,这些数据可能在注入的时候有需要。比如setContentView()必须在这个周期方法中实现。

Activity

为3.0-提供了几个函数.

setActionBarReloadMenuItem(MenuItem menuItem):设置某个菜单按钮为刷新按钮

setActionBarLoadingIndicatorVisible(boolean visible):设置正在刷新的指示器,就是loading动画。

setFragmentVisible(boolean visible,Fragment… fragments):设置fragment的可见性

 

SingleFragmentActivity

 

实现了一个包含单一Fragment的Activity,通过实现抽象方法onCreateFragment来实现

 

TabbedFragmentActivity

包含下面的一些方法:

addTab

setCustomAnimations

setCurrentTab,getCurrentTab设置和获得当前Tab

onTabChanged:当标签页改变时的操作

这些方法可以帮助我们构建一个包含标签页的Activity,每个Activity是一个Fragment

 

 (五)ORM

ORM:对象关系映射

DroidParts的实现是传统的DAO方式:一个Entity 类+EntityManager类。

DBOpenHelper

AbstractDBOpenHelper继承自SQLiteOpenHelper,他包含了一些方法包括基于Entity类的创建和升级表,当然,你也可以手动改变schema

Setup

  1. 继承AbstractDBOpenHelper抽象类
  2. 通过DependencyProvider返回一个它的实例
  3. 在子类中重写onCreateTables(…)方法,在里面调用createTables(…)去创建你需要的所有表
  4. 查看下面可用的一些工具类和方法(helpers)。

Helper方法

 

  1. createTables(SQLiteDatabase db,Class<? extends Entity>… entityClasses):

  创建表,后面的参数是不固定的,因此你可以创建任意多张表,当然是通过实体类创建的

  1. createIndex(SQLiteDatabase db,String table,boolean unique,String firstColumn,String… otherColumns):

  创建索引,只要要包含一个索引列,其他的索引列可选

  1. addMissingColumns(SQLiteDatabase db,Class<? extends Entity>…entityClasses):比如,可以在onUpgrade()方法中调用去增加一个新的列。
  2. dropTables(SQLiteDatabase db,String optionallTableNames):删除表了
  3. executeStatements(SQLiteDatabase db,ArrayList<String> statements):执行一系列的SQL语句并支持事务,如果失败则全部返回

Entity

我们的模型类必须继承Entity或者其子类,Entity已经定义了public long id作为主键,这个主键是自动生成的。

  1. @Table注解:name属性指定该实体类对应的表名
  2. @Column注解:name指定对应的列名,nullable定义是否可空,unique定义是否唯一,eager:表示这里对应一个外键,这个外键是急加载,和lazy(懒加载)对立。就是这个实体一旦创建就要填充这个值(外键对应的实体,立即从数据库中调出),lazy是只有当调用的时候才会去数据库查询这个值。

EntityManager:实体管理器

你可以为你的每一个实体类定义一个实体管理器,方式是继承一个EntityManger,比如下面为实体Post生成一个实体类:

public class PostEntityManager extends EntityManager<Post> {

public PostEntityManager(Context ctx) {
      super(Post.class, ctx);
   }

public Select<Post> selectPostsAfterYear(int year) {
      return select().where("year", Is.GREATER, year);
   }

}

CRUD方法

crud值得是简单的针对实体的create、read、update、delete实现。EntityManager里面都有默认实现:

  • boolean create(EntityType item), int create(Collection<EntityType> items):创建一个或多个实体类,即添加数据库记录
  • EntityType read(long id):按主键读取记录
  • boolean update(EntityType item), int update(Collection<EntityType> items):更新一个或多个实体,一般更新都是以主键为标识进行更新的。

boolean delete(long id), int delete(Collection<EntityType> items):删除一条或多条记录。

  • boolean createOrUpdate(EntityType item):创建或更新,数据库中没有则创建,有则更新。

SelectUpdateDelete构建器:

这几个类都是为了构建sql语句设计的,可以直接看例子:

 // Select is used to provide data to EntityCursorAdapter
Select<EntityType> select = select().columns("_id", "name").where("external_id", Is.EQUAL, 10);
//上面这个解释就是查询external_id等于10的所有记录,得到_id和name两个列的值的这样一个查询

// alternatively, call execute() to get the underlying Cursor
Cursor cursor = select().where("name", Is.LIKE, "%%alex%%").execute()

//调用execute()以后得到的是一个Cursor(游标)类,这个和数据库中的Cursor是一个意思。

// use Where object for complex queries
Where haveCoordinaltes = new Where("latitude", Is.NOT_EQUAL, 0).or("longitude", Is.NOT_EQUAL, 0);
select().where("country", Is.EQUAL, "us").where(haveCoordinates);

//其实就是构建SQL语句where子句的过程,这个不需要你写SQL语句而已。where和or这些方法都是非终结方法,所以可以用这种连续调用的方式进行构造。

 

Misc. Methods 其他杂项:

Select的其他一些方法:

long[] readIds(Select<EntityType> select):通过查询语句得到一个主键数组。

EntityType readFirst(Select<EntityType> select):得到一个查询的第一条结果。

ArrayList<EntityType> readAll(Select<EntityType> select):得到一个查询的所有结果。

EntityType的其他一些方法:

void fillForeignKeys(EntityType item,String… columnNames):看名字基本上知道意思了吧,就是填充外键了。

 

多对多映射:

 

@Table(name="track_to_tag")
public class TrackToTag extends Entity {
    @Column(nullable = false)
    public Track track;
    @Column(nullable = false)
    public Tag tag;
}

 

(六)EventBus

这个传递数据用的吗?其实还算有用。我的一个Fragment执行完毕的这样一个信息,可以传递给父Activity。

 

EventBus也就是Message Bus(消息总线),子系统之间通过消息总线进行消息的异步传输和处理,系统功能模块之间实现松散耦合。

 

Event

Event中包含了一些数据。

Event可以通过EventBus.postEvent(…)或者EventBus.postEventSticky(…)意思是他的最新版本将会传递给最新注册的接收者。

Event保证会传送到主线程中

EventReceiver:事件接收者

  1. 你的Activity必须是DroidParts的Activity。
  2. 通过注解:@ReceiveEvents
  3. 如果你的Activity不是继承自DroidPartsActivity,你也可以通过调用EventBus.registerReceiver(…)去实现。可以参见DroidParts的Activity源码。记得要EventBus.unregisterReceiver(…)。比如:注册可以在onResume()中,取消注册在onPause()中。

 (七)Adapters

EntityCursorAdapter

CursorAdapter的封装,可以让我们直接操作实体类而不是数据库表的行。辅助EntityManager

  1. Widget Adapters查看源码org.droidparts.adapter.widget下面:

1ArrayAdapter是官方ArrayAdapter的封装

2TextWatcherAdapter是官方TextWatcher的一个实现,默认为EditText提供,并传入一个回调接口。

3SpinnerAdapter:继承自上面的ArrayAdapter,应用在下拉列表(Spinner)中

4StringSpinnerAdapter继承自SpinnerAdapter,即Spinner的列表项的数据是String类型。

  1. ViewHolder:可以实现自动注入,其实就是Injector.inject(this)的封装。可以查看源码org.droidparts.adapter.holder下面,实现非常简单。

 (八)PrefsManager 

SharedPreferences的一个简单的封装,让他支持类似SQLIteOpenHelper的风格。貌似现在官方不推荐SharedPreferences,所以设置为公共字段的类型都是!@Deprecated的。很简单,大家看看源码就可以了,就一个类。

 

包位置:org.droidparts.persist

有一个AbstractPrefsManager类,它支持配置版本的升级(init())和各种类型数据的读取和保存(包括Integer,Long,Boolean,String)。

 (九)JSONSerializer

将模型转换为JSON字符串或者将JSON字符串转化为模型

 

Models你的转换类必须是Model或者其子类,我们用的实体类本身也是Model的子类

@Key注解
@key注解指定了该字段的值来源于JSON的那个字段

Nested JSON Object

嵌套的JSON对象。如果该字段的值来源于子对象中,则可以通过Key.SUB可以得到子对象的值。

举个例子:

{"c1":{"d1",2},"c2","22","c3":22}

要映射为这样的一个Model对应的注解就是下面这样的:

class Entity extends Model{

@Key(name=="c1"+Key.SUB+"d1")

String c1d1;

@Key(name="c2")

String c2;

@Key(name="c3")

int c3;

}

 (十)改进的AsyncTask和RestClient

AsyncTask

AsyncTask SimpleAsyncTask支持一个AsyncTaskResultListener接口,可以返回结果或者异常。

 

IntentService

 

IntentService继承自ResultReceiver,而且还提供了removePendingIntents()方法

 

RESTClient

RESTClient是异步的,提供网络交互:

  1. 使用gzip|deflate 压缩
  2. 支持请求和响应 headers
  3. 对用户透明的缓存支持
  4. HTTP基本验证
  5. Etag &If-Modified-Since支持
  6. 将响应返回为流或字符串
  7. 发送多个文件

构造方法

  1. RESTClient(Context ctx)
  2. RESTClient(Context ctx,String userAgent)
  3. RESTClient(Context ctx,HTTPWorker worker)

方法
setCookieCacheEnabled(boolean enabled,boolean persistent)

setHeader(String key,String value)

autheticateBasic(String userName,String password)

setFollowRedirects(boolean true)

使用

  1. get(String uri)
  2. getInputStream(String uri);
  3. get(String uri ,long ifModifiedSince, String etag, boolean body)
  4. post(String uri,String contentType,String data)
  5. postMultipart(String uri,String name,File file)
  6. postMultipart(String uri,String name,String contentType,File file)
  7. put(String uri, String contentType,String data)
  8. delete(String uri)

每个方法返回为HTTPResponse或者抛出HTTPException

RESTCLIENT2

和RESTClient的区别是增加了JSON的支持

JSONObject getJSONObject(String uri)

HTTPResponse post(String uri,JSONArray data)

Workers &OkHttp

REtcLIENT内部是用worker来完成工作的。HttpUrlConnection worker被默认使用(API>13,支持缓存),如果在API<10的情况下,需要Apache HttpClient

可以自定义Worker,通过包含一个OkHttp:

 

new RESTClient(ctx, new OkHttpWorker(UserAgent.getDefault()))

 

(十一)ImageFecher

ImageFetcher

主要用来下载图片并且关联到ImageViews上面

  1. 支持内存和硬盘缓存
  2. 高效的图片解码
  3. 支持inBitmap
  4. 支持Cross-fade

Customing:自定义

可以参见BitmapMemoryCache和BitmapDiskCache的构造方法

ImageReshaper

后台处理图片的大小,圆角等,并且进行结果缓存

ImageFetcherListener:处理并增加回调接口

Pausing

ImageFetcher支持当进行滚轮滚动的时候的暂停:

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
   switch (scrollState) {
   case OnScrollListener.SCROLL_STATE_FLING:
      imageFetcher.pause();
      break;
   default:
      imageFetcher.resume(true);
   }
}

 

参见: http://droidparts.org/image_fetcher.html

 (十二)Logging

只是做了一层封装,不需要我们填写标签了,他自动帮我们填写了。

 

L.i("DroidParts is awesome!");
// or
L.w(anyObject);
// or
L.a("WTF?! %s", str);

开发模式下结果是:

ClassName.methodName():50 DroidParts is awesome!

如果部署了以后,标签就是包名了

 

配置

在清单文件中添加一个元信息来配置日志级别:

 

<application >

<meta-data
        android:name="droidparts_log_level"
        android:value="warn" />

</application>

支持的级别有:

disable:禁用

verbose:所有

debug, info, warn, error

assert:这个应该是测试用的断言吧

如果不配置的话,框架默认是verbose

 (十三)Utils和Widget

Droidparts提供了很多的工具类,可以查看org.droidparts.util.*包下面查看: 

EditTextValidator

是一个帮助做输入校验的工具类

BitmapUtils

给图片做尺寸等方面改变的工具类:Resizing bitmaps, rounding corners, etc.

IntentFactory

Intent工厂,帮助用户生产一些有用的Intent,如发送邮件,分享等:The most useful Intents: sending email, sharing, etc.

 

来自 <http://droidparts.org/utils.html>

 

DroidParts实现了两个比较实用的View,包括如下:

ClearableEditText

自定义清除按钮图片的方法:

 

<org.droidparts.widget.ClearableEditText
    android:drawableRight="@android:drawable/ic_action_remove"
    ... />

MultiSelectListPreference:多选列表设置项

 具体实用方法可以看源码或者官方提供的样例。

来自 <http://droidparts.org/widgets.html

 

(十四)DroidParts Support Library & FAQ

可以查看org.droidparts.util.*包下面查看:

 

EditTextValidator

是一个帮助做输入校验的工具类

BitmapUtils

给图片做尺寸等方面改变的工具类:Resizing bitmaps, rounding corners, etc.

IntentFactory

Intent工厂,帮助用户生产一些有用的Intent,如发送邮件,分享等:The most useful Intents: sending email, sharing, etc.

 

来自 <http://droidparts.org/utils.html>

 

 

Frequent Asking Question

注解的性能的问题:

一般增加了注解都会降低性能:因为要用到反射,框架作者做了实验得出的结论是他的注解对性能的影响可以忽略不记,具体的实验就不说了

当布局改变的时候应用会崩溃:

说的是有时候Eclipse不更新id,所以会导致应用Crash,要修复的话,可以点击Project->Clean.Android Studio应该没这个问题。

 

posted @ 2014-04-18 16:24  天外的星星  阅读(895)  评论(0编辑  收藏  举报