Bmob平台为您的移动应用提供了一个完整的后端解决方案,我们提供轻量级的SDK开发包,让开发者以最小的配置和最简单的方式使用Bmob平台提供的服务,进而完全消除开发者编写服务器代码以及维护服务器的操作。
选择你要开发的应用,点击该应用下方对应的“应用密钥”
鉴于目前Google官方推荐使用 Android Studio
进行Android项目开发,自 V3.4.2
开始,Bmob Android SDK 可以使用Gradle来进行包依赖管理,如果你使用Android Studio来进行基于BmobSDK的项目开发,建议你进行如下操作:
1、在Project
的build.gradle
文件中添加如下配置(注意文字说明部分):
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' } } allprojects { repositories { jcenter() //Bmob的maven仓库地址,必须填写 maven { url "https://raw.github.com/bmob/bmob-android-sdk/master" } } }
2、在app
的build.gradle
文件中添加如下配置(注意文字说明部分):
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion '22.0.1' defaultConfig { applicationId "cn.bmob.android" minSdkVersion 14 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions{ abortOnError false } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.2.0' //以下SDK开发者请根据需要自行选择 //bmob-sdk :Bmob的android sdk包 compile 'cn.bmob.android:bmob-sdk:3.4.3' //bmob-push:Bmob的推送包 compile 'cn.bmob.android:bmob-push:0.6' //bmob-im:bmob的im包,以下两个配置是im功能所需 compile 'cn.bmob.android:bmob-im:1.1.9' compile 'cn.bmob.android:bmob-sdk:3.4.3' //okhttp(选填):为解决弱网情况下SDK请求失败及响应慢的问题,自`V3.4.3版本`开始使用`okhttp`优化SDK的网络框架。 compile 'com.squareup.okhttp:okhttp:2.4.0' compile 'com.squareup.okio:okio:1.4.0' //bmob-sms :Bmob单独为短信服务提供的包 compile 'cn.bmob.android:bmob-sms:1.0.1' }
注:
1、如果你只需要Bmob提供的短信功能,那么你只需单独配置bmob-sms,如果你既需要bmob的数据服务,也需要短信服务,那么你只需要配置bmob-sdk即可。
2、每个版本的im都对应特定版本的bmob-sdk,如果你使用的是1.1.8版本的im,那么配套的bmob-sdk的版本为3.3.5。使用的是1.1.9配套的bmob-sdk的版本为3.4.3.
3、与okhttp有关的配置依赖不是必填项,开发者可以不添加该依赖。
开发者到 SDK下载中心 找到对应SDK下载之后,在Eclipse工程的项目根目录中新建libs
文件夹,将下载的jar包添加到此文件夹即可。
注:
1、若配置不成功,则需要额外增加以下步骤:
右键工程根目录,选择Properties -> Java Build Path -> Libraries
,然后点击Add External JARs...
选择指向该libs文件夹下的jar的路径,点击OK即可。
2、对网络请求的响应速度要求高的开发者需要额外再导入两个与okhttp有关的包,分别是:okhttp-2.4.0.jar及okio-1.4.0.jar,当然不导入这两个包也可以正常使用SDK。
在Bmob平台注册后,每个账户可创建多个应用程序,创建的每个应用程序有各自的Application ID,应用程序将凭其Application ID使用Bmob SDK。
请大家在使用Bmob开发应用程序之前,仔细阅读“数据与安全”的文档:http://docs.bmob.cn/datasafety/index.html?menukey=otherdoc&key=datasafety
目前为止,我们支持的数据类型有String、Integer、Boolean、ArrayList以及BmobObject对象类型。同时Bmob也支持BmobDate、BmobGeoPoint、BmobFile数据类型。
一个数据对象(APP中创建的BmobObject类和子类)对应于Bmob后台的一个数据表。
Bmob存储的数据是建立在BmobObject基础上的,所以任何要保存的数据对象必须继承自BmobObject类。BmobObject类本身包含objectId
、createdAt
、updatedAt
、ACL
四个默认的属性,objectId是数据的唯一标示,相当于表的主键,createdAt是数据的创建时间,updatedAt是数据的最后修改时间,ACL是数据的操作权限。
如,你的游戏中使用GameScore表来记录玩家的比分信息,其中表的字段有:score(分数)、playerName(玩家名字)、isPay(是否付费玩家)、pic(玩家头像)属性,那么这个数据对象如下定义:
//必须要继承自BmobObject类 public class GameScore extends BmobObject{ private String playerName; private Integer score; private Boolean isPay; private BmobFile pic; public String getPlayerName() { return playerName; } public void setPlayerName(String playerName) { this.playerName = playerName; } public Integer getScore() { return score; } public void setScore(Integer score) { this.score = score; } public Boolean getIsPay() { return isPay; } public void setIsPay(Boolean isPay) { this. isPay = isPay; } public BmobFile getPic() { return pic; } public void setPic(BmobFile pic) { this.pic = pic; } }
需要注意的是:
objectId
、createdAt
、updatedAt
、ACL
四个属性进行定义。createdAt
和updatedAt
属性中的字母d
,写成createAt和updateAt。//这时候实际操作的表是T_a_b public class GameScore extends BmobObject{ private String playerName; private Integer score; private Boolean isPay; private BmobFile pic; public GameScore() { this.setTableName("T_a_b"); } public String getPlayerName() { return playerName; } //其他方法,见上面的代码 }
当然了,除了在构造函数中直接调用setTableName方法之外,你还可以在GameScore的实例中动态调用setTableName方法。
如果您使用了setTableName方法来自定义表名,那么在对该表进行数据查询的时候必须使用以下方法。需要注意的是查询的结果是JSONArray,需要自行解析JSONArray中的数据。
/** * 查询数据 */ public void queryData(){ BmobQuery query = new BmobQuery("T_a_b"); query.findObjects(this, new FindCallback() { @Override public void onSuccess(JSONArray arg0) { // TODO Auto-generated method stub //注意:查询的结果是JSONArray,需要自行解析 showToast("查询成功:"+arg0.length()); } @Override public void onFailure(int arg0, String arg1) { // TODO Auto-generated method stub showToast("查询失败:"+arg1); } }); }
自定义表名情况下的更新、删除数据和普通的更新、删除数据方式一样,没有变化。为方便大家了解学习,我们提供了一个关于自定义表名情况下增删改查数据的Demo,下载地址是:https://github.com/bmob/bmob-android-demo-dynamic-tablename。
为了提供更好的服务,BmobSDK中提供了BmobUser、BmobInstallation、BmobRole三个特殊的BmobObject对象来完成不同的功能,在这里我们统一称为特殊对象。
BmobUser对象主要是针对应用中的用户功能而提供的,它对应着web端的User表,使用BmobUser对象可以很方便的在应用中实现用户的注册、登录、邮箱验证等功能,具体的使用方法可查看文档的用户管理部分。
BmobInstallation对象主要用于应用的安装设备管理中,它对应着web端的Installation表,任何安装了你应用的设备都会在此表中产生一条数据标示该设备。结合Bmob提供的推送功能,还可以实现将自定义的消息推送给不同的设备终端,具体的使用方法可查看文档的消息推送部分。
BmobRole对象主要用于角色管理,对应用于Web端的Role表,具体的使用方法可查看文档的ACL和角色部分。
添加数据使用BmobObject对象的save
方法,就可以将当前对象的内容保存到Bmob服务端。
例如,你现在要保存一条游戏分数的记录,代码如下:
GameScore gameScore = new GameScore(); //注意:不能调用gameScore.setObjectId("")方法 gameScore.setPlayerName("比目"); gameScore.setScore(89); gameScore.setIsPay(false); gameScore.save(mContext, new SaveListener() { @Override public void onSuccess() { // TODO Auto-generated method stub toast("添加数据成功,返回objectId为:"+gameScore.getObjectId() + ”,数据在服务端的创建时间为:“ + gameScore.getCreatedAt()); } @Override public void onFailure(int code, String arg0) { // TODO Auto-generated method stub // 添加失败 } });
运行以上代码,如果添加成功,你可以在Bmob提供的后台的数据浏览中看到类似这样的结果:
objectId: "0c6db13c", score: 89, playerName: "比目", isPay: false,createdAt:"2013-09-27 10:32:54", updatedAt:"2013-09-27 10:32:54"
这里需要注意的是:
如果服务器端不存在GameScore表,那么系统将自动建表,并插入数据。
如果服务器端已经存在GameScore表,和相应的score、playerName、isPay字段,那么你此时添加的数据和数据类型也应该和服务器端的表结构一致,否则会保存数据失败。
每个BmobObject对象都有几个默认的键(数据列)是不需要开发者指定的,objectId
是每个保存成功数据的唯一标识符。createdAt
和updatedAt
代表每个对象(每条数据)在服务器上创建和最后修改的时间。这些键(数据列)的创建和数据内容是由服务器端自主来完成的。因此,使用save和insert方法时,不需要调用setObjectId方法,否则会出现提示:“It is a reserved field: objectId(105)”--表明objectId为系统保留字段,不允许修改。
。
数据的查询可能是每个应用都会频繁使用到的,BmobSDK中提供了BmobQuery
类,它提供了多样的方法来实现不同条件的查询,同时它的使用也是非常的简单和方便的。
查询某个数据表中的所有数据是非常简单的查询操作,例如:查询GameScore表中playerName为“比目”的50条数据记录。
BmobQuery<GameScore> query = new BmobQuery<GameScore>(); //查询playerName叫“比目”的数据 query.addWhereEqualTo("playerName", "比目"); //返回50条数据,如果不加上这条语句,默认返回10条数据 query.setLimit(50); //执行查询方法 query.findObjects(this, new FindListener<GameScore>() { @Override public void onSuccess(List<GameScore> object) { // TODO Auto-generated method stub toast("查询成功:共"+object.size()+"条数据。"); for (GameScore gameScore : object) { //获得playerName的信息 gameScore.getPlayerName(); //获得数据的objectId信息 gameScore.getObjectId(); //获得createdAt数据创建时间(注意是:createdAt,不是createAt) gameScore.getCreatedAt(); } } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub toast("查询失败:"+msg); } });
查询的结果不需要进行任何处理,BmobSDK已经为你封装成相应的JavaBean集合了,你直接使用即可。
注:
通过setLimit方法设置返回的记录数量。更多细节可点击查看分页查询一节。
当我们知道某条数据的objectId
时,就可以根据objectId
直接获取单条数据对象。例如:查询objectId
为a203eba875
的人员信息。
BmobQuery<GameScore> query = new BmobQuery<GameScore>(); query.getObject(this, "a203eba875", new GetListener<GameScore>() { @Override public void onSuccess(GameScore object) { // TODO Auto-generated method stub toast("查询成功:"); //获得playerName的信息 object.getPlayerName(); //获得数据的objectId信息 object.getObjectId(); //获得createdAt数据创建时间(注意是:createdAt,不是createAt) object.getCreatedAt(); } @Override public void onFailure(int code, String arg0) { // TODO Auto-generated method stub toast("查询失败:"+arg0); } });
在查询的使用过程中,基于不同条件的查询是非常常见的,BmobQuery同样也支持不同条件的查询。
如果要查询特定键的特定值,可以使用addWhereEqualTo
方法,如果要过滤掉特定键的值可以使用addWhereNotEqualTo
方法。
比如需要查询playerName不等于“Barbie”的数据时可以这样写:
query.addWhereNotEqualTo("playerName", "Barbie");
当然,你可以在你的查询操作中添加多个约束条件,来查询符合要求的数据。
query.addWhereNotEqualTo("playerName", "Barbie"); //名字不等于Barbie query.addWhereGreaterThan("score", 60); //条件:分数大于60岁
各种不同条件的比较查询:
// 分数 < 50 query.addWhereLessThan("score", 50); //分数 <= 50 query.addWhereLessThanOrEqualTo("score", 50); //分数 > 50 query.addWhereGreaterThan("score", 50); //分数 >= 50 query.addWhereGreaterThanOrEqualTo("score", 50);
如果你想查询匹配几个不同值的数据,如:要查询“Barbie”,“Joe”,“Julia”三个人的成绩时,你可以使用addWhereContainedIn
方法来实现。
String[] names = {"Barbie", "Joe", "Julia"}; query.addWhereContainedIn("playerName", Arrays.asList(names));
相反,如果你想查询排除“Barbie”,“Joe”,“Julia”这三个人的其他同学的信息,你可以使用addWhereNotContainedIn
方法来实现。
String[] names = {"Barbie", "Joe", "Julia"}; query.addWhereNotContainedIn("playerName", Arrays.asList(names));
时间查询
比较特殊,我们需要结合BmobDate
这个类来查询某个指定日期时间前后的数据,这里也给出示例供大家参考:
比如:
如果想查询指定日期之前的数据,则可以使用addWhereLessThan
或者addWhereLessThanOrEqualTo
(包含当天)来查询。
如果想查询指定日期之后的数据,则可以使用addWhereGreaterThan
或addWhereGreaterThanOrEqualTo
(包含当天)来查询。
如果想查询指定时间当天的数据,则需要使用复合查询
来查询。
BmobQuery<Person> query = new BmobQuery<Person>("Person"); String dateString = "2015-02-11"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = null; try { date = sdf.parse(dateString); } catch (ParseException e) { } //这是查询2015年2月11之前的Person数据 query.addWhereLessThan("createdAt",new BmobDate(date)); //这是查询2015年2月11之后的Person数据 //query.addWhereGreaterThan("createdAt",new BmobDate(date));
对于字段类型为数组的情况,需要查找字段中的数组值包含有xxx的对象,可以使用addWhereContainsAll
方法:
比如我想查询有阅读和唱歌爱好的人,可以这样:
BmobQuery<Person> query = new BmobQuery<Person>(); String [] hobby = {"阅读","唱歌"}; query.addWhereContainsAll("hobby", Arrays.asList(hobby)); query.findObjects(this, new FindListener<Person>() { @Override public void onSuccess(List<Person> object) { // TODO Auto-generated method stub toast("查询成功:共" + object.size() + "条数据。"); } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub toast("查询失败:" + code); } });
对字符串值的模糊查询 比如 查询包含字符串的值,有几种方法。
你可以使用任何正确的正则表达式来检索相匹配的值,使用addWhereMatches
方法:
query.addWhereMatches(("username", "^[A-Z]\\d");
还可以使用如下方法:
//查询username字段的值含有“sm”的数据 query.addWhereContains("username", "sm"); //查询username字段的值是以“sm“字开头的数据 query.whereStartsWith("username", "sm"); // 查询username字段的值是以“ile“字结尾的数据 query.whereEndsWith("username", "ile");
如果你想查询某个列的值存在,那么可以使用addWhereExists
方法:
//查询username有值的数据 query.addWhereExists("username");
如果想查询某个列的值不存在,则可以用addWhereDoesNotExists
方法
//查询username字段没有值的数据 query.addWhereDoesNotExists("username");
有时,在数据比较多的情况下,你希望查询出的符合要求的所有数据能按照多少条为一页来显示,这时可以使用setLimit
方法来限制查询结果的数据条数来进行分页。
默认情况下,Limit的值为100
,最大有效设置值1000
(设置的数值超过1000还是视为1000)。
query.setLimit(10); // 限制最多10条数据结果作为一页
在数据较多的情况下,在setLimit
的基础上分页显示数据是比较合理的解决办法。
setSKip
方法可以做到跳过查询的前多少条数据来实现分页查询的功能。默认情况下Skip的值为10。
query.setSkip(10); // 忽略前10条数据(即第一页数据结果)
大家也可以直接下载我们提供的Demo源码(https://github.com/bmob/bmob-android-demo-paging),查看如何使用分页查询,结合ListView开发下拉刷新查看更多内容的应用。
对应数据的排序,如数字或字符串,你可以使用升序或降序的方式来控制查询数据的结果顺序:
// 根据score字段升序显示数据 query.order("score"); // 根据score字段降序显示数据 query.order("-score"); // 多个排序字段可以用(,)号分隔 query.order("-score,createdAt");
说明:多个字段排序时,先按第一个字段进行排序,再按第二个字段进行排序,依次进行。
有些查询需要使用到复合“与”的查询条件,例如:你想查询出Person表中年龄在6-29岁之间且姓名以"y"或者"e"结尾的人,那么,可以采用and查询,示例代码如下:
//查询年龄6-29岁之间的人,每一个查询条件都需要New一个BmobQuery对象 //--and条件1 BmobQuery<Person> eq1 = new BmobQuery<Person>(); eq1.addWhereLessThanOrEqualTo("age", 29);//年龄<=29 //--and条件2 BmobQuery<Person> eq2 = new BmobQuery<Person>(); eq2.addWhereGreaterThanOrEqualTo("age", 6);//年龄>=6 //查询姓名以"y"或者"e"结尾的人--这个需要使用到复合或查询(or) //--and条件3 BmobQuery<Person> eq3 = new BmobQuery<Person>(); eq3.addWhereEndsWith("name", "y"); BmobQuery<Person> eq4 = new BmobQuery<Person>(); eq4.addWhereEndsWith("name", "e"); List<BmobQuery<Person>> queries = new ArrayList<BmobQuery<Person>>(); queries.add(eq3); queries.add(eq4); BmobQuery<Person> mainQuery = new BmobQuery<Person>(); BmobQuery<Person> or = mainQuery.or(queries); //最后组装完整的and条件 List<BmobQuery<Person>> andQuerys = new ArrayList<BmobQuery<Person>>(); andQuerys.add(eq1); andQuerys.add(eq2); andQuerys.add(or); //查询符合整个and条件的人 BmobQuery<Person> query = new BmobQuery<Person>(); query.and(andQuerys); query.findObjects(this, new FindListener<Person>() { @Override public void onSuccess(List<Person> object) { // TODO Auto-generated method stub toast("查询年龄6-29岁之间,姓名以'y'或者'e'结尾的人个数:"+object.size()); } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub toast("复合与查询失败:"+code+",msg:"+msg); } });
有些情况,在查询的时候需要使用到复合的“或”的查询条件。例如,你想查出 Person 表中 age 等于 29 或者 age 等于 6 的人,可以这样:
BmobQuery<Person> eq1 = new BmobQuery<Person>(); eq1.addWhereEqualTo("age", 29); BmobQuery<Person> eq2 = new BmobQuery<Person>(); eq2.addWhereEqualTo("age", 6); List<BmobQuery<Person>> queries = new ArrayList<BmobQuery<Person>>(); queries.add(eq1); queries.add(eq2); BmobQuery<Person> mainQuery = new BmobQuery<Person>(); mainQuery.or(queries); mainQuery.findObjects(this, new FindListener<Person>() { @Override public void onSuccess(List<Person> object) { // TODO Auto-generated method stub } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub } });
你还可以在此基础上添加更多的约束条件到新创建的 BmobQuery 对象上,表示一个 and 查询操作。
如果你只是想统计满足查询对象的数量,你并不需要获取所有匹配对象的具体数据信息,可以直接使用count
替代findObjects
。例如,查询一个特定玩家玩的游戏场数:
BmobQuery<GameSauce> query = new BmobQuery<GameSauce>(); query.addWhereEqualTo("playerName", "Barbie"); query.count(this, GameSauce.class, new CountListener() { @Override public void onSuccess(int count) { // TODO Auto-generated method stub toast("Barbie has played" + count + "games"); } @Override public void onFailure(int code, String msg) { // TODO Auto-generated method stub toast("count failure:"+msg); } });
缓存查询通常是将查询结果缓存在磁盘上。
当用户的设备处于离线状态时,就可以从缓存中获取数据来显示。
或者在应用界面刚刚启动,从网络获取数据还未得到结果时,先使用缓存数据来显示。
这样可以让用户不必在按下某个按钮后进行枯燥的等待。
默认的查询操作是没有启用缓存的,开发者可以使用setCachePolicy
方法来启用缓存功能。
例如:优先从缓存获取数据,如果获取失败再从网络获取数据。
bmobQuery.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK); // 先从缓存获取数据,如果没有,再从网络获取。 bmobQuery.findObjects(this, new FindListener<Person>() { @Override public void onSuccess(List<Person> object) { // TODO Auto-generated method stub toast("查询成功:共"+object.size()+"条数据。"); } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub toast("查询失败:"+msg); } });
Bmob SDK提供了几种不同的缓存策略,以适应不同应用场景的需求:
IGNORE_CACHE
:只从网络获取数据,且不会将数据缓存在本地,这是默认的缓存策略。CACHE_ONLY
:只从缓存读取数据,如果缓存没有数据会导致一个BmobException,可以忽略不处理这个BmobException.
NETWORK_ONLY
:只从网络获取数据,同时会在本地缓存数据。
NETWORK_ELSE_CACHE
:先从网络读取数据,如果没有,再从缓存中获取。
CACHE_ELSE_NETWORK
:先从缓存读取数据,如果没有,再从网络获取。
CACHE_THEN_NETWORK
:先从缓存取数据,无论结果如何都会再次从网络获取数据。也就是说会产生2次调用。
建议的做法:
第一次进入应用的时候,设置其查询的缓存策略为CACHE_ELSE_NETWORK
,当用户执行上拉或者下拉刷新操作时,设置查询的缓存策略为NETWORK_ELSE_CACHE
。
如果需要操作缓存内容,可以使用BmobQuery提供的方法做如下操作:
boolean isInCache = query.hasCachedResult(context,Class<?> clazz);
注:缓存和查询条件有关,此方法必须放在所有的查询条件(where、limit、order、skip、include等)都设置完之后,否则会得不到缓存数据。
query.clearCachedResult(context,Class<?> clazz);
BmobQuery.clearAllCachedResults(this);
query.setMaxCacheAge(TimeUnit.DAYS.toMillis(1));//此表示缓存一天
示例如下:
BmobQuery<Person> query = new BmobQuery<Person>(); query.addWhereEqualTo("age", 25); query.setLimit(10); query.order("createdAt"); //判断是否有缓存,该方法必须放在查询条件(如果有的话)都设置完之后再来调用才有效,就像这里一样。 boolean isCache = query.hasCachedResult(context,Person.class); if(isCache){--此为举个例子,并不一定按这种方式来设置缓存策略 query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK); // 如果有缓存的话,则设置策略为CACHE_ELSE_NETWORK }else{ query.setCachePolicy(CachePolicy.NETWORK_ELSE_CACHE); // 如果没有缓存的话,则设置策略为NETWORK_ELSE_CACHE } query.findObjects(this, new FindListener<Person>() { @Override public void onSuccess(List<Person> object) { // TODO Auto-generated method stub Log.i("smile","查询个数:"+object.size()) } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub Log.i("smile","查询失败:"+object.size()) } });
注:
1、只有当缓存查询的条件一模一样时才会获取到缓存到本地的缓存数据。
2、设置的默认的最大缓存时长为5小时。
有的时候,一张表的数据列比较多,而我们只想查询返回某些列的数据时,我们可以使用BmobQuery对象提供的addQueryKeys
方法来实现。如下所示:
//只返回Person表的objectId这列的值 BmobQuery<Person> bmobQuery = new BmobQuery<Person>(); bmobQuery.addQueryKeys("objectId"); bmobQuery.findObjects(this, new FindListener<Person>() { @Override public void onSuccess(List<Person> object) { // TODO Auto-generated method stub toast("查询成功:共" + object.size() + "条数据。"); //注意:这里的Person对象中只有指定列的数据。 } @Override public void onError(int code, String msg) { // TODO Auto-generated method stub toast("查询失败:" + msg); } });
指定多列时用,
号分隔每列,如:addQueryKeys("objectId,name,age")
;
从BmobSDKV3.3.6
开始,Bmob为开发者提供了以下关键字或其组合的统计查询操作,分别用于计算总和、平均值、最大值、最小值
,同时支持分组和过滤条件。