美文网首页androidAndroid TVAndroid知识
Android TV开发总结(六)构建一个TV app的直播节目

Android TV开发总结(六)构建一个TV app的直播节目

作者: 码农突围 | 来源:发表于2016-10-30 10:51 被阅读2443次

    近年来,Android TV的迅速发展,传统的有线电视受到较大的冲击,在TV上用户同样也可以看到各个有线电视的直播频道,相对于手机,这种直播节目,体验效果更佳,尤其是一样赛事节目,大屏幕看得才够痛快,还可以邀几好友一起欣赏。今天将介绍构建一个TV app的直播节目实例,此实例上传到Github: https://github.com/hejunlin2013/LivePlayback 喜欢可以star。Agenda如下:

    • 效果图
    • 代码实现:
    • 主页面:Recycleview对应Adapater
    • 直播节目源
    • 播放器
    • 播放页处理
    • 播放页的播放panel:
    先看下效果图:主界面: 这里写图片描述 这里写图片描述 CCTV-1: 这里写图片描述 湖南卫视: 这里写图片描述 CCTV-第一剧场: 这里写图片描述 CCTV-15(音乐): 这里写图片描述 CCTV-14(少儿): 这里写图片描述 CCTV-13(新闻): 这里写图片描述 CCTV-12(社会与法): 这里写图片描述 CCTV-11(戏曲): 这里写图片描述 CCTV-10(科教): 这里写图片描述 CCTV-9(纪录): 这里写图片描述 CCTV-8(电视剧): 这里写图片描述 CCTV-第一剧场: 这里写图片描述 CCTV-15: 这里写图片描述

    代码实现:

    • 主页面:Recycleview对应adapater
    • 直播节目源
    • 播放器
    • 播放页处理主页面:
    /*
     * Copyright (C) 2016 hejunlin <hejunlin2013@gmail.com>
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    public class MainActivity extends Activity {
    
        private MetroViewBorderImpl mMetroViewBorderImpl;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mMetroViewBorderImpl = new MetroViewBorderImpl(this);
            mMetroViewBorderImpl.setBackgroundResource(R.drawable.border_color);
            loadRecyclerViewMenuItem();
        }
    
        private void loadRecyclerViewMenuItem() {
            RecyclerView recyclerView = (RecyclerView) findViewById(R.id.ry_menu_item);
            GridLayoutManager layoutManager = new GridLayoutManager(this, 1);
            layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
            recyclerView.setLayoutManager(layoutManager);
            recyclerView.setFocusable(false);
            mMetroViewBorderImpl.attachTo(recyclerView);
            createOptionItemData(recyclerView, R.layout.detail_menu_item);
        }
    
        private void createOptionItemData(RecyclerView recyclerView, int id) {
            OptionItemAdapter adapter = new OptionItemAdapter(this, id);
            recyclerView.setAdapter(adapter);
            recyclerView.scrollToPosition(0);
        }
    }
    

    播放页:

    /*
     * Copyright (C) 2016 hejunlin <hejunlin2013@gmail.com>
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    public class LiveActivity extends Activity {
    
        private IjkVideoView mVideoView;
        private RelativeLayout mVideoViewLayout;
        private RelativeLayout mLoadingLayout;
        private TextView mLoadingText;
        private TextView mTextClock;
        private String mVideoUrl = "";
        private int mRetryTimes = 0;
        private static final int CONNECTION_TIMES = 5;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_live);
            mVideoUrl = getIntent().getStringExtra("url");
            mVideoView = (IjkVideoView) findViewById(R.id.videoview);
            mVideoViewLayout = (RelativeLayout) findViewById(R.id.fl_videoview);
            mLoadingLayout = (RelativeLayout) findViewById(R.id.rl_loading);
            mLoadingText = (TextView) findViewById(R.id.tv_load_msg);
            mTextClock = (TextView)findViewById(R.id.tv_time);
            mTextClock.setText(getDateFormate());
            mLoadingText.setText("节目加载中...");
            initVideo();
        }
    
        private String getDateFormate(){
            Calendar c = Calendar.getInstance();
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String formattedDate = df.format(c.getTime());
            return formattedDate;
        }
    
        public void initVideo() {
            // init player
            IjkMediaPlayer.loadLibrariesOnce(null);
            IjkMediaPlayer.native_profileBegin("libijkplayer.so");
            mVideoView.setVideoURI(Uri.parse(mVideoUrl));
            mVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(IMediaPlayer mp) {
                    mVideoView.start();
                }
            });
    
            mVideoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
                @Override
                public boolean onInfo(IMediaPlayer mp, int what, int extra) {
                    switch (what) {
                        case IjkMediaPlayer.MEDIA_INFO_BUFFERING_START:
                            mLoadingLayout.setVisibility(View.VISIBLE);
                            break;
                        case IjkMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
                        case IjkMediaPlayer.MEDIA_INFO_BUFFERING_END:
                            mLoadingLayout.setVisibility(View.GONE);
                            break;
                    }
                    return false;
                }
            });
    
            mVideoView.setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(IMediaPlayer mp) {
                    mLoadingLayout.setVisibility(View.VISIBLE);
                    mVideoView.stopPlayback();
                    mVideoView.release(true);
                    mVideoView.setVideoURI(Uri.parse(mVideoUrl));
                }
            });
    
            mVideoView.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(IMediaPlayer mp, int what, int extra) {
                    if (mRetryTimes > CONNECTION_TIMES) {
                        new AlertDialog.Builder(LiveActivity.this)
                                .setMessage("节目暂时不能播放")
                                .setPositiveButton(R.string.VideoView_error_button,
                                        new DialogInterface.OnClickListener() {
                                            public void onClick(DialogInterface dialog, int whichButton) {
                                                LiveActivity.this.finish();
                                            }
                                        })
                                .setCancelable(false)
                                .show();
                    } else {
                        mVideoView.stopPlayback();
                        mVideoView.release(true);
                        mVideoView.setVideoURI(Uri.parse(mVideoUrl));
                    }
                    return false;
                }
            });
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            if (!mVideoView.isBackgroundPlayEnabled()) {
                mVideoView.stopPlayback();
                mVideoView.release(true);
                mVideoView.stopBackgroundPlay();
            }
            IjkMediaPlayer.native_profileEnd();
        }
    
        public static void activityStart(Context context, String url) {
            Intent intent = new Intent(context, LiveActivity.class);
            intent.putExtra("url", url);
            context.startActivity(intent);
        }
    
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
        }
    
    }
    

    播放器是用二次封装的ijkplayer,从主页面传url到播放页面,关才mediaplayer相关,之前专门写了专题分析,mediaplayer的状态可参考《Android Multimedia框架总结(一)MediaPlayer介绍之状态图及生命周期》
    第三方播放器典型特点就是另起一个mediaplayerservice,注意这是另外一个进程,为什么是另一个进程,可参见我的文章:MediaPlayer的C/S模型。对于ijkplayer这个框架,因为做实例,才引入,不做评价,也不会去深究,满足基本播放需求就ok。市场上有很多第三方播放框架,ijkplayer,vitamio,百度云播放等。

    再看下播放页的播放panel:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#22000000"
        android:orientation="vertical">
    
        <RelativeLayout
            android:id="@+id/fl_videoview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/colorBlack">
    
            <com.hejunlin.liveplayback.ijkplayer.media.IjkVideoView
                android:id="@+id/videoview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_centerInParent="true"
                android:background="@color/colorBlack">
            </com.hejunlin.liveplayback.ijkplayer.media.IjkVideoView>
    
            <RelativeLayout
                android:id="@+id/rl_loading"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#de262a3b">
    
                <TextView
                    android:id="@+id/tv_load_msg"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/pb_loading"
                    android:layout_centerInParent="true"
                    android:layout_marginTop="6dp"
                    android:textColor="#ffffff"
                    android:textSize="16sp" />
    
                <ProgressBar
                    android:id="@+id/pb_loading"
                    android:layout_width="60dp"
                    android:layout_height="60dp"
                    android:layout_centerInParent="true"
                    android:layout_marginTop="60dp"
                    android:indeterminate="false"            
                    android:indeterminateDrawable="@drawable/video_loading"
                    android:padding="5dp" />
            </RelativeLayout>
            
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/player_panel_background_color">
    
                <TextView
                    android:id="@+id/tv_title"
                    android:layout_width="wrap_content"
                    android:layout_height="60dp"
                    android:textSize="24dp"
                    android:text="Android TV开发总结(六)构建一个TV app的直播节目实例"
                    android:layout_centerVertical="true"
                    android:layout_marginTop="18dp"
                    android:textColor="@color/white"/>
                
                <TextView
                    android:id="@+id/tv_time"
                    android:layout_width="wrap_content"
                    android:layout_height="60dp"
                    android:textSize="20dp"
                    android:layout_toRightOf="@id/tv_title"
                    android:layout_alignParentRight="true"
                    android:layout_centerVertical="true"
                    android:layout_marginLeft="60dp"
                    android:layout_marginTop="20dp"
                    android:textColor="@color/white"/>
            </LinearLayout>
        </RelativeLayout>
    </RelativeLayout>
    

    这里有几个点要注意:

    • 为演示,并未对层级进行使用FrameLayout,及viewstub,include等性能优化相关的,在实际商用项目中,建议写xml文件,尽可能遵循过少的层级,高级标签及FrameLayout等技巧。
    • 所有的size切勿直接写死,用 android:layout_marginTop="@dimen/dimen_20dp"表示,string值统一写到string.xml中,这些基本的规范,会让你提高不少效率。

    相关文章

      网友评论

      • 837545155:你好!出现画面卡顿,播放进度、声音一直在走,但是画面卡住不动怎么解决呢?
      • Greathfs:你好 能加个好友吗?最近开发Android 类似TV 应用,想咨询点问题:smile:
        837545155:你开发的怎么样了

      本文标题:Android TV开发总结(六)构建一个TV app的直播节目

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