• Android VLC播放器二次开发2——CPU类型检查+界面初始化


      上一篇讲了VLC整个程序的模块划分和界面主要使用的技术,今天分析一下VLC程序初始化过程,主要是初始化界面、加载解码库的操作。今天主要分析一下org.videolan.vlc.gui.MainActivity类,这个是VLC的整个程序入口。当然,严格来说,整个程序入口是VLCApplication类,因为VLC重载了Application,在我分析过的代码里面,貌似稍微复杂一点的程序,都喜欢重载Application。我自己写的一些程序也喜欢重载Application,因为可以提供一些全局功能和方便多页面数据交互。

      给出上一篇的截图,方便后面讲解程序初始化。 

    (PS:新建的QQ群,有兴趣可以加入一起讨论:Android群:322599434)

      

    1、VLCApplication类

      这里简单说一下VLCApplication,如果你对Application的作用的功能不了解,请仔细查阅相关资料,这是一个很重要知识点。VLCApplication做的事情不多,简单来说,做了两件事。第一个是一个本地配置的初始化,主要是Locale的配置,而且都是针对中文做特别处理,另外就是当系统内存不多的时候,回收一些bitmap的内存。剩下的就是保存一个全局的静态对象供其他类使用。

      本地配置初始化这里不多说,都是简单的SharedPreferences读写和Locale配置,有兴趣自己看看源码。下面给出系统内存低的时候,回收bitmap缓存。这个平时可能不会太留意。

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
      public void onLowMemory() 
        {
            super.onLowMemory();
            Log.w(TAG, "System is running low on memory");
         //系统内存降低的时候,回收bitmap缓存
            BitmapCache.getInstance().clear();
        }

    2、CPU类型检查

      这个功能基本上是用第三方库做解码器的,都需要做。因为我们编译FFMpeg解码库和其他解码库的时候,一般都需要指定CPU类型,例如现在的智能手机CPU架构有ARM和Intel的X86还有一些MIPS架构CPU。ARM里面又区分好几种类别。所以我们编译的时候都需要针对不同类型CPU做优化。这也导致了,我们需要检查我们的解码库跟机器的CPU类型是否匹配。

      之所以把这个能拿出来讲,是因为以后做一些跟CPU有关应用的时候,我们也需要检查CPU类型。

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
      public static boolean hasCompatibleCPU(Context context)
        {
            if(errorMsg != null || isCompatible) return isCompatible;
         //读取VLC解码库,这一部分里面还有很多处理,下面会讲解
            ElfData elf = readLib(context.getApplicationInfo().dataDir + "/lib/libvlcjni.so");
            if(elf == null) 
            {
                Log.e(TAG, "WARNING: Unable to read libvlcjni.so; cannot check device ABI!");
                Log.e(TAG, "WARNING: Cannot guarantee correct ABI for this build (may crash)!");
                return true;
            }
         //记录CPU的类型,注意android2.2以后,会有两个CPU类型
            String CPU_ABI = android.os.Build.CPU_ABI;
            String CPU_ABI2 = "none";
            if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { // CPU_ABI2 since 2.2
                try {
                    CPU_ABI2 = (String)android.os.Build.class.getDeclaredField("CPU_ABI2").get(null);
                } catch (Exception e) { }
            }
         //各种CPU参数记录,对这些不了解,可以百度一下。
            boolean hasNeon = false, hasFpu = false, hasArmV6 = false,
                    hasArmV7 = false, hasMips = false, hasX86 = false;
            float bogoMIPS = -1;
            int processors = 0;
         //判断CPU的架构
            if(CPU_ABI.equals("x86")) 
            {
                hasX86 = true;
            } 
            else if(CPU_ABI.equals("armeabi-v7a") ||
                      CPU_ABI2.equals("armeabi-v7a")) 
            {
                hasArmV7 = true;
                hasArmV6 = true; /* Armv7 is backwards compatible to < v6 */
            } 
            else if(CPU_ABI.equals("armeabi") ||
                      CPU_ABI2.equals("armeabi"))
            {
                hasArmV6 = true;
            }
          
         //......下面还有一些CPU参数识别,这里就省略了
         //把CPU参数保存起来 machineSpecs = new MachineSpecs(); machineSpecs.hasArmV6 = hasArmV6; machineSpecs.hasArmV7 = hasArmV7; machineSpecs.hasFpu = hasFpu; machineSpecs.hasMips = hasMips; machineSpecs.hasNeon = hasNeon; machineSpecs.hasX86 = hasX86; machineSpecs.bogoMIPS = bogoMIPS; machineSpecs.processors = processors; return true; }

       上面的CPU参数属性里面说到了好几种参数,如果你对ARM不了解,建议百度查一下。这里简单讲解一下ABI,CPU_ABI是我们经常都会碰见的,例如我们在Android工程里面的libs库下面就有armeabi-v7a类型的库。这里的ABI是指Application Binary Interface。

      ABI描述了应用程序和操作系统之间,一个应用和它的库之间,或者应用的组成部分之间的低层接口。ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。

      ABI掩盖了各种细节,例如:调用约定(控制着函数的参数如何传送以及如何接受返回值);系统调用的编码和一个应用如何向操作系统进行系统调用;以及在一个完整的操作系统ABI中,对象文件的二进制格式、程序库等等。一个完整的ABI,像Intel二进制兼容标准 (iBCS) ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作体统上运行。其他的 ABI 标准化细节包括 C++ name decoration 和同一个平台上的编译器之间的调用约定,但是不包括跨平台的兼容性。

      简单的说,ABI规范了应用程序对寄存器的使用方法,Call procedure,以及如何进入trap。符合某一平台ABI规范的应用程序就可以在这一平台上运行。这一规范是针对binary,而不是source的。所以同样的高级语言代码,使用不同的toolchain,可以得到符合不同ABI规范的binary。

      至于其他参数,如果有不清楚,请百度一下,其他参数比较好理解,这里不多说。

    3、VLCLib解码库初始化

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
      static {
            try {
                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
                    System.loadLibrary("iomx-gingerbread");  //第一个解码库
                else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2)
                    System.loadLibrary("iomx-hc");  //第二个解码库
                else
                    System.loadLibrary("iomx-ics");  //第三个解码库
            } catch (Throwable t) {
                Log.w(TAG, "Unable to load the iomx library: " + t);
            }
            try {
                System.loadLibrary("vlcjni");  //第四个解码库
            } catch (UnsatisfiedLinkError ule) {
                Log.e(TAG, "Can't load vlcjni library: " + ule);
                /// FIXME Alert user
                System.exit(1);
            } catch (SecurityException se) {
                Log.e(TAG, "Encountered a security issue when loading vlcjni library: " + se);
                /// FIXME Alert user
                System.exit(1);
            }
        }

      解码库初始化没有什么特别,这里主要是想讲讲VLC用到的解码库总共有4个,都是在LibVLC.java类里面的静态代码块加载。至于每个解码库具体作用,还没有仔细分析,后面分析清楚再说。

    4、界面初始化

    
    
    //Edited by mythou
    //http://www.cnblogs.com/mythou/
        //设置主界面画面
            View v_main = LayoutInflater.from(this).inflate(R.layout.main, null);
            mMenu.setContent(v_main);
            //SlidingMenu的view视图
            View sidebar = LayoutInflater.from(this).inflate(R.layout.sidebar, null);
            final ListView listView = (ListView)sidebar.findViewById(android.R.id.list);
            listView.setFooterDividersEnabled(true);
            mSidebarAdapter = new SidebarAdapter();
            listView.setAdapter(mSidebarAdapter);
            mMenu.setMenu(sidebar);
            //底下的信息条
            mInfoLayout = v_main.findViewById(R.id.info_layout);
            mInfoProgress = (ProgressBar) v_main.findViewById(R.id.info_progress);
            mInfoText = (TextView) v_main.findViewById(R.id.info_text);
            //ActionBar初始化
            prepareActionBar();

      界面初始化工作主要分为下面几个部分,可以对照我上面给出的效果图来分析。

    • 设置主Activity的Layout配置
    • SlidingMenu初始化
    • 底下信息条初始化
    • Actionbar初始化

      SlidingMenu的使用,我在另一篇(Android 开发自己的网络收音机2——电台列表(SlidingMenu侧滑栏))里面有详细使用介绍,这里不多说。Activity设置View这个也没什么好说。底下信息条,主要就是一个进度条,主要是搜索文件和生成视频缩略图的时候,给用户提示。本质上是ProgressBar和TextView的组合。最后Actionbar是使用了一个开源项目——ActionBarSherlock,在4.0的系统里面使用这个开源项目,会直接使用系统的ActionBar,所以跟我们使用系统的Actionbar差别不大,后面我们专门写一篇文章说明如何修改Actionbar。

    5、主Activity布局

    //Edited by mythou
    //http://www.cnblogs.com/mythou/
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <!-- 这里的FrameLayout负责显示切换的Fragment,是整个程序界面的主要显示模块-->
        <FrameLayout 
            android:id="@+id/fragment_placeholder"
            android:layout_weight="1"
            android:layout_width="fill_parent"
            android:layout_height="0dip" />
      
      <!--  下面缩略图生成进度-->  
        <RelativeLayout
            android:id="@+id/info_layout"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:visibility="gone">
            <ProgressBar
                android:id="@+id/info_progress"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:progressDrawable="@drawable/progress"/>
            <TextView
                android:id="@+id/info_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dip"
                android:layout_marginRight="5dip"
                android:singleLine="true"
                android:textColor="#FFFFFFFF"
                android:shadowColor="#CC000000"
                android:shadowRadius="1.5"
                android:shadowDx="1"
                android:shadowDy="1"/>
        </RelativeLayout>
      
       <!-- 音乐后台播放的小的控制台-->
        <FrameLayout
            android:id="@+id/audio_mini_player"
            android:layout_height="wrap_content"
            android:layout_width="fill_parent"/>
    </LinearLayout>

      最后说说主Activity的布局,这个主Activity布局很简单,上一篇文章我也说过,VLC里面大部分界面都是用Fragment作为显示模块,所以使用的Activity并不多,这里负责切换Fragment显示的Layout是一个FrameLayout,在切换不同的Fragment的时候,都是依靠这个Layout来显示。

      另外就是一个由ProgressBar和TextView组成的信息条,显示缩略图生成的进度。以及下面一个小控件显示,主要是当音乐后台播放的时候,切换到非音乐播放界面,会在底下显示一个音乐播放的控制台。也是使用一个FrameLayout实现。

    系列文章

    Linux 下编译Android-VLC开源播放器详解(附源码下载)

    Android VLC播放器二次开发1——程序结构分析

    Edited by mythou

    原创博文,转载请标明出处:http://www.cnblogs.com/mythou/p/3236071.html 

     

  • 相关阅读:
    工厂模式简介
    设计模式
    idea的安装与配置及基本用法
    软件架构设计的七大原则
    C#设计模式开启闯关之路
    基础知识详解系列目录
    .Net Core2.2 使用 AutoMapper进行实体转换
    通俗易懂设计模式解析——解释器模式
    通俗易懂设计模式解析——备忘录模式
    通俗易懂设计模式解析——访问者模式
  • 原文地址:https://www.cnblogs.com/mythou/p/3236071.html
Copyright © 2020-2023  润新知