`
Teok
  • 浏览: 147243 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Roman Guy的Android Trick系列文章笔记

阅读更多

Roman Guy是Android Framework的核心开发人员,从2009年开始,他在他的博客上发表多篇如何更好的开发android软件的文章(需要爬墙)。我的笔记的目的是把他这个系列的文章的核心内容总结起来。

 

第一篇.Faster Screen Orientation Change with Android

    由于Android运行在很多个硬件上不同的设备上,并且的它们的硬件配置在运行时是可以的。例如,你推开手机的键盘,那么屏幕就会从portrait切换到landscape。为了让Android应用开发更容易一些,Android OS自动处理了配置的变化并且用新的配置重启了当前的activity。这是一个默认行为。如果你不了解Android如何处理资源,强烈推荐你看官方说明

    最好不要自行处理这些变化,否则不能保证你的代码在以后的更多的设备中运行。

    文章介绍了一个很有用的Activity的api: onRetainNonConfigurationInstance(). This method can be used to pass an arbitrary object your future self and Android is smart enough to call this method only when needed.

     他给出了两个代码片段:

 

@Override
public Object onRetainNonConfigurationInstance() {
    final LoadedPhoto[] list = new LoadedPhoto[numberOfPhotos];
    keepPhotos(list);
    return list;
}

 然后在新创建的activity里用getLastNonConfigurationInstance()把你保存的object拿回来:

 

private void loadPhotos() {
    final Object data = getLastNonConfigurationInstance();

    // The activity is starting for the first time, load the photos from Flickr
    if (data == null) {
        mTask = new GetPhotoListTask().execute(mCurrentPage);
    } else {
        // The activity was destroyed/created automatically, populate the grid
        // of photos with the images loaded by the previous activity
        final LoadedPhoto[] photos = (LoadedPhoto[]) data;
        for (LoadedPhoto photo : photos) {
            addPhoto(photo);
        }
    }
}

 最后需要注意的是:

Be very careful with the object you pass through onRetainNonConfigurationChange() though. If the object you pass is for some reason tied to the Activity/Context, you will leak all the views and resources of the activity. This means you should never pass a View, a Drawable, an Adapter, etc. Photostream for instance extracts the bitmaps from the drawables and pass the bitmaps only, not the drawables. Finally, remember that onRetainNonConfigurationChange() should be used only to retain data that is expensive to load. Otherwise, keep it simple and let Android do everything.

也就是说,不要把跟Context/Activity绑定的东西传递过去,这样会导致memory leak。

 

示例程序有一个有意思的地方是,图片不是水平放置的,我开始以为ImageView什么的可以支持旋转,后来在源码中找了找,发现了如下的一个工具方法:

 

/**
     * Rotate specified Bitmap by a random angle. The angle is either negative or positive,
     * and ranges, in degrees, from 2.5 to 8. After rotation a frame is overlaid on top
     * of the rotated image.
     *
     * This method is not thread safe.
     *
     * @param bitmap The Bitmap to rotate and apply a frame onto.
     *
     * @return A new Bitmap whose dimension are different from the original bitmap.
     */
    static Bitmap rotateAndFrame(Bitmap bitmap) {
        final boolean positive = sRandom.nextFloat() >= 0.5f;
        final float angle = (ROTATION_ANGLE_MIN + sRandom.nextFloat() * ROTATION_ANGLE_EXTRA) *
                (positive ? 1.0f : -1.0f);
        final double radAngle = Math.toRadians(angle);

        final int bitmapWidth = bitmap.getWidth();
        final int bitmapHeight = bitmap.getHeight();

        final double cosAngle = Math.abs(Math.cos(radAngle));
        final double sinAngle = Math.abs(Math.sin(radAngle));

        final int strokedWidth = (int) (bitmapWidth + 2 * PHOTO_BORDER_WIDTH);
        final int strokedHeight = (int) (bitmapHeight + 2 * PHOTO_BORDER_WIDTH);
 // 宽高都是通过计算放大过的
        final int width = (int) (strokedHeight * sinAngle + strokedWidth * cosAngle);
        final int height = (int) (strokedWidth * sinAngle + strokedHeight * cosAngle);

        final float x = (width - bitmapWidth) / 2.0f;
        final float y = (height - bitmapHeight) / 2.0f;
        
        final Bitmap decored = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(decored);
// 最终的生成一个包含原有图片并且该图片之外的地方都是透明的的图片
        canvas.rotate(angle, width / 2.0f, height / 2.0f);
        canvas.drawBitmap(bitmap, x, y, sPaint);
        canvas.drawRect(x, y, x + bitmapWidth, y + bitmapHeight, sStrokePaint);

        return decored;
    }
 

第二篇:Android Layout Tricks #1

这篇文章主要以一个例子来说明Layout使用不当而导致的浪费。

这个例子是以LinearLayout为例的。我们只要对比一下用RelativeLayout代替示例场景中的LinearLayout后,view tree中产生的变化,如图:

 

很明显,后者节省了一个LinearLayout,如果这个是一个ListView的item的layout的话,那么这个节省就更加可观。

 

另外他提到:

 

The layout pass can be especially expensive when you nest several 
LinearLayout that use the weight parameter, 
which requires the child to be measured twice.

 

 的确,在LinearLayout的源码中有相应的证明,参看LinearLayout#onMeasure(int widthMeasureSpec, int heightMeasureSpec)部分:如果child view的weight大于0,那么这个child view就会额外onMeasure一次。

 

总结语:UI是组织结构,配置的改变很容易引起连锁效应,我想Roman也是想告诉人们,要简单化你的UI,包括该文章后面的评论的一个例子:有人说他写了一个很棒的AbsouluteLayout,然后Roman回复说:

I don’t understand why it was so complicated for you to do this. Just a simple FrameLayout with margins would work fine.

所以对于UI乃至任何事情,复杂永远都是要避免的,否则你可能很轻易的被掉到沟里(试想你并不知道LinearLayout会怎么处理weight参数,而你写了一个有复杂LinearLayout嵌套的view,然后你发现你的view怎么这么慢,而你并不知道android正在努力的onMeasure啊)。

我自己的一个例子是,我之前写了一个组件,自定义UI,自己写了一个view,自己onMeasure,自己onLayout,结果后来,另外一个同事接受我的这个组建,然后他直接给改掉了,所有东西都用android自己的。不管效率问题,首先我这段代码在别人看来,是不好的,至少不易理解。如果我有足够的理由和数据来证明我的实现是OK的,那么我可以保证我的代码有更长的生命周期。

 

3. Android Layout Trick #2: Include to Reuse

 

Android带来了各种各样的widget(small visual construction blocks you can glue together to present the users with complex and useful interfaces)。尽管application经常需要更高级的可视化组件。一个组件可被看作是由多个简单的既有的wdget组成的复杂widget。譬如你可以重用一个包含一个滚动条和一个button的panel,等等。。

 

In Android XML layout files, each tag is mapped to an actual class instance (the class is always a subclass of View.) The UI toolkit lets you also use three special tags that are not mapped to a View instance: <requestFocus />, <merge /> and <include />. The latter, <include />, can be used to create pure XML visual components. 

 

 

在这篇文章的评论处,有人提到使用include的文件和被include的文件之间引用ID的问题,他认为:

问题在于,included的views的id在include它们的文件里是不可见的,这种情况在多个included文件也存在。

Roman答道:

不,它们是可见的。

然后他又问道:

既然如此,那么为什么我引用included views的ID后,在编译的时候会出现:“Unknown resource”。

Roman答道:

IDs的声明和使用必须有正确的顺序。这跟你没有使用include是一样的。如果你要引用included layout里面的ID,那么你要使用@+id或者把把id声明在values/目录内。引用一个included layout外部的id,同样要使用@+id

 

总之include标签可以让layout得到重用,它的一些细节可以用到的时候再仔细看。

 

4. Android Layout Tricks #3: Optimize, Part 1

 

The <merge /> was created for the purpose of optimizing Android layouts by reducing the number of levels in view trees.  文章通过一个示例来解释这个标记的功能。

 

尽管merge标记很有用,但是它仍然受到一些限制:

a)<merge />只能被用作XML layout的root标记。

b)如果你通过Inflater来创建layout的话,你必须指定一个ViewGroup并且必须设置attachToRoot为true(详见inflate()

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics