AVOS Cloud是干啥的呢?提供paas(Platform as a Service)服务的云。也就是提供一些接口,像map一样保存数据,但这个map还有个save方法。
AVObject obj=new AVObject("Blogger");
obj.put("name","lzwjava");
obj.save();
于是在服务端可以看见:
当然还有其它增删改查操作,推送,数据分析,社交登录,事件流系统(微博,好友关系)。
工具是相当方便迅捷的,我唯一担心的就是用多了这么方便的工具,不利于自己的技术积累……
如何定义AVObject子类呢?这样做的好处是用上IDE的智能提示、类型检查。
1)定义一个类,extends AVObject,加上注释,且保证有一个空构造器方法。
@AVClassName("FilmItem") public class FilmItem extends AVObject{ public static final String NAME = "name"; public FilmItem(){ } public String getName(){ return getString(NAME); } public void setName(String name){ put(NAME,name); } }
2)在Application类中,注册一下。
AVObject.registerSubclass(FilmItem.class);
如何方便简单地用呢?
建议查找、判断存在等操作的时候返回一个子类对象,
public static VideoItem avGetVideoItem(int id) throws AVException { AVQuery<VideoItem> q=AVObject.getQuery(VideoItem.class); q.whereEqualTo(VideoItem.VIDEO_ID,id); q.setLimit(1); VideoItem item=null; List<VideoItem> items = q.find(); if(items!=null && items.isEmpty()==false){ item=items.get(0); } return item; }
像这个,根据id来找到一个VideoItem,之后便用子类中定义的方法来使用即可,
判断是否为空:
item=avGetVideoItem(id);
if(item!=null){
}else{
}
得到某个字段值
item.getName();
也可以,
item.getString("name");
就是一些键值对,像map,只不过还有save,delete等等方法,save一下保存到服务器上。
item.putName("suck");
item.save();
在服务器上看到的是数据库一样的表格:
注意这个save方法是要网络连接的,阻塞线程的。得另开一个线程。
可用
item.saveInBackgroud();
让其在后台线程,有相应的一个回调接口,可以处理save完之后的事情。
特别注意的是不建议请求某一个对象的时候,只用到一个字段值,就把其丢弃掉,
public static String avGetGetVideoName(String word) throws AVException { AVQuery<VideoItem> query = getQuery(VideoItem.class); query.whereEqualTo(WORD, word); query.setLimit(1); List<VideoItem> items = query.find(); VideoItem item = items.get(0); return item.getVideoName(); }像这个,一个单词对应一个短视频,根据单词来找某个VideoItem,找到后,
返回item.getVideoName();
这样返回的一大串键值对只用了其中一个。
可以用数据库把相应的值保存起来:
db.execSQL("insert into video_item (id,film_id,word,video_url," + "film_en_name,word_type,like_count,showAt,videoName) " + "values(?,?,?,?,?,?,?,?,?) ", new String[]{"" + item.getVideoId(), "" + item.getFilmId(),item.getWord(), videoUrl, item.getFilmEnName(), item.getWordType(),item.getLikeCount() + "", dateStr,videoName});
或者在前面的query中加上缓存策略,
query.setCachePolicy(AVQuery.CachePolicy.CACHE_ELSE_NETWORK);
public List<Integer> getTypedWordIdIn(int startIndex, int endIndex, String orderBy, String[] types) { AVQuery<VideoItem> query = getQueryOfTypes(types); int n = endIndex - startIndex + 1; query.whereLessThanOrEqualTo(VideoItem.SHOW_AT, new Date()); query.setLimit(n); query.setSkip(startIndex); query.orderByDescending(orderBy); SQLiteDatabase db = getWritableDatabase(); List<Integer> ids = new ArrayList<Integer>(); try { List<VideoItem> items = query.find(); for(VideoItem item:items){ ids.add(item.getVideoId()); } int succN=insertVideos(db, items); } catch (AVException e) { e.printStackTrace(); } return ids; }
private static AVQuery<VideoItem> getQueryOfTypes(String... types) { List<AVQuery<VideoItem>> qs = new ArrayList<AVQuery<VideoItem>>(); for (int i = 0; i < types.length; i++) { AVQuery<VideoItem> q = AVQuery.getQuery(VideoItem.class); q.whereContains("wordType", types[i]); qs.add(q); } return AVQuery.or(qs); }
首先,对单词进行了分类,四级、六级、高考的,现在要显示某一类的或者全部类的单词,为了统一,构造了复合查询,查找所有类别的单词的时候,不是去查找所有单词,而是去查找含有这个类别名称、那个类别等所有类别的单词。
这其实很不好,把类别的名称放在某个字段里,该对象是那个类别的话,它的某个字段的就含有类别名称。
不好在于:一来,无法用上索引,索引先排好序,但这contains用不上排好的顺序,无法用二分来快速定位。contains用来判断字符串包含关系。是要判断很多次的。
像这个"cet4,cet6,gaokao,gre,ielts",判断是否contains "ielts",从前面往后面的要判断10来20次。
而如果保存为数组的话,即[cet4,cet6,gaokao,gre,ielts],只用判断5次。
AVOS支持数组。是我刚出茅庐,糗大了。
分页展示的核心主要是:
query.setLimit(n); query.setSkip(startIndex); query.orderByDescending(orderBy);首先要有某种排序,AVOS的默认排序是数据的更新时间的逆序,即最近更新在前面,所以要定制我们自己的分页排序。这样调用
int startIndex = page * pageSize; int endIndex = startIndex + pageSize - 1; int askSize = endIndex - startIndex + 1; String type=getWordType(); List<Integer> ids; ids=DBHelper.sqlGetTypedWordIdIn(dbHelper,startIndex,endIndex, DBHelper.ID,new String[]{type}); if(size<askSize){ }
最后一句,if(size<askSize)表明返回的条数少于请求的条数,于是可以显示“没有更多”的消息了。
一些例子:
点赞的统计功能:
public static void incrementLikeCount(List<Integer> ids, int delta) throws AVException { if(ids.size()==0){ return; } List<AVObject> items = avGetVideoItems(ids); for(AVObject item :items){ item.increment(LIKE_COUNT,delta); } AVObject.saveAll(items); }
increment是AVOS已提供的方法。这样就可以为ids所代表的视频的LikeCount增加delta。
批量保存喜欢关系,两个字段,一个是用户名,另外一个是喜欢的视频id,
public static void avInsertUserLike(List<Integer> ids) throws AVException { String username= AVUser.getCurrentUser().getUsername(); List<AVObject> userLikes=new ArrayList<AVObject>(); for(int id :ids){ AVObject like=new AVObject(USER_LIKE); like.put(VIDEO_ID, id); like.put(USER_NAME, username); userLikes.add(like); } AVObject.saveAll(userLikes); VideoItem.incrementLikeCount(ids, 1); }
用户取消收藏了,删除喜欢关系:
public static void avDelUserLike(List<Integer> ids) throws AVException { List<AVObject> items = avGetUserLikes(ids); for(AVObject item :items){ item.delete(); } VideoItem.incrementLikeCount(ids, -1); }
AVOS的图片保存在七牛上,于是可以用七牛的接口获取缩略图或其它图片处理
String logoUrl=item.getLogo().getUrl()+"?imageView2/2/w/"+ App.imgWidth;
private void setImageWidth() { DisplayMetrics metrics=new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(metrics); App.imgWidth=metrics.widthPixels/3; }
在我的app中,分三栏展示图片,于是需要宽度为屏幕1/3的照片,
这样在图片的url后面加上 "?imageView2/2/w/"+ App.imgWidth
还可以旋转,各种截取操作的,见七牛文档。
音视频处理也可以,各种转换,截取帧。
登录:
AVUser.logInInBackground(userNameStr, passwordStr, new LogInCallback() { public void done(AVUser user, AVException e) { dialog.dismiss(); if (e == null) { // 登录成功 loginSucceed(); } else { // 登录失败 toast(getString(R.string.fail_login_tips)); } } });
注册:
user.signUpInBackground(new SignUpCallback() { public void done(AVException e) { dialog.dismiss(); if (e == null) { // successfully loginSucceed(); finish(); } else { toast(getString(R.string.have_been_registered)); } } });