快速入门¶
创建应用¶
登录账号进入bmob后台后,点击后台界面左上角“创建应用”,在弹出框输入你应用的名称,然后确认,你就拥有了一个等待开发的应用。
获取应用密钥¶
选择你要开发的应用,进入该应用
在跳转页面,进入设置/应用密钥,点击复制,即可得到Application ID
导入依赖¶
在app
的build.gradle
文件中添加依赖文件
:
dependencies {
implementation("io.github.bmob:android-sdk:4.0.7")
implementation("io.reactivex.rxjava3:rxjava:3.1.9")
implementation("io.reactivex.rxjava3:rxandroid:3.0.2")
implementation("com.squareup.okhttp3:okhttp:4.8.1")
implementation("com.squareup.okio:okio:2.2.2")
implementation("com.google.code.gson:gson:2.8.5")
}
创建Application子类¶
新建一个继承自Application
的子类BmobApp
。代码如下:
class BmobApp : Application() {
override fun onCreate() {
super.onCreate()
Bmob.initialize(this, "你的application id")
}
}
配置AndroidManifest.xml¶
在你的应用程序的AndroidManifest.xml
文件中添加如下的应用类名
、权限
和ContentProvider
信息:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.bmob.example"
android:versionCode="1"
android:versionName="1.0">
<!--允许联网 -->
<uses-permission android:name="android.permission.INTERNET" />
<!--获取GSM(2g)、WCDMA(联通3g)等网络状态的信息 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--获取wifi网络状态的信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--获取sd卡写的权限,用于文件上传和下载-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允许读取手机状态 用于创建BmobInstallation-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:name=".BmobApp"
....其他信息>
<activity
...其他信息
</activity>
<!--添加ContentProvider信息 -->
<provider
android:name="cn.bmob.v3.util.BmobContentProvider"
android:authorities="你的应用包名.BmobContentProvider">
</provider>
</application>
</manifest>
创建模型¶
首先创建模型文件,对应为Bmob后台的数据表。
class GameScore : BmobObject() {
var playerName: String? = null
var score: Int? = null
var isPay: Boolean? = null
}
注意,大部分模型类都继承自BmobObject
类。
但如果你想使用内置的注册、登录、验证码登录这些内置方法,则改为继承自BmobUser
类。
添加一行数据¶
private fun createOne() {
var gameScore = GameScore()
gameScore.playerName = "比目"
gameScore.score = 89
gameScore.isPay = false
/**
* 请不要给 gameScore.objectId 赋值,数据新增成功后将会自动给此条数据的objectId赋值并返回!
*/
gameScore.save(object : SaveListener<String>() {
override fun done(objectId: String?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(applicationContext, "新增数据成功:$objectId", Toast.LENGTH_LONG).show()
} else {
Log.e("CREATE", "新增数据失败:" + ex.message)
}
}
})
}
更新一条数据¶
private fun updateObject(objectId: String?) {
var person = Person()
person.objectId = objectId
person.name = "更新名字+" + System.currentTimeMillis()
person.update(object : UpdateListener() {
override fun done(ex: BmobException?) {
if (ex == null) {
Toast.makeText(applicationContext, "删除成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(applicationContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
}
删除一条数据¶
private fun deleteObject(objectId: String?) {
var person = Person()
person.objectId = objectId
person.delete(object : UpdateListener() {
override fun done(ex: BmobException?) {
if (ex == null) {
Toast.makeText(applicationContext, "删除成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(applicationContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
}
查询一条数据¶
private fun getObject(objectId: String?) {
var bmobQuery: BmobQuery<Person> = BmobQuery()
bmobQuery.getObject(objectId, object : QueryListener<Person>() {
override fun done(person: Person?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(applicationContext, "查询成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(applicationContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
}
查询多条数据¶
private fun queryObjects() {
var bmobQuery: BmobQuery<Person> = BmobQuery()
bmobQuery.findObjects(object : FindListener<Person>() {
override fun done(persons: MutableList<Person>?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(applicationContext, "查询成功", Toast.LENGTH_LONG).show()
if (persons != null) {
for (person: Person in persons) {
Log.e("Person", person.name)
}
}
} else {
Toast.makeText(applicationContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
}
开发文档¶
数据类型¶
Web端类型 | 支持的Kotlin类型 | 说明 |
---|---|---|
Number | Byte、Short、Int、Long、Float、Double | 基本数据类型 |
Array | MutableList | 数组类型 |
File | BmobFile | Bmob特有类型,用来标识文件类型 |
GeoPoint | BmobGeoPoint | Bmob特有类型,用来标识地理位置 |
Date | BmobDate | Bmob特有类型,用来标识日期类型 |
Pointer | 特定对象 | Bmob特有类型,用来标识指针类型 |
Relation | BmobRelation | Bmob特有类型,用来标识数据关联 |
默认表¶
Web端表名 | 支持的Kotlin类型 | 说明 |
---|---|---|
_User | BmobUser | 用户系统 |
_Role | BmobRole | 用户角色 |
_Installation | BmobInstallation | 用户设备 |
_Article | 用户自定义 | 图文消息 |
AppVersion | AppVersion | 版本升级 |
批量数据操作¶
批量新增数据¶
/**
* 批量新增数据
*/
private fun createBatch() {
val gameScores = ArrayList<BmobObject>()
for (i in 0..2) {
val gameScore = GameScore()
gameScore.playerName = "运动员$i"
gameScores.add(gameScore)
}
/**
* 3.5.0版本开始提供
*/
BmobBatch().insertBatch(gameScores).doBatch(object : QueryListListener<BatchResult>() {
override fun done(o: List<BatchResult>, e: BmobException?) {
if (e == null) {
for (i in o.indices) {
val result = o[i]
val ex = result.error
if (ex == null) {
Log.e("CREATE BATCH", "第" + i + "个数据批量添加成功:" + result.createdAt + "," + result.objectId + "," + result.updatedAt)
} else {
Log.e("CREATE BATCH", "第" + i + "个数据批量添加失败:" + ex.message + "," + ex.errorCode)
}
}
} else {
Log.e("CREATE BATCH", "失败:" + e.message + "," + e.errorCode)
}
}
})
}
批量更新数据¶
/**
* 批量更新数据
*/
private fun updateBatch() {
val gameScores = ArrayList<BmobObject>()
val gameScore1 = GameScore()
gameScore1.objectId = "此处填写已存在的objectId"
val gameScore2 = GameScore()
gameScore2.objectId = "此处填写已存在的objectId"
gameScore2.playerName = "赵大"
gameScore2.isPay = Boolean.FALSE
val gameScore3 = GameScore()
gameScore3.objectId = "此处填写已存在的objectId"
gameScore3.playerName = "王二"
gameScores.add(gameScore1)
gameScores.add(gameScore2)
gameScores.add(gameScore3)
/**
* 从3.5.0版本开始提供
*/
BmobBatch().updateBatch(gameScores).doBatch(object : QueryListListener<BatchResult>() {
override fun done(o: List<BatchResult>, ex: BmobException?) {
if (ex == null) {
for (i in o.indices) {
val result = o[i]
val ex = result.error
if (ex == null) {
Log.e("UPDATE", "第" + i + "个数据批量更新成功:" + result.updatedAt)
} else {
Log.e("UPDATE", "第" + i + "个数据批量更新失败:" + ex.message + "," + ex.errorCode)
}
}
} else {
Log.e("UPDATE", "失败:" + ex.message + "," + ex.errorCode)
}
}
})
}
批量删除操作¶
/**
* 批量删除数据
*/
private fun deleteBatch() {
val gameScores = ArrayList<BmobObject>()
val gameScore1 = GameScore()
gameScore1.objectId = "此处填写已存在的objectId"
val gameScore2 = GameScore()
gameScore2.objectId = "此处填写已存在的objectId"
val gameScore3 = GameScore()
gameScore3.objectId = "此处填写已存在的objectId"
gameScores.add(gameScore1)
gameScores.add(gameScore2)
gameScores.add(gameScore3)
/**
* 3.5.0版本开始提供
*/
BmobBatch().deleteBatch(gameScores).doBatch(object : QueryListListener<BatchResult>() {
override fun done(o: List<BatchResult>, e: BmobException?) {
if (e == null) {
for (i in o.indices) {
val result = o[i]
val ex = result.error
if (ex == null) {
Log.e("DELETE BATCH", "第" + i + "个数据批量删除成功")
} else {
Log.e("DELETE BATCH", "第" + i + "个数据批量删除失败:" + ex.message + "," + ex.errorCode)
}
}
} else {
Log.e("DELETE BATCH", "失败:" + e.message + "," + e.errorCode)
}
}
})
}
批量新增、更新、删除同步操作¶
/* * 批量新增、更新、删除同步操作 / private fun doBatch() {
val batch = BmobBatch()
//批量添加
val gameScores = ArrayList<BmobObject>()
val gameScore = GameScore()
gameScore.playerName = "张三"
gameScores.add(gameScore)
batch.insertBatch(gameScores)
//批量更新
val gameScores1 = ArrayList<BmobObject>()
val gameScore1 = GameScore()
gameScore1.objectId = "此处填写已经存在的objectId"
gameScore1.playerName = "李四"
gameScores1.add(gameScore1)
batch.updateBatch(gameScores1)
//批量删除
val gameScores2 = ArrayList<BmobObject>()
val gameScore2 = GameScore()
gameScore2.objectId = "此处填写已经存在的objectId"
gameScores2.add(gameScore2)
batch.deleteBatch(gameScores2)
//从3.5.0版本开始提供
batch.doBatch(object : QueryListListener<BatchResult>() {
override fun done(results: List<BatchResult>, ex: BmobException?) {
if (ex == null) {
//返回结果的results和上面提交的顺序是一样的,请一一对应
for (i in results.indices) {
val result = results[i]
if (result.isSuccess) {//只有批量添加才返回objectId
Log.e("BATCH", "第" + i + "个成功:" + result.objectId + "," + result.updatedAt)
} else {
val error = result.error
Log.e("BATCH", "第" + i + "个失败:" + error.errorCode + "," + error.message)
}
}
} else {
Log.e("BATCH", "失败:" + ex.message + "," + ex.errorCode)
}
}
})
}
注册登录¶
用户名密码注册¶
/**
* bmob注册方法
*/
var user = User()
user.username = username
user.setPassword(password)
user.signUp(object : SaveListener<User>() {
override fun done(currentUser: User?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "注册成功", Toast.LENGTH_LONG).show()
startActivity(Intent(mContext, MainActivity::class.java))
finish()
} else {
Toast.makeText(mContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
用户名密码登录¶
/**
* bmob登录方法
*/
var user = User()
user.username = username
user.setPassword(password)
user.login(object : SaveListener<User>() {
override fun done(currentUser: User?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "登录成功", Toast.LENGTH_LONG).show()
startActivity(Intent(mContext,MainActivity::class.java))
finish()
} else {
Toast.makeText(mContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
修改密码¶
/**
* 修改密码,必须先登录
*/
private fun resetPassword() {
BmobUser.updateCurrentUserPassword("旧密码", "新密码", object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Snackbar.make(btn_reset, "密码修改成功,可以用新密码进行登录啦", Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_reset, "密码修改失败:${e.message}", Snackbar.LENGTH_LONG).show()
}
}
})
}
文件管理¶
权限¶
/**
* 适配android6.0 动态申请访问文件权限
*/
private fun requestPermission() {
val checkSelfPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
if (checkSelfPermission == PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_CODE)
}
}
/**
* 权限申请回调结果
*/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_WRITE_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && permissions[0] == Manifest.permission.WRITE_EXTERNAL_STORAGE) {
Toast.makeText(this, "permission granted", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "permission denied", Toast.LENGTH_SHORT).show()
}
}
}
上传单个文件¶
/**
* 通过文件路径上传单个文件
*/
private fun uploadSingle(path: String?) {
var file = File(path)
var bmobFile = BmobFile(file)
bmobFile.upload(object : UploadFileListener() {
override fun done(ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "上传成功", Toast.LENGTH_SHORT).show()
setFileToTable(bmobFile)
} else {
Toast.makeText(mContext, "上传失败:"+ex.message, Toast.LENGTH_SHORT).show()
}
}
})
}
关联线上文件和表字段¶
/**
* 将文件设置到表中
*/
private fun setFileToTable(bmobFile: BmobFile) {
var person = Person()
person.file = bmobFile
person.save(object : SaveListener<String>() {
override fun done(objectId: String?, ex: BmobException?) {
Toast.makeText(mContext, "成功将文件设置到表中", Toast.LENGTH_LONG).show()
}
})
}
上传多个文件¶
/**
* bmob 上传多个文件
*/
private fun uploadMultiFile() {
/**
* 此处修改为你手机的文件路径
*/
var filePaths:Array<String> = arrayOf("/storage/emulated/0/1.png", "/storage/emulated/0/2.png", "/storage/emulated/0/3.png")
BmobFile.uploadBatch(filePaths,object : UploadBatchListener {
override fun onError(code: Int, error: String?) {
Toast.makeText(mContext, "上传出错:$error",Toast.LENGTH_LONG).show()
}
override fun onProgress(curIndex: Int, curPercent: Int, total: Int, totalPercent: Int) {
Toast.makeText(mContext, "上传进度:$curIndex",Toast.LENGTH_LONG).show()
}
override fun onSuccess(bmobFiles: MutableList<BmobFile>?, urls: MutableList<String>?) {
if (urls != null) {
if (urls.size==filePaths.size)
Toast.makeText(mContext, "全部上传成功",Toast.LENGTH_LONG).show()
}
}
})
}
删除单个文件¶
/**
* 删除单个文件
*/
private fun deleteSingleFile(bmobFile: BmobFile?) {
bmobFile!!.delete(object :UpdateListener(){
override fun done(ex: BmobException?) {
if (ex==null){
Toast.makeText(mContext, "删除成功", Toast.LENGTH_LONG).show()
}else{
Toast.makeText(mContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
}
删除多个文件¶
/**
* 删除多个文件
*/
private fun deleteMultiFiles(urls: Array<String>) {
BmobFile.deleteBatch(urls,object :DeleteBatchListener(){
override fun done(deleteUrls: Array<out String>?, ex: BmobException?) {
if (ex==null){
if (urls.size== deleteUrls!!.size){
Toast.makeText(mContext, "全部删除成功", Toast.LENGTH_LONG).show()
}
}else{
Toast.makeText(mContext, ex.message, Toast.LENGTH_LONG).show()
}
}
})
}
短信¶
短信功能目前属于按需付费功能,请到应用设置--付费升级中购买短信量。
发送短信验证码¶
/**
* bmob发送验证码
*/
BmobSMS.requestSMSCode(phone, "此处可填写控制台的短信模板名称,如果没有短信模板名称可填写空字符串使用默认模板", object : QueryListener<Int>() {
override fun done(smsId: Int?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "发送成功:$smsId", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(mContext, "发送成失败:$ex.message", Toast.LENGTH_LONG).show()
}
}
})
验证短信验证码¶
/**
* bmob验证验证码
*/
BmobSMS.verifySmsCode(phone,code,object :UpdateListener(){
override fun done(ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "验证成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(mContext, "验证失败:$ex.message", Toast.LENGTH_LONG).show()
}
}
})
邮箱¶
邮箱功能目前属于按需付费功能,请到应用设置--付费升级中购买邮件量。
验证激活¶
发送验证激活邮箱后,如果用户登录了邮箱并且点击了邮件中的激活链接,则可以使用邮箱+密码的方式进行登录。
/**
* 发送验证激活邮箱
*/
private fun sendEmailVerify() {
val email = input_email.text.toString()
if (TextUtils.isEmpty(email)){
Toast.makeText(mContext,"请输入邮箱",Toast.LENGTH_LONG).show()
return
}
BmobUser.requestEmailVerify(email, object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Log.e("sendEmailVerify","请求验证邮件成功,请到" + email + "邮箱中进行激活。")
} else {
Log.e("sendEmailVerify","请求验证邮件失败:" + e.message)
}
}
})
}
重置密码¶
发送重置密码邮箱后,如果用户登录了邮箱并且点击邮件中的链接进行密码重置,则可以使用邮箱+新密码方式进行登录。
/**
* 发送重置密码邮箱
*/
private fun sendEmailReset() {
val email = input_email.text.toString()
if (TextUtils.isEmpty(email)){
Toast.makeText(mContext,"请输入邮箱",Toast.LENGTH_LONG).show()
return
}
BmobUser.resetPasswordByEmail(email, object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Log.e("sendEmailReset","重置密码邮件成功,请到" + email + "邮箱中进行重置。")
} else {
Log.e("sendEmailReset","失败:" + e.message)
}
}
})
}
邮箱+密码登录¶
当邮箱通过验证激活后,即可使用邮箱+密码的方式进行登录。
/**
* 邮箱+密码登录
*/
private fun loginEmailPassword() {
val email = input_email.text.toString()
if (TextUtils.isEmpty(email)){
Toast.makeText(mContext,"请输入邮箱",Toast.LENGTH_LONG).show()
return
}
val password = input_password.text.toString()
if (TextUtils.isEmpty(password)){
Toast.makeText(mContext,"请输入邮箱",Toast.LENGTH_LONG).show()
return
}
BmobUser.loginByAccount(email, password, object : LogInListener<User>() {
override fun done(user: User?, ex: BmobException?) {
if (ex == null) {
Log.e("loginByAccount","登录成功")
} else {
Log.e("loginByAccount","登录失败:"+ex.message)
}
}
})
}
第三方账号¶
目前第三方账号功能只支持微博、QQ、微信三种社交账号。
注册登录¶
在第三方账号授权成功之后调用。
/**
* 1、snsType:只能是三种取值中的一种:weibo、qq、weixin
* 2、accessToken:接口调用凭证
* 3、expiresIn:access_token的有效时间
* 4、userId:用户身份的唯一标识,对应微博授权信息中的uid,对应qq和微信授权信息中的openid
*/
private fun thirdLoginSignup(snsType: String, accessToken: String, expiresIn: String, userId: String) {
val authInfo = BmobUser.BmobThirdUserAuth(snsType, accessToken, expiresIn, userId)
BmobUser.loginWithAuthData(authInfo, object : LogInListener<JSONObject>() {
override fun done(data: JSONObject?, ex: BmobException?) {
if (ex == null) {
Log.e("loginWithAuthData", "登录注册成功")
} else {
Log.e("loginWithAuthData", "登录注册失败:" + ex.message)
}
}
})
}
账号关联¶
在第三方账号授权成功之后调用。
/**
* 关联第三方账号
*/
private fun associateThird(snsType: String, accessToken: String, expiresIn: String, userId: String) {
val authInfo = BmobThirdUserAuth(snsType, accessToken, expiresIn, userId)
BmobUser.associateWithAuthData(authInfo, object : UpdateListener() {
override fun done(ex: BmobException?) {
if (ex == null) {
Log.e("associateWithAuthData", "关联成功")
} else {
Log.e("associateWithAuthData", "关联失败:" + ex.message)
}
}
})
}
取消关联¶
/**
* 取消关联
*/
private fun unAssociateThird(snsType: String) {
var currentUser: User? = BmobUser.getCurrentUser(User::class.java)
currentUser?.dissociateAuthData(snsType, object : UpdateListener() {
override fun done(ex: BmobException?) {
if (ex == null) {
Log.e("dissociateAuthData", "取消关联成功")
} else {
Log.e("dissociateAuthData", "取消关联失败:" + ex.message)
}
}
})
}
版本更新¶
版本更新设置¶
//TODO 初始化,当控制台表出现后,注释掉此句
BmobUpdateAgent.initAppVersion();
//TODO 设置仅WiFi环境更新
BmobUpdateAgent.setUpdateOnlyWifi(false);
//TODO 设置更新监听器
BmobUpdateAgent.setUpdateListener(new BmobUpdateListener() {
@Override
public void onUpdateReturned(int updateStatus, UpdateResponse updateInfo) {
BmobException e = updateInfo.getException();
if (e == null) {
updateResponse = updateInfo;
Toast.makeText(MainActivity.this, "检测更新返回:" + updateInfo.version + "-" + updateInfo.path, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "检测更新返回:" + e.getMessage() + "(" + e.getErrorCode() + ")", Toast.LENGTH_SHORT).show();
}
}
});
//TODO 设置对话框监听器
BmobUpdateAgent.setDialogListener(new BmobDialogButtonListener() {
@Override
public void onClick(int status) {
switch (status) {
case UpdateStatus.Update:
Toast.makeText(MainActivity.this, "点击了立即更新按钮", Toast.LENGTH_SHORT).show();
break;
case UpdateStatus.NotNow:
Toast.makeText(MainActivity.this, "点击了以后再说按钮", Toast.LENGTH_SHORT).show();
break;
case UpdateStatus.Close:
Toast.makeText(MainActivity.this, "点击了对话框关闭按钮", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
});
动态访问权限¶
/**
* 检查权限
*
* @param requestCode
*/
public void checkStoragePermissions(int requestCode) {
List<String> permissions = new ArrayList<>();
int permissionCheckWrite = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permissionCheckWrite != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
int permissionCheckRead = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
if (permissionCheckRead != PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
if (permissions.size() > 0) {
String[] missions = new String[]{};
ActivityCompat.requestPermissions(this, permissions.toArray(missions), requestCode);
} else {
switch (requestCode) {
case REQUEST_AUTO:
BmobUpdateAgent.update(this);
break;
case REQUEST_CHECK:
BmobUpdateAgent.forceUpdate(this);
break;
case REQUEST_SILENT:
BmobUpdateAgent.silentUpdate(this);
break;
case REQUEST_DELETE:
BmobUpdateAgent.deleteResponse(updateResponse);
break;
default:
break;
}
}
}
/**
* 检查授权结果
*
* @param grantResults
* @return
*/
public boolean checkResults(int[] grantResults) {
if (grantResults == null || grantResults.length < 1) {
return false;
}
for (int result : grantResults) {
if (result == PackageManager.PERMISSION_DENIED) {
return false;
}
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_AUTO:
if (checkResults(grantResults)) {
BmobUpdateAgent.update(this);
}
break;
case REQUEST_CHECK:
if (checkResults(grantResults)) {
BmobUpdateAgent.forceUpdate(this);
}
break;
case REQUEST_SILENT:
if (checkResults(grantResults)) {
BmobUpdateAgent.silentUpdate(this);
}
break;
case REQUEST_DELETE:
if (checkResults(grantResults)) {
BmobUpdateAgent.deleteResponse(updateResponse);
}
break;
default:
break;
}
}
数据监听¶
1、start方法开始连接;
2、onConnectCompleted回调方法后判断是否连接成功,若成功则设置监听内容;
3、onDataChange回调方法返回监听到的更新内容。
4、数据监听目前属于按需付费,请到应用设置--付费升级中购买。
private fun startListen() {
val rtd = BmobRealTimeData()
rtd.start(object : ValueEventListener {
override fun onDataChange(data: JSONObject) {
Log.d("onDataChange", "(" + data.optString("action") + ")" + "数据:" + data)
val action = data.optString("action")
if (action == BmobRealTimeData.ACTION_UPDATETABLE) {
//TODO 如果监听表更新
val data = data.optJSONObject("data")
Toast.makeText(mContext, "监听到更新:" + data.optString("name") + "-" + data.optString("content"), Toast.LENGTH_SHORT).show()
}
}
override fun onConnectCompleted(ex: Exception) {
if (ex == null) {
Log.i("onConnectCompleted", "连接情况:" + if (rtd.isConnected) "已连接" else "未连接")
if (rtd.isConnected) {
//TODO 如果已连接,设置监听动作为:监听Chat表的更新
rtd.subTableUpdate("Chat")
}
} else {
Log.e("onConnectCompleted", "连接出错:" + ex.message)
}
}
})
}
ACL¶
新增一条帖子数据,并且置当前用户可写,设置所有人可读。
/**
* ACL控制一条数据的访问权限
*/
private fun aclAccess() {
val user = BmobUser.getCurrentUser(User::class.java)
val acl = BmobACL() //创建一个ACL对象
acl.setPublicReadAccess(true) // 设置所有人可读的权限
acl.setWriteAccess(user, true) // 设置当前用户可写的权限
val post = Post()
post.author = user
post.title="ACL"
post.content="ACL控制访问权限"
post.save(object : SaveListener<String>(){
override fun done(objectId: String?, ex: BmobException?) {
if (ex == null) {
Log.e("ACL", "保存成功")
} else {
Log.e("ACL", "保存失败:" + ex.message)
}
}
})
}
角色¶
/**
* BmobRole:角色访问管理权限
*/
private fun roleAccess() {
//创建公司某用户的工资对象
val wage = Wage()
wage.wage= 10000.0
//这里创建四个用户对象,分别为老板、人事小张、出纳小谢和自己
val boss: BmobUser? =null
val hr_zhang: BmobUser? =null
val hr_luo: BmobUser? =null
val cashier_xie: BmobUser? =null
val me: BmobUser? =null
//创建HR和Cashier两个用户角色(这里为了举例BmobRole的使用,将这段代码写在这里,正常情况下放在员工管理界面会更合适)
val hr = BmobRole("HR")
val cashier = BmobRole("Cashier")
//将hr_zhang和hr_luo归属到hr角色中
hr.users.add(hr_zhang)
hr.users.add(hr_luo)
//保存到云端角色表中(web端可以查看Role表)
hr.save(object :SaveListener<String> (){
override fun done(objectId: String?, ex: BmobException?) {
if (ex == null) {
Log.e("ROLE", "保存成功")
} else {
Log.e("ROLE", "保存失败:" + ex.message)
}
}
})
//将cashier_xie归属到cashier角色中
cashier.users.add(cashier_xie)
//保存到云端角色表中(web端可以查看Role表)
cashier.save(object :SaveListener<String> (){
override fun done(objectId: String?, ex: BmobException?) {
if (ex == null) {
Log.e("ROLE", "保存成功")
} else {
Log.e("ROLE", "保存失败:" + ex.message)
}
}
})
//创建ACL对象
val acl = BmobACL()
acl.setReadAccess(boss, true) // 假设老板只有一个, 设置读权限
acl.setReadAccess(me, true) // 给自己设置读权限
acl.setRoleReadAccess(hr, true) // 给hr角色设置读权限
acl.setRoleReadAccess(cashier, true) // 给cashier角色设置读权限
acl.setWriteAccess(boss, true) // 设置老板拥有写权限
acl.setRoleWriteAccess(hr, true) // 设置hr角色拥有写权限
//设置工资对象的ACL
wage.acl = acl
wage.save(object :SaveListener<String>(){
override fun done(objectId: String?, ex: BmobException?) {
if (ex == null) {
Log.e("ROLE", "保存成功")
} else {
Log.e("ROLE", "保存失败:" + ex.message)
}
}
})
}
数组¶
添加数组¶
/**
* 添加数组
*/
private fun addArray() {
val user = BmobUser.getCurrentUser(User::class.java)
if (user==null){
Snackbar.make(btn_array_add,"请先登录",Snackbar.LENGTH_LONG).show()
return
}
user.add("hobbies", "唱歌")
user.update(object :UpdateListener(){
override fun done(e: BmobException?) {
if(e==null){
Log.i("bmob","更新成功")
}else{
Log.i("bmob","更新失败:"+e.message)
}
}
})
}
更新数组¶
/**
* 更新数组
*/
private fun updateArray() {
val user = BmobUser.getCurrentUser(User::class.java)
if (user == null) {
Snackbar.make(btn_array_add, "请先登录", Snackbar.LENGTH_LONG).show()
return
}
user.setValue("hobbies.0", "爬山")
user.update(object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Snackbar.make(btn_array_add, "更新成功", Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_array_add, "更新失败:" + e.message, Snackbar.LENGTH_LONG).show()
}
}
})
}
删除数组¶
/**
* 删除数组
*/
private fun deleteArray() {
val user = BmobUser.getCurrentUser(User::class.java)
if (user == null) {
Snackbar.make(btn_array_add, "请先登录", Snackbar.LENGTH_LONG).show()
return
}
user.removeAll("hobbies", Arrays.asList("阅读", "唱歌", "游泳"))
user.update(object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Log.i("bmob", "成功")
} else {
Log.i("bmob", "失败:" + e.message)
}
}
})
}
位置¶
/**
* 查询矩形范围内的用户
*/
private fun queryRectangle() {
val query = BmobQuery<User>()
val southwestOfSF = BmobGeoPoint(112.934755, 24.52065)
val northeastOfSF = BmobGeoPoint(116.627623, 40.143687)
query.addWhereWithinGeoBox("location", southwestOfSF, northeastOfSF)
query.findObjects(object : FindListener<User>() {
override fun done(persons: List<User>, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "查询成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(mContext, "查询失败:${ex.message}", Toast.LENGTH_LONG).show()
}
}
})
}
/**
* 查询指定距离范围内的用户
*/
private fun queryDistance() {
val query = BmobQuery<User>()
val southwestOfSF = BmobGeoPoint(112.934755, 24.52065)
//查询指定坐标指定半径内的用户
query.addWhereWithinRadians("location", southwestOfSF, 10.0)
query.findObjects(object : FindListener<User>() {
override fun done(persons: List<User>, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "查询成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(mContext, "查询失败:${ex.message}", Toast.LENGTH_LONG).show()
}
}
})
}
/**
* 查询最接近某个坐标的用户
*/
private fun queryShortest() {
val query = BmobQuery<User>()
val location = BmobGeoPoint(112.934755, 24.52065)
query.addWhereNear("location", location)
query.setLimit(10)
query.findObjects(object : FindListener<User>() {
override fun done(persons: List<User>, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "查询成功", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(mContext, "查询失败:${ex.message}", Toast.LENGTH_LONG).show()
}
}
})
}
关联关系¶
一对一关联¶
添加一对一关系¶
/**
* 发布帖子
*/
private fun publishPost() {
val content = input_post.text.toString()
if (TextUtils.isEmpty(content)) {
Toast.makeText(mContext, "请输入内容", Toast.LENGTH_LONG).show()
return
}
val user = BmobUser.getCurrentUser(User::class.java)
if(user==null) {
Toast.makeText(mContext, "请先登录", Toast.LENGTH_LONG).show()
return
}
val post = Post()
post.content = content
post.author = user
post.save(object : SaveListener<String>() {
override fun done(objectId: String?, ex: BmobException?) {
if (ex == null) {
Toast.makeText(mContext, "发布成功", Toast.LENGTH_LONG).show()
finish()
} else {
Toast.makeText(mContext, "发布失败:${ex.message}", Toast.LENGTH_LONG).show()
}
}
})
}
查询一对一关系¶
val user = BmobUser.getCurrentUser<User>(User::class.java)
val query = BmobQuery<Post>()
query.addWhereEqualTo("author", user) // 查询当前用户的所有帖子
query.order("-updatedAt")
query.include("author")// 希望在查询帖子信息的同时也把发布人的信息查询出来
query.findObjects(object : FindListener<Post>() {
override fun done(posts: List<Post>, e: BmobException?) {
if (e == null) {
Log.i("bmob", "成功")
} else {
Log.i("bmob", "失败:" + e.message)
}
}
})
一对多关联¶
添加一对多关系¶
/**
*
*/
private fun addComment(objectId: String?) {
val user = BmobUser.getCurrentUser<User>(User::class.java)
val content = input_comment_content.text.toString()
val post = Post()
post.objectId = objectId
val comment = Comment()
comment.content = content
comment.user = user
comment.post = post
comment.save(object : SaveListener<String>() {
override fun done(objectId: String, e: BmobException?) {
if (e == null) {
Snackbar.make(btn_add_comment, "评论发表成功", Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_add_comment, "评论发表失败:" + e.message, Snackbar.LENGTH_LONG).show()
}
}
})
}
查询一对多关系¶
/**
* 查询帖子的所有评论
*/
private fun queryComment(objectId: String?) {
val query = BmobQuery<Comment>()
val post = Post()
//用此方式可以构造一个BmobPointer对象。只需要设置objectId就行
post.objectId = objectId
query.addWhereEqualTo("post", BmobPointer(post))
//希望同时查询该评论的发布者的信息,以及该帖子的作者的信息,这里用到上面`include`的并列对象查询和内嵌对象的查询
query.include("user,post.author")
query.findObjects(object :FindListener<Comment>(){
override fun done(comments: MutableList<Comment>?, ex: BmobException?) {
if (ex == null) {
Snackbar.make(btn_add_comment, "评论发表成功", Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_add_comment, "评论发表失败:" + ex.message, Snackbar.LENGTH_LONG).show()
}
}
})
}
多对多关联¶
添加多对多关系¶
/**
* 喜欢该帖子
*/
private fun like(objectId: String?) {
val user = BmobUser.getCurrentUser<User>(User::class.java)
if (user != null) {
Snackbar.make(btn_like, "请先登录", Snackbar.LENGTH_LONG).show()
return
}
val post = Post()
post.objectId = objectId
//将当前用户添加到Post表中的likes字段值中,表明当前用户喜欢该帖子
val relation = BmobRelation()
//将当前用户添加到多对多关联中
relation.add(user)
//多对多关联指向`post`的`likes`字段
post.likes = relation
post.update(object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Snackbar.make(btn_like, "多对多关联添加成功", Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_like, "多对多关联添加失败:" + e.message, Snackbar.LENGTH_LONG).show()
}
}
})
}
查询多对多关系¶
/**
* 查询喜欢该帖子的所有用户
*/
private fun likes(objectId: String?) {
// 查询喜欢这个帖子的所有用户,因此查询的是用户表
val query = BmobQuery<User>()
val post = Post()
post.objectId = objectId
//likes是Post表中的字段,用来存储所有喜欢该帖子的用户
query.addWhereRelatedTo("likes", BmobPointer(post))
query.findObjects(object : FindListener<User>() {
override fun done(users: List<User>, e: BmobException?) {
if (e == null) {
Snackbar.make(btn_likes, "查询成功:" + users.size, Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_likes, "查询失败:" + e.message, Snackbar.LENGTH_LONG).show()
}
}
})
}
删除多对多关系¶
/**
* 取消喜欢该帖子
*/
private fun unlike(objectId: String?) {
val post = Post()
post.objectId = objectId
val user = BmobUser.getCurrentUser<User>(User::class.java!!)
val relation = BmobRelation()
relation.remove(user)
post.likes = relation
post.update(object : UpdateListener() {
override fun done(e: BmobException?) {
if (e == null) {
Snackbar.make(btn_unlike, "关联关系删除成功", Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_likes, "关联关系删除失败:" + e.message, Snackbar.LENGTH_LONG).show()
}
}
})
}
服务器时间¶
考虑到安全问题,要求客户端的时间必须是正常时间,否则会返回"sdk time error"错误,如果出现此问题,可以先获取服务器时间,再设置好客户端时间后重新请求。
/**
* 获取服务器时间
*/
private fun getBmobServerTime() {
Bmob.getServerTime(object : QueryListener<Long>() {
override fun done(time: Long, e: BmobException?) {
if (e == null) {
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm")
val times = formatter.format(Date(time * 1000L))
Snackbar.make(btn_get_server_time,"当前服务器时间为:$times",Snackbar.LENGTH_LONG).show()
} else {
Snackbar.make(btn_get_server_time,"获取服务器时间失败:" + e.message,Snackbar.LENGTH_LONG).show()
}
}
})
}
表结构¶
获取单表结构¶
/**
* 获取某张表的表结构
*/
private fun getTable(table: String?) {
Bmob.getTableSchema(table, object : QueryListener<BmobTableSchema>() {
override fun done(schema: BmobTableSchema, ex: BmobException?) {
if (ex == null) {
Log.i("bmob", "获取指定表的表结构信息成功:" + schema.className + "-" + schema.fields.toString())
} else {
Log.i("bmob", "获取指定表的表结构信息失败:" + ex.localizedMessage + "(" + ex.errorCode + ")")
}
}
})
}
获取所有表结构¶
/**
* 获取所有表结构
*/
private fun getAllTable() {
Bmob.getAllTableSchema(object : QueryListListener<BmobTableSchema>() {
override fun done(schemas: List<BmobTableSchema>?, ex: BmobException?) {
if (ex == null && schemas != null && schemas.isNotEmpty()) {
Log.i("bmob", "获取所有表结构信息成功")
} else {
Log.i("bmob", "获取所有表结构信息失败:" + ex!!.localizedMessage + "(" + ex.errorCode + ")")
}
}
})
}