bilibili项目学习

2017-07-07  本文已影响0人  Luliang

项目地址

https://github.com/HotBitmapGG/bilibili-android-client

已经存在的问题: 视频详情的接口都挂掉了, 新版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

项目地址

https://github.com/trello/RxLifecycle

该项目是为了防止RxJava中subscription导致内存泄漏而诞生的,核心思想是通过监听Activity、Fragment的生命周期,来自动断开subscription以防止内存泄漏。

3. java双冒号是什么操作符?

https://www.zhihu.com/question/28565691

4. Rxjava相关文章

http://www.jianshu.com/p/6fd8640046f1

比如被观察者产生的事件中只有图片文件路径,但是在观察者这里只想要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);
        });

项目使用的开源库

rx家族

https://github.com/MiguelCatalan/MaterialSearchView

上一篇下一篇

猜你喜欢

热点阅读