Quantcast
Channel: 英特尔开发人员专区文章
Viewing all 172 articles
Browse latest View live

样本代码:数据加密应用

$
0
0

下载 PDF 文档

下载样本代码

Christopher Bird,软件应用工程师

应用来源:
英特尔 SSG

简介

加密非常重要,因为对于您不希望任何人访问的数据,它能够帮助您确保其安全。 加密一直是安全领域的热门话题。 随着越来越多的移动设备开始存储宝贵信息,加密对于确保信息的安全变得至关重要。

本文介绍了可通过 Java* 或 OpenSSL* 使用的数据加密 API。 这两种解决方案都可以在 Android* 操作系统上使用。

我们建议您对这些特性进行尝试,并按照本文的内容编译代码。

数据加密代码及介绍

如果您想要在 Android 上加密数据,可以选择以下两种选择: Java Crypto API 和 OpenSSL API。 我们将为您介绍如何使用这两种方式加密数据。

Java Crypto API

在 Android 上使用 Java Crypto API 非常简单。  首先,您需要为该加密生成一个密钥。 javax.crypto 软件包中包含的 KeyGenerator 类可帮助您完成该任务。

	mKey = null;
	try {
		kgen = KeyGenerator.getInstance("AES");
		mKey = kgen.generateKey();

	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
	}

 

然后,您可以使用生成的密钥为数据文件加密。 这可以通过向 javax.crypto 创建的 AES 密码传送多数据块的字节来完成。

// open stream to read origFilepath. We are going to save encrypted contents to outfile
	InputStream fis = new FileInputStream(origFilepath);
	File outfile = new File(encFilepath);
	int read = 0;
	if (!outfile.exists())
		outfile.createNewFile();

	FileOutputStream encfos = new FileOutputStream(outfile);
	// Create Cipher using "AES" provider
	Cipher encipher = Cipher.getInstance("AES");
	encipher.init(Cipher.ENCRYPT_MODE, mKey);
	CipherOutputStream cos = new CipherOutputStream(encfos, encipher);

	// capture time it takes to encrypt file
	start = System.nanoTime();
	Log.d(TAG, String.valueOf(start));

	byte[] block = new byte[mBlocksize];

	while ((read = fis.read(block,0,mBlocksize)) != -1) {
		cos.write(block,0, read);
	}
	cos.close();
	stop = System.nanoTime();

	Log.d(TAG, String.valueOf(stop));
	seconds = (stop - start) / 1000000;// for milliseconds
	Log.d(TAG, String.valueOf(seconds));

	fis.close();

 

OpenSSL API

在 Android 上的 OpenSSL 中加密数据时,需要编写能够在 Java 中通过 JNI 调用访问的原生 C 代码。 它需要的任务更多,但是相应地您也可以获得更高的性能。 

首先,我们来生成该密钥和 iv。

unsigned char cKeyBuffer[KEYSIZE/sizeof(unsigned char)];
unsigned char iv[] = "01234567890123456";
int opensslIsSeeded = 0;
if (!opensslIsSeeded) {
	if (!RAND_load_file("/dev/urandom", seedbytes)) {
		return -1;
	}
	opensslIsSeeded = 1;
}

if (!RAND_bytes((unsigned char *)cKeyBuffer, KEYSIZE )) {
}

 

然后,您可以使用生成的密钥(cKeyBuffer)为文件加密。 通过输入密钥和 iv 来初始化 EVP。 然后向 EVP_EncryptUpdate 函数传送多数据块的字节。 文件最后一个数据块的字节需要传送至 EVP_EncryptFinal_ex 函数。


if (!(EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, cKeyBuffer, iv ))) {
	ret = -1;
	printf( "ERROR: EVP_ENCRYPTINIT_EXn");
}

// go through file, and encrypt
if ( orig_file != NULL ) {
   	origData = new unsigned char[aes_blocksize];
    	encData = new unsigned char[aes_blocksize+EVP_CIPHER_CTX_block_size(e_ctx)]; // potential for encryption to be 16 bytes longer than original

	printf( "Encoding file: %sn", filename);

	bytesread = fread(origData, 1, aes_blocksize, orig_file);
	// read bytes from file, then send to cipher
	while ( bytesread ) {


		if (!(EVP_EncryptUpdate(e_ctx, encData, &len, origData, bytesread))) {
			ret = -1;
			printf( "ERROR: EVP_ENCRYPTUPDATEn");
		}
		encData_len = len;

		fwrite(encData, 1, encData_len, enc_file );
		// read more bytes
		bytesread = fread(origData, 1, aes_blocksize, orig_file);
	}
	// last step encryption
	if (!(EVP_EncryptFinal_ex(e_ctx, encData, &len))) {
		ret = -1;
		printf( "ERROR: EVP_ENCRYPTFINAL_EXn");
	}
	encData_len = len;

	fwrite(encData, 1, encData_len, enc_file );

	// free cipher
	EVP_CIPHER_CTX_free(e_ctx);

 

结论

通过按照本文介绍的样本来实施代码,您可以快速了解如何使用 Java Crypto API 和 OpenSSL API 为运行 Android 的英特尔® 处理器平台上的数据加密。

作者介绍

Christopher Bird 是英特尔软件和解决方案事业部(SSG)、开发人员关系部门、英特尔® 凌动™ 处理器创新技术工程团队的一员。 

相关文章与资源:

声明

本文件中包含关于英特尔产品的信息。 本文件不构成对任何知识产权的授权,包括明示的、暗示的,也无论是基于禁止反言的原则或其他。 英特尔不承担任何其他责任。英特尔在此作出免责声明:本文件不构成英特尔关于其产品的使用和/或销售的任何明示或暗示的保证,包括不就其产品的(i)对某一特定用途的适用性、(ii)适销性以及(iii)对任何专利、版权或其他知识产权的侵害的承担任何责任或作出任何担保。

除非经过英特尔的书面同意认可,英特尔的产品无意被设计用于或被用于以下应用:即在这样的应用中可因英特尔产品的故障而导致人身伤亡。


英特尔有权随时更改产品的规格和描述,恕不另行通知。 设计者不应信赖任何英特产品所不具有的特性,设计者亦不应信赖任何标有保留权利摂或未定义摂说明或特性描述。 对此,英特尔保留将来对其进行定义的权利,同时,英特尔不应为因其日后更改该等说明或特性描述而产生的冲突和不相容承担任何责任。 此处提供的信息可随时更改,恕不另行通知。 请勿根据本文件提供的信息完成一项产品设计。

本文件所描述的产品可能包含使其与宣称的规格不符的设计缺陷或失误。 这些缺陷或失误已收录于勘误表中,可索取获得。

在发出订单之前,请联系当地的英特尔营业部或分销商以获取最新的产品规格。

如欲获得本文涉及的带编号文档的副本或其他英特尔文献,可致电 1-800-548-4725,或访问: http://www.intel.com/design/literature.htm

在性能检测过程中涉及的软件及其性能只有在英特尔微处理器的架构下方能得到优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。

对本文件中包含的软件源代码的提供均依据相关软件许可而做出,任何对该等源代码的使用和复制均应按照相关软件许可的条款执行。

英特尔、Intel 标识、Atom 和凌动是英特尔公司在美国和其他国家(地区)的商标。

英特尔公司 © 2014 年版权所有。 所有权保留。

* 其他的名称和品牌可能是其他所有者的资产。


教程:基于 Android* 操作系统的 OpenCL™ 入门

$
0
0

下载代码样本

下载文档

面向 Android* 操作系统的 OpenCL™ 基本指南可提供使用 Android 应用中的 OpenCL 的指南。 本指南是处理 Android 应用的交互式图片。
本指南主要用于展示如何在 Android 应用中使用 OpenCL,如何着手编写 OpenCL 代码以及如何链接至 OpenCL 运行时。 本指南展示了 OpenCL API 调用的典型序列,以及在 OpenCL 设备上获取配合动画运行的简单图像处理内核的一般工作流程。 本指南不包括高级主题,如有效数据共享或 Android OpenCL 性能 BKM 等。

复杂等级:初级
开发平台:任意平台
目标平台: Android* OS 4.2.2 及更高版本
目标设备:基于 Android* 设备的 GPU 设备

注:
Android 模拟器不提供 GPU OpenCL 设备支持。 如要在 Android 模拟器上运行样本,请将目标 OpenCL 设备类型从 GPU 更改为 CPU,更改方式为:在 jni/step.cpp 文件的 451 行将 CL_DEVICE_TYPE_GPU替换为 CL_DEVICE_TYPE_CPU

关于样本的更多信息,请参见样本软件包中的样本用户指南。

* OpenCL 和 OpenCL 标识是苹果公司的商标,需获得 Khronos 的许可方能使用。

为 Android* 设备构建动态 UI

$
0
0

下载文档

下载为 Android* 设备构建动态 UI [PDF 1MB]

摘要

“动态 UI”对于 Android* 开发人员意味着什么? 作为 Android 应用开发人员,你可能希望你的应用 UI 能够适应应用内容的动态环境。 你可以进行一定程度的定制,但一般来说最好能够符合最新的 Android 准则和趋势。 但是,这可能是一件困难的事情,因为市场上存在多种多样的 Android 设备,而且你还需要与最新的 Android SDK 保持同步。

在本文中,我们将主要介绍几种 Android UI 编程技术帮助您达成动态 UI 目标,包括使用包含动态应用数据的操作栏、标签和滑动视图来增强屏幕导航,使用 Android fragment 为使用不同尺寸显示器的屏幕设计多窗口和主视图布局,以及使用 Android 资源系统提高使用不同分辨率和密度的屏幕的图形和文本内容呈现。

目录

  1. 简介
  2. 示例餐厅菜单应用
  3. 操作栏、标签、滑动视图和动态应用数据
  4. Android UI fragment、多窗口和主从视图
  5. Android 资源系统、图形和文本、屏幕分辨率和密度
  6. 结论
  7. 参考文献

简介

本文从三个方面讨论了动态 UI 的构建:

  1. 使用最新的 Android UI 模式和控制显示动态内容 — 至少,你希望你的应用 UI 像其他 Android 系统一样“生存”和“呼吸”。 关键一点是使用最新的 Android UI 组件、控制和导航模式来设计应用。 你可能希望,能够进行一定程度的定制,而且总体上紧跟最新的 Android 准则和趋势。 那么,我们如何使用此最新 UI 趋势呈现动态应用内容呢? 在本文中,我们将演示如何使用最新 Android UI 组件,如操作栏、标签和滑动视图,来呈现动态应用数据。
  2. UI 布局和导航流程– 为 4 英寸的手机和 11 英寸的平板电脑使用相同的 UI 布局是否可行? 为了最大限度地利用大显示器的空间,有时,你希望为显示器尺寸不同的设备使用不同的布局。 在本文中,我们将讨论如何使用 Android Fragment 设计多窗口/单窗口布局和主从视图,以适应拥有不同显示器尺寸的屏幕。
  3. 显示器分辨率和密度— 除了 UI 布局之外,如果你在应用中使用了图形,如何确保在采用不同屏幕分辨率和像素密度的设备上图形不被拉伸或像素化? 文本项的字号如何处理? 文本项采用 20 号字体在手机上可能非常合适,但是在平板电脑上可能会太小。 我们将讨论如何使用 Android 资源系统来处理这些问题。

示例餐厅菜单应用

为了解释本文中介绍的编程概念,我编写了一款可帮助用户浏览按照食物分类安排的餐厅菜单的应用。 餐厅菜单应用可为本文中讨论的话题提供编程示例。

请注意,本文面向对 Java 编程和 Android 开发概念的基本知识有一定了解的读者。 本文不提供 Android 教程,而主要介绍几种基本的 UI 技术帮助开发人员达成动态 UI 的构建目标。

此外,本文中的代码片段是取自餐厅应用的示例代码。 我们选取这些代码,仅为解释本文中摘录的编程内容。 它们并不提供应用结构和细节的完整视图。 如果您希望全面了解该示例应用提供的 Android 应用开发,如 fragment 的生命循环处理、单元格项选择的 UI 更新、在配置更改时保留 fragment 中的用户选择和应用数据或资源中的 UI 风格示例,请参见“参考文献”部分的 Android 开发人员链接,了解更多信息。

操作栏、标签、滑动视图和动态应用数据

在为“餐厅菜单”应用设计 UI 时,我考虑到以下几个方面:

  1. UI 应支持用户从主窗口访问基本应用功能
  2. UI 应支持餐厅菜单应用的所有者动态添加/删除食物项目
  3. UI 在切换食物分类时应可提供一致的外观和视图
  4. UI 应可在需要时呈现带有图片的食物和菜单信息
  5. UI 应支持使用手势进行界面导航,这种方式在 Android 系统中非常直观

我选择了 Android 操作栏、标签和滑动视图来满足上述要求。 事实上,自 Android 3.0 起,这些 UI 元素成为 Android 推荐的重点 UI 模式。 你会发现大部分的常用 Android 应用中都使用了这些 UI 元素,如电子邮件、日历、hangouts、音乐和 play store。 下面的截屏展示了我们如何使用这些 UI 元素呈现可浏览的餐厅菜单。



图 1 餐厅菜单应用的屏幕概观

使用滑动视图创建操作栏和标签

本部分内容介绍了如何为 Android 应用创建带有操作栏和标签的滑动视图。

 

  1. 向主屏幕的布局文件中添加 ViewPager,以便处理标签的滑动视图(activity_main.xml)
    <android.support.v4.view.ViewPager
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity"><android.support.v4.view.PagerTitleStrip
                android:id="@+id/pager_title_strip"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="top"
                android:background="#333333"
                android:paddingBottom="4dp"
                android:paddingTop="4dp"
                android:textColor="#ffffff"/></android.support.v4.view.ViewPager>
  2. 在 MainActivity.java(应用的主界面)中,activity_main.xml 布局进行 inflate 处理,从活动布局文件中检索 ViewPager,创建 ViewPagerAdapter 以处理每页标签的创建和初始化,并将 OnPageChangeListener 分配至 ViewPager
     
    // Setting up swipe view for each tab
    mViewPager = (ViewPager) findViewById(R.id.pager);
    mViewPager.setOnPageChangeListener(this);
    mPagerAdapter = new PagerAdapter(getSupportFragmentManager(), this);
    mViewPager.setAdapter(mPagerAdapter);
  3. 执行 OnPageChangeListener,在视图切换时处理与应用相关的任务。 至少,当用户将视图滑动至下一个标签时,代码应可在操作栏上设置选择的标签。
    /**
     * This method is called when the user swipes the tab from one to another
     */
    public void onPageSelected(int position) {
            // on changing the page
            // make respected tab selected
            mActionBar.setSelectedNavigationItem(position);
            mCurrentViewedCategory = (String) mActionBar.getTabAt(position).getText();
    }
    
    /**
     * Tab swipe view related callback
     */
    public void onPageScrolled(int arg0, float arg1, int arg2) {
    }
    
    /**
     * Tab swipe view related callback
     */
    public void onPageScrollStateChanged(int arg0) {
    }
    
    
  4. 对 PagerAdapter(继承自 FragmentStatePagerAdapter)进行定义,以处理每个标签的视图。 示例中,Pager Adapter 被定义为 MainActivity.java 的内部类。 初始化每个页面时将会调用 “getItem”。 在本案例中,每一页包括一个含有菜单数据的主从视图的碎片,如图 1 所示。 碎片编程的具体内容将会在下一部分进行介绍。

    技巧

    编程中共有两种 PagerAdapter:FragmentPagerAdapter 和 FragmentStatePagerAdapter。 从内存效率角度来看,如果页面数固定则推荐使用前者;如果动态分配页面数则推荐使用后者。 如果使用了 FragmentStatePagerAdapter,当用户离开页面时,页面调度程序就会被破坏。 本示例使用了 FragmentStatePagerAdapter,因为食物种类的数量能够随着应用数据变化。

    /**
    * Fragment pager adapter to handle tab swipe view. Each tab view contains
    * an ultimate fragment which includes a grid menu view and a detail view.
    * Depending on the orientation of the device, the app decides whether to
    * show both views or just grid view.
    */
    
    class PagerAdapter extends FragmentStatePagerAdapter {
        UltimateViewFragment ultimateViewFragment;
        FragmentActivity mFragmentActivity;
        UltimateViewFragment[] fragmentArray = new
            UltimateViewFragment[mCategory.size()];
    
    public PagerAdapter(FragmentManager fm, FragmentActivity fragmentActivity) {
         super(fm);
         mFragmentActivity = fragmentActivity;
    }
    
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        super.instantiateItem(container, position);
        UltimateViewFragment fragment = (UltimateViewFragment)
            super.instantiateItem(container, position);
            fragment.setGridItemListener((GridItemListener)
            mFragmentActivity);
            fragmentArray[position] = fragment;
            return fragment;
    }
    
    @Override
    public Fragment getItem(int position) {
        Bundle args = new Bundle();
        // Each ultimate view is associated with one menu category
        args.putString(MenuFactory.ARG_CATEGORY_NAME,
        mCategory.get(position));
        ultimateViewFragment = new UltimateViewFragment();
        ultimateViewFragment.setArguments(args);
        // Register as a GridItemListener to receive the notification of grid
        // item click
        ultimateViewFragment.setGridItemListener((GridItemListener)
        mFragmentActivity);
        fragmentArray[position] = ultimateViewFragment;
        return ultimateViewFragment;
    }
    
    @Override
        public int getCount() {
            // Return number of tabs
            return mCategory.size();
     }
    
    @Override
        public CharSequence getPageTitle(int position) {
           //Return the title of each tab
            return mCategory.get(position);
        }
    }
    
    
  5. 从 Activity 中检索 ActionBar (mActionBar),并将导航模式设置为 NAVIGATION_MODE_TABS
  6. 使用 mActionBar.addTab 向 ActionBar 添加标签,并使用指定文本初始化标签标题
    // Setting up action bar and tabs
    mActionBar = getActionBar();
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    for (int i = 0; i < mCategory.size(); i++) {
        mActionBar.addTab(mActionBar.newTab().setText(
        mCategory.get(i)).setTabListener(this));
        // Initialize selected items in the hashtable with the first item
        // of each category
        if (savedInstanceState == null) {
             mSelectedItems.put(mCategory.get(i), mMenuFactory.getMenuWithCategory(
                   mCategory.get(i)).get(0));
        } else {
             //update the mSelectedItems from the last saved instance
             String[] selectedItems = savedInstanceState.getStringArray("selectedItems");
             mSelectedItems.put(mCategory.get(i), mMenuFactory.getMenuItem(
             mCategory.get(i),selectedItems[i]));
         }
     }

技巧

ActionBar API 在 Android 3.0 (API level 11) 中首次推出,但是由于具备对 Android 2.1 (API level 7) 及更高版本的兼容性,它也能够在支持的库中使用。 本示例代码使用了 android-support-v4.jar 库。 此 jar 格式文件位于应用根目录下的库文件夹中。 如果您使用 Eclipse 作为 IDE,请将路径添加至 Project Properties->Java Build Path->Libraries 中的库

向操作栏中添加操作项

操作栏中包含用户经常使用的操作项。 这些操作项位于操作栏顶部,以便于用户轻松访问。 在样本代码中,我们对几个操作项进行了定义,如摄像头、调用、查看和设置,请见下面截屏中的突出显示。



图 2 带有操作项的操作栏

以下介绍了如何向操作栏中添加操作项。

  1. 在 res/menu 下的 xml 中定义操作项(如 action_bar.xml)
    <menu xmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@+id/action_camera"
              android:icon="@drawable/ic_action_camera"
              android:title="@string/action_camera"
              android:showAsAction="always" /><item android:id="@+id/action_search"
              android:title="@string/action_search_str"
              android:icon="@drawable/ic_action_search"
              android:showAsAction="always"
              android:actionViewClass="android.widget.SearchView" /><item android:id="@+id/action_call"
              android:icon="@drawable/ic_action_phone"
              android:title="@string/action_call"
              android:showAsAction="always" /><item android:id="@+id/action_checkout"
              android:icon="@drawable/ic_action_shoppingcart_checkout"
              android:title="@string/action_checkout"
              android:showAsAction="always"/><item android:id="@+id/action_settings"
            android:orderInCategory="100"
            android:showAsAction="never"
            android:title="@string/action_settings"/></menu>
  2. 在代码中 Inflate 操作项菜单(MainActivity.java)
    /**
    * Initialize the action menu on action bar
    */
    public boolean onCreateOptionsMenu(Menu menu) {
    	getMenuInflater().inflate(R.menu.action_bar, menu);
    
    	//Set up the search feature
    	SearchManager searchManager =
    		(SearchManager) getSystemService(Context.SEARCH_SERVICE);
    	SearchView searchView =
    		(SearchView) menu.findItem(R.id.action_search).getActionView();
    		searchView.setSearchableInfo(
    		           searchManager.getSearchableInfo(getComponentName()));
    		return super.onCreateOptionsMenu(menu);
    }
    
    
  3. 在代码中处理操作项触发(click)
    /**
    * This method is called when the user click an action from the action bar
    */
    public boolean onOptionsItemSelected(MenuItem item) {
    	if (mDrawerToggle.onOptionsItemSelected(item)) {
    	          return true;
    	 }
    
    	 // Handle presses on the action bar items
    	 switch (item.getItemId()) {
    	    // Handle up/home navigation action
    	    case android.R.id.home:
    	    	NavUtils.navigateUpFromSameTask(this);
    	    	return true;
    	    // Handle search
    	    case R.id.action_search:
    	            	return true;
    	    // Handle settings
    	    case R.id.action_settings:
    	            	return true;
    	    // Handle camera
    	    case R.id.action_camera:
    	            	return true;
    	    //Handle check out feature
    	    case R.id.action_checkout:
    	            return true;
    	        default:
    	            return super.onOptionsItemSelected(item);
    }
    
    

技巧

如图 1 所示,操作栏中包含用户经常执行的操作项,如摄像头、搜索和查看。 如要确保整个系统中的外观和风格一致,你可以从 https://developer.android.com/design/downloads/index.html下载操作栏图示包(Action Bar Icon Pack)并将其用于应用资源区。

操作栏风格

Android 使用系统定义的颜色来渲染操作栏,这种颜色有时可能与你的应用颜色主题不相配。 有时,你可能想要使用专为应用主题设计的风格和颜色(或企业主题颜色)来设计操作栏。

例如,本示例应用中的操作栏使用了与应用主题相匹配的“栗色”。


图 3 操作栏风格示例

在本部分中,我们将介绍如何使用 Android Action Bar Style Generator 来增强操作栏的外观。

  1. 如欲获取该工具,请访问: http://android-ui-utils.googlecode.com/hg/asset-studio/dist/index.html
  2. 指定选择的颜色并通过链接下载生成的资源文件。 以下是我在本示例应用中所使用的颜色和风格。



    图 4 操作栏风格生成工具

  3. 该工具生成的示例资源图示、图像、风格 xml 文件如下所示。 向应用资源 drawable 区域中添加所有资源文件,然后更新 AndroidManifest.xml application:theme,以使用该工具生成的风格 xml 文件。



    图 5 操作栏资源示例

    <activity
                android:theme="@style/Theme.Example"
                android:name="com.example.restaurant.MainActivity"
                android:label="@string/app_name"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity>

处理 UI 中的动态应用数据

应用 UI 并非一直都是静态状态,尤其当它呈现的数据可能在应用生命周期内发生变化时。 例如,图片集应用支持用户查看和编辑大量图片,而这些图片在设备开启时随时可能会发生更改。 电子邮件应用需要处理的消息在每个服务器所配置的时间段内都会刷新一次。 在该样本餐厅菜单应用中,食物菜单是动态的。 操作栏标签和菜单格的内容可以随着食物种类和菜单而变化。

处理动态数据的一种方法是创建数据工厂;数据工厂相当于原始数据和 UI 之间的转换器。 数据工厂可以从 UI 中抽取数据操作逻辑,从而能够在不改变 UI 逻辑的情况下改变数据。 从更高层面而言,数据工厂能够与数据来源通信以获取原始应用数据,处理多种来源(如网络、本地文件系统、数据库或缓存)的原始数据,并将原始数据转化为 UI 组件可以使用的数据对象。

下图简要介绍了 UI、数据工厂和数据来源之间的流程。



图 6 处理动态应用内容的一般流程

技巧

处理动态应用内容的步骤

  • 确认可能的数据来源,如网络、本地文件系统、数据库或设备缓存
  • 创建一个数据工厂,“倾听”数据来源的变化,或根据应用的需求定期查询数据
  • 数据工厂在单独的线程中处理数据更新请求以避免阻止 UI 线程,因为数据请求和处理需要耗费一定的时间
  • UI 组件登记为数据工厂的数据变更监听器,收到数据变更通知后,刷新 UI 组件
  • 数据工厂可管理数据变更事件的监听器,并提供便捷的方式以便调用程序(caller)在不了解原始数据格式情况下也可查询数据

为了简化样本餐厅应用中的实施,我们将食物菜单数据在 Android strings.xml 中存储为一个字符串阵列。 每个阵列元素包含一个食物项记录。 在实际情况下,这些数据记录可在服务器中进行定义和存储,以便能够进行动态变更。 无论数据的来源为何处,数据格式都可以在 string.xml 中重复使用。 下文介绍了应用如何在 MenuFactory.java 中进行处理,以及如何使用应用数据初始化 UI 组件。

  1. 在 strings.xml 中定义菜单数据。 每个食物项目中都包括一个含有种类名称、菜单名称、介绍、营养成分、价格和图片名称的字符串。 数据字段由分隔符 “,,,” 进行划分。
    <string-array name="menu_array"><item>Appetizer,,,Angels on horseback,,,Oysters wrapped in bacon, served hot. In the United Kingdom they can also be a savoury, the final course of a traditional British ,,,Calories 393; Fat 22 g( Saturated 4 g); Cholesterol 101 mg; Sodium 836 mg; Carbohydrate 19g; Fiber 3g; Protein 31g,,,6.99,,,Angels on horseback.jpg</item><item>Appetizer,,,Batata vada,,,A popular Indian vegetarian fast food in Maharashtra, India. It literally means potato fritters. The name \"Batata\" means potato in English. It consists of a potato mash patty coated with chick pea flour, then deep-fried and served hot with savory condiments called chutney. The vada is a sphere, around two or three inches in diameter.,,,Calories 393; Fat 22 g( Saturated 4 g); Cholesterol 101 mg; Sodium 836 mg; Carbohydrate 19g; Fiber 3g; Protein 31g,,,7.99,,,Batata vada.jpg</item><item>Appetizer,,,Barbajuan,,,An appetizer mainly found in the eastern part of French Riviera and Northern Italy.,,,Calories 393; Fat 22 g( Saturated 4 g); Cholesterol 101 mg; Sodium 836 mg; Carbohydrate 19g; Fiber 3g; Protein 31g,,,8.99,,,Barbajuan.jpg</item><item>Appetizer,,,Blooming onion,,,Typically consists of one large onion which is cut to resemble a flower, battered and deep-fried. It is served as an appetizer at some restaurants.,,,Calories 393; Fat 22 g( Saturated 4 g); Cholesterol 101 mg; Sodium 836 mg; Carbohydrate 19g; Fiber 3g; Protein 31g,,,9.99,,,Blooming onion.jpg</item></string-array>
  2. 在应用启动过程中, MainActivity.java 将创建一个单独的 MenuFactory 参考,并加载 Android 资源区域的数据。
    mMenuFactory = MenuFactory.getInstance(res);
    mMenuFactory.loadDataFromAndroidResource();
  3. MenuFactory 处理来自 strings.xml 的数据,并将其转换为 UI 视图使用的 MenuItem 对象。
    /* Allows caller to load the app data from Android resource area */
    public void loadDataFromAndroidResource() {
        if (mMenuItems != null && mMenuItems.size() > 0) {
            clear();
        }
        mMenuItems = new ArrayList<MenuItem>();
        mCategoryList = new ArrayList<String>();
    
        String[] menuList = mResources.getStringArray(R.array.menu_array);
        MenuItem menuItem;
        String[] currentMenu;
        String currentCategory = "";
    
        for (int i = 0; i<menuList.length; i++) {
    	currentMenu = menuList[i].split(",,,");
    	menuItem = new MenuItem();
    	for (int j = 0; j< currentMenu.length; j++) {
    	    switch (j) {
    		case 0:
    			menuItem.setCategory(currentMenu[j]);
    			if (!currentMenu[j].equals(currentCategory)) {
    				currentCategory = currentMenu[j];
    				mCategoryList.add(currentMenu[j]);
    			}
    			break;
    		case 1:
    			menuItem.setName(currentMenu[j]);						                break;
    		case 2:
    			menuItem.setDescription(currentMenu[j]);
    			break;
    		case 3:
    			menuItem.setNutrition(currentMenu[j]);						                break;
    		case 4:
    			menuItem.setPrice(currentMenu[j]);						                break;
    		case 5:
    			menuItem.setImageName(currentMenu[j]);						                break;
    	    }
                   }
                   menuItem.setId(Integer.toString(i));
                   mMenuItems.add(menuItem);
        }
    }
  4. MenuFactory.java 可提供便捷的方式,以便调用程序查询 UI 更新相关信息。
    /* Allows caller to retrieve the category list based on menu items */
    public ArrayList<String> getCategoryList() {
    	return mCategoryList;
    }
    
    /* Allows caller to retrieve a list of menu based on passed in category */
    public ArrayList<MenuItem> getMenuWithCategory (String category) {
    	ArrayList<MenuItem> result = new ArrayList<MenuItem>();
    	for (int i = 0; i<mMenuItems.size(); i++) {
    		MenuItem item = mMenuItems.get(i);
    		if (item.category.equals(category)) {
    			result.add(item);
    		}
    	}
    
    	return result;
    }
    
    /* Allows caller to retrieve menu item based on passed category and index */
    public MenuItem getMenuItem (String category, int index) {
    	ArrayList<MenuItem> menuList = getMenuWithCategory(category);
    	if (menuList.size() == 0) {
    		return null;
    	}
    	return menuList.get(index);
    }
    
    /* Allows caller to retrieve menu item based on passed category and name */
    public MenuItem getMenuItem (String category, String name) {
    	MenuItem result = null;
    	for (int i = 0; i<mMenuItems.size(); i++) {
    		MenuItem item = mMenuItems.get(i);
    		if (item.category.equals(category) && item.name.equals(name)) {
    			result = item;
    		}
    	}
    	return result;
    }
    
    /* Data structure for menu item */
    class MenuItem {
    	String category;
    	String name;
    	String price;
    	String description;
    	String nutrition;
    	ImageView image;
    	String imageName;
    	String id;
    
    	public void setCategory(String str) {
    		category = str;
    	}
    
    	public void setName(String str) {
    		name = str;
    	}
    
    	public void setDescription(String str) {
    		description = str;
    	}
    
    	public void setNutrition(String str) {
    		nutrition = str;
    	}
    
    	public void setImageName(String str) {
    		imageName = str;
    	}
    
    	public void setId(String str) {
    		id = str;
    	}
    
    	public void setPrice(String str) {
    		price = str;
    	}
    
    	public void setImageView(ImageView imageView) {
    		image = imageView;
    	}
    }
    
    
  5. 使用 MenuFactory 的食物项初始化网格菜单视图。 此操作是在 MenuGridFragment.java 中完成。 在菜单应用中,每种类型中的食物项都通过内嵌于 Fragment 中的 GridView 显示 。 在 fragment 初始化过程中,将会调用 MenuFactory 检索每类中的食物项。 网格视图的数据适配器(ImageAdapter)可在初始化过程中创建和渲染每个食物项。
    /* Inflating view item for the grid view and initialize the image adapter
    * for the grid view.
    */
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    	Bundle savedInstanceState) {
    
    	View rootView = inflater.inflate(
    		R.layout.fragment_menu_grid, container, false);
    
    	mMenuList = mMenuFactory.getMenuWithCategory(mCategory);
    	mGridView = (GridView) rootView.findViewById(R.id.gridview);
    	mGridView.setAdapter(mImageAdapter);
    	mGridView.setOnItemClickListener(this);
    	return rootView;
    }
    
    
        /* Image adapter for the grid view */
        class ImageAdapter extends BaseAdapter {
    	    private LayoutInflater mInflater;
    
    	    public ImageAdapter(Context c) {
    	        mInflater = LayoutInflater.from(c);
    	    }
    
    	    public int getCount() {
    	    	return mMenuList.size();
    	    }
    
    	    public Object getItem(int position) {
    	    	return mMenuList.get(position);
    	    }
    
    	    public long getItemId(int position) {
    	        return position;
    	    }
    
    	    // create a new ImageView for each item referenced by the Adapter
    	    public View getView(int position, View convertView, ViewGroup parent) {
    
    	        View v = convertView;
    	        ImageView picture;
    	        TextView name;
    	        TextView price;
    
    	        if(v == null) {
    	            v = mInflater.inflate(R.layout.view_grid_item, parent, false);
    	            v.setTag(R.id.picture, v.findViewById(R.id.picture));
    	            v.setTag(R.id.grid_name, v.findViewById(R.id.grid_name));
    	            v.setTag(R.id.grid_price, v.findViewById(R.id.grid_price));
    	        }
    
    	        picture = (ImageView)v.getTag(R.id.picture);
    	        name = (TextView)v.getTag(R.id.grid_name);
    	        price = (TextView) v.getTag(R.id.grid_price);
    
    	        MenuItem item = (MenuItem) mMenuList.get(position);
    
    	        InputStream inputStream = null;
    	        AssetManager assetManager = null;
    	        try {
    	        	assetManager = getActivity().getAssets();
    	        	inputStream =  assetManager.open(item.imageName);
    	        	picture.setImageBitmap(BitmapFactory.decodeStream(inputStream));
    	        } catch (Exception e) {
    	        } finally {
    	        }
    
    	        name.setText(item.name);
    	        price.setText(item.price);
    
    	        //Highlight the item if it's been selected
    	        if (mSelectedPosition == position){
                    	updateGridItemColor(v, true);
    	        } else {
    	            	updateGridItemColor(v, false);
    	        }
    
    	        return v;
    	    }
    	}
    
    
  6. 使用 MenuFactory 中的食物分类初始化操作栏标签
    // Retrieve the category list from MenuFactory
    mCategory = mMenuFactory.getCategoryList();
    // Setting up action bar and tabs
    mActionBar = getActionBar();
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    for (int i = 0; i < mCategory.size(); i++) {
    mActionBar.addTab(mActionBar.newTab().setText(
    	mCategory.get(i)).setTabListener(this));
    	 // Initialize selected items in the hashtable with the first item
    	 // of each category
    	 if (savedInstanceState == null) {
    	  	mSelectedItems.put(mCategory.get(i), mMenuFactory.getMenuWithCategory(
    	    	            mCategory.get(i)).get(0));
    	    } else {
    	    	 //update the mSelectedItems from the last saved instance
    	    	 String[] selectedItems = savedInstanceState.getStringArray("selectedItems");
    	    	 mSelectedItems.put(mCategory.get(i), mMenuFactory.getMenuItem(
    	    	          mCategory.get(i),selectedItems[i]));
    	    }
    }

Android Fragment、多窗口布局和主从视图

动态 UI 的另一个重点是如何设计 Android 应用的 UI 才能让相同的应用适用于不同尺寸的设备(如平板电脑和手机)。 在本部分内容中,我们将讨论如何使用 Android fragment 为显示器尺寸不同的设备设计多窗口布局。

Android 在 3.0 中引入 “fragment” 的概念。 你可以把 “fragment” 看作 “组件化”屏幕布局的一种方式。 屏幕被划分为多个 UI 组或视图。 每个 UI 组或视图将作为一个 fragment 来实施。 你的应用将根据其所在设备的显示器来决定在运行时为用户使用哪种 UI 组/视图和导航流程。

多窗口布局是 Android fragment 的一种常见用途,在这种布局中,屏幕呈现为组合的多视图。 与屏幕中的一个视图交互可能会更新屏幕上的另一个视图。 主从视图便是此概念的一个重要 UI 设计模式。 该应用使用一个列表或网格视图 widget 在主视图中呈现总体内容。 选择网格或列表中的一个项目,将会在同一个屏幕或另一个屏幕上显示出该项目的细节视图。 在采用大尺寸显示器的设备(平板电脑)上,主视图和细节视图能够在相同的屏幕上显示。 在采用较小尺寸显示器的设备(手机)上,主视图和细节视图能够在不同的屏幕中呈现。

餐厅菜单应用在网格视图中呈现每类食物的菜单信息。 如果设备为大尺寸显示器,选择网格项将会在同一屏幕上显示食物项的详细内容;如果设备为较小的屏幕,则会显示在另一个屏幕中显示。 该设计通过 3 个 Fragment 实现:

  1. UltimateVIewFragment — 包含一个网格视图 fragment 和一个细节视图 fragment 的 fragment;内部 fragment 的可见性将取决于运行时屏幕的尺寸(如果设备采用大尺寸显示器,则仅显示细节视图)
  2. GridViewFragment — 在网格视图中呈现每类食物菜单数据的 fragment
  3. DetailViewFragment — 在网格视图中呈现选中的食物项的细节的 fragment

技巧

Android 开发人员网站上的大部分代码样本仅展示了将两个 Fragment 嵌入一个 Activity 中的主从视图的操作,而没有在一个 fragment 内嵌入两个 fragment 的操作。 在样本餐厅应用中,我们执行了第二种方式。 操作栏的滑动视图要求使用 fragment 而非 activity。 样本餐厅代码将详细展示如何使用标签滑动视图实现该操作。



图 7 适用于采用不同屏幕尺寸的设备的多窗口和主从视图

fragment 的创建

本部分内容将介绍创建本样本中使用的 fragment 所需的步骤。

  1. 使用 fragment 在 xml 中定义屏幕布局。 以下代码是 UltimateVIewFragment、GridViewFragment 和 DetailViewFragment 的屏幕布局。 请注意,以下细节视图的可见性最初设置为 “gone”,在不同屏幕尺寸的设备的布局文件中应分别对其进行更改。 例如,如果设备拥有更大的显示器,可见性应设置为 “visible”。 请参见下面的“基于屏幕尺寸的多窗口和单窗口布局”部分,了解更多信息。

    本部分内容仅展示了 UltimateViewFragment 的屏幕布局。 请参见 GridViewFragment (layout/fragment_menu_grid.xml) 和 DetailViewFragment (layout/fragment_disch_detail.xml) 的屏幕布局的完整代码样本。

    <!—UltimateViewFragment screen layout --><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal"><FrameLayout
        	android:id="@+id/grid_fragment"
    		android:layout_width="wrap_content"
    		android:layout_height="wrap_content"
    		android:layout_weight="1"/><FrameLayout
    		android:id="@+id/detail_fragment"
    		android:layout_width="wrap_content"
    		android:layout_height="wrap_content"
    		android:layout_weight="1"
    		android:visibility="gone"
    		android:orientation="vertical"/></LinearLayout>
  2. 以编程方式创建和初始化 fragment,并使用 FragmentManager 处理 fragment 的交易。 以下代码片段展示了 UltimateViewFragment 的执行; UltimateViewFragment 可在运行过程中创建 MenuGridFragment 和 DetailViewFragment。 关于执行 MenuGridFragment 和 DetailViewFragment 的详细信息,请参见完整的样本代码。
    public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            if (savedInstanceState != null) {
                String tmp = savedInstanceState.getString("selectedIndex");
                if (tmp != null) {
                    mSelectedIndex = Integer.parseInt(tmp);
                }
                mCategory = savedInstanceState.getString("currentCategory", mCategory);
            } else {
                mCategory = (String) getArguments().getString(MenuFactory.ARG_CATEGORY_NAME);
            }
            mDetailViewFragment = new DetailViewFragment();
            mGridViewFragment = new MenuGridFragment();
    
            mGridViewFragment.setCategory(mCategory);
            mGridViewFragment.setOnGridItemClickedListener(this);
    
            FragmentManager fragmentManager = this.getChildFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();
    
            if (savedInstanceState != null) {
                transaction.replace(R.id.detail_fragment, mDetailViewFragment);
                transaction.replace(R.id.grid_fragment, mGridViewFragment);
            } else {
                transaction.add(R.id.detail_fragment, mDetailViewFragment, "detail_view");
                transaction.add(R.id.grid_fragment, mGridViewFragment, "grid_view");
            }
            transaction.commit();
    }

技巧

fragment 应在一项活动运行时创建,尤其如果准备以动态方式将 fragment 从屏幕中调入或调出时。 你可以使用 FragmentManager 在屏幕中添加、更换和删除 Fragment。 如代码中所示,FragmentManager 能够从 getChildFragmentManager 而非 getFragmentManager 中检索到,因为子 fragment 的容器是 fragment 而非 Activity。 此外,在改变方向时,为了避免在 UltimateViewFragment 中在彼此上面添加相同的 fragment,代码应用使用 FragmentTransaction 中的 “replace” 而非 “add” ,用新的 fragment 替换现有的 fragment。

fragment 和活动之间的通信

fragment 之间的通信可以通过使用监听器模式来实现,这包括两个简单的步骤:

  1. 定义一个监听器界面,这可以通过希望从其他组件接收通知的组件来执行。
  2. 在使用多窗口和主从 fragment 的情况下,如果子 fragment 发送通知,子 fragment 的容器将注册为子 fragment 的监听器。 收到通知后,母 fragment 或活动能够根据接收到的信息执行相应的操作。

以下是餐厅应用中的做法:

  1. 定义一个 GridItemListener界面。该界面通过网格 fragment 容器来实现。 当网格选择操作发生时,网格 fragment 将通知母容器。
    /**
     * An interface implemented by classes which want to receive notification
     * when a menu item is clicked on the grid. This interface is used by
     * UltimateViewFragment, ActionBarActivity, DetailView to communicate the selected
     * menu item.
    */
    public interface GridItemListener {
    	public void onGridItemClick(com.example.restaurant.MenuFactory.MenuItem itemSelected, int position);
    }
  2. UltimateViewFragment 支持调用程序将其注册为 GridItemListener。
    /* Allow caller to set the grid item listener */
    public void setGridItemListener(GridItemListener gridItemListener) {
    	mGridItemListener = gridItemListener;
    }
  3. UltimateViewFragment 通知其监听器网格选择中的变化。
    /* Handle the event of item click from the menu grid */
    public void onGridItemClick(MenuItem itemSelected, int position) {
    	mGridItemListener.onGridItemClick(itemSelected, position);
    	mSelectedIndex = position;
    	View detail = getActivity().findViewById(R.id.detail_fragment);
    	//portrait mode
    	if (detail != null && detail.getVisibility() == View.GONE) {
    		Intent intent = new Intent(this.getActivity(), DetailActivity.class);
               		 intent.setAction("View");
                		intent.putExtra("category", itemSelected.category);
              		intent.putExtra("entree_name", itemSelected.name);
                		Activity activity = getActivity();
                		activity.startActivity(intent);
    
           	 //landscape mode
    	} else {
    		mDetailViewFragment.update(itemSelected);
    	}
    }
    
    
  4. 在 MainActivity 中,每个标签视图都是 UltimateViewFragment 的母视图。 MainActivity 将其自身注册为 GridItemListener,以追踪上一个在每类中选中的食物项。
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
    super.instantiateItem(container, position);
    UltimateViewFragment fragment = (UltimateViewFragment)
    super.instantiateItem(container, position);
    	fragment.setGridItemListener((GridItemListener)
    	mFragmentActivity);
    fragmentArray[position] = fragment;
    return fragment;
    }
  5. 收到通知后,MainActivity 将在监听器回调中采用相应的操作。
    /**
     * This method is called when a grid menu item is clicked
    */
    public void onGridItemClick(com.example.restaurant.MenuFactory.MenuItem
    	itemSelected, int position) {
    mSelectedItems.put(itemSelected.category, itemSelected);
    }

基于屏幕尺寸的多窗口和单窗口布局

如上文所述,Android fragment 可帮助你为屏幕定义多窗口布局。 但是,如何使用屏幕尺寸确定多窗口/单窗口布局,如何根据多窗口/单窗口设计提供不同的屏幕流程呢? Android 资源系统可为应用提供处理多界面布局的配置限定符。

根据屏幕尺寸,资源系统中将提供不同的布局文件。 在应用启动过程中,Android 将根据显示器尺寸运用相应地布局文件。 如果采用 Android 3.2 之前的版本,你可以为小型、正常、大型或超大型屏幕定义布局。 对于屏幕尺寸小于 426dpx320dp 的设备,布局文件可以在 res/layout-small 中进行定义;对于屏幕尺寸大于 960dpx720dp 的设备,可以在 res/layout-xlarge 中定义。 以下是这些屏幕尺寸的定义。

  • 超大尺寸最低为 960dp x 720dp
  • 大型尺寸最低为 640dp x 480dp
  • 正常尺寸最低为 470dp x 320dp
  • 小型尺寸最低为 426dp x 320dp

下图展示了每种尺寸与以英寸计的实际设备尺寸的关系。



图 8 屏幕尺寸限定符与以英寸计的实际显示器尺寸的关系

自 Android 3.2 起,以上屏幕尺寸限定符被限定符 sw<N>dp取代;其中的 N 是指屏幕看度的像素定义。 例如,对于“大型”屏幕,可从 layout-sw600dp 目录中获得布局文件。

我们可以进一步在资源限定符中添加 “portrait” 或 “landscape” 定义。 例如,我只想为屏幕宽度为 600dp 的纵向模式进行特别布局。 在这种情况下,需要创建 layout-sw600dp-port来存储布局。

以下是基于样本应用中使用的屏幕尺寸和设备方向的布局结构。 对于中等尺寸的平板电脑,我希望为纵向模式设置单窗口布局,因为如果在中等尺寸的平板电脑(7 英寸或 8 英寸的平板电脑)上使用多窗口布局,UI 可能会被挤压。



图 9 不同显示器尺寸在资源系统中的多布局文件

不同尺寸的 fragment_ultimate_view.xml 布局大体相同。 唯一的不同是子 fragment 的可见性。 对于中等尺寸的平板电脑,最终的视图布局如下:

<!—UltimateViewFragment screen layout 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"><FrameLayout
    	android:id="@+id/grid_fragment"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_weight="1"/><FrameLayout
		android:id="@+id/detail_fragment"
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:layout_weight="1"
		android:visibility="gone"
		android:orientation="vertical"/></LinearLayout>
如何处理不同布局的导航流程? 在应用运行时,根据视图的可见性,应用可以决定是否需要相同的屏幕(多窗口)上更新其他视图,还是重新打开一个屏幕(但窗口)。 以下代码片段展示了这是如何实现的:
/* Handle the event of item click from the menu grid */
public void onGridItemClick(MenuItem itemSelected, int position) {
	mGridItemListener.onGridItemClick(itemSelected, position);
	mSelectedIndex = position;
	View detail = getActivity().findViewById(R.id.detail_fragment);
	//portrait mode
	if (detail != null && detail.getVisibility() == View.GONE) {
		Intent intent = new Intent(this.getActivity(), DetailActivity.class);
           		 intent.setAction("View");
            		intent.putExtra("category", itemSelected.category);
          		intent.putExtra("entree_name", itemSelected.name);
            		Activity activity = getActivity();
            		activity.startActivity(intent);

       	 //landscape mode
	} else {
		mDetailViewFragment.update(itemSelected);
	}
}

Fragment 生命周期处理

如同 Android Activity,fragment 在应用的启动、暂停、恢复、停止和破坏状态中执行生命周期回调和相应操作。 fragment 的生命周期管理与 Activity 相似。

技巧

除了常规的 Activity 生命周期回调,如 onCreate、onStart、onResume、onPause、onStop 和 onDestroy 之外,fragment 很少有其他的生命周期回调:

onAttach() – 当 fragment 与活动相关时调用

onCreateView() – 创建与 fragment 相关的视图层级时调用

onActivityCreated() – 当返回活动的 onCreate() 时调用

onDestroyView() – 当与 fragment 相关的视图层级被删除时调用

onDetach() – 当 fragment 与活动断开关联时调用

下图展示了活动生命周期对 fragment 生命周期回调的影响。


图 10 应用生命周期与 fragment 生命周期回调之间的关系

与 Activity 相同,fragment 需要在设备配置变更过程中对应用状态进行保存和恢复,如设备方向的变化、意外破坏活动或暂停活动。 保存应用状态需要在 onSaveInstanceState () 回调中进行处理,它应在 Activity 破坏之前调用;恢复应用状态可在 onCreate ()、onCreateView () 或 onActivityCreated () 回调中进行处理。 以下代码片段展示了餐厅应用如何在 onSaveInstanceState () 中保存选中的网格项索引并在 onCreate () 进行恢复。

public void onCreate (Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);

	if (savedInstanceState != null) {
            		String selectedIndex = savedInstanceState.getString("selectedIndex");
           		if(selectedIndex != null) {
                		mSelectedPosition = Integer.parseInt(selectedIndex);
           		 }
       	 }

	mImageAdapter = new ImageAdapter(getActivity());
}

/* Saved the last selected Index before orientation change */
public void  onSaveInstanceState (Bundle outState) {
        	//only save the selected position if user has clicked on the item
       	 if (mSelectedPosition != -1) {
            		outState.putString("selectedIndex", Integer.valueOf(mSelectedPosition).toString());
        	}
}

技巧

有时,由于应用状态数据的复杂性,你可能不希望在配置变化(设备方向变化)时重新创建 fragment。 在 fragment 的容器类中调用 setRetainInstance (true) 能够阻止 fragment 在配置变化时重新创建。

Android 资源系统、图形和文本、屏幕分辨率和密度

屏幕上的文本和图形呈现如何处理? 针对手机设备选择的字体对于采用大屏幕的平板电脑可能太小。 图形图标可能适合平板电脑,但是在屏幕较小的手机上则会太大。 如何防止图像在屏幕分辨率不同的设备上被拉伸?

在本部分内容中,我们将介绍几种能够确保文本和图形资源在采用不同分辨率和密度的屏幕上正常显示的技术。

图像和屏幕像素密度

屏幕像素密度对于图形在屏幕上的呈现非常重要。 因为每像素的尺寸在低像素密度设备上更大,所以同一个图像会在低像素密度的屏幕上显示得更大。 相反,同一个图像在采用高像素密度的屏幕上将显示得更小。 在设计 UI 时,你可能会希望在采用不同屏幕像素密度的设备上尽可能保留相同的图像外观。

大部分情况下,这可以通过使用像素独立的单元来实现,如使用 “dp” 和 “wrap_content” 在资源系统中指定图像图示的尺寸和布局。 借助像素独立的单元,Android 能够根据屏幕的像素密度来调整图像。 以下样本展示了如何使用 “dp” 和 “wrap_content” 在细节视图中展示食物项的图像。

<ImageView
	android:id="@+id/dish_image"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	android:layout_below="@+id/cart_area"
	android:minWidth="600dp"
	android:minHeight="400dp"
android:scaleType="centerCrop"/>

有时候,仅通过这种方式无法完成。 例如,由于图像伸展,小图像可能在屏幕上出现像素化。 在这种情况下,你应该在 res/drawable 区域提供可替代的图像以避免这种问题。 采用不同尺寸的图像可在 res/drawable-<xxxxxx> 文件夹中提供;其中 xxxxxx 是通用的密度类型。 下图介绍了实际屏幕密度中的通用密度,以供参考。



图 11 各种 drawable 区域,包含采用不同尺寸显示器的设备的图形资源



图 12 通用屏幕密度在实际 dpi 值中的映射

文本大小和屏幕尺寸

为了增强文本的可辨认性,有时需要根据显示器的尺寸来调整字体。 例如,在样本餐厅应用中,对于屏幕宽度小于 600 像素的设备(如手机),我在网格菜单中为食物的名称和价格采用了较小的字号。 我创建了一种文本 “style” ,能够在采用较大和较小显示器的屏幕中使用不同的字号。 该 style 文件存储在与屏幕尺寸有关的 res/values-sw<N>dp中。



图 13 不同尺寸屏幕的不同 style 文件

以下 style 文件指定了网格菜单项中使用的文本的字体大小。

<!—text style for screen with dp smaller than 600 
 <style name="GridItemText"><item name="android:textColor">@color/grid_item_unselected_text</item><item name="android:textStyle">italic</item><item name="android:textSize">14sp</item></style>
<!—text style for screen with dp larger than 600 
<style name="GridItemText"><item name="android:textColor">@color/grid_item_unselected_text</item><item name="android:textStyle">italic</item><item name="android:textSize">20sp</item></style>

网格项的布局文件引用了上面定义的文本 style(view_grid_item.xml)。

<TextView
            android:id="@+id/grid_price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:paddingRight="5dp"
            android:paddingTop="15dp"
            android:paddingBottom="15dp"
            android:layout_gravity="right"
 style="@style/GridItemText"/>

结论

Android UI 是 Android 编程中最有趣的领域。 设计和编程 Android UI 时需要考虑许多因素。 本文讨论了四种基本概念,以帮助你实现构建动态 UI 的目标:

  1. 在屏幕导航中使用最新推荐的 UI 元素,如操作栏、标签和滑动视图。
  2. 如何处理动态应用数据以及将它们运用到操作栏、标签和滑动视图中的编程实践。
  3. 使用 Android fragment 为采用不同尺寸屏幕的设备执行多窗口和主从视图布局
  4. 使用 Android 资源系统增强采用不同分辨率和像素密度的屏幕的图形和文本的外观

随着 Android 不断发展, 明智的做法是积极采用最新的 UI 技术,并及时了解最新的 UI 概念。 掌握这些技术将会帮助你为未来出现的各种 Android 设备轻松设计动态 UI。

参考文献

  1. 操作栏创建: https://developer.android.com/training/basics/actionbar/index.html
  2. 操作栏风格: https://developer.android.com/training/basics/actionbar/styling.html
  3. 多窗口布局: https://developer.android.com/design/patterns/multi-pane-layouts.html
  4. Fragment 编程指南: https://developer.android.com/guide/components/fragments.html
  5. 多屏幕设计: https://developer.android.com/guide/practices/screens_support.html
  6. Android SDK 参考: https://developer.android.com/reference/packages.html

关于作者

Mei-Lin Hsieh 是一位在英特尔及其他公司的移动开发领域拥有 15 年工作经验的软件工程师。 她目前就职于软件解决方案事业部,主要负责为平板电脑和手机上的 Android 应用提供大规模项目支持。

相关文章与文献:


本文件所描述的产品可能包含使其与宣称的规格不符的设计缺陷或失误。 这些缺陷或失误已收录于勘误表中,可索取获得。

在发出订单之前,请联系当地的英特尔营业部或分销商以获取最新的产品规格。

如欲获得本文涉及的带编号文档的副本或其他英特尔文献,可致电 1-800-548-4725,或访问:

http://www.intel.com/design/literature.htm

在性能检测过程中涉及的软件及其性能只有在英特尔微处理器的架构下方能得到优化。 性能测试(如 SYSmark* 和

MobileMark*)使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。

请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。

** 该示例源代码根据英特尔示例源代码许可协议发布

英特尔® 硬件加速执行管理器

$
0
0

上次更新日期:2014 年 4 月 15 日

英特尔® 硬件加速执行管理器是一个硬件辅助的虚拟化引擎(hypervisor,虚拟机监视器),它使用英特尔® 虚拟化技术加速安卓应用在主机上的模拟。 英特尔® 硬件加速执行管理器与英特尔提供的安卓 x86 模拟器映像及官方Android SDK Manager(安卓软件开发套件)相结合,可在启用英特尔虚拟机的系统上更快地模拟安卓系统。

英特尔硬件加速执行管理器支持以下平台:

Microsoft Windows*
Windows 8 和 8.1(32/64 位)、Windows 7(32/64 位)、Windows Vista*(32/64 位)

安装指南和系统要求 - Windows

haxm-windows_r04.zip (1.0.8)
描述:
系统驱动程序
(2014 年 4 月 15 日)
大小: 1.93MB
校验和:
(MD5)
dbec9d4145a2a7acdf19cb10eb7a9539
(SHA-1)
cc72b38962fc53823f969d4fb9155f9efa3558b8

Mac OS* X
Mac OS X* 10.6(32/64 位)或 10.7(32/64 位)或 10.8(64 位)或 10.9(64 位)

安装指南和系统要求 - Mac OS X

haxm-macosx_r04.zip (1.0.8)
描述:
系统驱动程序
(2014 年 4 月 15 日)
大小: 245KB
校验和:
(MD5)
a34986b66a55a4bebbc939927339b54a
(SHA-1)
e3d68c7d59f0f8b289cdc38e203d0952917bf2cb

Linux
Ubuntu(64 位)

安装指南和系统要求 - Linux

面向基于英特尔® 架构的平台的实时端到端 H.265/HEVC 解决方案

$
0
0

目录

1. 摘要
2. 简介
  2.1 视频编解码器和 H.265/HEVC
  2.2 HEVC 性能问题
  2.3 当前 H.265/HEVC 解决方案调查
3. 基于 IA 的平台上的优化实时解决方案
  3.1 基于英特尔® 至强™ 处理器的实时 HEVC 编码器解决方案
    3.1.1 针对 HEVC 编码函数调优的英特尔 SIMD 矢量化
    3.1.2 线程并发和核心可扩展性调优
    3.1.3 使用 SMT/HT 进一步调优
  3.2 基于英特尔® 酷睿™ 处理器的平台的高性能 H.265/HEVC 编码器
    3.2.1 Strongene HEVC 编码器的优化和性能分析
    3.2.2 英特尔 SSE 优化的视骏 HEVC 解码器与同类开源产品之比较以及未来优化机遇
  3.3 在基于英特尔® 凌动™ 处理器的平台上优化 H.265/HEVC 编码器
    3.3.1 使用 YASM 与英特尔 ® C++ 编译器进行优化
    3.3.2 使用英特尔® 流式单指令多数据扩展(英特尔® SSE)指令进行优化
    3.3.3 使用英特尔® 线程构建模块(英特尔® TBB)工具进行优化
    3.3.4 H.265/HEVC 编码器性能比较
4. 总结
5. 其他相关文章
参考文献
作者简介

1. 摘要

国际电信联盟(ITU)发布了新的视频编解码标准: High Efficiency Video Coding (HEVC)/H.265,据声称新标准比目前的 H.264/MPEG-4 标准的效率高 50%。 但是,其算法的复杂程度和 H.265 的数据结构是 H.264 的 4 倍。 这便意味着,基于 H.265 的编解码器比上一代需要更多的计算资源/能力。 本文中,我们将调查 HEVC 编解码字符并优化基于 CPU 的软件转码技术,从而提供最佳的视频质量和最灵活的编程模式。 我们的端到端解决方案可在 HEVC 编解码器上最大化英特尔® 架构(IA)平台的功能,并实现实时性能。

2. 简介

视频编码标准主要通过大众熟悉的 ITU-T 和 ISO/IEC 标准的开发演进而来。 ITU-T 制定的 H.261 和 H.263,ISO/IEC 制定的 MPEG-1 和 MPEG-4 Visual,以及这两个组织联合制定的 H.262/MPEG-2 Video 和 H.264/MPEG-4 高级视频编码(AVC)标准[1]

去年推出的 H.265/HEVC(高效视频编码)是 ISO / IEC 和 ITU-T 推出的最新视频编解码器标准,旨在最大程度地提高压缩能力和降低数据损失。 H.265/HEVC 的压缩率是上一个 H.264/AVC 标准的 2 倍,但是可以提供相同的主观质量。 HEVC 技术是下一代视频编解码器解决方案,可帮助在线视频提供商在占用更少带宽的情况下提供高质量的视频。

2.1 视频编解码器和 H.265/HEVC

HEVC 为获得高效率的编码标准提出多个新的视频编码语法架构和算法[1][2]

a) 随机存取和位流剪接功能
全新设计支持随机存取和位流剪接的特殊功能。 在 H.264/MPEG-4 AVC 中,位流必须始终从 IDR 访问单元开始,但是在 HEVC 中,支持随机存取。

b) 树形编码单元结构
每个图像可划分为多个树形编码单元(CTU),每个单元包括亮度树形编码数据块(CTB)和色度 (CTB)。 L 的值可能等于 16、32 或 64,具体由序列参数集(SPS)中指定的编码语法元素决定。 CTU 包含四叉树算法,该算法支持根据 CTB 覆盖的区域的信号特征将编码块(CB)划分为指定的适当尺寸。 以前所有的视频编码标准仅使用固定阵列尺寸 16×16 亮度样本,但是 HEVC 支持根据编码器对内存和计算的需求指定尺寸可变的 CTB。

c) 按照树形结构划分为转换数据块和单元
CB 能够以递归的方式划分为转换数据块(TB)。 分区由一个剩余四叉树表示。 与以前的标准相比,HEVC 设计支持一个 TB 跨多个预测块(PB)处理图像间预测编码单元(CU),从而最大限度地提高四叉树结构 TB 划分的潜在编码效率优势。

d) 图像内预测
包含 33 种不同方向的方向预测适用于尺寸从 4×4 至 32×32 的(正方形)转换数据块(TB)。 预测方向可以是 360 度任意方向。 HEVC 支持多种图像内预测编码方法,如 Intra_Angular、Intra_Planar 和 Intra_DC。

这种高级的编码标准需要客户端设备和后端转码服务器具备非常高的处理能力。

2.2 HEVC 性能问题

当前的 HEVC 测试模型(HM)项目[6]仅可实施该标准的主要功能;实际性能尚未生产并在实际中部署。 该项目的两大主要缺点是:

  • 无并行方案
  • 矢量化调优性能差


图 1. HM 项目分析 – 线程并发


图 2. HM 项目分析 – 热代码

在服务器端上,该 HEVC 编码器消耗的 CPU 资源是 H.264 的 100 倍;在客户端上,是 10 多倍。

2.3 当前 H.265/HEVC 解决方案调查

H.265/HEVC 编解码器吸引了全球许多企业/代理优化性能并在实际中部署。 涉及的开源项目包括:

我们运行了一个 720p 24 FPS 视频,以评估基于英特尔® 至强™ 处理器的平台上 x.265 编码器的性能(速率为 2.70GHz 的 E5-2680,8*2 物理内核,代号: Sandy Bridge)。 该编解码器的实施者采取了大量措施来优化任务和数据并行度的初始标准;但是,在我们的基准测试中,它在采用 32 个逻辑内核的系统中只能使用 6 个内核(SMT ON)。 因此,它无法在当前的多核平台上最大限度地利用计算资源。


图 3. X.265 项目 CPU 使用


图 4. 采用英特尔® SIMD 调优的 X.265 项目

在 x.265 项目中,采用了英特尔® SSE 指令执行矢量调优,这可将性能加速 70% 以上。 随着英特尔® C 编译器进一步优化编译,我们在 IA 架构上的性能能够提高 2 倍1。 但是,此处的编码器性能仍然与实时编码器部署有一段差距,尤其对于 HD 1080p 视频。

在中国,超过 20 加多媒体 ISV 购买可用的 HEVC 解决方案和平台,以节约在线视频服务成本并确保提供高质量的视频。


图 5. 中国的在线视频市场

3. 基于 IA 的平台上的优化实时解决方案

视骏是一家以内核视频编码技术为主的中国企业。 它提供了高级 H.265/HEVC 编码器/解码器,该技术被用于迅雷在线视频服务。 其编码器/解码器解决方案集成了开源 FFMPEG,以便 ISV 使用。 我们与视骏联手利用全新的基于 IA 的平台技术来优化采用英特尔® 至强™ 处理器、英特尔® 酷睿™ 处理器和英特尔® 凌动™ 处理器的平台上的 H.265/HEVC 编码器和解码器,以实现实时、端到端的 HEVC 编解码器解决方案。

3.1 基于英特尔® 至强™ 处理器的实时 HEVC 编码器解决方案

我们的视频编码应用是一种标准的 CPU 和内存密集型工作负载,它需要较高的服务器平台能力,如内核计算效率、可靠性和稳定性。 H.265/HEVC 编解码器的计算复杂度是以前的 H.264/MPEG 的 4 倍。 它对后端服务器平台提出了前所未有的处理要求。 在本部分,我们将介绍能够帮助视骏 HEVC 编解码器达到 1080p 实时编码标准主要的基于 IA 的技术。

3.1.1 针对 HEVC 编码函数调优的英特尔 SIMD 矢量化

大部分耗时的视频和图像处理函数是基于数据块的数据密集型计算,它可使用英特尔® SIMD(单指令多数据)矢量化指令进行优化。 英特尔 SIMD 指令可处理一个 CPU 循环中的多个数据集的数据,这能够极大地提高数据吞吐率和执行效率。 英特尔 SIMD 适用的平台非常广泛,从 MMX、英特尔® SSE、英特尔® 高级矢量扩展指令集(英特尔® AVX),到可在多代 x86 平台上使用的英特尔® 高级矢量扩展指令集 2(英特尔® AVX2)。

根据对分析数据的观察,在视骏编码器中,所有的主要热函数均可使用英特尔 SSE 指令进行矢量化处理,如低复杂度的动作补偿帧插值、免转置的整数转化、蝶形 Hadamard 变换以及内存冗余最少的 SAD/SSD 计算。 我们支持在基于英特尔至强处理器的平台上使用英特尔 SSE 指令,如图 6 所示。


图 6. 在视骏编解码器上支持英特尔® SIMD/SSE 指令的样本

借助这些英特尔 SIMD 编程模型和算法,视骏重新编写了编码器中的所有热函数,以最大限度地提升性能。 图 7 是我们在标准 1080p HEVC 编码场景中的分析数据,它显示出,60% 的热函数使用英特尔 SIMD 指令运行。


图 7. 视骏编码函数的分析结果

采用 256b int 计算的英特尔 AVX2 指令的性能将达到以前的 128b 英特尔 SSE 代码的 2 倍。 2014 年推出后,英特尔 AVX2 将可在基于英特尔至强处理器的平台(代号:Haswell)上使用。 我们以常见的 64*64 模块 SAD 计算为例对英特尔 AVX2 的基本性能进行了测试:

表 1 英特尔® SSE 和英特尔® AVX2 实施结果

CPU 周期初始英特尔® SSE英特尔® AVX2
run 198877977679
run 2984631092690
run 398152978679
run 498003943679
run 598118954678
avg.98322.6988.8681
speedup1.0099.44144.38

如表 1 所示,在该函数中,英特尔 SSE 和英特尔 AVX2 指令能够将性能提升 100 倍,而且英特尔 AVX2 代码能够进一步改进性能,使其比英特尔 SSE 高 40%2。 当在将要发布的 Haswell 平台上将英特尔 SSE 代码升级至英特尔 AVX2 时,我们能够对性能做出进一步提升。

3.1.2 线程并发和核心可扩展性调优

如我们在第 2、3 部分看到的内容所示,大部分目前的实施并未利用多核平台上的全部内核。 根据最新的英特尔至强多核架构,以及基于 HEVC 和 CTB 的算法之间明确的并行度依赖性,视骏计划使用 Inter-Frame Wave-front (IFW) 并行框架替换最初的 OWF (Overlapped Wave-Front) 和 WPP (Wave-front Parallel Processing) 方法,然后开发一个包含三个等级的线程管理方案,以确保 IFW 能够充分利用所有的 CPU 内核加速 HEVC 加密流程。 借助这款新的并行度框架,基于一个 Ivy Bridge 平台(英特尔至强处理器 E5-2697 @2.70GHz,12*2 物理内核,SMT OFF),视骏编解码器能够利用 18-24 个物理内核的计算资源,实现出色的线程并发性能。


图 8. 视骏编码器中的线程并发和 CPU 利用

借助全新的 WHP 并行度框架以及分别在任务级别和数据级别完全实施的英特尔 SIMD 指令,视骏编码器能够针对 1080p 视频序列大幅提升 x86 处理器的性能,利用所有内核计算能力,如图 8 所示。

3.1.3 使用 SMT/HT 进一步调优

同步多线程(SMT),也成超线程(HT)技术,可广泛适用于所有基于 IA 的平台。 它支持操作系统从每个物理内核映射两个虚拟或逻辑内核,并在可能时在两个内核之间共享资源。 超线程的主要功能是减少流水线上依赖指令的数量。 它可在 CPU 内核从高级别完全运行时提供性能优势,但是,并非所有的应用都能够获得优势,如无法利用所有内核的应用便无法获得。 在这些情况下,SMT 技术将带来任务/线程切换开销。 因此,我们关闭了视骏编码器平台上的 SMT,并在 Ivy Bridge 平台(英特尔至强处理器 E5-2697 v2)上使用 HEVC 1080p 视频实时编码标准,如下表标黄内容。

表 2 基于英特尔® 至强™ 处理器的平台上的视骏 HEVC 编码性能3

大幅提升性能后,我们进一步对视骏 HEVC 编码器在 Ivy Bridge 平台上的能力进行了评估,主要关注带宽和质量方面的问题。
表 3 H.264 和 H.265 编解码器性能比较

文件: BQTerrance_1920x1080_60.yuv
分辨率: 1920x1080 大小: 1869Mbyte,622080 kbps
平台: E5-2697 v2 @2.70GHz,RAM 64GB DDR3-1867,QPI 8.0 GT /s OS/SW: Red Hat 6.4,kernel 2.6.32,gcc v4.4.7,ffmpeg v2.0.1,Lentoid HEVC Encoder r2096 linux
编解码器大小(字节)速率(kbps)PSNR_Y/U/V (db)
H.264122546964078.132.311/39.369/42.043
H.26562156152064.2834.016/39.822/42.141

从表 3 中,我们看到 H.265/HEVC 编解码器能够在保持相同视频质量的同时节省 50% 的带宽4

3.2 基于英特尔® 酷睿™ 处理器的平台的高性能 H.265/HEVC 编码器

视骏 HEVC/H.265 解码器是一款优化的 H.265 解码器,可在所需计算能力相对较低的情况下提供较高的性能。 视骏 HEVC 解码器高效性的实现是通过完全并行的架构设计和 Wavefront Parallel Processing(WPP)的实施。 此外,它还采用了适用于基于英特尔酷睿处理器的平台(如英特尔 SSE2、英特尔 SSE3 和英特尔 SSE4)的英特尔 SIMD 指令来加速多种解码块和发掘底层英特尔架构的能力。 借助这些特性的优势,视骏 HEVC 解码器能够在主流 CPU 上实现实时 4K 解码性能,并为 1080p 视频流提供高达 200 FPS 的解码率。

3.2.1 视骏 HEVC 解码器的优化和性能分析

视骏 HEVC 解码器中的多线程优化通过 WPP 和帧层并行性来实现。 WPP 是 HEVC 中实现并行处理的一种功能,其实现方式是,将一部分划分为多行树形编码单元(CTU),然后将每一行分配至每个线程(每一行可在上一行的参考 CTU 解码后进行处理)。 视骏 HEVC 解码器中的帧级别并行度可利用 HEVC 标准中采用的层级结构,因为在构建层级参考架构时 B 帧之间能够相互参考。 例如,如果图像组(GOP)等于 8,则该序列可按照如下方式编码:


图 9. 当 GOP = 8 时,可利用帧层并行性的编码帧结构(显示顺序)

在本案例中,在第一阶段,B1 使用 2 P 帧作为参考。 在第二阶段中,两个 B2 帧使用一个 P 帧和一个 B1 帧作为参考。 因此,这两个 B2 帧可进行并行处理。 在第三阶段中,四个 B3 帧使用一个 P 帧和一个 B2 帧,或一个 B1 帧和一个 B2 帧作为参考。 因此,四个 B3 帧也可进行并行处理。 如果使用了更大的 GOP,帧层并行性可以进一步提升,因为 HEVC 解码器中的线程数量足以支持 B 帧同时解码。 视骏 HEVC 解码器的设计能够通过多线程解码和 WPP 提升解码速度,从而实现最高的并行性。

以下是英特尔 SSE 在 Sandy Bridge 平台5(运行于 1080p 和 4K 序列,启用了不同数量的线程)上优化前(表 4)后(表 5),视骏 HEVC 解码器的最大解码帧速率。

表 4 英特尔 SSE 在 1080p 和 4K 视频流上优化(Lentoid C)并启用不同数量的线程前,视骏 HEVC 解码器的解码速率

 1080p 1.2Mbps4K 5.6Mbps
不包含
渲染的解码速率(FPS)
CPU
平均利用率
不包含
渲染的解码速率(FPS)
CPU
平均利用率
1 条线程25.3325%6.8525%
2 条线程43.0349%11.847%
4 条线程51.7993%14.1386%
8 条线程53.198%15.0399%

表 5 英特尔 SSE 在 1080p 和 4K 视频流上优化(v2.0.1.14)并启用不同数量的线程后,视骏 HEVC 解码器的解码速率

 1080p 1.2Mbps4K 5.6Mbps
不包含
渲染的解码速率(FPS)
CPU
平均利用率
不包含
渲染的解码速率(FPS)
CPU
平均利用率
1 条线程7525%2125%
2 条线程12045%3340%
4 条线程14070%3663%
8 条线程15498%4096%

从上述数据中我们可以看到,英特尔 SSE 在 Sandy Bridge 平台上优化5后,1080p 流的性能可提升 3 倍,4K 流的性能约可提升 2.6 倍。 此外,相比单线程模式,视骏 HEVC 解码器中的多线程设计提升性能的能力明显更高: 如果同步解码线程的数量从 1 提高到 8,解码帧速率约可提升 2 倍。 它显示,在整体性能方面,即使在双核 Sandy Bridge 移动平台上,带有英特尔 SSE 优化的视骏 HEVC 解码器能够实时解码 4K 流,且 CPU 利用率低于 40%;毫无疑问,它绝对是业内最佳的 HEVC 软件解码器。 对于比特率范围在 1Mbps 至 3Mbps(通过互联网传输的 1080p 视频的一般比特率设置)之间的 1080p 流,可以达到实时解码,且 CPU 利用率低于 20%。

3.2.2 英特尔 SSE 优化的视骏 HEVC 解码器与同类开源产品之比较以及未来优化机遇

你可以通过对比一些知名的开源实施,如 HM 和 FFMPEG,对视骏 HEVC 解码器的性能进行进一步检验。 下表中,使用分辨率、帧和比特率各异的视频流对不同 HEVC 解码器的解码速率进行了比较。

HM10.0: HEVC 参考解码器 HM10.0
 FFMPEG:
单线程上运行的 FFMPEG 2.1 HEVC 解码器
 FFMPEG 4 线程:
四线程上运行的 FFMPEG 2.1 HEVC 解码器
 Lentoid C:
SSE 优化前基于单线程的视骏 HEVC 解码器
 Lentoid SIMD:
SSE 优化后基于单线程的视骏 HEVC 解码器(v2.0.1.16)
 Lentoid SIMD 4 线程:
SSE 优化后基于 4 线程的视骏 HEVC 解码器(v2.0.1.16)


图 10. 各种解码器和配置下 4K 视频的 H.265 解码帧速率


图 11. 各种解码器和配置下 A 类视频的 H.265 解码帧速率


图 12. 各种解码器和配置下 B 类视频的 H.265 解码帧速率


图 13. 各种解码器和配置下 C 类视频的 H.265 解码帧速率


图 14. 各种解码器和配置下 E 类视频的 H.265 解码帧速率


图 15. 各种解码器和配置下 F 类视频的 H.265 解码帧速率

根据各类视频的性能数据,在英特尔 SSE 优化后,视骏 HEVC 解码器比 HM10 解码器的加速性能快 10 倍6。 其比特率流更低,但性能提升更高。 但是,当比特率上时,英特尔 SSE 优化(Lentoid SIMD 4 线程 / Lentoid C)的加速比率将会下降,因为英特尔 SIMD 指令在能够并行的模块(如运动补偿)上的作用更大,而在不能并行的模块(CABAC、IDCT 和解块(deblocking))上的作用更小。 如果我们观察一下英特尔 SSE 优化前后 VTune™ Amplifier XE 热点函数,便能够对该现象有更加具体的了解:


图 16. 从 VTune™ Amplifier 的角度来看,运行在 4K 5.6Mbps 工作负载上的视骏 HEVC 解码器在英特尔 SSE 优化前(Lentoid C 8 线程)的热点函数


图 17. 从 VTune™ Amplifier 的角度来看,运行在 4K 5.6Mbps 工作负载上的视骏 HEVC 解码器在 SSE 优化后(Lentoid SIMD 8 线程)的热点函数

在图 16 中,我们发现 Lentoid C 解码器中的大部分热点出现在运动补偿(MC)模块中,因为 MC 必须在每个 CTB 中完成,且需要大量的计算资源。 但是,MC 能够在 CTB 级别并行,因此,当英特尔 SSE 优化后,它能够实现最高的加速比率:

随着比特率增加,为解码和处理视频数据,需要在 CABAC、IDCT 和解块中使用更多的计算资源,从而使这些模块的英特尔 SSE 加速比率更低。 这就是英特尔 SSE 优化后,热点函数从 MC 迁移至 IDCT 和解块模块的原因,如图 17 所示。

除此之外,从 VTune 的 CPU 并发中我们可以看到,当在 4K 5.6Mbps 上在 8 线程流中运行英特尔 SSE 优化的解码器时,3 个以上的逻辑 CPU 需要 74% 的解码时间,仅 1 个或 2 个逻辑 CPU 需要 26% 的时间,因为 B 帧之间的工作负载不平衡。


图 18. CPU 使用率直方图

对于热点分析,前五个热点函数实际上均与计算而非内存绑定,这意味着,这些函数可通过英特尔 AVX 和英特尔 AVX2 指令进行进一步优化。


图 19. 重要热点

3.3 在基于英特尔® 凌动™ 处理器的平台上优化 H.265/HEVC 编码器

观看视频是移动设备的首要用途。 多媒体处理需要密集的计算,且对电池寿命和用户体验有重要影响。 移动设备上的 LCD 分辨率从 480p 提高到 720p,现已提高到 1080p。 最终用户希望看到高品质的视频,但是对于在线视频提供商(优酷、爱奇艺、乐视)而言,购买网络宽带的费用逐年攀升。

2013 年,英特尔推出全新的基于第 4 代英特尔凌动处理器的平台(代号 Bay Trail),该平台采用 22 纳米 Silvermont 架构。 该架构的详情如下所示:


图 20. Bay Trail 平台简介

我们使用了英特尔 VTune 工具来调试视骏 H.265/HEVC 解码器。 然后,我们使用了工具集(详见以下三部分内容)对其进行了优化。 我们在基于英特尔凌动处理器的平台上获得了超高的解码速度和较低的 CPU 并发。

3.3.1 使用 YASM 与英特尔 ® C++ 编译器进行优化

我们没有使用默认的 Android* 编译器来编译开源 FFMPEG 中的优化的 ASM 汇编码,而使用了 YASM 和英特尔® C++ 编译器。

YASM 在“全新的” BSD 许可下对 NASM 汇编器进行了完全重写,它能够在 x86 平台上重复使用英特尔 SIMD 优化的 ASM 汇编码。 开发人员可通过以下链接下载并安装 YASM 编译器:http://yasm.tortall.net。 如要使用它,请在编译 FFMPEG 前修改 configure.sh 文件,以启用 YASM 和 ASM 选项,如下所示:


图 21. 修改 FFMPEG 配置文件

此外,我们还鼓励 ISV 使用英特尔 C++ 编译器来编译原生代码。

3.3.2 使用英特尔® 流式单指令多数据扩展(英特尔® SSE)指令进行优化

使用英特尔 VTune 工具进行调试,我们发现,视骏编解码器仅使用了 C 代码来实现 YUV2RGB,这并未达到最佳性能。

基于英特尔凌动处理器的平台支持英特尔 SSE 指令代码,包括 MMX、MMXEXT、英特尔 SSE、英特尔 SSE2、英特尔 SSE3、英特尔 SSSE3 和英特尔 SSE4。 在开源 FFMPEG 中启用英特尔 SSE 代码能够大幅提升 YUV2RGB 性能。

我们使用 MMX EXT 代码在 FFMPEG 中打开英特尔 SSSE3 编译器选项,详见以下代码片段。


图 22. 在 FFMPEG 中支持英特尔® SSE 代码

Bay Trail 平台可支持英特尔 SSE 4.1 指令,从而对 H.265/HEVC 解码器进行优化以实现更出色的性能。

3.3.3 使用英特尔® 线程构建模块(英特尔® TBB)工具进行优化

当我们运行 VTune 工具时,我们发现了视骏编解码器创建了四个线程。 但是,在创建理想内核时,最快的线程需要等待最慢的线程。

如果单独使用,英特尔 SSE 只能在单个内核上使用。 将英特尔 TBB 与英特尔 SSE 结合使用能够让代码在多核上运行,从而实现更高的性能。

为执行多任务,我们对多线程代码进行了修改,然后使用英特尔 TBB 将任务分配至闲置内核,以便充分利用多内核。

您可通过以下链接下载英特尔 TBB:http://threadingbuildingblocks.org/download


图 23. YUV 转 RGB 之比较数据

使用英特尔 TBB 进行优化能够将性能提升 2.6 倍7

3.3.4 H.265/HEVC 解码器性能比较

此外,我们还可启用 OpenGL* 进行渲染,因为通过测试我们发现,使用 YASM英特尔 C++ 编译器进行优化能够将性能提升 1.5 倍, 使用英特尔 SSE进行优化能够将性能提升 6 倍(相比 C 代码),使用英特尔® TBB能够将性能提升 2.6 倍8

我们使用了英特尔® 图形性能分析器(英特尔® GPA)来测试播放视频时的刷新率。 当使用优化的 H.265/HEVC 解码器在 Bay Trail 平板电脑上进行测试时,播放 HEVC 1080p 视频的刷新率可达到 90 FPS(帧每秒),而 Clover Trail+ 平板电脑可达到 40 FPS。


图 24. Clover Trail+/ Bay Trail 平板电脑的性能比较

如果我们在 Bay Trail 平板电脑上将刷新率设置为 24 FPS,当播放 1080p 视频时,CPU 工作负载将低于 25%。 因此,我们为中国的大众在线视频提供商隆重推荐视骏 HEVC 解码器解决方案,以推广其业务。

4. 总结

未来十年,H.265/HEVC 将有可能成为最普遍的视频标准。 目前,许多媒体应用和产品正在准备购买 HEVC 支持。 本文中,我们在采用全新 IA 平台技术的英特尔平台上实施了一款基于 CPU 、实时、端到端的 HEVC 解决方案。 基于英特尔处理器的高级解决方案已在迅雷[4]在线视频和产品中进行部署,并将加速 H.265/HEVC 技术产品和部署。

5. 其他相关文章

  1. http://software.intel.com/en-us/articles/optimizing-h265hevc-decoder-on-intel-atom-processor-based-platforms
  2. http://software.intel.com/en-us/articles/real-time-cpu-based-h265hevc-encoding-solution-with-intel-platform-technology-1

参考资料

[1] 高效视频编码(HEVC)标准概述,IEEE 会报:视频技术电路和系统,卷 22,编号 12,2012 年 12 月。
[2] 高效率视频编码(HEVC)文本规格草案 10,JCTVC-L1003_v34
[3] http://www.strongene.com/en/homepage.jsp
[4] http://yasm.tortall.net
[5] http://threadingbuildingblocks.org
[6] http://hevc.hhi.fraunhofer.de/

作者简介

  1. Yang Lu 是英特尔® 软件和解决方案事业部(SSG)开发人员关系部门英特尔® 至强™ 处理器云平台支持团队的高级应用工程师。 她已在英特尔工作 8 年,主要负责与亚太区I ISV 合作,提供英特尔架构技术支持。
  2. Finn Wong 是英特尔® 软件和解决方案事业部(SSG)开发人员关系部门英特尔® 酷睿™ 处理器客户支持团队的高级应用工程师。 他的研究兴趣包括视频编码、数字图像处理、计算机愿景和功率优化。
  3. Songyue Wang 是英特尔® 软件和解决方案事业部(SSG)开发人员关系部门英特尔® 凌动™ 处理器移动支持团队的高级应用工程师。 Songyue 的任务范围是基于英特尔凌动处理器的 Android 应用。 他主要负责优化 Bay Trail 平台上的多媒体性能,与中国地区最受欢迎的在线视频提供商合作,提供 H.265/HEVC 编码器和解码器解决方案,以及针对 x86 平台为 Android 提供英特尔® 无线显示独特特性。

 

1 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 32GB DDR3-1333 内存的英特尔® 至强™ 处理器 E5-2680@2.70GHz,基于 HEVC 编解码器的视频转码工作负载,作者:YANG LU。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

2 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 64GB DDR3-1867 内存的英特尔® 至强™ 处理器 E5-2697 v2 @2.70GHz,基于视骏 HEVC 编解码器的视频转码工作负载,作者:YANG LU。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

3 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 64GB DDR3-1867 内存的英特尔® 至强™ 处理器 E5-2697 v2 @2.70GHz,基于视骏 HEVC 编解码器的视频转码工作负载,作者:YANG LU。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

4 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 64GB DDR3-1867 内存的英特尔® 至强™ 处理器 E5-2697 v2 @2.70GHz,基于视骏 HEVC 编解码器的视频转码工作负载,作者:YANG LU。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

5 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 4GB 内存和 Windows* 7 操作系统的基于英特尔® 酷睿™ 处理器 i5-2520M 的 PC,解码帧速率和 CPU 利用率,Finn Wong]。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

6 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 8GB 内存和 Windows* 7 操作系统的基于英特尔® 酷睿™ 处理器 i7-2600 的 PC,各种解码器和配置的解码帧速率,Finn Wong]。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

7 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 2GB 内存和 Android 4.2 的英特尔® 凌动™ Bay Trail 平板电脑 FFRD8,解码帧速率,Songyue Wang]。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

8 性能测试中使用的软件和工作负载可能仅在英特尔® 微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。
配置: 采用 2GB 内存和 Android 4.2 的英特尔® 凌动™ Bay Trail 平板电脑 FFRD8,解码帧速率,Songyue Wang]。 如欲了解更多信息,请访问:http://www.intel.com/content/www/cn/zh/library/benchmarks.html

 

英特尔、英特尔标识、Atom、凌动、Core、酷睿、VTune、Xeon 和至强是英特尔公司在美国和/或其他国家的商标。
英特尔公司 © 2014 年版权所有。 所有权保留。
* 其他的名称和品牌可能是其他所有者的资产。

在英特尔® 设备上测试安卓应用的服务。

$
0
0

越来越多的设备配备 Intel Inside。 除了亲身体验基于英特尔的安卓设备来测试您的应用并确保这些应用为用户提供最佳体验外, 还有其他服务让您不需购置并维护大量物理设备,即可在各种硬件上测试应用。 这里是我们的一些推荐。

AppThwack

AppThwack 让您在实验室环境中在数百款真实的物理设备上测试您的应用。 上载代码,运行测试,取得报告,以便在用户发现问题之前先予解决。 在一定时间内,可在 AppThwack 中免费在基于英特尔的安卓设备上进行测试;这是英特尔® 开发人员专区的优惠。 使用我们的用户指南了解更多信息并开始使用 appthwack.com

Applause

Applause 的全球 QA 专业人员社区确保安卓应用在用户手中的基于英特尔的设备上运行时,与在实验室中的运行同样给力。 在用户的实际环境中测试,并确保您的安卓应用在英特尔及 ARM 设备上,针对多款浏览器、多加运营商和多个地理区进行了测试。 了解更多信息并开始使用 applause.com

使用英特尔编译器编译Android程序

$
0
0

介绍

针对Android*的Intel(R) C++ 编译器(以下称为ICC Android),和Android NDK*中的GCC*编译器兼容,能用于开发基于x86的Android设备上的应用程序。开发机器可以使用Windows*,OS X*或者Linux*。使用Android版本的Intle C++编译器,需要Android NDK。ICC Android的主要优势是提供杰出的应用程序性能(说明:针对C/C++等本地代码,针对x86设备)。

安装

安装ICC Android很简单,在安装Google Android NDK之后,如果是Windows版本,只需要双击安装即可,在安装过程中根据需要填写Android NDK的路径,对于OS X*和Linux*,只需要在安装包解压后找到install.sh,在终端运行该安装脚本,安装过程中同样需要指定NDK的路径。

安装完成后,ICC Android会自动集成到NDK中,并且,ICC会成为默认的Android x86编译器。

在Android NDK编译系统中,使用ICC工具链:编译一个示例程序

对于使用NDK开发应用程序的程序员来说,切换到ICC Android编译器非常简单。下面是关于如何编译NDK自带的hello-jni程序的过程。

hello-jni工程的代码在:<NDK安装路径>\samples\hello-jni\jni\中。

方式一:使用命令行选项

打开cmd.exe,进入jni目录,执行ndk-build APP_ABI=x86 NDK_TOOLCHAIN=x86-icc命令。其中,APP_ABI的含义是编译目标为x86(可以参考NDK文档理解),NDK_TOOLCHAIN变量是NDK文档中没有介绍的变量,这个变量用于切换编译器,在安装了ICC Android编译器并集成到NDK中后,就可以使用NDK_TOOLCHAIN=x86-icc来切换了。

如何切换回gcc编译器?

前面提到,安装ICC后,ICC Android会成为x86的默认编译器,所以使用"ndk-build APP_ABI=x86"也会调用ICC编译器。那么,如果你需要切换回NDK自带的GCC编译器,可以使用选项“NDK_TOOLCHAIN=x86-<gcc version>” (其中<gcc version>为你所使用的NDK自带的GCC的版本,不同版本的NDK提供的GCC版本会发生变化,比如对于ndk-r9d来说,其提供了gcc4.6和gcc4.8,所以可以使用NDK_TOOLCHAIN=x86-4.8切换为GCC 4.8编译)。

说明:可以使用ndk-build的选项V=1查看log,其中会显示所使用的编译器,使用-B选项强制重新编译,更多ndk-build的选项请参考NDK文档。

方式二:使用Application.mk

了解NDK就知道,NDK的一些选项不仅仅可以通过命令行指定,也可以通过Application.mk来指定。可以在你的工程的Application.mk(如果没有,就创建一个)中添加如下上述设置:

APP_ABI := x86
NDK_TOOLCHAIN := x86-icc

比如hello-jni,可以在jni目录中修改Application.mk即可。设置后,就可以使用ndk-build编译了(比如ndk-build -B V=1等)。

说明:方式一和方式二本质上是一样的,关键点是要了解NDK_TOOLCHAIN变量,该变量在NDK的文档中可能没有提及。其它选项,参考NDK文档理解即可。比如,如果你需要设置ICC Android编译器的优化选项,那么可以使用Application.mk中的APP_CFLAGS (如APP_CFLAGS="-xATOM_SSSE3 -O3 -ipo"),也可以在命令行使用APP_CFLAGS选项。

在Eclipse中使用NDK,并且使用ICC Android编译器

如果了解Eclipse中使用NDK的方法,那么,对于使用ICC Android编译器也不是问题了。因为,Eclipse不过是自动调用ndk-build脚本而已,所以,仍然只需要在工程的Application.mk中设置如上的选项,那么,自然,Eclipse中也可以使用ICC Android了。

将ICC Android作为独立工具链使用

我们知道,在NDK中,有一个脚本用于提取一个独立工具链,提取的独立工具链,就可以用于类似于Linux*的方式来编译了,比如"gcc xxx.c....",这样的好处是,对于某些基于一般的makefile的方式的项目,很容易的移植到Android平台。NDK中创建独立工具链的脚本在:

<NDK安装路径>\build\tools\make-standalone-toolchain.sh,运行该脚本就可以创建独立工具链,当然,只能在Linux、OS X或Cygwin等下运行该脚本。

对于ICC Android,使用该脚本无法提取工具链,ICC Android提供了更直接的方法,它本身就是一个独立工具链。

对于windows用户:

只需要从开始菜单(Start > All programs > Intel C++ Compiler 14.0 
for Android > Command Prompt > Build Environment for Android)打开build environmet窗口:

或者打开一个普通的cmd.exe窗口,调用[icc-install-dir]\bin\compilervars.bat脚本,也可以正确的设置该环境。

使用独立工具链的方法,就是直接调用icc命令(类似于gcc命令),并且添加适当的选项即可。如,下面是将hello-jni的NDK源码,编译为动态库的过程:

说明:以上为英特尔的Android编译器的基本使用,关于英特尔编译器的优化选项和优化技术,可以参考其文档:

https://software.intel.com/en-us/compiler_14.0_ug_c

产品主页:http://software.intel.com/en-us/c-compiler-android

在 Android* 商务应用中实施地图和地理围栏特性

$
0
0

摘要

本案例研究讨论了如何将地图和地理定位特性构建到 Android* 商务应用中,包括在 Google Maps* 上覆盖商店位置,以及在设备进入商店地理围栏邻近区域时借助地理围栏通知用户。

目录

  1. 摘要
  2. 概述
  3. 在 Google Maps 上显示商店位置
    1. Google Maps Android API v2
    2. 在应用清单中指定应用设置
    3. 添加地图 Fragment
  4. 发送地理围栏通知
    1. 注册和取消注册地理围栏
    2. 实施位置服务回调
    3. 实施意向服务
  5. 总结
  6. 参考文献
  7. 作者介绍

概述

在本案例研究中,我们将会把地图和地理定位功能集成到基于 Android 平板电脑的餐馆商务应用中(图 1)。 用户可以从主菜单项“位置和地理围栏”访问地理定位功能(图 2)。


图 1 餐馆应用主界面


图 2 浮出控件菜单项

在 Google Maps 上显示商店位置

对于一款商务应用而言,显示商店在地图上的位置对用户非常直观和有用(图 3)。 Google Maps Android API 可提供一种简单的方式将 Google Maps 集成至 Android 应用。

Google Maps Android API v2

Google Maps Android API v2 是 Google Play 服务 APK 的一部分。 为了创建使用 Google Maps Android API v2 的 Android 应用,需要下载并配置 Google Play 服务 SDK,获取 API 密钥并在应用的 AndroidManifest.xml 文件中添加所需的设置来对开发环境进行设置。

首先,你需要按照以下网站上的说明来设置 Google Play 服务 SDK:http://developer.android.com/google/play-services/setup.html

然后,你需要从谷歌开发人员控制台(Google Developers Console)上对你的项目进行注册并获取一个 API 密钥:https://console.developers.google.com/project。 你需要在 AndroidManifest.xml 文件中添加 API 密钥。


图 3 餐馆应用在谷歌地图上显示商店的位置。

在应用清单中指定应用设置

为了使用 Google Maps Android API v2,需要将一些权限和特性指定为 <manifest> 元素的子项(代码示例 1)。 其中包括网络连接、外部存储和位置访问的一些必要权限。 此外,为了使用 Google Maps Android API,需要使用 OpenGL ES 版本 2 特性。

<uses-permission android:name=”android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/><!-- The following two permissions are not required to use
	     Google Maps Android API v2, but are recommended. --><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/><uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /><uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>

代码示例 1。 建议在使用 Google Maps Android API 的应用上指定的权限。 包括 “ACCESS_MOCK_LOCATION” 权限(仅当需要使用模拟位置对应用进行测试时使用)

我们同样需要将在 <meta-data> 元素中获得的 Google Play 服务版本和 API 密钥作为 <application> 元素的子项(代码示例 2)。

     <meta-data
              android:name="com.google.android.gms.version"
              android:value="@integer/google_play_services_version" />
        
      <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
          android:value="copy your API Key here"/>

代码示例 2。 指定 Google Play 服务版本和 API 密钥 **

添加地图 Fragment

首先,在你的 activity 布局 xml 文件中,添加一个 MapFragment 元素(代码示例 3)。

<fragment
        android:id="@+id/storelocationmap"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:name="com.google.android.gms.maps.MapFragment"
    />

代码示例 3。 在 Activity 布局中添加 MapFragment **

在你的 activity 类中,您可以检索 Google Maps MapFragment 对象并在每个商店位置处绘制商店图标。

…
private static final LatLng CHANDLER = new LatLng(33.455,-112.0668);
…
private static final StoreLocation[] ALLRESTURANTLOCATIONS = new StoreLocation[] {
        new StoreLocation(new LatLng(33.455,-112.0668), new String("Phoenix, AZ")),
        new StoreLocation(new LatLng(33.5123,-111.9336), new String("SCOTTSDALE, AZ")),
        new StoreLocation(new LatLng(33.3333,-111.8335), new String("Chandler, AZ")),
        new StoreLocation(new LatLng(33.4296,-111.9436), new String("Tempe, AZ")),
        new StoreLocation(new LatLng(33.4152,-111.8315), new String("Mesa, AZ")),
        new StoreLocation(new LatLng(33.3525,-111.7896), new String("Gilbert, AZ"))
};
…
      @Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.geolocation_view);

		mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.storelocationmap)).getMap();
		mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(CHANDLER, ZOOM_LEVEL));
		Drawable iconDrawable = getResources().getDrawable(R.drawable.ic_launcher);
		Bitmap iconBmp = ((BitmapDrawable) iconDrawable).getBitmap();
		for(int ix = 0; ix < ALLRESTURANTLOCATIONS.length; ix++) {
			mMap.addMarker(new MarkerOptions()
			    .position(ALLRESTURANTLOCATIONS[ix].mLatLng)
		        .icon(BitmapDescriptorFactory.fromBitmap(iconBmp)));
		}
…

代码示例 4。 在 Google Maps 上绘制商店图标 **

发送地理围栏通知

地理围栏是一个圆形区域,该区域由一点的经纬度坐标和半径决定。 Android 应用可以注册带有 Android 位置服务的地理围栏。 Android 应用还可指定地理围栏的使用期限。 无论地理围栏何时切换,例如,当 Android 设备进入注册的地理围栏或从其中退出时,Android 位置服务都会即时通知 Android 应用。

在我们的餐馆应用中,我们能够为每个商店位置定义地理围栏。 当设备进入商店附近时,应用将会发送一条通知,如“您已进入最喜爱的餐馆的附近!” (图 4)。


图 4 我们根据兴趣点和半径将地理围栏定义为一个圆形范围。

注册和取消注册地理围栏

在 Android SDK 中,位置服务也是 Google Play 服务 APK 的一部分,位于 “Extras” 目录下。

如要申请地理围栏监控,首先我们需要在应用的清单文件中指定 “ACCESS_FINE_LOCATION” 权限,该操作我们已经在上一部分中完成。

此外,我们还需要查看 Google Play 服务的可用性(代码示例 5 中的 checkGooglePlayServices()方法)。 locationClient().connect()调用与位置客户端成功建立连接后,位置服务将会调用 onConnected(Bundle bundle)函数,位置客户端可通过该函数申请添加或删除地理围栏。

public class GeolocationActivity extends Activity implements
        GooglePlayServicesClient.ConnectionCallbacks
…
{
…
    private LocationClient mLocationClient;

…

    static class StoreLocation {
        public LatLng mLatLng;
        public String mId;
        StoreLocation(LatLng latlng, String id) {
            mLatLng = latlng;
            mId = id;
        }
    }

    @Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.geolocation_view);

        mLocationClient = new LocationClient(this, this, this);

        // Create a new broadcast receiver to receive updates from the listeners and service
        mGeofenceBroadcastReceiver = new ResturantGeofenceReceiver();

        // Create an intent filter for the broadcast receiver
        mIntentFilter = new IntentFilter();

        // Action for broadcast Intents that report successful addition of geofences
        mIntentFilter.addAction(ACTION_GEOFENCES_ADDED);

        // Action for broadcast Intents that report successful removal of geofences
        mIntentFilter.addAction(ACTION_GEOFENCES_REMOVED);

        // Action for broadcast Intents containing various types of geofencing errors
        mIntentFilter.addAction(ACTION_GEOFENCE_ERROR);

        // All Location Services sample apps use this category
        mIntentFilter.addCategory(CATEGORY_LOCATION_SERVICES);

		createGeofences();

		mRegisterGeofenceButton = (Button)findViewById(R.id.geofence_switch);
		mGeofenceState = CAN_START_GEOFENCE;

    }

    @Override
    protected void onResume() {
        super.onResume();
        // Register the broadcast receiver to receive status updates
        LocalBroadcastManager.getInstance(this).registerReceiver(
            mGeofenceBroadcastReceiver, mIntentFilter);
    }

    /**
     * Create a Geofence list
     */
    public void createGeofences() {
        for(int ix=0; ix > ALLRESTURANTLOCATIONS.length; ix++) {
            Geofence fence = new Geofence.Builder()
                .setRequestId(ALLRESTURANTLOCATIONS[ix].mId)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
                .setCircularRegion(
                    ALLRESTURANTLOCATIONS[ix].mLatLng.latitude, ALLRESTURANTLOCATIONS[ix].mLatLng.longitude, GEOFENCERADIUS)
                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                .build();
            mGeofenceList.add(fence);
        }
    }

    // callback function when the mRegisterGeofenceButton is clicked
    public void onRegisterGeofenceButtonClick(View view) {
        if (mGeofenceState == CAN_REGISTER_GEOFENCE) {
            registerGeofences();
            mGeofenceState = GEOFENCE_REGISTERED;
            mGeofenceButton.setText(R.string.unregister_geofence);
            mGeofenceButton.setClickable(true);
        else {
            unregisterGeofences();
            mGeofenceButton.setText(R.string.register_geofence);
            mGeofenceButton.setClickable(true);
            mGeofenceState = CAN_REGISTER_GEOFENCE;
        }
    }

    private boolean checkGooglePlayServices() {
        int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (result == ConnectionResult.SUCCESS) {
            return true;
        }
        else {
            Dialog errDialog = GooglePlayServicesUtil.getErrorDialog(
                    result,
                    this,
                    CONNECTION_FAILURE_RESOLUTION_REQUEST);

            if (errorDialog != null) {
                errorDialog.show();
            }
        }
        return false;
   }


    public void registerGeofences() {

        if (!checkGooglePlayServices()) {

            return;
        }
        mRequestType = REQUEST_TYPE.ADD;

        try {
            // Try to add geofences
            requestConnectToLocationClient();
        } catch (UnsupportedOperationException e) {
            // handle the exception
        }

    }

    public void unregisterGeofences() {

        if (!checkGooglePlayServices()) {
            return;
        }

        // Record the type of removal
    	  mRequestType = REQUEST_TYPE.REMOVE;

        // Try to make a removal request
        try {
            mCurrentIntent = getRequestPendingIntent());
            requestConnectToLocationClient();

        } catch (UnsupportedOperationException e) {
            // handle the exception
        }
    }

    public void requestConnectToLocationServices () throws UnsupportedOperationException {
        // If a request is not already in progress
        if (!mRequestInProgress) {
            mRequestInProgress = true;

            locationClient().connect();
        }
        else {
            // Throw an exception and stop the request
            throw new UnsupportedOperationException();
        }
    }


    /**
     * Get a location client and disconnect from Location Services
     */
    private void requestDisconnectToLocationServices() {

        // A request is no longer in progress
        mRequestInProgress = false;

        locationClient().disconnect();

        if (mRequestType == REQUEST_TYPE.REMOVE) {
            mCurrentIntent.cancel();
        }

    }

    /**
     * returns A LocationClient object
     */
    private GooglePlayServicesClient locationClient() {
        if (mLocationClient == null) {

            mLocationClient = new LocationClient(this, this, this);
        }
        return mLocationClient;

}

    /*
     Called back from the Location Services when the request to connect the client finishes successfully. At this point, you can
request the current location or start periodic updates
     */
    @Override
    public void onConnected(Bundle bundle) {
        if (mRequestType == REQUEST_TYPE.ADD) {
        // Create a PendingIntent for Location Services to send when a geofence transition occurs
        mGeofencePendingIntent = createRequestPendingIntent();

        // Send a request to add the current geofences
        mLocationClient.addGeofences(mGeofenceList, mGeofencePendingIntent, this);

        }
        else if (mRequestType == REQUEST_TYPE.REMOVE){

            mLocationClient.removeGeofences(mCurrentIntent, this);
        }
}
…
}

代码示例 5。 通过位置服务申请地理围栏监控 **

实施位置服务回调

位置服务申请通常是非阻塞或异步调用。 事实上,在上一部分的代码示例 5 中,我们已经实施了这些函数中的一个:locationClient().connect()调用和位置客户端建立连接后,位置服务将会调用 onConnected(Bundle bundle)函数。 代码示例 6 列出了我们需要实施的其他位置回调函数。

public class GeolocationActivity extends Activity implements
        OnAddGeofencesResultListener,
        OnRemoveGeofencesResultListener,
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener {
…


    @Override
    public void onDisconnected() {
        mRequestInProgress = false;
        mLocationClient = null;
}



    /*
     * Handle the result of adding the geofences
     */
    @Override
    public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) {

        // Create a broadcast Intent that notifies other components of success or failure
        Intent broadcastIntent = new Intent();

        // Temp storage for messages
        String msg;

        // If adding the geocodes was successful
        if (LocationStatusCodes.SUCCESS == statusCode) {

            // Create a message containing all the geofence IDs added.
            msg = getString(R.string.add_geofences_result_success,
                    Arrays.toString(geofenceRequestIds));

            // Create an Intent to broadcast to the app
            broadcastIntent.setAction(ACTION_GEOFENCES_ADDED)
                           .addCategory(CATEGORY_LOCATION_SERVICES)
                           .putExtra(EXTRA_GEOFENCE_STATUS, msg);
        // If adding the geofences failed
        } else {
            msg = getString(
                    R.string.add_geofences_result_failure,
                    statusCode,
                    Arrays.toString(geofenceRequestIds)
            );
            broadcastIntent.setAction(ACTION_GEOFENCE_ERROR)
                           .addCategory(CATEGORY_LOCATION_SERVICES)
                           .putExtra(EXTRA_GEOFENCE_STATUS, msg);
        }

        LocalBroadcastManager.getInstance(this)
            .sendBroadcast(broadcastIntent);

        // request to disconnect the location client
        requestDisconnectToLocationServices();
    }

    /*
     * Implementation of OnConnectionFailedListener.onConnectionFailed
     * If a connection or disconnection request fails, report the error
     * connectionResult is passed in from Location Services
     */
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        mInProgress = false;
        if (connectionResult.hasResolution()) {

            try {
                connectionResult.startResolutionForResult(this,
                    CONNECTION_FAILURE_RESOLUTION_REQUEST);
            }
            catch (SendIntentException e) {
                // log the error
            }
        }
        else {
            Intent errorBroadcastIntent = new Intent(ACTION_CONNECTION_ERROR);
            errorBroadcastIntent.addCategory(CATEGORY_LOCATION_SERVICES)
                     .putExtra(EXTRA_CONNECTION_ERROR_CODE,
                                 connectionResult.getErrorCode());
             LocalBroadcastManager.getInstance(this)
                 .sendBroadcast(errorBroadcastIntent);
        }
    }

    @Override
    public void onRemoveGeofencesByPendingIntentResult(int statusCode,
            PendingIntent requestIntent) {

        // Create a broadcast Intent that notifies other components of success or failure
        Intent broadcastIntent = new Intent();

        // If removing the geofences was successful
        if (statusCode == LocationStatusCodes.SUCCESS) {

            // Set the action and add the result message
            broadcastIntent.setAction(ACTION_GEOFENCES_REMOVED);
            broadcastIntent.putExtra(EXTRA_GEOFENCE_STATUS,
                    getString(R.string.remove_geofences_intent_success));

        }
        else {
            // removing the geocodes failed


            // Set the action and add the result message
            broadcastIntent.setAction(ACTION_GEOFENCE_ERROR);
            broadcastIntent.putExtra(EXTRA_GEOFENCE_STATUS,
                    getString(R.string.remove_geofences_intent_failure,
                        statusCode));
        }
        LocalBroadcastManager.getInstance(this)
                .sendBroadcast(broadcastIntent);

        // request to disconnect the location client
        requestDisconnectToLocationServices();
    }


    public class ResturantGeofenceReceiver extends BroadcastReceiver {


	  @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            // Intent contains information about errors in adding or removing geofences
            if (TextUtils.equals(action, ACTION_GEOFENCE_ERROR)) {
                // handleGeofenceError(context, intent);
            }
            else if (TextUtils.equals(action, ACTION_GEOFENCES_ADDED)
                    ||
                    TextUtils.equals(action, ACTION_GEOFENCES_REMOVED)) {
                // handleGeofenceStatus(context, intent);
            }
            else if (TextUtils.equals(action, ACTION_GEOFENCE_TRANSITION)) {
                // handleGeofenceTransition(context, intent);
            }
            else {
                // handle error
            }

        }
    }


    public PendingIntent getRequestPendingIntent() {
        return createRequestPendingIntent();
    }

    private PendingIntent createRequestPendingIntent() {

        if (mGeofencePendingIntent != null) {

            // Return the existing intent
            return mGeofencePendingIntent;

        // If no PendingIntent exists
        } else {

            // Create an Intent pointing to the IntentService
            Intent intent = new Intent(this,
                ReceiveGeofenceTransitionIntentService.class);

            return PendingIntent.getService(
                    this,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
        }
    }


    @Override
public void onRemoveGeofencesByRequestIdsResult(int statusCode,
    String[] geofenceRequestIds) {

        // it should not come here because we only remove geofences by PendingIntent
        // Disconnect the location client
        requestDisconnection();
    }

代码示例 6。 实施位置服务回调 **

实施意向服务

最后,我们需要实施 IntentService 类,以处理地理围栏切换(代码示例 7)。

public class ReceiveGeofenceTransitionIntentService extends IntentService {

	    /**

	     * Sets an identifier for the service

	     */

	    public ReceiveGeofenceTransitionIntentService() {

	        super("ReceiveGeofenceTransitionsIntentService");

	    }

    @Override

	    protected void onHandleIntent(Intent intent) {

	        

	        // Create a local broadcast Intent

	        Intent broadcastIntent = new Intent();

        // Give it the category for all intents sent by the Intent Service

	        broadcastIntent.addCategory(CATEGORY_LOCATION_SERVICES);

       

	        // First check for errors

	        if (LocationClient.hasError(intent)) {

	            // Get the error code with a static method

	            int errorCode = LocationClient.getErrorCode(intent);

	        } 

	        else {

	            // Get the type of transition (entry or exit)

	            int transition =

	                    LocationClient.getGeofenceTransition(intent);

	            

	            if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)  ||

	                    (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {

                // Post a notification

	            } 

	            else {

	                // handle the error

	            }

	        }

	    }

	}

代码示例 7。 实施 IntentService 类以处理地理围栏切换

总结

在本文中,我们介绍了如何将地图和地理围栏特性集成至 Android 商务应用。 这些特性可支持丰富的地理定位内容和基于强大定位功能的服务,并参照了应用中的已有案例。

参考文献

关于作者

Miao Wei 是英特尔软件及服务事业部的软件工程师。 他目前负责英特尔® 凌动™ 处理器大规模支持项目。

* 其他的名称和品牌可能是其他所有者的资产。
** 该示例源代码根据英特尔示例源代码许可协议发布

优化声明

英特尔的编译器针对非英特尔微处理器的优化程度可能与英特尔微处理器相同(或不同)。 这些优化包括 SSE2,SSE3 和 SSSE3 指令集以及其它优化。 对于在非英特尔制造的微处理器上进行的优化,英特尔不对相应的可用性、功能或有效性提供担保。

此产品中依赖于处理器的优化仅适用于英特尔微处理器。 某些不是专门面向英特尔微体系结构的优化保留专供英特尔微处理器使用。 请参阅相应的产品用户和参考指南,以了解关于本通知涉及的特定指令集的更多信息。

通知版本 #20110804


为基于 x86 的 Android* 游戏选择合适的引擎

$
0
0

摘要

游戏开发人员知道 Android 中蕴藏着巨大的机遇。 在 Google Play 商店的前 100 款应用中,约一半是游戏应用(在利润最高的前 100 款应用中,它们所占的比例超过 90%)。 如要跻身该市场,开发速度非常关键。 一些刚起步的独立开发人员更愿意从零开始来开发自己的所有代码;但是为了达到更高的质量而不用花费数年的时间进行开发,其他人可能会选择已有的游戏引擎。 在选择引擎时,你可以考虑以下几个因素:

  • 成本 — 你计划支出多少费用?
  • 维度 — 你的游戏是二维还是三维?
  • 语言 — 你的开发人员了解哪些编程语言?
  • 艺术家管线(Artist pipeline) — 它与内容创建工具的集成情况如何?
  • 全部资源访问 — 你是否需要进行访问,以便对该引擎做出深层的更改?

目前可以使用的 Android 游戏引擎有很多,但是这些引擎在这些标准上有很大差别。 随着采用英特尔® 处理器的高性能 Android 设备越来越多地向市场普及,选择一款针对基于 x86 的 Android 设备进行优化的引擎比以往更重要。 本文将可帮助选择适合你的游戏的引擎,以便为你的 Android 客户提供最佳的性能。

最佳选择

本文对最佳备选引擎从以下三个方面进行了检测。


Epic Games 的 Unreal* Engine自 1998 年首次发布起,长期以来一直占有重要地位,它在电脑、控制台和移动平台领域促成了大量的成功案例。 Unreal Engine 包含以下工具:

  • Unreal Editor (3D 内容编辑器)
  • 蓝图视觉脚本编辑(拖放编辑)
  • Persona 动画工具
  • 面向 AI 、光线和其他效果的编辑器和工具

 


Project Anarchy* 是 Havok 最近开发的一款引擎。 它将 Havok 业经证明的主要游戏技术与针对移动开发进行定制的引擎相结合。 Project Anarchy 包括以下组件:

  • Havok Vision Editor (3D 内容编辑器)
  • 采用 Havok Physics 的 Havok Vision Engine
  • Havok Animation Studio (拖放编辑)
  • Autodesk Scaleform* (UI 工具)
  • 面向 AI 、光线和其他效果的编辑器和工具

 


Marmalade* 是一款跨平台软件开发套件,专为移动游戏的快速创建和构建。 Marmalade 比前两款技术都要新,于 2011 年刚作为一款游戏发布。 Marmalade 包含以下元素:

  • Marmalade C++ SDK
  • Marmalade Quick (Lua)
  • Marmalade Juice (Obj-C)
  • Web Marmalade (HTML5/JavaScript*)

 

 


成本

许多引擎可根据所需的功能提供多种价位。 决定价格最常见的因素有目标平台和你团队或企业的规模。

Unreal Engine 要求每月交付 19 美元的注册费,而且要求收取基于其技术所开发的所有项目所得的全部收入的 5%。 该款技术支持定制价格结构、额外支持和面向控制台的开发选项。

Project Anarchy 目前(而且将来会继续)免费供各种规模的团队在开发移动游戏时使用。 如果用户需要针对 PC 或控制台进行开发,获得深层的访问和其他工具,也可购买高级版本。 对于基本开发而言, Project Anarchy 无疑是最经济的选择,它只要求与你的游戏一起进行联合推广。

写这篇文章时,Marmalade 可供收入低于 500,000 美元,开发人员低于 3 人的团队免费使用 1 年的时间。 这是暂时的促销活动,获得许可一般需要支付 149 美元以上的税费。 更高的价位可提供更多的目标平台开发和直接的开发人员支持。

维度

3D 游戏的吸引力无可争辩,但是对于许多简单的游戏而言,2D 是更好的选择,它需要的时间和资源投资比 3D 少得多。 你的游戏维度与所选的引擎有密切的关系。

Marmalade 没有 3D 图形支持,但是 Unreal Engine 和 Project Anarchy 可提供该支持。 这些引擎为在 3D 开发中提供最佳体验,已做出了无数次改进。

简而言之,三款引擎都可用于 2D 开发,但是 Unreal Engine 和 Project Anarchy 的功能不仅于此。 Project Anarchy 目前可提供一款 alpha 级的 2D 工具集示例,Unreal Engine 和 Marmalade 也均可提供 2D 示例代码。 专为快速开发而设计的 Marmalade 工具的一个子集 Marmalade Quick 仅针对 2D 。

三款引擎均可用于 3D 和 2D 游戏,但是 Marmalade 更适合 2D,Project Anarchy 和 Unreal Engine 更适合 3D。

语言

如果你团队中的开发人员熟悉某款引擎中使用的编程语言(如果使用过更好),学习过程将会显著缩短。

在 Unreal Engine 4 之前的版本中,Unreal Engine 的游戏编程是在 UnrealScript 中完成的,后者是 Epic 所有的脚本编写语言,其采用类似 C 语言的语法。 随着 Unreal Engine 4 的发布,C++ 成为主要的开发语言。

Project Anarchy 基于 C++,使用 Lua 编写脚本。 许多引擎会尽可能地使用轻型脚本语言(如 Lua),以减少低级别任务(如内存管理)的风险。

Marmalade 包括多种工具集,具体取决于所需的语言。 基本 SDK 使用 C++,Marmalade Juice 使用 Objective-C*,Marmalade Quick 使用 Lua,Web Marmalade 专门针对 HTML5/JavaScript 开发而设计。

在语言选择的丰富性方面,Marmalade 的确具备突出的优势。 你的开发人员能够更好地根据其喜欢的开发方式使用某种形式的 Marmalade。 该特性与抽象层“只需编写一次,即可随处应用”的本质密切相关。

艺术家管线

过去,开发团队中只有编程人员。 现在,游戏工作室(无论大小)都明确划分了编程人员和艺术家的职责。 能够与内容创建工具密切集成的引擎可在竞争中占据优势。

Unreal Engine 和 Project Anarchy 均包含针对布局、关卡设计、资产操作等的 3D 编辑器。这些对 3D 艺术家尤为有用,因为其界面与许多 3D 艺术工具相似(它们还可与引擎的编辑器结合使用来创建资产)。

Unreal* Editor(左)和 Havok Vision Editor (右)

Unreal Engine 包含“蓝图编写(blueprint scripting)”功能,在该功能中,脚本组件(如动作)可形象化为拖放元素。

Unreal* Editor 中的蓝图编写(Blueprint scripting)功能

Project Anarchy 可提供一款相似的基于状态机的设计工具用于动画设计。

Project Anarchy* 分层状态机动画工具

虽然 Marmalade 拥有一个大型社区,用于开发开源插件和工具来支持开发工作(其中的大部分可从该网站获取:http://github.com/marmalade),但是 Marmalade 官方套件并未针对非程序员提供任何资源。 凭借全面的工具套件,Project Anarchy 和 Unreal Engine 在该方面占有优势。

全部资源访问

从可能性方面而言,创建自己的引擎还有一个好处,即能够对其进行修改。 因为能够访问全部的源代码,所以你能够更改其中的任何内容,或在其他的新目标平台上添加自己的支持。

Unreal Engine 在这一方面最慷慨;如果你支付了Unreal Engine 4 注册费,便能够访问全部资源。

Project Anarchy 可提供一些组件的源,如果需要全部的源,需要支付更高的费用。 用户可随时访问低级别的资源,但是仅可对免费版本进行修改。

虽然 Marmalade 拥有大量开源插件,但是其本身为封闭源。 你能够访问最高层网,但是无法访问其他源。

大部分开发人员可能并不担心这一点,但是游戏引擎设计越复杂,你就越需要对每一个细节有更高的控制力。

特性比较

 

Marmalade

Project Anarchy

Unreal Engine

最低成本

免费促销/149 美元 + 增值税

免费

19 美元/月 + 5%

维度

3D

可实现

适用

适用

2D

最轻松

alpha 级工具集

可实现,示例

语言

C++、Obj-C、Lua、HTML5

C++、Lua

C++

非程序员功能

第三 方

3D 编辑器,Animation Studio

3D 编辑器,Blueprint scripting

全源

不可以

部分,但都在最高层

可以

 

总结

此处进行比较的每款引擎都有其专长和一处独有优势。 如果需要考虑未来成本,则可以选择 Project Anarchy。 如果考虑到技能和深层修改和扩展引擎的需求,Unreal 是成本最低的方式。 对于快速开发 2D 游戏,Marmalade 是不二之选。 市场上还有许多其他的免费和开源引擎 ,这些引擎堪与本文中所比较的三款引擎相媲美,但是我们选择这三款引擎而非其他,是因为这些引擎的性能能够助力你的游戏在多种 Android 设备上绽放华彩。

参考文献

关于作者

Brad Hill 目前担任英特尔开发人员关系部门的软件工程师。 Brad 负责调研关于英特尔硬件的新技术,并通过英特尔® 开发人员专区以及在开发人员会议上与软件开发人员分享最佳方法。 此外,他还担任学生黑客松的工程总监,负责为全美范围内高等院校的优秀黑客松提供代码运行支持。

将 Android* Bullet 物理引擎移植至英特尔® 架构

$
0
0

简介

由于目前的移动设备上能够使用更高的计算性能,移动游戏现在也可以提供震撼的画面和真实物理(realistic physics)。 枪战游戏中的手雷爆炸效果和赛车模拟器中的汽车漂移效果等便是由物理引擎所提供,其核心是物理模拟。 一般而言,物理模拟决定了游戏引擎的性能。 一款游戏成功与否通常取决于物理引擎计算物理模型的速度和准确度。

本文将介绍如何构建 Android 版 Bullet 物理引擎并将其移至到基于英特尔® 凌动 SoC 的平台。

Bullet 物理

Bullet 物理库是一个实时物理引擎,通常作为其他游戏引擎的组件用于许多电脑游戏、电影、3D 建模系统以及其他应用中 [http://bulletphysics.org/]。 2011 年年中,发布了支持 Android OS 的版本(ARM NEON* 优化)。

首先,我们在 Samsung Galaxy* Tab 3 10.1 (采用基于 ARM 的处理器,每秒帧数为 30)上运行 Bullet 物理应用。 然后,我们将该 Bullet 物理应用移植到 x86 架构, 并在该 Samsung Galaxy* Tab 3 10.1 (现在采用英特尔® x86 处理器,每秒帧数为 60)上运行 Bullet 物理应用。 我们使用英特尔® 图形性能分析器对每种情况下的性能进行了比较 [http://software.intel.com/en-us/vcsource/tools/intel-gpa]。

将应用移植到 x86 架构后,开发人员能够获得额外的帧时间,从而提高其游戏中的物理计算速度,因此他们拥有更多的时间处理更多游戏中的真实物理或动作。

准备工作

构建并移植我们需要的 Bullet:

整个流程可以在 Windows*、Linux* 或 Mac OS* 上运行;这三种系统在这一方面并无本质差别。 我们在 Lenovo K900 和 Samsung Galaxy* Tab 10.1 3 上运行了测试。 两台设备均基于英特尔凌动处理器 Z2460。

此处随附了本文中介绍的能够自动执行所有操作的脚本。

构建

第一步是在 ARM 下构建并运行示例应用 PfxApp_1_Simple。



图 1. 示例应用 PfxApp_1_Simple (设备Samsung Galaxy* tab 3 10.1)。

然后,我们将会构建 PfxLibrary 库 — 物理引擎的主要组件。 如要完成该操作,需要访问库项目目录:

<BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxLibrary\jni

<BulletPhysics>是访问 bullet-2.80-rev2531 文件夹的路径。 在目录中打开 Android.mk 文件,然后找到并更换声明的变量,如下所示:

LOCAL_PATH := <BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects

然后,打开控制台并导航至:

<BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxLibrary

运行命令:

ndk-build

成功! 我们为 armeabi-v7a 成功构建了 PfxLibrary。

接下来,我们来构建示例应用。 导航至以下目录:

<BulletPhysics>\bullet-2.80-ev2531\Extras\PhysicsEffects\project\Android\PfxApp_1_Simple\jni

打开 Android.mk 文件并更改声明:

LOCAL_PATH := <BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects

在命令提示符下,在项目文件夹中更改目录:

<BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxApp_1_Simple

运行命令:

ndk-build

我们使用 Eclipse IDE 启动应用。 将该项目导入 Eclipse:

File => Import => Android => Existing Android Code Into Workspace => Browse… =><BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxApp_1_Simple\ =>
OK => Finish

运行相同的应用。 在项目图标上点击鼠标右键,并选择 "Run As => Android Application",如图 2 所示。



图 2. 从 Eclipse* IDE启动应用

示例将会在转换模式下运行。

移植

接下来,我们将把该示例 PfxApp_1_Simple 移植到 x86。 从核心 PfxLibrary 库开始。 导航至项目文件夹:

<BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxLibrary\jni

打开 Android.mk 文件并更改此声明:

APP_ABI := x86

对 Android.mk 文件做如下更改:

LOCAL_PATH := <BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects
LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%) -DUSE_PTHREADS –pthread
LOCAL_ARM_NEON := false

通过将以下内容从 LOCAL_SRC_FILES 声明列表中删除来移除 ARM NEON* 优化的汇编文件:

src/base_level/solver/pfx_constraint_row_solver_neon.cpp \
include/vecmath/neon/vectormath_neon_assembly_implementations.S

重新构建物理引擎。 在命令提示符下,更改工作目录:

<BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxLibrary

运行 ndk-build。 我们现在为 x86 架构创建了 PfxLibrary。 重复上述操作,移植示例应用。 导航至以下项目目录:

<BulletPhysics>\bullet-2.80-ev2531\Extras\PhysicsEffects\project\Android\PfxApp_1_Simple\jni

打开 Application.mk 文件并更换声明:

APP_ABI := x86

更换 Android.mk 文件中的变量:

LOCAL_PATH := \bullet-2.80-rev2531\Extras\PhysicsEffects
LOCAL_SRC_FILES := project/Android/PfxLibrary/obj/local/x86/libpfxlibrary.a
LOCAL_CFLAGS := $(LOCAL_C_INCLUDES:%=-I%)
LOCAL_ARM_NEON := false

从 LOCAL_SRC_FILES 中删除以下内容:

sample/test_ARM_NEON_performance/neon_dot_product.S \
sample/test_ARM_NEON_performance/neon_cross_product.S \
sample/test_ARM_NEON_performance/neon_matrix4_operator_multiply.S \
sample/test_ARM_NEON_performance/neon_matrix3_operator_multiply.S \
sample/test_ARM_NEON_performance/neon_orthoInverse_transform3.S \
sample/test_ARM_NEON_performance/neon_transform3_operator_multiply.S \
sample/test_ARM_NEON_performance/neon_transpose_matrix3.S \
sample/test_ARM_NEON_performance/test_neon_cross_product.cpp \
sample/test_ARM_NEON_performance/test_neon_dot_product.cpp \
sample/test_ARM_NEON_performance/test_neon_matrix3_operator_multiply.cpp \
sample/test_ARM_NEON_performance/test_neon_matrix4_operator_multiply.cpp \
sample/test_ARM_NEON_performance/test_neon_orthoInverse_transform3.cpp \
sample/test_ARM_NEON_performance/test_neon_transform3_operator_multiply.cpp \
sample/test_ARM_NEON_performance/test_neon_transpose_matrix3.cpp \
sample/test_ARM_NEON_performance/test_neon_solve_linear_constraint_row.cpp

为项目文件夹更改工作目录:

<BulletPhysics>\bullet-2.80-rev2531\Extras\PhysicsEffects\project\Android\PfxApp_1_Simple

使用 ndk-build 命令构建项目,然后在设备上运行示例。

使用 Google Play 提供的 APK Info 应用查看支持的架构 [https://play.google.com/store/apps/details?id=com.intelloware.apkinfo]。



图 3. 截屏APK Info(设备 Lenovo K900)

结论

本文分步介绍了如何构建和移植物理引擎 — Bullet Physics。 将该应用成功移植至 x86 架构后,应用的物理移植速度提高 2 倍,帧速率(FPS)得到提高。

作者介绍

Ilya Krjukov (ilya.krjukov@intel.com) — 高级软件工程师

Denis Smirnov (denis.smirnov@intel.com) — 软件实习生

相关文章与资源

  • 将 Android* 原生应用的低级别组件移植至基于英特尔® 架构的平台
  • 面向 Android* 开发人员的英特尔学习系列计划 #7: 为英特尔® 架构创建和移植基于 NDK 的 Android* 应用
  • 面向英特尔® 架构(IA)创建和移植基于 NDK 的 Android* 应用
  • 在英特尔® 凌动™ 处理器上将 OpenGL* 游戏移植到 Android* (第一部分)
  • 在英特尔® 凌动™ 处理器上将 OpenGL* 游戏移植到 Android* (第二部分)

英特尔、Intel 标识、凌动是英特尔公司在美国和/或其他国家(地区)的商标。

英特尔公司 © 2014 年版权所有。 所有权保留。

* 其他的名称和品牌可能是其他所有者的资产。

英特尔 Android* 开发人员指南上的对等应用

$
0
0

简介

当没有 Wi-Fi 访问点或互联网访问时,Android* 应用可能需要对等连接在两台或多台 Android* 设备之间建立连接。 比如,文件共享应用和多人游戏。

该功能可使用 NFC、蓝牙或 Wi-Fi 对等技术来实施。 特定案例中的首选技术需要根据所需的距离、连接速度、功耗和独特的技术特性来决定。

本文将对 Wi-Fi 对等技术进行评估。 Wi-Fi 对等(P2P)支持具备适当应用的 Android 4.0 或更高版本在没有接入点的情况下通过 Wi-Fi 彼此连接。 Android Wi-Fi P2P 框架符合 Wi-Fi Direct 标准。 一般情况下,Wi-Fi Direct 支持更快的速度和更远的距离,但是所需的功耗比蓝牙连接高。

实施

本文将重点介绍在两台 Android 设备之间建立套接字连接的基本步骤。

如要使用 Wi-Fi P2P,应用必须将最低软件开发套件版本正确设置为 14,并在 AndroidManifest.xml 文件中申请如下许可:

  • ACCESS_WIFI_STATE
  • CHANGE_WIFI_STATE
  • ACCESS_NETWORK_STATE
  • CHANGE_NETWORK_STATE
  • INTERNET

应用需要调用 WifiP2pManager对象的 initialize()方法,以使用 Wi-Fi P2P 框架注册应用。 这种方法必须在执行 Wi-Fi P2P 操作前调用。

应用需要为以下 Wi-Fi P2P intent 创建和注册广播接收器:

  • WIFI_P2P_CONNECTION_CHANGED_ACTION
  • WIFI_P2P_PEERS_CHANGED_ACTION
  • WIFI_P2P_STATE_CHANGED_ACTION
  • WIFI_P2P_THIS_DEVICE_CHANGED_ACTION

执行以上步骤后,应用将能够在 WifiP2pManager 对象中调用 Wi-Fi P2P 方法,并接收 Wi-Fi P2P intent。 大部分的 WifiP2pManager 方法是异步的,因此开发人员需要向每种方法调用提供 listener 来获取状态和结果。

如要发现可用的 Wi-Fi P2P 设备,需要调用 WifiP2pManager对象的 discoverPeers()方法。 发现流程将会一直执行,知道设备打开 Wi-Fi P2P 连接或调用 stopPeerDiscovery方法。

当应用接收 WIFI_P2P_PEERS_CHANGED_ACTION intent 时,使用 WifiP2pManager 对象的 requestPeers()方法将能够获得发现的对等设备列表。

如要连接获取的对等设备列表中的某台设备,需要准备包含完整 deviceAddress字段的 WifiP2pConfig对象,并调用 WifiP2pManager 对象的 connect()方法。

成功连接后,通过调用 WifiP2pManager对象的 requestConnectInfo()方法获取设备 IP 地址。

然后,使用 IP 地址创建一个 Socket 和 ServerSocket 对象。 使用标准的套接字接口执行通信。

以下是调用方法并接收 intent 以准备为 Wi-Fi P2P 设备建立连接的图表:

如要在两台 Android 设备之间建立 Wi-Fi P2P 连接,至少需要执行这些步骤。 应用应为所有 Wi-Fi P2P intent 和 listener 实施处理程序,以充分响应不同的事件,如连接丢失或 Wi-Fi 禁用。 请参阅 Android 文档,了解更多信息。

参考文献

相关文章与资源: 

 

面向基于英特尔® 架构的 Android* 的 CoCos2D

$
0
0

Cocos2D 是一款游戏引擎,可与从电脑到手机等多种设备配合使用。 该引擎支持丰富的特性,可帮助创建出色的 2D 游戏;它甚至包括具备全面功能的物理引擎。 CoCos2D 的核心元素是基本动画元素(即 sprite)的使用。 sprite 可能是一个简单的 2D 图像,也可能是其他 sprite 的容器。 在该游戏引擎中,sprite 排列在一起形成场景、游戏级别或菜单。

如你所知,英特尔® 针对最新的工具推出了许多处理器,这些处理器可为开发人员和用户带来出色的计算机图形。 在本文中,我将介绍如何使用 CoCos2D 构建项目。

安装软件

第一步是在 PC 上安装一下软件: JDK 与 JRE、Android SDK、Android NDK 以及 Eclipse(与 ADT 插件集成的开发环境)。 对于游戏引擎,你需要下载一版 CoCos2D。 我使用了 cocos2d—1.0.1-x-0.9.1。

如要编写非活动代码,你还需要下载 Cygwin。 下载 setup.exe 并选择 “Install from Internet” 选项。

当你进入"Select Packages"步骤时,你需要选择 "make"软件包(在搜索字段中进行查找)。

现在已安装完所有软件,能够执行下一步。

设置

下载并安装 CoCos2D 时,编辑 create-android-project.bat 文件。 该文件位于工作目录(在我的案例中是 C:\cocos2d-1.0.1-x-0.9.1)。 使用文本编辑器打开文件,用以下内容进行更改:

set _CYGBIN=f:cygwinbin
set _ANDROIDTOOLS=d:android-sdktools
set _NDKROOT=e:android-ndk-r5

对以上内容进行更改后,插入以下命令:

set _CYGBIN=с:cygwinbin
set _ANDROIDTOOLS=c:androidandroid-sdk-windowstools
set _NDKROOT=c:androidandroid-ndk-r5-crystax-1

如要使用 CoCos2D 成功构建项目,你需要更改文件 build_native.sh,该文件位于 \template\android\。 编辑以下内容:

RESOURCE_ROOT=$GAME_ROOT/Resource

将其更改为:

RESOURCE_ROOT=$GAME_ROOT/Resources

这一点非常重要,因为版本 1.0.1 – x – 0.9.1 有一个 bug。 开发人员需要依次更改每个资源的目录名称,以便对 Android 和 iPhone 下的项目进行统一。 但是,他们经常会更改了一处,而忘记更改另一处。

运行可编辑的文件 create-android-project.bat。 在控制台中,键入 java 包 org.cocos2dx.myapp 的名称以及你的项目名称(例如: SampleProject)。

然后,你需要指定你的 Android 版本。 在控制台中,输入与你的设备上的 Android 版本相匹配的编号。 例如,我使用的是 Lenovo K900,它的 Android 版本是 4.2.1。 对于我而言,编号是 14(在你的控制台对话框中输入该编号)。

然后,你需要等待至流程结束。 脚本创建文件夹(使用你输入的项目名称(SampleProject))并生成一个 java 项目模板。 它将目录 "Resource"的资源文件重新写入文件夹 "Resources"。 现在,你将可以在 Android 中编写(Lenovo K900)。  

构建

启动 CygWin 并查找你的项目文件夹(cd /cygdrive/c/android/rep/cocos2d-1.0.1-x-0.9.1/SampleProject/android)。 运行文件 build_native.sh (./build_native.sh)。

如果所有操作执行正确,你将会获得三个编译库:libcocos2d.so、libcocosdenshion.so 和 libgame.so。 打开 Eclipse 并创建新的 Android 项目(新建>项目>Android 项目)。 必须确保项目名称与以前生成的项目名称保持一致,即 SampleProject。 选择选项 "Create project from existing source"。 在字段 "Location"中指定项目路径(\cocos2d-1.0.1-x-0.9.1\SampleProject\android)。 针对 Android 4.2.1 选择 API。 选择“完成”,项目即准备就绪。 在 Lenovo K900 运行它。

如果你决定对项目做几处更改,为了成功操作,你需要额外执行以下步骤。 首先,你需要使用“项目>清理(Project>Clean)”对项目进行清理。 然后,在控制台上,重复运行 build_native.sh,然后,在 Eclipse 中运行该项目。

因此,你的项目需要成功启动英特尔设备。

相关文章与资源

声明

本文件中包含关于英特尔产品的信息。 本文件不构成对任何知识产权的授权,包括明示的、暗示的,也无论是基于禁止反言的原则或其他。 除英特尔产品销售的条款和条件规定的责任外,英特尔不承担任何其他责任。英特尔在此作出免责声明:本文件不构成英特尔关于其产品的使用和/或销售的任何明示或暗示的保证,包括不就其产品的(i)对某一特定用途的适用性、(ii)适销性以及(iii)对任何专利、版权或其他知识产权的侵害的承担任何责任或作出任何担保。

除非经过英特尔的书面同意认可,英特尔的产品无意被设计用于或被用于以下应用:即在这样的应用中可因英特尔产品的故障而导致人身伤亡。


英特尔有权随时更改产品的规格和描述而毋需发出通知。 设计者不应信赖任何英特产品所不具有的特性,设计者亦不应信赖任何标有“保留权利”或“未定义”说明或特性描述。 对此,英特尔保留将来对其进行定义的权利,同时,英特尔不应为因其日后更改该等说明或特性描述而产生的冲突和不相容承担任何责任。 此处提供的信息可随时改变而毋需通知。 请勿根据本文件提供的信息完成一项产品设计。

本文件所描述的产品可能包含使其与宣称的规格不符的设计缺陷或失误。 这些缺陷或失误已收录于勘误表中,可索取获得。

在发出订单之前,请联系当地的英特尔营业部或分销商以获取最新的产品规格。

索取本文件中或英特尔的其他材料中提的、包含订单号的文件的复印件,可拨打 1-800-548-4725,或登陆: http://www.intel.com/design/literature.htm

在性能检测过程中涉及的软件及其性能只有在英特尔微处理器的架构下方能得到优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。

对本文件中包含的软件源代码的提供均依据相关软件许可而做出,任何对该等源代码的使用和复制均应按照相关软件许可的条款执行。

英特尔和 Intel 标识是英特尔在美国和/或其他国家的商标。

英特尔公司 © 2014 年版权所有。 所有权保留。

* 其他的名称和品牌可能是其他所有者的资产。

Crosswalk简介

$
0
0

            Web技术的优势早已被广大应用开发者熟知,比如可与云服务轻松集成,基于响应式UI设计的精美布局,高度的开放性,跨平台能力, 高效的分发与部署等等。但是要充分利用Web技术的优势,仍然有许多障碍。Crosswalk项目正是为了跨越这些障碍而生。本文讲会简单介绍Crosswalk项目相关的概念和基本功能。

            首先,Crosswalk采用Chrome浏览器的Blink渲染引擎并不断的快速演进(六周一次更新周期),使Web应用在4.0版本之后的Android平台上充分享受Blink的性能优势。同时,我们支持最新的HTML5 API,包括WebGL,WebRTC,WebAudio,Screen Orientation,WebSocket等等。

            有人可能会问,Android WebView自Android 4.4起已经采用Blink渲染引擎,这与 Crosswalk有和不同?基于Chrome的WebView(Chrome WebView)和Crosswalk比起来目前存在两大缺陷:一是不被4.4之前的Android支持;二是性能以及功能与Chrome还有较大差别。主要的原因是Chrome WebView要向前兼容基于Android 4.4之前的WebView的应用。这意味着Chrome WebView要支持许多旧的功能,所以架构设计更为复杂, 从而导致部分功能还没有完善,同时在某些情况下会降低性能。目前Chrome WebView的Canvas的性能所受影响最大,WebGL的性能与Crosswalk比也有所差距。由于Crosswalk不需要保持这种兼容性,它可以采用与Chrome浏览器非常相近的设计,   事实上Crosswalk正是构建于Chromium的content模块之上,这使得它速度飞快并易于扩展与维护。同时还有相应的增强 ,比如Web应用不需要采用Chrome的多进程架构, 这样运行时内存可以更加节省,等等。

起步

            Crosswalk支持比较完善的Web技术,可以帮助Web开发者提高开发效率。例如像Polymer这样的Web组件模型框架可以在Crosswalk上顺利的工作,如同在Chrome浏览器中一样。同时对HTML5 API最大程度的支持让你的游戏与应用开发得心应手。

            如果Crosswalk提供的API不能满足你的需求,我们还支持通过编写原生的Java代码来创建你自己的Web API。通过这种扩展机制用户可以轻松地获得他们所需的平台和设备能力。

            如果你正在使用Cordova并且渴望更好的性能和更新的功能,如WebGL,那么Crosswalk是一个很好的选择。Crosswalk Cordova项目帮助你在Cordova中用Crosswalk替换原生的Android WebView,并将两者完美的融合,是不是两全其美?!当然,我们仍然支持Crodova的扩展机制,不过如果你的APP扩展对性能要求较高, 采用Crosswalk自带的扩展机制是更好的选择。

打包你的应用

            Crosswalk允许Web开发者将他们的应用打包成Android系统的应用安装包(APK),之后可以发布至应用商店,如Google Play。Crosswalk支持多个应用同时使用一个Crosswalk库的共享模式。但是大多数情况下开发者更倾向于将Crosswalk直接嵌入到应用本身。在这种嵌入模式下Web应用开发者可以完全控制Crosswalk的更新。例如在发布到应用商店之前在最新的Crosswalk上对应用进行测试,这是开发者这一直想要的功能。

            创建一个APK的另外一种方法是使用Intel的XDK或者其它类似的工具,如Construct 2。Crosswalk也提供了自己的命令行工具, 让开发者比较灵活地控制打包过程。这个命令行工具使用起来十分简单:写一个包含基本应用信息的manifest文件,然后运行一条命令就直接搞定!

将Web带入全新的领域

            英特尔是开放Web应用开发平台的坚定支持者。我们致力于不断扩展Web平台的能力, 帮助开发者开发更加精良的应用而无需任何妥协。其中的一部分重要的工作是参与Web标准化社区,如W3C。英特尔积极参与各种规范制定工作组与其它Web领域的公司,如谷歌,Adobe,Mozilla等一同推动网络的标准化进程。

图1,基于Presentation API的HexGL游戏(http://hexgl.bkcore.com)演示

 

            标准化的制定需要大量时间,在整个过程中每一位参与者的反馈都需要被慎重考虑。Crosswalk一边不断试验各种Web前沿的功能,一边积极地反馈与影响标准的制定。更重要的是,Crosswalk确保这些被标准化的功能解决了现实的问题并帮助开发者创造更具吸引力的应用与用户体验。Presentation API就是这些实验性功能的一个典范。它由英特尔向标准化组织提出,并在Crosswalk中最早实现。使用Presentation API的Web应用可以将网页内容以无线连接的方式显示在其它的屏幕上。例如,使用一块大屏幕显示游戏的场景,用一块小触摸屏作为游戏的控制器,如图1所示。Crosswalk还在不断推出新的实验性API(比如将并行计算引入Web等 ) ,革新已有的API并积极参与长期的标准化工作, 不断推进Web技术进步!

 后记

            我们今后会带来一系列的文章来剖析Crosswalk项目的技术内幕,敬请期待!

新版英特尔®XDK为您带来了哪些新特性?

$
0
0

现在开发者可以简便地整合第三方服务的API来使应用变现或整合后端服务来创建内容更加丰富多彩的应用。英特尔®XDK使开发者能够便捷地在应用中加入上百种开源的第三方Cordova*插件,以及Android*, iOS*, Window 8*平台上的各种专有插件。


新建项目

一体式工作流,让你的App从创意迸发到打包发布一气呵成:

  • 多合一的解决方案

  • 使用多种方式开始创建你的App


项目管理视图

一个页面轻松管理你的所有项目:

  • 使用更多方式配置你的项目属性,自定义项目信息

  • 使用丰富的Cordova*插件来配置你的项目

  • 同时管理多个项目

 

API和插件管理

高效地接入和管理API:

  • 提供所有操作系统平台上的Cordova* 3.5基础容器,包括上百种Cordova* API和Intel XDK API

  • 提供对Cordova* 3.5,PhoneGap*和Github* repo等第三方插件的支持

  • 允许自定义的第三方插件

 

新的Services Tab, 成就精彩的App体验

利用应用变现和广告等多种服务创建内容丰富的应用:

  • Google*和Apple*的应用内支付等应用变现服务

  • 使用第三方插件的Google* AdMob, Facebook*, 和Urban* Airship

  • Google* Analysis,以及使用OAuth* 2协议的Dropbox*文件存储和Foursquare*

  • Kinvey* 数据存储

  • App Designer内置的Google*地图插件

 

代码实时预览

用更简便的应用开发方式节省你的时间:

  • 将代码改动实时呈现在浏览器或设备

  • 利用App Preview 实时测试和编辑你的应用

  • 允许开发者即时看到代码改动

 

丰富的应用商店

一次开发,多个商店同时发布:

  • 打包应用并提交到更多应用商店

  • 为多种设备形式部署你的应用

  • 新增Firefox*应用商店

 

欲下载最新版XDK,请前往 http://xdk.intel.com

您可关注英特尔XDK官方微博 http://weibo.com/xdktool 即时了解我们的信息,或扫一扫下面的二维码关注我们的微信账号:

如何使用 Android* 智能手机开发智能自主无人机

$
0
0

简介

使用小型民用无人机非常有趣, 构建它们更有趣! 对于希望开发自己的“智能”无人机的用户,本文可为您使用 Android* 智能手机、OpenCV*、C++ 和 Java* 轻松制造自主无人机提供说明。 这只是开始。 一旦掌握了这些说明后,您还可使用其他程序进一步改进无人机。 访问英特尔® 软件学术计划 [1],自行学习其他有关英特尔® OpenCourseWare 的信息。

资料和方法

自主和智能?

对于一架自主飞行的无人机,必须在其中构建所有必要的传感器、处理能力和通信芯片。虽然这看起来要求不算高,但是事实上它阻碍了目前的许多商用民用无人机向普通公众开放。

您是否见过 Lexus 和 KMEL Robotics [2] 制造的采用大量小型超精确无人机的商用无人机? 无人机的运动可通过以下方式精心设计,即借助房间内强大的传感器,在操作无人机的室内对无人机的的运动精确定位。 鉴于近来无人机备受人们的关注,可能您也知道无人机可以使用 GPS 进行导航。 由于 GPS 为数字设备,所以它非常便利且可轻松访问,这也是为什么人们在高海拔巡航时用它来导航飞机的原因。 但是虽然无人机精确度误差范围不超过 2.5 米且延迟较低,但是它还是无法将披萨送到门前,因为小于 2.5 米,无人机可能会撞到您的房子上,向左或向右偏离 2.5 米以上,它也可能会发生撞机。 没错,这些无人机确实拥有独立的导航,但是它们不够“智能”。

若要称得上“智能”,您的无人机必须拥有足够的嵌入式处理能力,如捕获视频并实时分析 QR 代码(易)、形状或运动(难)等目标。 甚至,您还可以测量体积并重新实时构建空间,如同使用 MIT UAV一样来执行[3]。 但是,所有上述的“升级”需要一款强大的处理器以及诸如加速计和 GPS 的传感器,并且可能需要通过 3G/4G 网络实时通信的能力。 您可能还希望它们轻便、易于编程且由耐久的电池来提供动力。 最终,我们的选择只能是 — 一款强大的手机,它能够依次为无人机的飞行能力提供支持。

近来,我们使用了一部基于 Android 的智能手机开发了一台无人机远程控制设备,在我们的案例 ZTE V975 Geek,我们使用了英特尔® 凌动™ 处理器 Z2580。 使用 Android 可支持我们轻松地开发软件,并在电脑和智能手机之间共享代码。 即使诸如英特尔® 集成性能基元库(英特尔® IPP) [4] 或 OpenCV [5] 的本地库也可在英特尔 Android 智能机和电脑两种设备上使用。 因此,没有必要重新开发,因为智能手机已经具备了所有必要组件:摄像头、GPS、加速计和 3G。

发动机控制

选择了嵌入式电脑,就代表我们要连接发动机。 我们选择了 Pololu Maestro* 伺服系统控制器,它的费用约为 5 欧元,可通过 USB 进行连接,甚至还具备采用可选蓝牙串口的蓝牙* 功能。 本卡用于控制标准的伺服系统发动机。 无论您指挥的是四轴飞行器(quadcopter)还是其他类型的无人机,只需使用基于 Android 的智能手机和此 Pololu 卡替换无线控制组件即可制造出一台自主智能无人机。 这种方法可以无限次重复使用。 非常简单,对吗?

仅需几行代码并使用 Android USB 标准软件包,我们便能够控制伺服系统,从而控制飞机的运动。 再多使用几行代码,我们便能够访问 GPS,捕获图片并通过 3G 来发送。 在软件层面,使用 Android 可支持您快速地演进设计。

UsbDeviceConnection调用 controlTransfer

import android.hardware.usb.UsbDeviceConnection;
// …
private UsbDeviceConnection connection;
// …
connection.controlTransfer(0x40, command, value, channel, null, 0, 5000);

本卡支持您移动伺服系统来决定目标位置、速度和加速度,可满足平稳运动的所有需求。 参数 "command"可能是下列三个值中的一个:

	public static final int USB_SET_POSITION = 0x85;
	public static final int USB_SET_SPEED = 0x87;
	public static final int USB_SET_ACCELERATION = 0x89;

您必须使用相应的值并使用 "channel"来定义右侧的伺服系统。 完整的源代码和应用清单中的 USB 访问配置包含在 ZIP 文件中 [1]。

四轴飞行器特例

目前为止一切顺利。 硬件是即插即用型,代码非常简单,所有操作都在 Android 中完成。 但是嵌入式系统的开发还具备一些特殊性质 — 正如我们将在四轴飞行器的开发中看到的。 多轴无人机和简单机型(如遥控汽车或飞机)之间有一个主要区别。 如果您驾驶的是 RC 电动汽车,只需要一个针对加速度的电子速度控制器 (Electronic Speed Control, ESC) 伺服系统和一个针对方向的伺服系统。 但是,多轴飞行器需要一个固定的平衡引擎来维持所需的位置。 所幸,四轴飞行器自带了稳定性卡。 相比直接连接 Pololu 卡和四轴飞行器的四个 ESC 发动机以及必须在 Android 上用 C/C++ 或 Java 编写复杂的稳定性软件,将稳定性卡连接到 Pololu 卡上并使用该稳定性卡来管理四个引擎则更为简单。 其他需要管理的内容仅需使用简单的 Java 命令(如 +/- altitude+/- speed/+/- inclination+/- direction),即可实现。 我们选择在基于 Android 的智能手机上完成所有操作是因为它拥有强大的处理器,但是引擎平衡需要一个专用的小型卡,该卡的花费仅为数欧。 作为开发人员,您应了解此卡以及我们为何决定使用它,但是您也应该知道它最初需要进行一次校准,完成后,您再也不用管它。

第一阶段的结论

开发自主四轴飞行器初始阶段最后,硬件链路是:

mobile phone <> micro USB-USB host adapter <> USB-mini USB cable <> Pololu Maestro card <>
 4 JR cables <> stabilization card <> JR cables <> ESC <> UAV engines

相比之下,更简单的无人机的硬件链路是:

mobile phone <> micro USB-USB host adapter <> USB-mini USB cable <> Pololu Maestro card <> JR cables <> ESC <> UAV engines

此外,您还可控制其他伺服系统,如四轴飞行器上配备的 2 通道 Direct 3D 摄像头。 或者,如果您愿意,也可以控制副翼、起落架等。Pololu Maestro 卡可控制 6 到 24 个发动机,这远多于此项目中实际需要的发动机,但是可以提高极高地灵活性。

到目前为止,我们已经讨论了可作为嵌入式项目的组件的首选软件和硬件。 下一部分,我们将介绍如何开发图像分析软件将它制作成智能无人机。

计算机视觉

虽然我们已经确定了无人机需要良好的嵌入式处理能力来保持智能和自主,但是我们还没有充分利用这种能力的代码。 所以,我们来赋予无人机“眼睛”!

为什么?

一些无人机可以使用 GPS 来导航,但是 GPS 的准确性和延迟性无法在建筑、人或树之间形成精确的轨道。 其他的无人机能够在实验室中精准、灵敏地飞行,但是它们从室内精密的摄像头和传感器获得了定位提示。 这种技术无法扩展到室外。

在实际情况下,无人机需要能够“看到”,如识别和追踪标志物或从视觉上确认人。 因此,接下来便是要能够从 Android 捕获图像并使用 OpenCV 对其进行分析。

如何执行?

OpenCV是包含图像分析编程函数的开源库,是大量计算机视觉和虚拟现实项目的基础。 OpenCV 最初由英特尔开发,目前可用于多种硬件和操作系统平台。 您可以在电脑上开发代码,然后将其部署到服务器、智能手机或物联网 (IoT) 平台上。

在实践过程中,我们首先尝试识别一个简单的标记(如一个圆圈)并使用智能手机进行导航,进入圆圈前的固定位置。 无人机能够由 GPS 导航到全球范围内一个跑道(约 3 米)的某个位置。 它必须能够识别地面或人行道上的标记才能够在固定的高度将自己精准定位到着陆位置上方。 然后,无人机在距离标记点数厘米处着陆。 为了简化测试,我们将把飞行控制器放到屏幕上,您可以通过手来移动运动物体从而模拟无人机的运动。

使用智能手机导航圆圈

本地 Java 项目 +

OpenCV 不是可从 Android 上的 Java 直接获取的函数库的组件。 它是通常可从 C++ 获取的本地库,因此,您需要使用 Android NDK。 图像捕获和显示部分将使用 Java,Java 和 C++ 之间的通信将使用 JNI。 我们需要安装 Android NDK、Android SDK,创建上面提到的“圆圈”项目,添加 C/ C++ 组件,并更改项目设置以使用 OpenCV 库,它们可从以下的 Eclipse 截屏中捕获:


Java* 项目设置

 


C/C++ 项目设置

 


C/C++ 项目解析器设置

 


包括针对 STL、OpenCV* 和 NDK 的设置

 

最后,我们的项目将包括:

Java 主文件 « Src/MainActivity.java »
XML 布局文件 « Res/layout/activity_main.xml » 和清单
两个 Makefile « Jni/Android.mk » 和 « Jni/Application.mk »
cpp 代码 « Jni/ComputerVision_jni.cpp » 和标题 « Jni/ComputerVision_jni.h »

硬件架构

与 Java 不同,C++ 必须针对特定处理器进行编写。 仅需在 Application.mk文件中添加变量 APP_ABI即可对此进行调整。 对于基于英特尔凌动处理器的智能手机,正确的值是 "x86";NDK 将处理其他内容。

部署

无数个 Android 应用在使用 OpenCV 库,每个应用可能使用不同版本的库。 作为应用开发人员,您可以将所需的 OpenCV 版本与您的应用绑定到一起,但是还有一个更好的方法:使用名为 “OpenCV Manager” 的依赖管理器(dependency manager)。 它是一款 Android 应用,可检测何时需要 OpenCV,什么版本并/或帮助安装,然后加载 OpenCV。 您必须将应用与 OpenCV Manager 建立连接,其余的操作将会“自动”完成。

C++/Java 与算法的交互作用

我们希望在 OpenCV 中检测圆圈,确定中心和半径,然后向智能手机的操作器显示顺序,从而完成一个中心准确、大小正确的圆圈。 以下的 Java 代码使用面向 Android 的 Java API 从摄像头中检索图像。 它通过 JNI 向 C++ 进行调用,并向内存中的图像添加了一个指针。 然后,C++ 代码执行图像处理以检测圆圈。 Java 被回调以显示检测到的圆圈,并对图片进行评论。

远程操作

在测试中,我将手机移动到一张打印纸前。 为了模拟无人机的最终位置,我根据自己手机摄像头的光学情况选取了一个距离来观察它可显示的宽度。 示例: 我在纸上画了一个 10 厘米的圆圈,将手机放置在离纸 20 厘米远的位置,圆圈显示为 300 px 宽。 这是它处于正确的距离时应显示的宽度。 如果圆圈太大,我会向后退;如果太小,我会向前进。 最终,无人机将以准确的距离位于圆圈中。

我们的第一个测试案例非常简单: 1 圆圈 = 1 距离。 但是,您也可以推进一步,使用多个同心圆圈:大圆圈可以从远距离检测,当无人机太近而看不到最大的圆圈时可以使用中等大小的圆圈,无人机最后在最小的内层圆圈中着陆。 如果圆圈不够精确,您还可以使用更加复杂的图形,如箭头。

定位智能手机图形的圆心,以便在着陆标记上方模拟无人机的中央定位。 事实上,这种控制非常简单,在结合其他信息(如颜色、GPS 定位和深度)时,非常容易实施。

Java 代码
…
// capture images from the camera
import org.opencv.Android.CameraBridgeViewBase;
// load OpenCV native dependancy
import org.opencv.Android.OpenCVLoader;
…
public void onResume()
{
super.onResume();
// OpenCV loading with a callback
// non typical code specific to OpenCV
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_6, this, mLoaderCallback);
}
…
// once the OpenCV manager link established,
// we can load the dynamic library
System.loadLibrary("jni_part");
…

C++ 代码

…
// typical for JNI : Java class method name
// pointer to RGB image as argument
JNIEXPORT int JNICALL Java_com_example_circles_MainActivity_process
(JNIEnv *jenv, jobject obj, jlong addrGray, jlong addrRgba)
…
// Get the bitmap from pointer
Mat& mRgb = *(Mat*)addrRgba;
// blur, required before detecting circles
medianBlur(mGr,mGr,5);
// OpenCV detection – Hough transformation
HoughCircles(mGr, //grayscale input image
*circles, //output vector
CV_HOUGH_GRADIENT, //detection method to use
4, //inverse ratio of the accumulator resolution to the image
mGr.rows/8, //min distance between centers of detected circles
220, //higher threshold of the two passed intern canny edge detector
200, //accumulator threshold	100
20, //min radius
mGr.cols/8 //max radius
);

执行和下一步计划

安装从 Google Play* 获取的 OpenCV Manager 以及从 Eclipse* 获取的应用 APK 文件 [7]。 启动应用,让它引导你完成安装;它将检测视野内的圆圈,并引导你使用预定义的直径聚焦圆圈。

检测圆圈非常基础。 最好在开始时使用简单的背景和突出的对比,以使其轻松可见。 简单的计算机视觉功能可处理简单的图形,如同初学走路的孩子:圆圈、正方形、星形 — 所有定义为矢量的几何图元。 但是,高级计算机视觉功能可处理任何类型的图形输入,而不仅是简单的几何图元。 这些库用于增强现实应用中;它们能检测到任何图片在另一个图片中的存在,即使其为失真图片。 示例:如果是食品包图片,应用将检测到该食品包的位置和 3D 方向,并显示为较大的图片。 大部分的视觉应用可通过将对象移动到固定的手机的前方来使用。 对象可检测或增强为 3D 对象,如此视频中的介绍: http://www.dailymotion.com/video/xco8xm_la-realite-augmentee-par-total-imme_tech [8]。

当然,我们不希望无人机仅用在食品包上。 但是该库还可用于其他用途,这时仍然需要检测固定的相片图案,但是要使用无人机上运动着的手机。 示例: 我向无人机提供着陆点的图片后,无人机将能够检测到它,对照图案精准理解它在空间中的位置,并准备目视进近。 同样的库,不同的用途。

如要控制无人机,仅需向携带摄像头的无人机发送 “power”、“roll”、“pitch” 和 “yaw” 命令即可替换显示的方向。 所幸,控制无人机的命令与打印出来的命令完全匹配,因此从室内的手动测试向室外飞行迁移将会非常简单。

在我们的测试智能手机上,每 1/800 秒便可捕获并处理一个图像,每秒可处理 12.5 帧。 这证明我们能够在有限的开发时间/处理器/能源预算下仅向无人机添加计算机“视觉”,它对于小型无人机而言合理且可实现。 所有数据都是独立计算,每个计算都不会遗漏。

它能够进一步发展。 OpenCV 是一款开源库,可广泛移植。 另外,通过添加针对英特尔处理器进行高度优化的例程,英特尔 IPP [5] 可替换一些低级别的 OpenCV 调用并加速您的代码。 您可以确保代码的可移植性及对开源库的依赖性,并获得英特尔 IPP 性能的可选优势。

最后,使用基于英特尔处理器的 Android 智能手机控制无人机是一款兼具性能、耐用性和易开发性的解决方案。 如果您的智能手机不够用或您希望迁移至更大的无人机,借助第一个项目,您可以选择迁移至其他操作系统或更强大的硬件平台。 了解其他软件程序[1],这些程序展示了无人机和机器人领域的其他可用软件开发指令。

作者介绍

作者:Paul Guermonprez、Nicolas Vailliet、Cedric Andreolli
链接:http://intel-software-academic-program.com/pages/courses#drones
联系方式:paul.guermonprez@intel.com
团队:法国巴黎面向 EMEA 俄罗斯的英特尔软件学术计划

参考资料和资源

[1] 英特尔软件学术计划: http://intel-software-academic-program.com/pages/courses#drones

[2] The Mill: Lexus “Swarm” Behind The Scenes: http://vimeo.com/78549177

[3] MIT 的室内自主机器人飞机文件:  http://www.youtube.com/watch?v=kYs215TgI7c

[4] 英特尔® 集成性能基元(英特尔® IPP): http://software.intel.com/en-us/intel-ipp

[5] OpenCV: http://opencv.org/

[6] Google Play: https://play.google.com/store

[7] Eclipse: https://www.eclipse.org/

[8] www.dailymotion.com: La réalité augmentée, par Total Immersion. http://www.dailymotion.com/video/xco8xm_la-realite-augmentee-par-total-imme_tech

 


面向 Android* 应用的本地库压缩 SDK

$
0
0

下载 PDF 文档

简介

Android 应用一般使用 Java* 进行编写,因为其讲究的设计面向对象,而且 Android 软件开发套件 (Android SDK) 可在 Java 内提供跨平台功能。 但是有时,开发人员需要使用 Android 的本地接口,尤其如果内存管理和性能至关重要时。 Android 的本地接口通过原生开发套件 (NDK) 提供,可支持在 C/C++ 内进行本地开发。

Google 软件市场上有许多 NDK 应用。 为了降低数据包的尺寸,一些 ISV 发布了单独的 APK 而非 FAT APK (单独的 APK 包括一个 ARM* 或一个 x86 函数库,但是 FAT APK 包含两者)。 FAT APK 由于单个的 APK。 它们更便于最终用户访问,且库在应用更新过程中不会被覆盖。 因此,我们鼓励开发人员为 x86 Android 软件生态系统发布 FAT APK。 但是,开发人员如何降低大型 FAT APK 文件的尺寸呢?

Zip* 与 LZMA

为了解决 FAT APK 的尺寸问题,作者开发了一款本地库压缩 SDK。 其基本理念是使用 Lempel–Ziv–Markov chain algorithm (LZMA) (http://www.7-zip.org/sdk.html) 来压缩本地库。 Google 使用 Zip 来压缩全部内容,虽然 Zip 速度快,但是其压缩率低于 LZMA。 LZMA 尤其适合压缩本地库中的大型目录文件。 下图展示了使用 Zip 和 LZMA 进行压缩后文件尺寸的区别。

 

图 1: 一个文件在使用 Zip* 和 LZMA 压缩后的尺寸比较1

 

图 2: 多个文件(相同的 CPU 架构)在使用 Zip* 和 LZMA 压缩后的尺寸比较1

 

图 3: 多个文件(不同的 CPU 架构)在使用 Zip* 和 LZMA 压缩后的尺寸比较1

 

图 4: 三个相同的文件在使用 Zip* 和 LZMA 压缩后的尺寸比较1

从上面的四个图标中,我们可以得出,LZMA 能够减少文件之间的冗余。 在极端案例(三个相同的文件)中,LZMA 的压缩率高于 Zip。 该功能尤其适合压缩本地库。 一般而言,本地库使用相同的代码获取 “armeabi”、”armeabi-v7a”、”x86” 甚至 “mips” 库,对于 “armeabi-v7a” ,有 ARM NEON* 和非 NEON 代码。 由于有相同的源代码,这些库有冗余。 即使使用不同的架构,例如 libffmpeg_armv7_vfpv3.solibffmpeg_x86.so,一个是 ARMEABI,另一个是 x86,压缩率也为 40%,然而对于单个文件而言,压缩率仅为 20%。

本地库压缩 SDK

作者开发的本地库压缩 SDK 能够帮助应用开发人员整合 LZMA 本地库压缩以获取更小的文件。 此 SDK 的核心理念是按照如下方式将整个本地库压缩至 asserts文件夹。

此 SDK 中的 API 非常简单。 首次运行此应用时,代码可从“资产(asserts)”文件夹中加压整个本地库。

static boolean NewInstance(Context cont,Handler hdl,boolean showProgress)
static DecRawso GetInstance()
String GetPath(String libname)

我们建议应用开发人员在应用启动时修改源代码并添加 NewInstance,然后将 system.loadlibrary(***)更改为 system.load(DecRawso。 GetInstance ().GetPath(***)). 做出这些小改动后,APK 的尺寸将会更小,但是能够像以往一样运行。

如果开发人员能够确保从调用 NewInstance 到首次加载本地库之间有足够的延迟,他们需要调用 (NewInstance (cont,null,false)),但不包括 Handler。 或者,传递 Handler来接收 “decode end” 异步消息。

作者将此 SDK 整合到基于英特尔® 凌动™ Z2760 处理器的平板电脑(代号 Clover Trail)上的 MoboPlayer* 中。 当用户启动应用且出现闪屏时,代码将以异步方式调用 NewInstance 。 调用 NewInsance 对最终用户透明,因为解码在后台完成。 下图展示了压缩结果。

 

表 5: MoboPlayer* FAT APK 尺寸压缩1

本地库压缩 SDK 的增强功能

除了 LZMA 压缩以外,该 SDK 可提供其他功能,以支持开发人员在以下特性中加入 x86 本地库: 

  1. 云存储: ISV 能够将 x86 库存储到云服务器上,这些库可在安装后从服务器进行下载。 该操作在 x86 设备上可自动完成,且仅在连接 Wi-Fi* 时启用。
  2. 丢失库检测:如果 x86 库丢失,SDK 将自动重新提取 ARM 库。 ISV 可将 ARM 库复制到 x86 文件夹中以避免该问题,但是必须确保 ARM 和 x86 库之间没有交叉引用。
  3. 针对封装的 Java 工具: Java 封装工具可用于将正常的 APK 转换为使用 LZMA 压缩的 APK。 该工具支持 Windows*、Linux* 和 Mac* 系统。 您可以将它用作: ComPressApk.jar -a C:/my/test.APK -k c:/key *** ### alias, 如果 “-k” 丢失(也就是说,您可以调用 ComPressApk.jar -a C:/my/test.APK,Eclipse* 默认测试密钥将用于在此 APK 上签名。 该工具可集成至 ants构建脚本,以支持自动编译和发布。
  4. 过滤器: 借助 ConfigureFilter函数,您可以只抽取必要的库。 例如,输入 ConfigureFilter("libffmpeg", "libffmpeg_armv7_neon.so")表示只抽取以 “ libffmpeg” 开头的所有库中抽取 “libffmpeg_armv7_neon.so”。 这对于降低安装后尺寸非常有帮助。

结论

使用针对 Android 应用的本地库压缩 SDK 能够显著降低本地库包的尺寸,并使采用大型本地库的应用(一般情况下,这些应用时视频播放器、浏览器和 3D 游戏)受益。 关于来源和技术支持,请联系作者。

关于作者

Yuming Li (Yuming.li@intel.com) 是英特尔软件与服务事业部的一位软件应用工程师。 目前,他主要关注多媒体相关应用的支持和性能优化,尤其在 Android 移动平台上。

 

1结果根据内部英特尔® 分析预测得出,仅供参考。 任何系统硬件、软件的设计或配置的不同均可能影响实际性能。

 

 

高质量视频压缩:为基于英特尔® 凌动™ 的 Android* 平台集成 H.265/HEVC 解决方案

$
0
0

摘要

据国际数据公司的统计,2012 年全球数据总数达 2.7 ZB [1],比 2011 年增加 48%。 全球 90% 的数据是视频。 视频应用消耗了互联网数据总流量的 66%,且该数字不断快速增长。 最终用户希望看到高品质的视频,但是对于在线视频提供商而言,购买网络宽带和存储设备的费用逐年攀升。 视频内容提供商如何应对大量数据来源的挑战并满足其日益增长的存储需求? 是否能够利用更少的带宽实现高质量的视频? 解决这些挑战的方法是利用名为高效率视频编码 (H.265/HEVC) 的视频核心压缩技术。

一些开源社区(如 FFmpeg [2])正在开发 H.265/HEVC 解码器,但是其性能尚未达到商用能力。 一家视频编解码核心技术生产商视骏[3] 开发了高级 H.265/HEVC 解码器/编码器解决方案,包括优化的 H.265/HEVC 编码器/解码器库和基于英特尔® 凌动™ 处理器的 Android 平台的演示代码。 本文介绍了基于英特尔凌动处理器的 Android 平板电脑(代号 Bay Trail)上的视骏 H.265/HEVC 解决方案。

视骏 H.265/HEVC 解决方案

对视频标准进行评估时,我们一般使用效率和兼容性标准。 过去 20 年的视频标准演进如下图所示:



图 1. 视频标准历史

H.265/HEVC 是接替 H.264/AVC(高级视频编码) 的编辑器,二者均为 ISO/IEC 动态图像专家组 [4] 与 ITU-T 视频编码专家组 (VCEG) [5] 联合开发。 新编解码器的主要目标是比 H.264 的压缩效率高 50%,并支持高达 8192 x 4320 的分辨率。

如图 1 所示,从 MPEG-2 发展到 H.264/AVC 需要 9 年的时间,因此从 H.264/AVC 标准发展到 H.265/HEVC 标准将会遇到更多挑战。 H.265/HEVC 的技术概念和实际质量之间仍然存在差距。 但是,H.265/HEVC 很好地平衡了效率和兼容性要求,形成了新级别的视频标准。

视骏 H.265/HEVC 解决方案使用 YASM [6] 汇编编译器、英特尔® C++ 编译器 [7]、英特尔® 流式单指令多数据扩展(英特尔® SSE) [8] 和英特尔® 线程构建模块 (英特尔® TBB) [9] 进行了优化,并使用 OpenGL* [10] 进行渲染。 它可以将编码的 YUV420 数据直接上传到 GPU,从而终结了从 YUV 数据向 RGB 数据转码的历史,并可将 RGB 数据渲染为 LCD。 这样可降低 CPU 工作负载并改进性能。 数据流如下图所示:



图 2. 渲染方法比较

在基于英特尔凌动处理器的平板电脑上(代号 Bay Trail),使用英特尔® 图形性能分析器(英特尔® GPA)[11] 工具进行测试后,播放 1080p HEVC 视频时,优化的 H.265/HEVC 解码器刷新率可达到 90 FPS(帧/秒)。 如果我们在 Bay Trail 平板电脑上将刷新率设置为 24 FPS,当播放 1080p 视频时,CPU 工作负载将低于 25%。 因此,视骏 H.265/HEVC 解决方案可达到商用能力

视骏 H.265/HEVC 解决方案的 API 分析

视骏 H.265/HEVC 解决方案的 API 可在 FFmpeg 开源上轻松使用或与其集成。 视骏为 H.265/HEVC 解码器定义了五个功能和一个结构,如下所示:

结构:

lenthevcdec_ctx;

描述: 它是解码器确认不同解码器的环境。

函数:
1. Unit32_t lenthevcdec_version(void);

描述:用于获取当前库的 API 版本。

2. lenthevcdec_ctx lenthevcdec_create(int threads, int comoatibility, void* reserved);

描述:使用特定的参数创建解码器。

示例:

lenthevcdec_ctx ctx;
ctx = lenthevcdec_create(2, 0x7fffffff, NULL);
if ( NULL == ctx ) {
fprintf(stderr, "call lenthevcdec_create failed!n");
exit(1);}

3. Void lenthevcdec_destroy(lenthevcdec_ctx ctx);
描述:关闭解码器并释放全部资源。

4. Void lenthevcdec_flush(lenthevcdec_ctx ctx);
描述:转储清除(flush)解码器并清理缓存。

5. int LENTAPI lenthevcdec_decode_frame( lenthevcdec_ctx ctx, const void* bs, intbs_len, int64_tpts, int* got_frame, int* width, int* height, intline_stride[3], void* pixels[3], int64_t* got_pts);

描述:解码一帧。 将一帧的位流输入解码器,如果有任何一帧解码,从解码器获取一帧的像素数据。

示例:

int32_t got_frame, width, height, stride[3], ret, i;
uint8_t* pixels[3];
int64_tpts, got_pts;
for ( i = 0; i <au_count; i++ ) {
pts = i * 40;
got_frame = 0;
ret = lenthevcdec_decode_frame(ctx, au_buf + au_pos[i], au_pos[i + 1] -
au_pos[i], pts,&got_frame, &width, &height, stride, (void**)pixels,&got_pts);
if ( ret < 0 ) {
fprintf(stderr,"calllenthevcdec_decode_frame failed! ret=%dn", ret);
exit(1);
}
if ( got_frame> 0 ) {
printf("decode frame, %dx%d, pts is %" PRId64 "n",
width, height, got_pts);
/* got frame, do something ... */
}
}

如何整合视骏 H.265/HEVC 解决方案

开发人员可在开发 HEVC 视频播放器时轻松整合这五个函数。 文档和示例代码可从视骏的下载网站上获取 [12]。

开发人员可直接调用视骏的 H.265/HEVC API 来开发视频播放器,或可将视骏 H.265/HEVC 补丁添加到 FFmpeg 中,然后使用添加后的 FFmpeg API 开发视频播放器。 下面一个段落分别介绍了两种方法。

直接使用视骏的 API 开发视频播放器

视骏的网站上,开发人员能够下载:

  • 样本代码 testdec.c/lenthevcdec.h
  • 解码器: liblenthevcdec.so
  • 文档: lenthevcdec_en.pdf, the Makefile

将 Ubuntu* 构建设备的示例代码分别复制到以下文件夹中:

wangsy@ubuntu:~/Desktop cp testdec.c Makefille ~/hevc/src
wangsy@ubuntu:~/Desktop cp lenthevcdec.h  ~/hevc/include
wangsy@ubuntu:~/Desktop cp liblenthevcdec.so ~/hevc/lib/Android_x86

使用 "export"命令设置 ANDROID_NDK_HOME环境:

export ANDROID_NDK_HOME= $ ANDROID_NDK_HOME :/~/android-ndk-r9c

然后运行 "make"命令。

然后构建样本代码并获取输出文件 testdec。 将输出文件和解码器 lib 复制到 /data/hevc_test 下的根 Android 设备。

使用 "export"命令设置 LD_LIBRARY_PATH环境:

导出 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/data/hevc_test

然后可以直接在 Android 设备上运行演示。

使用 FFmpeg 的 H.265/HEVC 补丁开发视频播放器。

事实上,大部分的开发人员使用开源 FFmpeg 开发视频播放器。 此外,视骏的网站还可提供 FFmpeg 补丁,它可从以下位置下载: lentoid_ffmpeg2.0_patch_2014_01_23_new_encoder_interface.patch

如要添加该补丁,您还需要下载并解压缩 FFmpeg 2.0.4 版本 [13]。 然后添加补丁:

wangsy@ubuntu:~/ffmpeg-2.0.4
patch –p1 < ./lentoid_ffmpeg2.0_patch_2014_01_23_new_encoder_interface.patch

将下载的 headfile和 lib 代码复制到 ~/ffmpeg-2.0.4/thirdparty

cp lenthevcdec.h lenthevcenc.h liblenthevcdec.so liblenthevcenc.so ~/ffmpeg-2.0.4/thirdparty

将以下配置文件复制到 ~/ffmpeg-2.0.4,并添加 "run"许可:

wangsy@ubuntu: cp build_x86.sh ~/ffmpeg-2.0.4
wangsy@ubuntu: sudo chmod a+x ~/ffmpeg-2.0.4/build_x86.sh

下载 build_x86.txt

运行配置文件 "build_x86.sh"(包含上面的内容),然后运行 "make"和 "make install"命令。 添加了视骏 H.265/HEVC 补丁的 FFmpeg 现应可成功构建。 输出 lib 位于 ~/FFmpeg2.0.4/android/x86/lib下。 您可以在视频播放器中使用这些输出 lib。

总结

H.265/HEVC 标准在移动市场上日益普遍。 这一新视频革命为在线视频提供商、移动互联网用户和广播/电视运营商以及用户带来了颇多益处。 为了达到本文中介绍的结果,视骏的 H.265/HEVC 解决方案针对基于英特尔® 凌动™ 处理器的 Android 平台进行了全面优化,因此立即采用此解决方案开发 HEVC 播放器吧!

相关文章

参考文献

[1] 国际数据公司 (IDC)。 “前 10 名预测/IDC 预测 2012 : 为 2020 竞争。” IDC 网站: http://cdn.idc.com/research/Predictions12/Main/downloads/IDCTOP10Predictions2012.pdf

[2] FFmpeg: http://www.ffmpeg.org/index.html

[3] 视骏http://xhevc.com/en/about/about-shijun.jsp

[4] ISO/IEC 活动图像专家组 (MPEG): http://mpeg.chiariglione.org/

[5] ITU-T 视频编码专家组 (VCEG): http://www.itu.int/en/ITU-T/studygroups/com16/video/Pages/default.aspx

[6] Yasm Modular Assembler Project: http://yasm.tortall.net/

[7] 英特尔® C++ 编译器(英特尔® ICC): https://software.intel.com/en-us/c-compilers

[8] 英特尔® SIMD 流指令扩展(英特尔® SSE): https://software.intel.com/en-us/articles/performance-tools-for-software-developers-intel-compiler-options-for-sse-generation-and-processor-specific-optimizations

[9] 英特尔® 线程构建模块(英特尔® TBB): https://software.intel.com/sites/default/files/m/d/4/1/d/8/tutorial.pdf

[10] OpenGL: http://www.opengl.org/

[11] 英特尔® 图形性能分析器(英特尔® GPA): https://software.intel.com/en-us/articles/gpa-faq

[12] 视骏下载: http://www.strongene.com/en/downloads/downloadCenter.jsp

[13] FFmpeg 下载: http://www.ffmpeg.org/olddownload.html

关于作者

Songyue Wang 是英特尔® 软件和解决方案事业部(SSG)开发人员关系部门英特尔® 凌动™ 处理器移动支持团队的高级应用工程师。 Songyue 负责管理基于英特尔凌动处理器的 Android 应用。 他主要负责优化 Bay Trail 平台上的多媒体性能,与中国地区最受欢迎的在线视频提供商合作,提供 H.265/HEVC 编码器和解码器解决方案,以及针对 x86 平台为 Android 提供英特尔® 无线显示独特特性。


[1] 性能测试中使用的软件和工作负载可能仅在英特尔微处理器上针对性能进行了优化。 SYSmark* 和 MobileMark* 等性能测试均使用特定的计算机系统、组件、软件、操作和功能进行测量。 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。

配置: 采用 2GB 内存和 Android 4.4 的英特尔®凌动™ Bay Trail 平板电脑 FFRD8,解码帧速率。 如欲了解更多信息,请访问:http://www.intel.com/performance

使用 Intel GPA System Analyzer 改进 Android* 应用

$
0
0

下载样本代码 (ZIP)

移动应用在模拟器和设备之间有着不同的行为,随着应用越来越复杂,调试性能瓶颈变得非常困难。 英特尔® INDE 的组件 GPA System Analyzer 是一款能够诊断各种性能问题的工具。

安装英特尔® 集成式本机开发人员体验测试版(英特尔® INDE)

英特尔 INDE 可为在基于英特尔® 架构的设备上设置环境、创建代码、编译、调试和分析提供一套完整、一致的 C++/Java* 工具、库和示例,并可为基于 ARM* 的 Android* 设备提供指定功能。

通过以下链接安装英特尔 INDE: http://software.intel.com/en-us/intel-inde

针对远程调试构建应用

为了允许 GPA System Analyzer 连接您的应用,您需要确保它有 INTERNET许可且 debuggable设置为 “真(true)”。

了解应用

Art Browser 样本应用是一个基本样本,它融合了大型实际应用的某些方面。  它是一个极为简单的图像滚动插件(image carousel),您可能会在媒体播放器或图片浏览应用中看到。 它有两个按钮您不会在产品应用中看到,这两个按钮对于解决性能问题非常有帮助:

  1. 启用和禁用 “Advanced Math Calculations” 的按钮。 这模拟了一些真实状况,即需要选择使用高级的 CPU 密集型物理引擎、极为冗长的记录、重负载位图效果或其他 CPU 密集型特性,它们对应用是否成功不起关键作用。 该按钮在启用状态下回通过使 CPU 过载来显著降低应用的速度,从而为我们提供机会了解如何调试占用大量 CPU 资源的应用。 默认情况下,该按钮为禁用状态,以便允许应用顺利加载。
  2. 选择几何复杂性的下拉菜单。 通常情况下,最好将大型平面划分为较小的方块以避免深度分类的问题。 默认值是 128x128 网格,这比基本需求复杂,但是可以帮助解释我们的观点。  此几何复杂性将会使 GPU 过载,并为我们提供一个机会了解占用大量 GPU 的应用在 GPA 中的情况。 将其减少到任意更低的值将会让应用恢复顺畅运行。

 

连接到您的应用上

在您的设备或模拟器上构建和安装 Art Browser 示例应用。  然后,确保您的 Android 开发工具(Eclipse 等)没有运行,因为这可能会导致无法连接到您应用上。  启动 GPA System Analyzer 工具。  它将会列出您的本地设备以及任何运行中的模拟器或添加的设备。 

确保您的设备或模拟器没有锁定,且启用了“USB 调试”。  点击“连接”按钮将其连接。  GPA 将会列出所有启用了调试的应用。

GPA 能够监控多种数据类型,但是对于此应用,我们对帧速率和 CPU 加载最有兴趣。  将 CPU → Aggregated CPU Load 从左侧侧栏拖至上方的图表,将 OpenGL → FPS 拖至下方的图表。 

优化代码

为了演示 CPU 密集使用的情况,我们需要通过该应用中的按钮启用 Complex Math Calculation。  我们可以立刻看到它正在消耗 100% 的处理器。 这解释了帧速率为何为 0 FPS。  再次点击该按钮关闭重负载运算能够稍微改善一下此状况,使 CPU 总负载下降 30%,并使 FPS 上升至略微可用的值 — 10。

优化 OpenGL

我们仅禁用了 CPU 密集型代码片段,但是我们的性能仍然较差。 我们现在唯一能做的是优化 OpenGL 渲染。 突破图形瓶颈比 CPU 瓶颈要难,因为 OpenGL 图形管线是一个复杂的流程,解决一个问题没有一成不变的单一标准。 所幸,GPA 配备了丰富的 OpenGL 优化工具,这些工具由许多能够关闭或更换 OpenGL 渲染管线组件的复选框构成。

确定您的应用是否为 GPU 密集型且有 OpenGL 瓶颈的最简单的方法是使用“禁用绘制调用(Disable Draw Calls)”状态覆盖。 这将会关闭发送到 GPU 的所有操作。  如果使用该覆盖无法改善性能,我们便知道问题与 CPU 有关。  但是,如果 FPS 显著攀升,我们肯定会有 OpenGL 瓶颈。

如您所见,它导致 FPS 图表迅速上涨,因此我们知道我们的应用是 GPU 密集型。 我们可以看到是否高分辨率纹理可能通过禁用所有状态覆盖,然后使用 Texture 2x2 覆盖导致了问题的出现。

这带来的变化很小。 然后,我们可以尝试使用 Simple Fragment Shader 覆盖观察着色器代码是否太复杂。

我们要了解的不是改善了多少。 我们可以通过比较 TA Load 标准和 USSE Vertex Load 标准来测试过度复杂的几何。 将 GPU → TA Load 标准拖至上方图表,然后按 CTRL 并将 GPU → USSE Vertex Load 标准也拖至上方图表,在 TA Load 旁边用曲线图来表示它。 可能与您预计的情况相反,高 TA Load 和低 Vertex Load 代表处理了太多顶点。

很明显,这是一个问题,因为 TA Load 数量级更高。 但是,请注意,它仍然在 50% 以下徘徊。 还需注意,即使严重的图形瓶颈也无法使任何一个标准达到 100%。

使用我们应用中的 Geometry Complexity spinner,我们可以将自己的几何简化为 2x2 网格。

这可以即可加速 FPS,而且 TA Load 与 USSE Vertex Load 也更平衡。  如果我们希望找到性能和深度分类之间的平衡点,还可以尝试 8x8 和 32x32。  现在,此应用可以闪亮登场!

注: 应用的测试以及结果的分析基于英特尔凌动处理器 Z2760 平板电脑。

结论

虽然性能问题很难在商务级别的应用上进行调试,但是 GPA System Analyzer 能够在调查复杂的性能瓶颈上提供有力的支持。 如欲更多信息和完整文档,请查看 Intel.com 上的 GPA System Analyzer 主页。

全新 Android* 世界的可信赖工具:优化技术 — 从英特尔® SSE 内部指令到英特尔® Cilk™ Plus

$
0
0

作者: 英特尔高级软件应用工程师 Zvi Danovich

简介

大部分的 Android 应用 — 即使是仅基于脚本和管理语言 (Java*, HTML5,…) 的应用 — 最终都会使用中间件功能,因为该功能能够利用优化特性。

本文将介绍基于 Android 的优化需求和方法,并详述一个优化多媒体和增强现实应用的案例。

英特尔为 Android 平台(智能手机和平板电脑)提供了多种不同的英特尔®凌动™ 处理器,至少包括英特尔® SIMD 流指令扩展补充版(英特尔® SSSE3)级别的矢量功能,通常包括两个内核和超线程。

理解并使用这些优化功能吧!

iOnRoad* 案例研究

iOnRoad* 是一款增强现实应用,可提供实时个人驾驶协助函数,如碰撞报警将现代计算机视觉算法用于智能手机。

为了保证实时,iOnRoad 应用需要在处理前将手机摄像头生成的每帧 YUV420/NV21 输入转换为 RGB 形式。

iOnRoad 执行此转换的最初代码需要 40% 的CPU 运行时,这限制了图像处理的使用并使优化强制执行。

我们发现唯一可以使用的优化软件是英特尔®集成性能基元例程 YUV420ToRGB,但是它没有 iOnRoad 应用需要的基本输入和输出格式组合。 除此之外,此例程不是多线程!

因此,我们决定创建新的优化代码来执行所需的转换。

YUV420/NV21 向 RGB 转换

YUV420/NV21 格式拥有 8 位亮度(黑白)Y 和 2 度色度(彩色)的 U & V 组件。

四个为一组的 Y 组件仅需要一对相应的 V & U 组件便可生成标准的 RGB 格式(在一个像素中包含其 3 种颜色元素)。

上图中的 Y (四个为一组并着有相同的颜色)配备了成对的 V & U。

此格式(广泛称为 YUV)相比 RGB 可提供双倍的压缩。

YUV 向 RGB 的转换 — 整数查找表方法

YUV 向 RGB 的转换使用了简单的线性公式。

为了避免转换(cast)为浮点,我们使用了众所周知的整数近似值:

这些公式可得出中间结果 >216,这一点我们在稍后的矢量讨论中将会提及。

对于标量计算,iOnRoad 使用了查找表 (LUT) 方法:由于 Y、U 和 V 为 8 位,上述乘法公式可在 32-bit-out LUT 的 5 个 256 条目中预先计算 。

YUV 向 RGB 的转换 — 使用英特尔 SSE 的固定点方法

英特尔® SSE 不包括矢量化的 LUT (收集)指令。 打包的 16 位乘法通常比后续打包的标量 LUT 操作快。

但是无法使用基本的英特尔 SSE 16 位乘法 (PMULLW),因为预计的中间结果 >216

英特尔® SSSE3 包含 _mm_mulhrs_epi16() 指令,该指令将完整的 16 位乘法与将中间状态的 32 位结果向右漂移(shift)相结合,为最终的 16 位结果提供了凑整。

为了提供最终结果中有效位的最大数量,需要将最初的乘法操作数向左漂移(在我们的案例中,我们能够达到 13 位的最终结果)。

YUV 向 RGB 的转换 — 固定点 SSE 方法的实施

该流程首先需要加载两份 16x8 位 Y 和 8 对 8 位 (U,V) 。

最后,该数据将会转换为 16x32 位 RGB 像素(格式为 FRGB,最高字节是 0xff)。

使用 8 位饱和减法运算从 16x8 位 Y 中减去 16,这样我们便无需检查和纠正不是负数的结果。

8 对 (U,V) 可为 2 行 16 Y “服务”。

为了对输入数据解包,使用了字节 shuffle 运算,生成两份的:

  • 2 组 8x16 位 Y
  • 1 组 4x16 位双 U
  • 1 组 4x16 位双 V

以下是生成一份的具体方案:

在使用 U & V 前,使用打包的 16 位 _mm_sub_epi16() 指令减去 128。

减去后,所有 8x16 位 Y、U & V 打包数据都将向左漂移,以最佳的方式配合 _mm_mulhrs_epi16() 指令,该指令使用了适当打包的系数

注: 在标量算法中准备上述步骤(减法和漂移)而非执行 LUT 操作。

将乘法结果相加以接收最后的 16 位打包值,该值使用 _mm_min_epi16() 和 _mm_max_epi16() 裁剪并落在 0 和 213-1 (8191) 之间。

所有运算完成后,结果将以 13/16 位分离的 R、G 和 B 值的打包形式呈现。

可通过两个阶段将这些值重新打包为 FRGB 形式(其中 F 是用所有值填充的 alpha 通道,这是应 iOnRoad 应用的要求)。

在第一个阶段,我们将用采用 16 位 <0xff00> 值填充的寄存器把 13/16 位分离的 R、G 和 B 重新打包到 16 位对 FR 和 GB。

此阶段由逻辑左和右漂移和逻辑 OR/AND 操作构成,如下图所示:

在第二个阶段中,最终使用 interleaving _mm_unpacklo_epi16() 和 _mm_unpackhi_epi16() instructions 将 FR & GB 中间结果打包到 FRGB:

上述用于从 YUV 向 RGB 转换的基于英特尔 SSE 指令的代码比最初基于标量 LUT 的代码提高了 4 倍Ω

为并行化使用英特尔® Cilk™ Plus

智能手机和平板电脑中使用的大部分的英特尔凌动处理器至少包括两个逻辑内核(目前,有些机型使用了双核和超线程)。 未来核心数量肯定会增长,因此并行算法将会越来越重要。

大部分的并行方法由英特尔®编译器中的英特尔® Cilk™ Plus 扩展提供,可用于 C 和 C++ 代码(但是英特尔®线程构建模块仅可用于 C++!)

英特尔 Cilk Plus 最简单的并行运算符 “cilk_for” (用于 YUV 向 RGB 转换的外层循环而非标准的 C/C++ “for”)可将基于双核英特尔凌动处理器 Z2760 的设备(代号 Clover Trail)的性能提高 2 倍Ω

将针对矢量化的英特尔 SSE 指令与英特尔 Cilk Plus 并行化一起使用可使总体性能提高 8 倍Ω

Ω 性能测试中的软件和工作负载可能仅在英特尔微处理器上针对性能进行了优化。 诸如 SYSmark 和 MobileMark 等测试均系基于特定计算机系统、硬件、软件、操作系统及功能, 上述任何要素的变动都有可能导致测试结果的变化。 请参考其他信息及性能测试(包括结合其他产品使用时的运行性能)以对目标产品进行全面评估。 配置: [基于双核 Clover Trail 凌动 2.00Ghz Z2760 处理器的工程样例系统,采用 1Gb RAM 内存,运行 Android 4.1.2 (Jelly Bean) 操作系统和特定的 iOnRoad 测试应用]。 如欲了解更多信息,请访问:http://www.intel.com/performance

结论和行动号召

英特尔® SSE 指令(英特尔 SSSE3 级别)可显著提升性能,英特尔® Cilk™ Plus 非常适合在基于英特尔凌动处理器并运行 Android 的设备上对应用执行并行化处理。

我们建议针对基于英特尔凌动处理器的设备编写应用的 Android 开发人员使用英特尔® SSE 和英特尔® Cilk™ Plus 优化多媒体应用和游戏。 这些可信赖的工具可带来出色的性能提升!

关于 iOnRoad

关于公司的描述,请访问 iOnRoad 网站

英特尔、Intel 标识、凌动和 Cilk 是英特尔公司在美国和/或其他国家(地区)的商标。
英特尔公司 © 2014 年版权所有。 所有权保留。
* 其他的名称和品牌可能是其他所有者的资产。

面向英特尔® 架构上的 Android* 的英特尔® 设备保护技术

$
0
0

当前的 Android 平台环境

最新的产业报告显示,全球超过 70% 的平板电脑和智能手机使用 Android 操作系统。 但是,由于 Android 是开发平台,该操作系统无法为抵御恶意应用或木马程序提供充分保护。 此外,许多家企业对 BYOD 计划的日益普及忧心忡忡,从而禁止了员工使用无法与企业安全需求兼容的 Android 设备。 以下是对 Android 开放平台操作系统的主要顾虑:

  • 对恶意应用和木马程序的防御不充分
  • 设备安全/管理 API 不完善
  • RootKit 公开
  • 启动加载器未授权

在 2014 年国际消费电子产品展 (CES) 上,英特尔 CEO 宣布了英特尔计划通过使用英特尔设备保护技术为基于英特尔架构的运行 Android 操作系统的移动设备提供更强的安全功能,从而消除上述的顾虑。

英特尔设备保护技术可为用户提供全新的安全功能,以帮助主动保护 Android 移动设备并阻止恶意软件的入侵。 此外,企业用户管理的设备和 BYOD 也可利用硬件和软件增强的安全功能,借助这些功能,IT 管理人员能够以更高的级别控制和高效区分个人和企业应用和数据。 这种特性组合可帮助防止数据泄漏并保护设备上的个人内容。

英特尔增强安全功能的要点

  • 英特尔设备保护技术将集成至基于英特尔架构的 Android 平台,从而为企业用户和消费者带来优势。
  • 英特尔将在英特尔增强的 Android 平台上提供一套软件扩展,当与迈克菲移动安全* (MMS) 一起使用时可为移动设备提供主动保护。 这些扩展可提供动态白名单创建、恶意软件高效扫描、URL/web 过滤和环境权限管理功能。 此外,它还可主动扫描应用,并包括能够在恶意软件安装到移动设备之前将其拦截的网站。 由于例程的速度和效率更高,设备扫描得以优化,从而降低了对设备处理器和电池续航时间的影响。

  • Manageability Extension 可通过基于软件的方式扩展到 Android 框架和内核,从而帮助支持面向企业 IT 的 增强型移动设备管理 (eMDM) 。此外,eMDM 还可用于英特尔的安全引擎,从而增强加密和关键保护。 Manageability Extensions 支持 MEM 厂商安全锁定 Android 设备,从而进行更好地管理。
  • 企业管理的运行英特尔增强版本 Android 的移动设备可利用硬件和软件增强安全功能,帮助 IT 人员更好地控制和高效分离企业和个人应用和数据,从而防止数据泄漏并保护设备上的个人内容。
  • 对于客户而言,英特尔设备保护技术可主动阻拦恶意软件,分离 BYOD 的个人和企业数据,并可提供本地 Android 应用增强用户体验。

英特尔设备保护技术如何运行

英特尔的目标是提供一套功能强大的安全特性。 英特尔的安全扩展将预先加载到基于 IA 的设备上,并支持所有 OEM 更快地提供可立即供企业使用的解决方案。 这些扩展将随基于 IA 的设备推出,可在支持的 MDM 厂商控制台解决方案上使用。 英特尔与 MDM 厂商合作,提供了多种解决方案可能性。

英特尔积极与谷歌合作,增强了整个面向最终用户的 Android 堆栈。 英特尔是典型的提供商(除谷歌之外),向 Android 开源项目贡献了大量代码,且随着谷歌不断为这些企业功能开发原生支持,英特尔将继续迁移。

这些解决方案的交付有多种方法。 英特尔的解决方案与其软件堆栈深度集成,可通过其功能提供一款可信的 BYOD 解决方案。 其深度集成可完全与 Android 兼容,英特尔竭尽全力地减少对 Android 操作系统的变更。

英特尔将使其 API 更广泛地为安全软件厂商使用。 通过在核心平台上实施扩展,可信厂商能够开发能够更好地管理设备和拦截恶意软件的解决方案。

企业可管理性

Secure Container 和 Extended Mobile Device Management 功能支持 IT 部门管理其资产,且不妨碍客户的个人体验或数据。 企业 IT 管理人员能够从任何应用商店压缩(encapsulate)任何应用并安全锁定其内容。 大多数情况下,IT 管理员来决定一个容器中安装哪些应用,并根据该目的创建一个许可应用列表。 应用可从企业商店或公共应用商店(Google Play)安装(如果管理员启用了该功能)。

个人相片和电子邮件可以存储在容器之外,这样如要 IT 组织需要锁定或删除企业容器,将不会影响客户的个人内容。

例如,为了保护企业数据,一些解决方案需要从设备中删除所有数据以确保敏感数据的安全。 假定企业容器被 IT 人员锁定或删除。 利用容器,IT 人员能够划分信息,这样当删除一个容器时不会对容器外的信息产生任何应用。 即使将容器删除或锁定,用户仍然能够访问容器外存储的设备和个人数据。

容器还可防止企业信息泄漏,BYOD 上的个人数据与托管设备上的工作数据混在一起。 当数据在容器中存储时,只有登录容器的用户才能访问它,而且它无法从容器中以电子的方式复制。

这些安全扩展还可在支持的 MDM 厂商控制台上使用。 英特尔与 MDM 生态系统中的厂商合作,以支持在其控制台中使用这些增强功能。 对于 eMDM 中的新 MDM 功能,英特尔的解决方案可提供更精细的应用管理、设备库存和配置,添加网络配置控制,以及为电话费用管理设置策略,从而增强了标准的 Android 设备管理 API。 我们实施了近乎本地的解决方案,这些解决方案不需要对应用进行云封装便可防止企业数据泄漏。 这支持用户从任意应用商店灵活下载应用,并支持企业灵活管理这样的开放 Android 设备并仍然确保企业数据的安全。 这些增强功能为 IT 管理人员提供了更多有效且高效管理设备的工具。

个人和企业数据的分离

该技术以及采用扩展的功能的软件和服务可为消费者带来诸多优势。 所有最终用户均可通过使用支持 Security Extension 的产品主动保护设备。 但是,如要利用可管理性和容器功能,将需要一款移动设备管理工具,该工具一般由企业提供。

如果没有预先安装安全服务,最终用户也可获得优势。 通过添加扩展,您还可在增强安全解决方案上市时使用这些解决方案。 可信合作伙伴安全解决方案可从应用商店(Google Play)下载,并可在选中的安全解决方案安装到设备上之后使用 API。

2014 可用性

英特尔预计于 2014 年上半年向市场推出这些增强安全功能,首次将于新版 McAfee Mobile Security (MMS v3.2) 一起推出,这可为消费者提供增强安全优势。 英特尔设备保护技术还可在采用针对英特尔优化的 Android 4.4 版本的英特尔® 凌动™ 处理器 Z3xxx 平台 (Bay Trail) 上使用。 关注 2014 年 2 月的移动通信世界峰会 (Mobile World Congress) 和 RSA,了解更多信息。

 

英特尔、Intel 标识、凌动是英特尔公司在美国和/或其他国家(地区)的商标。
英特尔公司 © 2014 年版权所有。 所有权保留。
* 其他的名称和品牌可能是其他所有者的资产。

Viewing all 172 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>