项目地址
已经存在的问题: 视频详情的接口都挂掉了, 新版bilibili的视频详情接口又加密了, 估计只能等哪位大神破解了
项目的整体框架图
Paste_Image.png
项目中出现的一些知识点
1. lambda表达式
可以参考以下2篇文章
http://blog.csdn.net/future234/article/details/51919545
http://zh.lucida.me/blog/java-8-lambdas-insideout-language-features/
1. (int x, int y) -> x + y
2. () -> 42
3. (String s) -> { System.out.println(s); }
第一个 lambda 表达式接收 x 和 y 这两个整形参数并返回它们的和;
第二个 lambda 表达式不接收参数,返回整数42;
第三个 lambda表达式接收一个字符串并把它打印到控制台,不返回值。
以下程序是一种典型的写法:
FileFilter java = (File f) -> f.getName().endsWith("*.java");
String user = doPrivileged( () -> System.getProperty("user.name") );
new Thread(() -> {
connectToService();
sendNotification();
}).start();
下面看看使用lambda表达式是如何简化代码的。
这是原始代码:
List<Person> people = ...
Collections.sort(people, new Comparator<Person>() {
public int compare(Person x, Person y) {
return x.getLastName().compareTo(y.getLastName());
}
})
冗余代码实在太多了!
有了lambda表达式,我们可以去掉冗余的匿名类:
Collections.sort(people, (Person x, Person y) -> x.getLastName().compareTo(y.getLastName()));
再来看另一个例子:
Button clickButton = 初始化 button;
clickButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("你点击了按钮");
}
});
Lambda表达式的写法:
Button clickButton = 初始化button;
clickButton.setOnClickListener((View v)-> System.out.println("你点击了按钮");
2. RxLifecycle
项目地址
该项目是为了防止RxJava中subscription导致内存泄漏而诞生的,核心思想是通过监听Activity、Fragment的生命周期,来自动断开subscription以防止内存泄漏。
3. java双冒号是什么操作符?
4. Rxjava相关文章
比如被观察者产生的事件中只有图片文件路径,但是在观察者这里只想要bitmap,那么就需要类型变换。
Observable.just(getFilePath()
//使用map操作来完成类型转换
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String s) {
//显然自定义的createBitmapFromPath(s)方法,是一个极其耗时的操作
return createBitmapFromPath(s);
}
})
.subscribe(
//创建观察者,作为事件传递的终点处理事件
new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(Bitmap s) {
//处理事件
showBitmap(s)
}
);
实际上在使用map操作时,new Func1<String,Bitmap>()就对应了类型的转换方向,String是原类型,Bitmap是转换后的类型。在call()方法中,输入的是原类型,返回转换后的类型
你认真看完上面的代码就会觉得,何必在过程中变换类型呢?我直接在事件传递的终点,在观察者中变换就行咯。老实说,你这个想法没毛病,但实际上,上面写的代码是不合理的。
我在代码中也提到,读取文件,创建bitmap可能是一个耗时操作,那么就应该在子线程中执行,主线程应该仅仅做展示。那么线程切换一般就会是比较复杂的事情了。但是在Rxjava中,是非常方便的,如下代码所示:
Observable.just(getFilePath()
//指定了被观察者执行的线程环境为newThread
.subscribeOn(Schedulers.newThread())
//将接下来执行的线程环境指定为io线程
.observeOn(Schedulers.io())
//使用map操作来完成类型转换
.map(new Func1<String, Bitmap>() {
@Override
public Bitmap call(String s) {
//显然自定义的createBitmapFromPath(s)方法,是一个极其耗时的操作
return createBitmapFromPath(s);
}
})
//将后面执行的线程环境切换为主线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
//创建观察者,作为事件传递的终点处理事件
new Subscriber<Bitmap>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(Bitmap s) {
//处理事件
showBitmap(s)
}
);
由上面的代码可以看到,使用操作符将事件处理逐步分解,通过线程调度为每一步设置不同的线程环境,完全解决了你线程切换的烦恼。可以说线程调度和操作符,才真正展现了RxJava无与伦比的魅力。
再看一个例子:
//创建被观察者,获取所有班级
Observable.from(getSchoolClasses())
.flatMap(new Func1<SingleClass, Observable<Student>>() {
@Override
public Observable<Student> call(SingleClass singleClass) {
//将每个班级的所有学生作为一列表包装成一列Observable<Student>,将学生一个一个传递出去
return Observable.from(singleClass.getStudents());
}
})
.subscribe(
//创建观察者,作为事件传递的终点处理事件
new Subscriber<Student>() {
@Override
public void onCompleted() {
Log.d("DDDDDD","结束观察...\n");
}
@Override
public void onError(Throwable e) {
//出现错误会调用这个方法
}
@Override
public void onNext(Student student) {
//接受到每个学生类
Log.d("DDDDDD",student.getName())
}
);
subscribeOn()
它指示Observable在一个指定的调度器上创建(只作用于被观察者创建阶段)。只能指定一次,如果指定多次则以第一次为准。
observeOn()
指定在事件传递(加工变换)和最终被处理(观察者)的发生在哪一个调度器。可指定多次,每次指定完都在下一步生效。
在bilibili项目中,是使用Retrofit+RxJava
来进行网络访问,以下是一个典型的代码片段:
RetrofitHelper.getBiliAppAPI().getRecommendedBannerInfo().compose(bindToLifecycle())
.map(RecommendBannerInfo::getData)
// RecommendBannerInfo::getData获取数据的结果就是
// List<RecommendBannerInfo.DataBean>
.flatMap(new Func1<List<RecommendBannerInfo.DataBean>, Observable<RecommendInfo>>() {
@Override
public Observable<RecommendInfo> call(List<RecommendBannerInfo.DataBean> dataBeans) {
recommendBanners.addAll(dataBeans);
return RetrofitHelper.getBiliAppAPI().getRecommendedInfo();
}
})
.compose(bindToLifecycle())// 这句应该不需要吧
.map(RecommendInfo::getResult)
/*.subscribeOn(Schedulers.io())*/
.observeOn(AndroidSchedulers.mainThread())
.subscribe(resultBeans -> {
results.addAll(resultBeans);
finishTask();
}, throwable -> {
initEmptyView();
});
再看另外一个例子, 在VideoPlayerActivity中:
RetrofitHelper.getBiliGoAPI().getHDVideoUrl(cid, 4, ConstantUtil.VIDEO_TYPE_MP4)
.compose(bindToLifecycle())
.map(videoInfo -> Uri.parse(videoInfo.getDurl().get(0).getUrl()))
.observeOn(AndroidSchedulers.mainThread())
.flatMap(new Func1<Uri, Observable<BaseDanmakuParser>>() {
@Override
public Observable<BaseDanmakuParser> call(Uri uri) {
mPlayerView.setVideoURI(uri);
mPlayerView.setOnPreparedListener(mp -> {
mLoadingAnim.stop();
startText = startText + "【完成】\n视频缓冲中...";
mPrepareText.setText(startText);
mVideoPrepareLayout.setVisibility(View.GONE);
});
String url = "http://comment.bilibili.com/" + cid + ".xml";
return BiliDanmukuDownloadUtil.downloadXML(url);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(baseDanmakuParser -> {
mDanmakuView.prepare(baseDanmakuParser, danmakuContext);
mDanmakuView.showFPS(false);
mDanmakuView.enableDanmakuDrawingCache(false);
mDanmakuView.setCallback(new DrawHandler.Callback() {
@Override
public void prepared() {
mDanmakuView.start();
}
@Override
public void updateTimer(DanmakuTimer danmakuTimer) {
}
@Override
public void danmakuShown(BaseDanmaku danmaku) {
}
@Override
public void drawingFinished() {
}
});
mPlayerView.start();
}, throwable -> {
startText = startText + "【失败】\n视频缓冲中...";
mPrepareText.setText(startText);
startText = startText + "【失败】\n" + throwable.getMessage();
mPrepareText.setText(startText);
});
项目使用的开源库
- Glide
- jsoup
- OkHttp
- retrofit2
- ijkplayer
rx家族
-
rxjava
-
rxandroid
-
rxbinding
rxbinding可以参考下面这篇文章:
一些RxBinding使用场景 -
RxLifecycle
Lifecycle handling APIs for Android apps using RxJava
RxLifecycle -
stetho
stetho使用介绍 -
FlycoTabLayout
一个Android TabLayout库, 目前有3个TabLayout
FlycoTabLayout -
TagFlowLayout: tag标签的流式布局
https://github.com/hongyangAndroid/FlowLayout -
glide-transformations
一个Android转换库,为Glide提供各种图像转换 -
MagicaSakura
MagicaSakura 是 Android 多主题框架。 -
MaterialSearchView
- leakcanary
这个没的说, 内存泄漏检测
网友评论