首页 文章 精选 留言 我的

精选列表

搜索[端侧决策],共10000篇文章
优秀的个人博客,低调大师

[Android]Android端ORM框架——RapidORM(v1.0)

以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4748077.html Android上主流的ORM框架有很多,常用的有ORMLite、GreenDao等。 ORMLite: -优点:API很友好,使用比较方便简单稳定,相关功能比较完整。 -缺点:内部使用反射实现,性能并不是很好。 GreenDao: -优点:性能很不错, -缺点:API却不太友好,而且不支持复合主键,主键必须要有并且必须是long或者Long。持久类可以用它提供的模版生成,但是一旦使用了它的模版,持久类、DAO就不能随意去修改,扩展性不是很好。如果不使用它的模版,代码写起来就很繁琐。 所以结合了两者重新写了一个ORM:RapidORM(https://github.com/wangjiegulu/RapidORM) 特点: 1. 支持使用反射和非反射(模版生成)两种方式实现执行SQL。 2. 支持复合主键 3. 支持任何主键类型 4. 兼容android原生的android.database.sqlite.SQLiteDatabase和SqlCipher的net.sqlcipher.database.SQLiteDatabase。 缺点: 1. 不支持链表查询。 快速指南: 一、新建持久类Person: 1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 6/25/15. 5 */ 6 @Table(propertyClazz = PersonProperty.class) 7 public class Person implements Serializable{ 8 9 @Column(primaryKey = true) 10 private Integer id; 11 12 @Column(primaryKey = true) 13 private Integer typeId; 14 15 @Column 16 private String name; 17 18 @Column 19 private Integer age; 20 21 @Column 22 private String address; 23 24 @Column 25 private Long birth; 26 27 @Column 28 private Boolean student; 29 30 // getter/setter... 31 32 } 1. Table注解用于标记这个类需要映射到数据表,RapidORM会创建这个表。 -name属性:表示对应表的名称,默认为类的simpleName。 -propertyClazz属性:表示如果不使用反射执行sql时,需要的ModelProperty类的Class,RapidORM会自动在这个ModelProperty中去绑定需要执行SQL的数据。不填写则表示使用默认的反射的方式执行SQL。具体后面会讲到。 2. Column注解用语标记这个字段映射到的数据表的列(如果)。 -name属性:表示对应列的名称。默认为字段的名称。 -primaryKey属性:表示改列是否是主键,默认是false。(可以在多个字段都适用这个属性,表示为复合主键)。 -autoincrement属性:表示主键是否是自增长,只有该字段是主键并且只有一个主键并且是Integer或int类型才会生效。 -defaultValue属性:表示该列默认值。 -notNull属性:表示该列不能为空。 -unique属性:表示该列唯一约束。 -uniqueCombo属性:暂未实现。 -index属性:暂未实现。 二、注册持久类,并指定SQLiteOpenHelper: 新建类DatabaseFactory,继承RapidORMConnection: 1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 6/25/15. 5 */ 6 public class DatabaseFactory extends RapidORMConnection<RapidORMDefaultSQLiteOpenHelperDelegate> { 7 private static final int VERSION = 1; 8 9 private static DatabaseFactory instance; 10 11 public synchronized static DatabaseFactory getInstance() { 12 if (null == instance) { 13 instance = new DatabaseFactory(); 14 } 15 return instance; 16 } 17 18 private DatabaseFactory() { 19 super(); 20 } 21 22 @Override 23 protected RapidORMDefaultSQLiteOpenHelperDelegate getRapidORMDatabaseOpenHelper(@NonNull String databaseName) { 24 return new RapidORMDefaultSQLiteOpenHelperDelegate(new MyDatabaseOpenHelper(MyApplication.getInstance(), databaseName, VERSION)); 25 } 26 27 @Override 28 protected List<Class<?>> registerAllTableClass() { 29 List<Class<?>> allTableClass = new ArrayList<>(); 30 allTableClass.add(Person.class); 31 // all table class 32 return allTableClass; 33 } 34 } DatabaseFactory推荐使用单例,实现RapidORMConnection的getRapidORMDatabaseOpenHelper()方法,该方法需要返回一个RapidORMDatabaseOpenHelperDelegate或者它的子类对象,RapidORMDatabaseOpenHelperDelegate是SQLiteOpenHelper的委托,因为需要兼容android原生的android.database.sqlite.SQLiteDatabase和SqlCipher的net.sqlcipher.database.SQLiteDatabase两种方式。 如果使用android.database.sqlite.SQLiteDatabase,则可以使用RapidORMDefaultSQLiteOpenHelperDelegate,然后构造方法中传入真正的android.database.sqlite.SQLiteDatabase(也就是这里的MyDatabaseOpenHelper)即可,使用SqlCipher的方式后面再讲。 还需要实现registerAllTableClass()方法,这里返回一个所有需要映射表的持久类Class集合。 三、在需要的地方进行初始化RapidORM(比如在登录完成之后): DatabaseFactory.getInstance().resetDatabase("hello_rapid_orm.db"); 如上,调用resetDatabase()方法即可初始化数据库。 四、编写Dao: 新建PersonDaoImpl继承BaseDaoImpl<Person>(这里简略了PersonDao接口): 1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 6/26/15. 5 */ 6 public class PersonDaoImpl extends BaseDaoImpl<Person> { 7 public PersonDaoImpl() { 8 super(Person.class); 9 } 10 11 } BaseDaoImpl中默认实现了下面的基本方法(T为范型,指具体的持久类): -void insert(T model) throws Exception; //插入一条数据。 -void update(T model) throws Exception; //更新一条数据。 -void delete(T model) throws Exception; // 删除一条数据。 -void deleteAll() throws Exception; // 删除表中所有的数据 -void insertOrReplace(T model) throws Exception; // 删除或者替换(暂不支持) -List<T> queryAll() throws Exception; // 查询表中所有数据 -List<T> rawQuery(String sql, String[] selectionArgs) throws Exception; // 使用原生sql查询表中所有的数据 -void rawExecute(String sql, Object[] bindArgs) throws Exception; // 使用原生sql执行一条sql语句 -void insertInTx(T... models) throws Exception; // 插入多条数据(在一个事务中) -void insertInTx(Iterable<T> models) throws Exception; // 同上 -void updateInTx(T... models) throws Exception; // 更新多条数据(在一个事务中) -void updateInTx(Iterable<T> models) throws Exception; // 同上 -void deleteInTx(T... models) throws Exception; // 删除多条数据(在一个事务中) -void deleteInTx(Iterable<T> models) throws Exception; // 同上 -void executeInTx(RapidORMSQLiteDatabaseDelegate db, RapidOrmFunc1 func1) throws Exception;// 执行一个方法(可多个sql),在一个事务中 -void executeInTx(RapidOrmFunc1 func1) throws Exception; // 同上 面向对象的方式使用Where和Builder来构建sql语句: 1. Where: Where表示一系列的条件语句,含义与SQL中的where关键字一致。 支持的where操作: -eq(String column, Object value) //相等条件判断 -ne(String column, Object value) // 不相等条件判断 -gt(String column, Object value) // 大于条件判断 -lt(String column, Object value) // 小于条件判断 -ge(String column, Object value) //大于等于条件 -le(String column, Object value) // 小于等于条件 -in(String column, Object... values) // 包含条件 -ni(String column, Object... values) // 不包含条件 -isNull(String column) //null条件判断 -notNull(String column) // 不为null条件判断 -between(String column, Object value1, Object value2) //BETWEEN ... AND ..." condition -like(String column, Object value) // 模糊匹配条件 -Where raw(String rawWhere, Object... values) // 原生sql条件 -and(Where... wheres) // 多个Where与 -or(Where... wheres) // 多个Where或 Where举例: 1 Where.and( 2 Where.like(PersonProperty.name.column, "%wangjie%"), 3 Where.lt(PersonProperty.id.column, 200), 4 Where.or( 5 Where.between(PersonProperty.age.column, 19, 39), 6 Where.isNull(PersonProperty.address.column) 7 ), 8 Where.eq(PersonProperty.typeId.column, 1) 9 ) 如上, Line2条件:name模糊匹配wangjie Line3条件:id小于200 Line5条件:age在19到39之间 Line6条件:address不是null Line8条件:typeId等于1 Line4条件:Line5条件和Line6条件是or关系; Line1条件:Line2、Line3、Line4、Line8条件是and关系。 2. QueryBuilder: QueryBuilder用于构建查询语句。 -setWhere(Where where) // 设置Where条件; -setLimit(Integer limit) // 设置limit -addSelectColumn(String... selectColumn) // 设置查询的数据列名(可以调用多遍来设置多个,默认是查询所有数据) -addOrder(String column, boolean isAsc) // 设置查询结果的排序(可以调用多遍来设置多个) QueryBuilder举例: 1 public List<Person> findPersonsByWhere() throws Exception { 2 return queryBuilder() 3 .addSelectColumn(PersonProperty.id.column, PersonProperty.typeId.column, PersonProperty.name.column, 4 PersonProperty.age.column, PersonProperty.birth.column, PersonProperty.address.column) 5 .setWhere(Where.and( 6 Where.like(PersonProperty.name.column, "%wangjie%"), 7 Where.lt(PersonProperty.id.column, 200), 8 Where.or( 9 Where.between(PersonProperty.age.column, 19, 39), 10 Where.isNull(PersonProperty.address.column) 11 ), 12 Where.eq(PersonProperty.typeId.column, 1) 13 )) 14 .addOrder(PersonProperty.id.column, false) 15 .addOrder(PersonProperty.name.column, true) 16 .setLimit(10) 17 .query(this); 18 } 如上,通过queryBuilder()方法构建一个QueryBuilder,并设置查询的列名、Where、排序、limit等参数,最后调用query()执行查询。 3. UpdateBuilder: UpdateBuilder用于构建更新语句。 -setWhere(Where where) // 设置Where条件; -addUpdateColumn(String column, Object value) // 添加需要更新的字段; UpdateBuilder举例: 1 public void updatePerson() throws Exception { 2 long now = System.currentTimeMillis(); 3 updateBuilder() 4 .setWhere(Where.and( 5 Where.like(PersonProperty.name.column, "%wangjie%"), 6 Where.lt(PersonProperty.id.column, 200), 7 Where.or( 8 Where.between(PersonProperty.age.column, 19, 39), 9 Where.isNull(PersonProperty.address.column) 10 ), 11 Where.eq(PersonProperty.typeId.column, 1) 12 )) 13 .addUpdateColumn(PersonProperty.birth.column, now) 14 .addUpdateColumn(PersonProperty.address.column, "address_" + now).update(this); 15 } 如上,通过updateBuilder()方法构建一个UpdateBuilder,并设置要更新的字段,最后调用update()执行查询。 4. DeleteBuilder: DeleteBuilder用于构建删除语句。 -setWhere(Where where) // 设置Where条件; DeleteBuilder举例: 1 public void deletePerson() throws Exception { 2 deleteBuilder() 3 .setWhere(Where.and( 4 Where.like(PersonProperty.name.column, "%wangjie%"), 5 Where.lt(PersonProperty.id.column, 200), 6 Where.or( 7 Where.between(PersonProperty.age.column, 19, 39), 8 Where.isNull(PersonProperty.address.column) 9 ), 10 Where.eq(PersonProperty.typeId.column, 1) 11 )) 12 .delete(this); 13 } 如上,通过deleteBuilder()方法构建一个DeleteBuilder,并设置Where条件,最后调用delete()执行查询。 使用模版生成ModelProperty: 如果你打算使用非反射的方式执行sql,则可以使用ModelPropertyGenerator模版来生成 1. 新建一个类,在main方法中执行: /** * Author: wangjie * Email: tiantian.china.2@gmail.com * Date: 7/2/15. */ public class MyGenerator { public static void main(String[] args) throws Exception { Class tableClazz = Person.class; new ModelPropertyGenerator().generate(tableClazz, "example/src/main/java", tableClazz.getPackage().getName() + ".config"); } } 如上,generate()方法参数如下: generate(Class tableClazz, String outerDir, String packageName) 参数一:要生成的Property持久类的Class 参数二:生成的ModelProperty类文件存放目录 参数三:生成的ModelProperty类文件的包名 执行完毕后,就会在com.wangjie.rapidorm.example.database.model.config包下生成如下的ModelProperty: 1 // THIS CODE IS GENERATED BY RapidORM, DO NOT EDIT. 2 /** 3 * Property of Person 4 */ 5 public class PersonProperty implements IModelProperty<Person> { 6 7 public static final ModelFieldMapper id = new ModelFieldMapper(0, "id", "id"); 8 public static final ModelFieldMapper typeId = new ModelFieldMapper(1, "typeId", "typeId"); 9 public static final ModelFieldMapper name = new ModelFieldMapper(2, "name", "name"); 10 public static final ModelFieldMapper age = new ModelFieldMapper(3, "age", "age"); 11 public static final ModelFieldMapper address = new ModelFieldMapper(4, "address", "address"); 12 public static final ModelFieldMapper birth = new ModelFieldMapper(5, "birth", "birth"); 13 public static final ModelFieldMapper student = new ModelFieldMapper(6, "student", "student"); 14 15 public PersonProperty() { 16 } 17 18 19 @Override 20 public void bindInsertArgs(Person model, List<Object> insertArgs) { 21 Integer id = model.getId(); 22 insertArgs.add(null == id ? null : id); 23 24 Integer typeId = model.getTypeId(); 25 insertArgs.add(null == typeId ? null : typeId); 26 27 String name = model.getName(); 28 insertArgs.add(null == name ? null : name); 29 30 Integer age = model.getAge(); 31 insertArgs.add(null == age ? null : age); 32 33 String address = model.getAddress(); 34 insertArgs.add(null == address ? null : address); 35 36 Long birth = model.getBirth(); 37 insertArgs.add(null == birth ? null : birth); 38 39 Boolean student = model.isStudent(); 40 insertArgs.add(null == student ? null : student ? 1 : 0); 41 42 } 43 44 @Override 45 public void bindUpdateArgs(Person model, List<Object> updateArgs) { 46 String name = model.getName(); 47 updateArgs.add(null == name ? null : name); 48 49 Integer age = model.getAge(); 50 updateArgs.add(null == age ? null : age); 51 52 String address = model.getAddress(); 53 updateArgs.add(null == address ? null : address); 54 55 Long birth = model.getBirth(); 56 updateArgs.add(null == birth ? null : birth); 57 58 Boolean student = model.isStudent(); 59 updateArgs.add(null == student ? null : student ? 1 : 0); 60 61 } 62 63 @Override 64 public void bindPkArgs(Person model, List<Object> pkArgs) { 65 Integer id = model.getId(); 66 pkArgs.add(null == id ? null : id); 67 68 Integer typeId = model.getTypeId(); 69 pkArgs.add(null == typeId ? null : typeId); 70 71 } 72 73 @Override 74 public Person parseFromCursor(Cursor cursor) { 75 Person model = new Person(); 76 int index; 77 index = cursor.getColumnIndex("id"); 78 if(-1 != index){ 79 model.setId(cursor.isNull(index) ? null : (cursor.getInt(index))); 80 } 81 82 index = cursor.getColumnIndex("typeId"); 83 if(-1 != index){ 84 model.setTypeId(cursor.isNull(index) ? null : (cursor.getInt(index))); 85 } 86 87 index = cursor.getColumnIndex("name"); 88 if(-1 != index){ 89 model.setName(cursor.isNull(index) ? null : (cursor.getString(index))); 90 } 91 92 index = cursor.getColumnIndex("age"); 93 if(-1 != index){ 94 model.setAge(cursor.isNull(index) ? null : (cursor.getInt(index))); 95 } 96 97 index = cursor.getColumnIndex("address"); 98 if(-1 != index){ 99 model.setAddress(cursor.isNull(index) ? null : (cursor.getString(index))); 100 } 101 102 index = cursor.getColumnIndex("birth"); 103 if(-1 != index){ 104 model.setBirth(cursor.isNull(index) ? null : (cursor.getLong(index))); 105 } 106 107 index = cursor.getColumnIndex("student"); 108 if(-1 != index){ 109 model.setStudent(cursor.isNull(index) ? null : (cursor.getInt(index) == 1)); 110 } 111 112 return model; 113 } 114 115 } 注意:此Property类一旦生成,则不能修改;如果该持久类相关的注解属性改变,则需要重新生成这个Property类。 2. 在相应的持久类的@Table注解中设置propertyClazz: 1 @Table(propertyClazz = PersonProperty.class) 2 public class Person implements Serializable{ 如何兼容SqlCipher 1. 新建MySQLCipherOpenHelper,继承net.sqlcipher.database.SQLiteOpenHelper: 1 import android.content.Context; 2 import com.wangjie.rapidorm.core.dao.DatabaseProcessor; 3 import com.wangjie.rapidorm.core.delegate.database.RapidORMSQLiteDatabaseDelegate; 4 import net.sqlcipher.database.SQLiteDatabase; 5 import net.sqlcipher.database.SQLiteDatabaseHook; 6 import net.sqlcipher.database.SQLiteOpenHelper; 7 8 /** 9 * Author: wangjie 10 * Email: tiantian.china.2@gmail.com 11 * Date: 8/17/15. 12 */ 13 public class MySQLCipherOpenHelper extends SQLiteOpenHelper { 14 15 private RapidORMSQLiteDatabaseDelegate rapidORMSQLiteDatabaseDelegate; 16 17 public MySQLCipherOpenHelper(Context context, String name, int version) { 18 super(context, name, null, version); 19 } 20 21 public MySQLCipherOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { 22 super(context, name, factory, version); 23 } 24 25 public MySQLCipherOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, SQLiteDatabaseHook hook) { 26 super(context, name, factory, version, hook); 27 } 28 29 @Override 30 public void onCreate(SQLiteDatabase db) { 31 // super.onCreate(db); 32 rapidORMSQLiteDatabaseDelegate = new MySQLCipherDatabaseDelegate(db); 33 DatabaseProcessor databaseProcessor = DatabaseProcessor.getInstance(); 34 for (Class<?> tableClazz : databaseProcessor.getAllTableClass()) { 35 databaseProcessor.createTable(rapidORMSQLiteDatabaseDelegate, tableClazz, true); 36 } 37 } 38 39 @Override 40 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 41 // super.onUpgrade(db, oldVersion, newVersion); 42 rapidORMSQLiteDatabaseDelegate = new MySQLCipherDatabaseDelegate(db); 43 DatabaseProcessor databaseProcessor = DatabaseProcessor.getInstance(); 44 // todo: dev only!!!!! 45 databaseProcessor.dropAllTable(rapidORMSQLiteDatabaseDelegate); 46 47 } 48 } 2. 新建MySQLCipherDatabaseDelegate继承RapidORMSQLiteDatabaseDelegate,并实现以下的方法: 1 import android.database.Cursor; 2 import android.database.SQLException; 3 import com.wangjie.rapidorm.core.delegate.database.RapidORMSQLiteDatabaseDelegate; 4 import net.sqlcipher.database.SQLiteDatabase; 5 6 /** 7 * Author: wangjie 8 * Email: tiantian.china.2@gmail.com 9 * Date: 8/17/15. 10 */ 11 public class MySQLCipherDatabaseDelegate extends RapidORMSQLiteDatabaseDelegate<SQLiteDatabase> { 12 public MySQLCipherDatabaseDelegate(SQLiteDatabase db) { 13 super(db); 14 } 15 16 @Override 17 public void execSQL(String sql) throws SQLException { 18 db.execSQL(sql); 19 } 20 21 @Override 22 public boolean isDbLockedByCurrentThread() { 23 return db.isDbLockedByCurrentThread(); 24 } 25 26 @Override 27 public void execSQL(String sql, Object[] bindArgs) throws Exception { 28 db.execSQL(sql, bindArgs); 29 } 30 31 @Override 32 public Cursor rawQuery(String sql, String[] selectionArgs) { 33 return db.rawQuery(sql, selectionArgs); 34 } 35 36 @Override 37 public void beginTransaction() { 38 db.beginTransaction(); 39 } 40 41 @Override 42 public void setTransactionSuccessful() { 43 db.setTransactionSuccessful(); 44 } 45 46 @Override 47 public void endTransaction() { 48 db.endTransaction(); 49 } 50 51 @Override 52 public void close() { 53 db.close(); 54 } 55 } 注意这里的import,这里的SQLiteDatabase需要导入net.sqlcipher.database.SQLiteDatabase。 3. 新建MySQLCipherOpenHelperDelegate继承RapidORMDatabaseOpenHelperDelegate,实现如下方法: 1 import com.wangjie.rapidorm.core.delegate.openhelper.RapidORMDatabaseOpenHelperDelegate; 2 3 4 /** 5 * Author: wangjie 6 * Email: tiantian.china.2@gmail.com 7 * Date: 6/18/15. 8 */ 9 public class MySQLCipherOpenHelperDelegate extends RapidORMDatabaseOpenHelperDelegate<MySQLCipherOpenHelper, MySQLCipherOpenHelperDelegate> { 10 public MySQLCipherOpenHelperDelegate(MySQLCipherOpenHelper sqLiteOpenHelper) { 11 super(sqLiteOpenHelper); 12 } 13 14 public static final String SECRET_KEY = "1234567890abcdef"; 15 16 @Override 17 public MySQLCipherOpenHelperDelegate getReadableDatabase() { 18 return new MySQLCipherDatabaseDelegate(openHelper.getReadableDatabase(SECRET_KEY)); 19 } 20 21 @Override 22 public MySQLCipherOpenHelperDelegate getWritableDatabase() { 23 return new MySQLCipherDatabaseDelegate(openHelper.getWritableDatabase(SECRET_KEY)); 24 } 25 26 } 4. 在DatabaseFactory中使用SqlCipher版本: 1 /** 2 * Author: wangjie 3 * Email: tiantian.china.2@gmail.com 4 * Date: 6/25/15. 5 */ 6 public class DatabaseFactory extends RapidORMConnection<MySQLCipherOpenHelperDelegate> { 7 private static final int VERSION = 1; 8 9 private static DatabaseFactory instance; 10 11 public synchronized static DatabaseFactory getInstance() { 12 if (null == instance) { 13 instance = new DatabaseFactory(); 14 } 15 return instance; 16 } 17 18 private DatabaseFactory() { 19 super(); 20 } 21 22 @Override 23 protected MySQLCipherOpenHelperDelegate getRapidORMDatabaseOpenHelper(@NonNull String databaseName) { 24 return new MySQLCipherOpenHelperDelegate(new MySQLCipherOpenHelper(applicationContext, databaseName, VERSION)); 25 } 26 27 @Override 28 protected List<Class<?>> registerAllTableClass() { 29 List<Class<?>> allTableClass = new ArrayList<>(); 30 allTableClass.add(Person.class); 31 // register all table class here... 32 return allTableClass; 33 } 34 } 本文转自天天_byconan博客园博客,原文链接:http://www.cnblogs.com/tiantianbyconan/p/4748077.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

移动端基于动态路由的架构设计

好久好久没写过文章了,一是最近项目太忙了,没时间写。二是也没有时间学习新的东西,想写点什么却又无从下笔。一味的去写这个API怎么用,那个新技术怎么用,又显的没意思。没有项目经验总结的技术知识讲解,总感觉有些苍白。 最近在做混合App开发这块,从开始的ionic 框架,到后来的mui框架,让我在混合开发这块有了更深的理解,如果在这块要写点什么无非漫天盖地的这个指令怎么用,那个模版怎么用,数据怎么进行双向绑定,等等,但是这些网上已经很多资料了,等不太忙了,我想我会总结一篇这些框架的使用心得吧。但是我今天不讲这个,我们来谈一谈在原生app中(iOS android)如何使用动态路由机制来搭建整个app的框架。 路由机制在web开发中是比较常见的,app开发中还是很少听到这种概念的,目前有些大公司采用的组件化开发(手淘,携程,蘑菇街等),倒是跟我们讲的有很多相同之处,不过它们的比较复杂,而且网上很多组件化开发,路由机制,没有一个能给出完整代码示例的,看着让人云里雾里的。索性自己就借鉴它们的思想,加上一点个人的理解,搞出了一个简单实用的可行性demo出来。我们主要介绍以下三方面内容: 1 什么是动态路由 2 它能解决我们什么问题 3 如何在项目中实现 一 什么是动态路由 原生开发没这概念,我们借助angular路由机制来解释这一概念,所谓路由,就是一套路径跳转机制,事先通过配置文件定义好一个路径映射文件,跳转时根据key去找到具体页面,当然angular会做一些缓存,页面栈的管理等等一些操作,它大致的定义是这样的 angular.module('app',[]) .config('$routeProvider',function($routeProvider){ $routeProvider .when('/',{ templateUrl:'view/home.html', controller:'homeCtrl' } ) .when('/',{ templateUrl:'view/home.html', controller:'homeCtrl' } ) .when('/',{ templateUrl:'view/home.html', controller:'homeCtrl' } ) .ontherwise({ redirective:'/' }) }) config函数是一个配置函数。在使用 $routeProvider这样的一个服务。when:代表当你访问这个“/”根目录的时候 去访问 templateUrl中的那个模板。 controller可想已知,就是我们配套的controller,就是应用于根目录的这个 模板时的controller。 ontherwise 就是当你路径访问错误时,找不到。最后跳到这个默认的 页面。 为此我们可以总结一下几个特点: 1 一个映射配置文件 2 路径出错处理机制 这就是路由的基本意思,我们看看,在原生开发中,采用此种方式,他能解决我们什么问题。 二 它能解决我们什么问题 首先我们来比较一下我们以前的结构模式以及与 加入路由机制后的项目结构,实现路由机制,不仅需要一个映射文件,还需要一套路由管理机制,那么采用路由机制,我们的项目架构就跟原来不一样了,如下图: 原先业务之间的调用关系.png 加入路由后的页面调用关系.png 接下来我们看一下平时我们采用的页面跳转方法: iOS 下 [selfpresentViewController:controlleranimated:YEScompletion:nil]; [self.navigationControllerpushViewController:controlleranimated:YES]; android 下 Intentintent=newIntent(this,A.class);startActivity(intent);startActivityForResult(Intentintent,IntrequestCode) 我们看一下它有哪些缺点: (1)都要在当前页面引入要跳转页面的class 类。这就造成了页面的耦合性很高。 (2)遇到重大bug,不能够及时的修复问题,需要等待更新发版后才能解决。 (3)推送消息,如果入口没有关与页面的引入处理,则不能跳转到指定页面。 引入路由机制后我们能否解决这些问题呢? 试想一下,如果我们通过一个配置文件来映射页面跳转关系,而且通过反射机制来取消头文件的引入问题,是不是我们就可以解决以上那些弊端了呢,比如,我们线上应用出现bug, 导致某个页面一打开,app就跪了,那我们是不是就可以通过更新路由配置文件,把它映射到另一个页面去:一个错误提示文件,或者一个线上H5能实现相同功能的页面。这样的话,原生app也具有了一定的动态更新能力,其实想想还有很多好处,比如项目功能太多原生开发要很长时间,但是领导又急着要上线,那么我们是不是就可以先开发一个网页版的模块,app路由映射到这个web页面,让用户先用着,等我们原生开发完了,然后再改一下映射文件,没升级的依旧用H5的路由,升级的就用原生的路由,如果H5页面我们要废弃了,那我们整体就可以路由到一个升级提升的页面去了。 总结一下路由机制能解决我们哪些问题: 1 避免引入头文件,是页面之间的依赖大大变少了(通过反射动态生成页面实例)。 2 线上出现重大bug,给我们提供了一个及时修补的入口 3 网页和原生切换更方便,更自由。 4 可以跳转任意页面 例如我们常用的推送,要打开指定的页面,以前我们怎么做的,各种启动判断,现在呢,我们只要给发送消息配个路由路径就行了,打开消息,就能够跳转到我们指定的页面。 等等,其它好处自行发掘。 三 如何在项目中实现 说了这么多概念性问题,下面我们就用代码来实现我们的构想, 下面先以IOS平台为例: 我们先看一下demo结构 iOS demo结构图.png 说明:WXRouter 路由管理文件 demo 路由使用示例 urlMap.plist 路由配置文件 我们主要讲解一下 WXRouter里面的几个文件,以及ViewController文件,还有urlmap.plist文件,其他请下载示例demo,文末我会给出demo地址。 #import #import @interfaceWXRouter:NSObject +(id)sharedInstance; -(UIViewController*)getViewController:(NSString*)stringVCName; -(UIViewController*)getViewController:(NSString*)stringVCNamewithParam:(NSDictionary*)paramdic; @end #import"WXRouter.h" #import"webView.h" #import"RouterError.h" #import"PlistReadUtil.h" #defineSuppressPerformSelectorLeakWarning(Stuff)\ do{ _Pragma("clangdiagnosticpush")\ _Pragma("clangdiagnosticignored\"-Warc-performSelector-leaks\"")\ Stuff;\ _Pragma("clangdiagnosticpop")\ } while(0) @implementationWXRouter +(id)sharedInstance{ staticdispatch_once_tonceToken; staticWXRouter*router; dispatch_once(&onceToken,^{ router=[[WXRouteralloc]init]; }); returnrouter; } -(UIViewController*)controller:(UIViewController*)controllerwithParam:(NSDictionary*)paramdicandVcname:(NSString*)vcName{ SELselector=NSSelectorFromString(@"iniViewControllerParam:"); if(![controllerrespondsToSelector:selector]){//如果没定义初始化参数方法,直接返回,没必要在往下做设置参数的方法 NSLog(@"目标类:%@未定义:%@方法",controller,@"iniViewControllerParam:"); returncontroller; } if(paramdic==nil){ //如果参数为空URLKEY页面唯一路径标识别 paramdic=[[NSMutableDictionaryalloc]init]; [paramdicsetValue:vcNameforKey:@"URLKEY"]; SuppressPerformSelectorLeakWarning([controllerperformSelector:selectorwithObject:paramdic]); } else{ [paramdicsetValue:vcNameforKey:@"URLKEY"]; } SuppressPerformSelectorLeakWarning([controllerperformSelector:selectorwithObject:paramdic]); returncontroller; } -(UIViewController*)getViewController:(NSString*)stringVCName{ NSString*viewControllerName=[PlistReadUtilplistValueForKey:stringVCName]; Classclass=NSClassFromString(viewControllerName); UIViewController*controller=[[classalloc]init]; if(controller==nil){//此处可以跳转到一个错误提示页面 NSLog(@"未定义此类:%@",viewControllerName); returnnil; } returncontroller; } -(UIViewController*)getViewController:(NSString*)stringVCNamewithParam:(NSDictionary*)paramdic{ UIViewController*controller=[selfgetViewController:stringVCName]; if(controller!=nil){ controller=[selfcontroller:controllerwithParam:paramdicandVcname:stringVCName]; } else{ //异常处理可以跳转指定的错误页面 controller=[[RouterErrorsharedInstance]getErrorController]; } returncontroller; } @end 说明:通过反射机制根据传入的string来获取 viewcontroller实例,实现了两个方法,一个是不需要传入参数的,一个是需要传入参数的,当跳转到第二个页面需要传值 就使用第二个带参数的方法,所传的值通过NSDictionary来进行封装,跳转后的页面通过实现 -(void)iniViewControllerParam:(NSDictionary *)dic 方法来获取传过来的参数。 #import @interfacePlistReadUtil:NSObject @property(nonatomic,strong)NSMutableDictionary*plistdata; +(id)sharedInstanceWithFileName:(NSString*)plistfileName; +(NSString*)plistValueForKey:(NSString*)key; @end #import"PlistReadUtil.h" @implementationPlistReadUtil +(id)sharedInstanceWithFileName:(NSString*)plistfileName{ staticdispatch_once_tonceToken; staticPlistReadUtil*plistUtil; dispatch_once(&onceToken,^{ plistUtil=[[PlistReadUtilalloc]init]; NSString*plistPath=[[NSBundlemainBundle]pathForResource:plistfileNameofType:@"plist"]; plistUtil.plistdata=[[NSMutableDictionaryalloc]initWithContentsOfFile:plistPath]; }); returnplistUtil; } +(NSString*)plistValueForKey:(NSString*)key{ PlistReadUtil*plist=[PlistReadUtilsharedInstanceWithFileName:@"urlMap"]; return[plist.plistdataobjectForKey:key]; } @end 说明:路由配置文件读取工具类,我这里读取的是plist 文件,我这里也可以读取json,或则访问网络获取后台服务器上的路由配置文件,这个根据我们业务需求的不同,可以添加不同的读取方法。 #import<Foundation/Foundation.h> #import<UIKit/UIKit.h> @interfaceRouterError:NSObject +(id)sharedInstance; -(UIViewController*)getErrorController; @end #import"RouterError.h" #import"WXRouter.h" @implementationRouterError +(id)sharedInstance{ staticdispatch_once_tonceToken; staticRouterError*routerError; dispatch_once(&onceToken,^{ routerError=[[RouterErroralloc]init]; }); returnrouterError; } #pragmamark自定义错误页面此页面一定确保能够找到,否则会进入死循环 -(UIViewController*)getErrorController{ NSDictionary*diction=[[NSMutableDictionaryalloc]init]; [dictionsetValue:@"https://themeforest.net/item/octopus-error-template/2562783"forKey:@"url"]; UIViewController*errorController=[[WXRoutersharedInstance]getViewController:@"MSG003"withParam:diction]; returnerrorController; } @end 说明:在读取配置文件时如果没有读到相应的路径,或者未定义相应的class,我们可以在这里处理,我这边处理的是如果出现错误,就返回一个webview页面,我们可以在项目里写一个统一的错误处理webview页面,其实每个页面默认都添加了一个参数[paramdic setValue:vcName forKey:@"URLKEY"]; 就是这个URLKEY,这个key标示配置文件中每个跳转动作的key,这个key是唯一的,我们可以根据不同的URLKEY然后通过后台统一的一个接口来判断跳转到不同的错误处理H5页面。 #import"ViewController.h" #import"view2.h" #import"WXRouter.h" #import"PlistReadUtil.h" @interfaceViewController() @end @implementationViewController -(void)viewDidLoad{ [superviewDidLoad]; UILabel*lable=[[UILabelalloc]initWithFrame:CGRectMake(0,0,100,50)]; lable.textColor=[UIColorblueColor]; lable.text=@"helloword"; [self.viewaddSubview:lable]; UIButton*button=[[UIButtonalloc]initWithFrame:CGRectMake(0,50,200,50)]; [buttonsetTitle:@"访问view1"forState:UIControlStateNormal]; [buttonsetTitleColor:[UIColorblackColor]forState:UIControlStateNormal]; button.tag=1; [buttonaddTarget:selfaction:@selector(back:)forControlEvents:UIControlEventTouchUpInside]; [self.viewaddSubview:button]; UIButton*button2=[[UIButtonalloc]initWithFrame:CGRectMake(0,110,200,50)]; [button2setTitle:@"访问view3"forState:UIControlStateNormal]; [button2setTitleColor:[UIColorblackColor]forState:UIControlStateNormal]; button2.tag=2; [button2addTarget:selfaction:@selector(back:)forControlEvents:UIControlEventTouchUpInside]; [self.viewaddSubview:button2]; UIButton*butto3=[[UIButtonalloc]initWithFrame:CGRectMake(0,170,200,50)]; [butto3setTitle:@"访问webview"forState:UIControlStateNormal]; [butto3setTitleColor:[UIColorblackColor]forState:UIControlStateNormal]; butto3.tag=3; [butto3addTarget:selfaction:@selector(back:)forControlEvents:UIControlEventTouchUpInside]; [self.viewaddSubview:butto3]; UIButton*button4=[[UIButtonalloc]initWithFrame:CGRectMake(0,230,200,50)]; [button4setTitle:@"访问wei定义的页面"forState:UIControlStateNormal]; [button4setTitleColor:[UIColorblackColor]forState:UIControlStateNormal]; button4.tag=4; [button4addTarget:selfaction:@selector(back:)forControlEvents:UIControlEventTouchUpInside]; [self.viewaddSubview:button4]; } -(void)back:(UIButton*)btn{ switch(btn.tag){ case1:{ NSMutableDictionary*dic=[[NSMutableDictionaryalloc]init]; [dicsetValue:@"nihaoshijie"forKey:@"title"]; UIViewController*controller=[[WXRoutersharedInstance]getViewController:@"MSG001"withParam:dic]; [selfpresentViewController:controlleranimated:YEScompletion:nil]; } break; case2:{ NSMutableDictionary*dic=[[NSMutableDictionaryalloc]init]; [dicsetValue:@"nihaoshijie"forKey:@"title"]; UIViewController*controller=[[WXRoutersharedInstance]getViewController:@"MSG002"withParam:dic]; [selfpresentViewController:controlleranimated:YEScompletion:nil]; } break; case3:{ NSMutableDictionary*dic=[[NSMutableDictionaryalloc]init]; [dicsetValue:@"https://www.baidu.com"forKey:@"url"]; UIViewController*controller=[[WXRoutersharedInstance]getViewController:@"MSG003"withParam:dic]; [selfpresentViewController:controlleranimated:YEScompletion:nil]; } break; case4:{ UIViewController*controller=[[WXRoutersharedInstance]getViewController:@"MSG005"withParam:nil]; [selfpresentViewController:controlleranimated:YEScompletion:nil]; } default: break; } } -(void)didReceiveMemoryWarning{ [superdidReceiveMemoryWarning]; //Disposeofanyresourcesthatcanberecreated. } @end 说明:这个是使用示例,为了获取最大的灵活性,这里我并没有把跳转动作presentViewcontroller,pushViewController,以及参数的组装封装在路由管理类里。看过很多大神写的路由库,有些也通过url schema的方式。类似于:xml:id/123/name/xu,这样的路径方式,但是个人感觉,如果界面之间传递图片对象,或者传嵌套的类对象,就有点麻烦了。因为怕麻烦,所以就先写个简单的吧。 #import"view3.h" @interfaceview3() @end @implementationview3 -(void)viewDidLoad{ [superviewDidLoad]; UILabel*lable=[[UILabelalloc]initWithFrame:CGRectMake(0,0,100,50)]; lable.textColor=[UIColorblueColor]; lable.text=@"我是view3"; [self.viewaddSubview:lable]; UIButton*button=[[UIButtonalloc]initWithFrame:CGRectMake(200,200,200,200)]; [buttonsetTitle:@"back"forState:UIControlStateNormal]; [buttonsetTitleColor:[UIColorblackColor]forState:UIControlStateNormal]; [buttonaddTarget:selfaction:@selector(back)forControlEvents:UIControlEventTouchUpInside]; [self.viewaddSubview:button]; } -(void)back{ [selfdismissViewControllerAnimated:YEScompletion:nil]; } -(void)iniViewControllerParam:(NSDictionary*)dic{ self.title=[dicobjectForKey:@"title"]; } 说明:这个是要跳转的页面我们可以通过iniViewControllerParam:(NSDictionary *)dic方法获取上一个界面传过来的参数。 urlMap.plist 说明:路由配置文件,key:value的形式,页面里的每个跳转动作都会对应一个唯一的key,这里如果两个页面都跳转到同一个页面,就会产生不同的key 对应相同的value,感觉是有点冗余了,如果有更好的优化,我会更新下文章的,这里的配置文件我们可以怎么玩,由于我在android的这块的描述已经很详细了,所以这里就不再赘述。只是android的配置有点坑,类前需要加上包名,这点就没有iOS方便灵活了,至此iOS示例我就讲完了。 总结:代码是简陋的,只是简单的实现了自己的构想,还有很多值得细细琢磨的地方,关键是架构思路,通过中间路由根据下发的路由配置文件来动态跳转页面,解决原生开发的遇到的一些问题,不同的项目有不同的业务逻辑,这种思路有什么缺陷,或者解决不了什么问题,大家一起讨论分享。基于这种思路搭建架子的话,对于将来的组件化开发,应该也会很方便转换吧。 本文作者:佚名 来源:51CTO

优秀的个人博客,低调大师

几个连接HiveServer的客户端界面工具

Hive的官网上介绍了三个可以在Windows中通过JDBC连接HiveServer2的图形界面工具,包括:SQuirrel SQL Client、Oracle SQL Developer以及DbVisualizer。 其中,SQuirrel SQL Client也可以正常连接SparkThriftServer。 我目前使用的Hive版本为apache-hive-2.1.0-bin,Hadoop版本为hadoop-2.3.0-cdh5.0.0。 SQuirrel SQL Client 从http://squirrel-sql.sourceforge.net/下载最新的squirrel-sql,版本为3.7.1,安装后打开。 1. 新建Driver,注意添加运行所依赖的Hive和Hadoop jar包: 2. 新建别名(Alias): 如果HiveServer2没有启用认证,密码直接留空即可。 3. 双击保存好的别名(Alias),即可连接HiveServer2: Oracle SQL Developer 从http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html下载SQL Developer 4.1.5,并解压; 从http://www.cloudera.com/downloads/connectors/hive/jdbc/2-5-15.html下载Hive JDBC Driver for Oracle SQL Developer,并解压,进入解压后的目录,将Cloudera_HiveJDBC4_2.5.15.1040.zip解压。 打开sqldeveloper.exe,点击”工具”–>“首选项”,在”数据库”–>”第三方JDBC驱动”中,添加Hive JDBC驱动: 添加后重启sqldeveloper。 再次打开sqldeveloper后,点击”新建连接”之后,多了”Hive”数据库: 连接Hive: DbVisualizer DbVisualizer我没有试用,应该都差不多。 本文作者:lxw的大数据田地 来源:51CTO

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册