美文网首页
bilibili项目学习

bilibili项目学习

作者: Luliang | 来源:发表于2017-07-07 23:52 被阅读0次

    项目地址

    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);
            });
    

    项目使用的开源库

    • Glide
    • jsoup
    • OkHttp
    • retrofit2
    • ijkplayer

    rx家族

    https://github.com/MiguelCatalan/MaterialSearchView

    • leakcanary
      这个没的说, 内存泄漏检测

    相关文章

      网友评论

          本文标题:bilibili项目学习

          本文链接:https://www.haomeiwen.com/subject/uyzrhxtx.html