Android性能优化的一些理解

前言

Android性能优化对Android程序的维护和拓展是有很大帮助的,我们知道Android手机不管是内存还是CPU都无法同PC相比,这也就意味着我们必须要谨慎的去使用内存和CPU资源。因为稍稍不注意可能就会引发诸如OOM、ANR、内存泄漏等问题,所以熟悉Android性能优化的几个方法可以有效地提高应用程序的性能,我们可能都能说出一些性能优化的方法,比如布局优化、绘制优化、线程优化等等,但是可能我们会忽视某些小细节,比如布局优化我们可能都知道可以使用< include >来减少布局的层级和布局重用,但是我们很少会考虑使用< merge >标签,甚至有些人都不知道有这个标签(说这句话前我特地询问了十来个Android程序员..),所以这篇文章我尝试把Android性能优化的方法列举出来,然后对某些细节也进行解析。


布局优化

布局优化的核心思想就是尽量减少布局文件的层级,层级越少Android在进行布局绘制时工作量也就越少,所以程序的性能也就得到提高了。下面是一些常见的布局优化方法

尽量使用性能较高的ViewGroup

所以我们在设计界面布局时要避免使用多余的嵌套以及在使用ViewGroup时尽量选择性能较高的布局,比如如果一个布局既可以用LinearLayout实现也可以用RelativeLayout实现,这时我们就应该采用LinearLayout,我们知道一个view的绘制流程是经过onMeasure(),onLayout,onDraw()三个流程才得以呈现的,比较它们的性能无非也就比较它们在这三个方法中的操作耗时,由于篇幅

有限这里就不进行方法耗时测试了,我们直接看源码说结论RelativeLayout的onLayout和onDraw两个方法耗时差别不大,所以就不列举了,真正导致它们性能差异的是onMeasure()这个方法

RelativeLayout的onMeasure方法源码如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    ...
        View[] views = mSortedHorizontalChildren;
        int count = views.length;

        for (int i = 0; i < count; i++) {
            View child = views[i];
            if (child.getVisibility() != GONE) {
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                int[] rules = params.getRules(layoutDirection);

                applyHorizontalSizeRules(params, myWidth, rules);
                measureChildHorizontal(child, params, myWidth, myHeight);

                if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
                    offsetHorizontalAxis = true;
                }
            }
        }

        views = mSortedVerticalChildren;
        count = views.length;
        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;

        for (int i = 0; i < count; i++) {
            final View child = views[i];
            if (child.getVisibility() != GONE) {
                final LayoutParams params = (LayoutParams) child.getLayoutParams();

                applyVerticalSizeRules(params, myHeight, child.getBaseline());
                measureChild(child, params, myWidth, myHeight);
                if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
                    offsetVerticalAxis = true;
                }

                if (isWrapContentWidth) {
                    if (isLayoutRtl()) {
                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                            width = Math.max(width, myWidth - params.mLeft);
                        } else {
                            width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
                        }
                    } else {
                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                            width = Math.max(width, params.mRight);
                        } else {
                            width = Math.max(width, params.mRight + params.rightMargin);
                        }
                    }
                }

                if (isWrapContentHeight) {
                    if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                        height = Math.max(height, params.mBottom);
                    } else {
                        height = Math.max(height, params.mBottom + params.bottomMargin);
                    }
                }

                if (child != ignore || verticalGravity) {
                    left = Math.min(left, params.mLeft - params.leftMargin);
                    top = Math.min(top, params.mTop - params.topMargin);
                }

                if (child != ignore || horizontalGravity) {
                    right = Math.max(right, params.mRight + params.rightMargin);
                    bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
                }
            }
        }

从源码可以看到,RelativeLayout在onMeasure对子view进行了两次measure,之所以会measure两次是因为RelativeLayout子view本身是相对的关系,由于子view在布局中的顺序不同,在确定子view的具体位置时要先给子view进行排序,RelativeLayout允许A,B 2个子View,在横向上A相对B,纵向上B相对A。所以需要横向纵向分别进行一次排序测量。

LinearLayout的onMeasure()方法源码如下:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

LinearLayout的onMeasure是先判断线性规则是竖直还是水平,然后再在对应方向上进行测量,下面是竖直方向的测量源码

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
  mTotalLength = 0;//保存已经measure过的child所占用的高度,初始为0
  float totalWeight = 0;//累计所有子视图的weight值
 ...
  for (int i = 0; i < count; ++i) {
      final View child = getVirtualChildAt(i);

      if (child == null) {
        mTotalLength += measureNullChild(i);
        continue;
      }

      if (child.getVisibility() == View.GONE) {
       i += getChildrenSkipCount(child, i);
       continue;
      }

      if (hasDividerBeforeChildAt(i)) {
        mTotalLength += mDividerHeight;
      }

      LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

      totalWeight += lp.weight;

      if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
        // Optimization: don‘t bother measuring children who are going to use
        // leftover space. These views will get measured again down below if
        // there is any leftover space.
        final int totalLength = mTotalLength;
        mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
      } else {
        int oldHeight = Integer.MIN_VALUE;

        if (lp.height == 0 && lp.weight > 0) {
          // heightMode is either UNSPECIFIED or AT_MOST, and this
          // child wanted to stretch to fill available space.
          // Translate that to WRAP_CONTENT so that it does not end up
          // with a height of 0
          oldHeight = 0;
          lp.height = LayoutParams.WRAP_CONTENT;
        }

        // Determine how big this child would like to be. If this or
        // previous children have given a weight, then we allow it to
        // use all available space (and we will shrink things later
        // if needed).
        //对每一个child进行测量
        measureChildBeforeLayout(
           child, i, widthMeasureSpec, 0, heightMeasureSpec,
           totalWeight == 0 ? mTotalLength : 0);

        if (oldHeight != Integer.MIN_VALUE) {
         lp.height = oldHeight;
        }

        final int childHeight = child.getMeasuredHeight();
        final int totalLength = mTotalLength;
        mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
           lp.bottomMargin + getNextLocationOffset(child));

        if (useLargestChild) {
          largestChildHeight = Math.max(childHeight, largestChildHeight);
        }
      }
 }

这里面还有个细节,就是如果不使用LinearLayout的weight属性,LinearLayout会在当前方向上进行一次measure的过程,如果使用weight属性,LinearLayout会避开设置过weight属性的view做第一次measure,然后再对设置过weight属性的view进行第二次measure。所以,weight属性对性能是有影响的,使用时要谨慎。

所以如果RelativeLayout和LinearLayout或者FrameLayout都能完成布局的情况下优先考虑使用LinearLayout或FrameLayout这些比较高效的ViewGroup,而如果单纯一个LinearLayout或FrameLayout无法完成要求而要进行嵌套时,而RelativeLayout一层就可以绘制时就建议使用RelativeLayout了。

布局可以重用时就封装好进行重用

这里说的重用时使用< include >标签,这个标签可以将一个指定的布局文件加载进当前的布局文件中。

<include layout="@layout/xxx"/>

比如xxx是一个特定的布局(例如标题栏),这样就不用在当前布局中再写一次xxx的布局文件的内容了,值得说的是这个标签支持除了android:id这个属性之外只支持android:layout_开头的属性,比如

android:layout_width="match_parent"

还有如果指定了其他除android:layout_width,和android:layout_height的android:layout_*属性时,前两个属性必须存在,否则android:layout_*是没有效果的。这个标签我们应该很常用,所以就不举例了。值得一说的是< merge >标签,这个标签一般是和< include >标签进行配合使用的,比如当前布局是竖直的LinearLayout,而 < include >要引用的布局也是竖直LinearLayout时,就可以把要引入布局的LinearLayout换成< merge>即可,这样又可以减少了一个布局的层级。

ViewStub

ViewStub是一个轻量级且宽高都是0,它不参与任何布局和绘制过程。ViewStub的作用在于按需加载所需布局,在我们开发过程中,有些布局是在某些情况下不需要显示的,比如,一个界面需要网络数据时才显示,网络异常时会显示另一个界面,这时候我们就不需要一开始就把异常界面加载金布局,只有当网络异常时才加载这个布局,这时候通过ViewStub就可以做到在使用的时候再加载,提高了程序初始化时的性能。

<ViewStub
    android:layout_width="wrap_content"
    android:id="@+id/vs"
    android:layout_height="wrap_content"
    android:inflatedId="@+id/ll_import"
    android:layout="@layout/ll_network_error"
    />

比如上面的例子,ll_network_error就是网络异常时要加载的布局,而ll_import是这个布局根元素的id。通过这个标签就可以做到按需加载了,这样只要在java代码中

findviewById(R.id.vs).setVisibility(View.VISIBLE);

findviewById(R.id.vs).inflate()

ViewStub就会被ll_network_error替换,这时View就不在是整个布局结构的一部分了,当然目前ViewStub不支持< merge >标签。


绘制优化

绘制优化是说在View调用onDraw时应该避免大量操作,比如在onDraw时不要创建新局部对象,因为onDraw方法可能会被频繁调用。如果被频繁调用就会瞬间产生大量的临时变量,占用过多内存和导致系统频繁触发gc降低了程序的执行效率。还有一个是不要再onDraw中进行耗时操作,比如上千次循环,因为大量循环会抢占CPU造成View绘制不流畅Google给出的性能优化典范标准中,view的绘制帧率保证60fps是最佳的,也就是说,每帧的绘制事件不能超过16ms(1000/60),所以在View绘制时,应该尽量避免在onDraw上进行复杂耗时的操作。


线程优化

线程优化是避免在程序中使用大量的Thread,而是采用内部的线程池,这样就可以避免线程的创建和销毁时带来性能上不必要的开销,线程池也能控制线程的最大并发数,避免了大量线程因为互相抢占系统资源导致程序阻塞的现象。在实际开发中,我们是不应该创建Thread对象而是使用线程池策略的,关于线程池策略可以参考我的另一篇文章:

Android 关于线程池的理解


ListView优化

ListView优化主要是以下几个方面:

  1. 复用ConvertView
  2. 自定义静态类ViewHolder
  3. 使用分页加载
  4. 使用弱引用(WeakRefrence)引用ImageView对象
  5. 避免在getView中执行耗时操作

Bitmap优化

Bitmap优化主要是以下几个方面:

  1. 及时回收Bitmap的内存
  2. 缓存通用的Bitmap对象
  3. 压缩图片

    4. 及时关闭资源


内存泄漏优化

内存泄漏优化主要有两个方面,一个是在开发过程中避免写出有内存泄漏的代码,一个是通过分析工具找出可能存在的内存泄漏问题然后解决。

避免写出内存泄漏的代码比较考验的是程序员的经验和开发意识了,下面列出一些常见的内存泄漏的例子,让我们在以后开发时积累和避免写出导致内存泄漏的代码。

静态变量导致内存泄漏

public class MainActivity extends Activity {

    private static Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
    }
}

如上,这是一种最简单的内存泄漏,Activity是无法正常销毁的,因为静态变量mContext引用了这个Activity。

属性动画没停止导致内存泄漏

这个理解起来也比较简单,如果一个Activity中播放了无限循环的属性动画而不在onDestroy方法中去停止动画时,动画就会一直播放下去(即使界面上已经没有动画效果),解决方法也很简单,只要在onDestroy调用xx.cancel()停止动画即可。

单例模式导致内存泄漏

这种情况可以有下面的情形:比如在一个单例中持有一个Activity的引用,当Activity退出时这个Activity应该被回收,但是单例的生命周期是和Application一致的,这就导致了Activity不能被回收,造成内存泄漏

内存泄漏分析工具

MAT这个工具是一款强大的内存泄漏分析工具,这个工具的使用方法就不在这篇博客里面详述了(因为感觉写得太多了你们看着也烦..),具体可以参考这篇博文:

内存分析工具 MAT 的使用


响应速度优化

其实这个优化也就是在开发时避免在主线程中进行耗时操作,因为Android系统规定,Activity5秒无法响应屏幕触摸事件或键盘输入事件就会出现ANR,BroadcastReceiver在10秒内没有执行完毕也会出现ANR,当我们程序出现ANR时一般我们是很难直接从代码上定位到问题的,这个Android系统也帮我们考虑到了,所以当程序出现ANR时,系统会在/data/anr目录下创建一个traces.txt文件,童工分析文件就可以找到ANR的原因了,下面用一个例子去模拟ANR

public class MainActivity extends AppCompatActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            SystemClock.sleep(30000);

        }
    }

例子很简单就在主线程睡了30秒,然后肯定会引起ANR了,所以我们查看traces.txt文件夹,信息很多我只截取跟我们有关的

----- pid 1472 at 2016-07-16 03:07:28 -----
Cmd line: com.example.sjr.zhenanim

JNI: CheckJNI is off; workarounds are off; pins=0; globals=155

DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)

"main" prio=5 tid=1 TIMED_WAIT
  | group="main" sCount=1 dsCount=0 obj=0xa4c13480 self=0xb94b0bd0
  | sysTid=1472 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1216614368
  | state=S schedstat=( 350771760 398313811 1915 ) utm=12 stm=22 core=0
  at java.lang.VMThread.sleep(Native Method)
  at java.lang.Thread.sleep(Thread.java:1013)
  at java.lang.Thread.sleep(Thread.java:995)
  at android.os.SystemClock.sleep(SystemClock.java:115)
  at com.example.sjr.zhenanim.MainActivity.onCreate(MainActivity.java:20)
  at android.app.Activity.performCreate(Activity.java:5133)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2175)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2261)
  at android.app.ActivityThread.access$600(ActivityThread.java:141)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:137)
  at android.app.ActivityThread.main(ActivityThread.java:5103)
  at java.lang.reflect.Method.invokeNative(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:525)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
  at dalvik.system.NativeStart.main(Native Method)

"Thread-103" prio=5 tid=11 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0xa509c578 self=0xb94fd1a8
  | sysTid=1486 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1185982336
  | state=S schedstat=( 12313 10224280 3 ) utm=0 stm=0 core=0
  #00  pc 0002e2a1  /system/lib/libc.so (accept+17)
  at android.net.LocalSocketImpl.accept(Native Method)
  at android.net.LocalSocketImpl.accept(LocalSocketImpl.java:299)
  at android.net.LocalServerSocket.accept(LocalServerSocket.java:94)
  at com.android.tools.fd.runtime.Server$SocketServerThread.run(Server.java:150)
  at java.lang.Thread.run(Thread.java:841)

"Binder_2" prio=5 tid=10 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0xa507d268 self=0xb94f66c0
  | sysTid=1485 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1185979272
  | state=S schedstat=( 3756308 15291656 13 ) utm=0 stm=0 core=0
  #00  pc 0002cff4  /system/lib/libc.so (__ioctl+20)
  at dalvik.system.NativeStart.run(Native Method)

"Binder_1" prio=5 tid=9 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0xa507d180 self=0xb94f6010
  | sysTid=1484 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1185990552
  | state=S schedstat=( 8102476 41618382 11 ) utm=0 stm=0 core=0
  #00  pc 0002cff4  /system/lib/libc.so (__ioctl+20)
  at dalvik.system.NativeStart.run(Native Method)

"FinalizerWatchdogDaemon" daemon prio=5 tid=8 WAIT
  | group="system" sCount=1 dsCount=0 obj=0xa5078640 self=0xb94f2fb0
  | sysTid=1483 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1185991656
  | state=S schedstat=( 3031794 41298066 7 ) utm=0 stm=0 core=0
  at java.lang.Object.wait(Native Method)
  - waiting on <0xa4c1b3b8> (a java.lang.Daemons$FinalizerWatchdogDaemon)
  at java.lang.Object.wait(Object.java:364)
  at java.lang.Daemons$FinalizerWatchdogDaemon.waitForObject(Daemons.java:230)
  at java.lang.Daemons$FinalizerWatchdogDaemon.run(Daemons.java:207)
  at java.lang.Thread.run(Thread.java:841)

"FinalizerDaemon" daemon prio=5 tid=7 WAIT
  | group="system" sCount=1 dsCount=0 obj=0xa50784a0 self=0xb94f26f8
  | sysTid=1482 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1185993888
  | state=S schedstat=( 1334759 16682975 5 ) utm=0 stm=0 core=0
  at java.lang.Object.wait(Native Method)
  - waiting on <0xa4c04568> (a java.lang.ref.ReferenceQueue)
  at java.lang.Object.wait(Object.java:401)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:102)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:73)
  at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)
  at java.lang.Thread.run(Thread.java:841)

"ReferenceQueueDaemon" daemon prio=5 tid=6 WAIT
  | group="system" sCount=1 dsCount=0 obj=0xa5078348 self=0xb94ca578
  | sysTid=1481 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1185996120
  | state=S schedstat=( 1647741 1925287 6 ) utm=0 stm=0 core=0
  at java.lang.Object.wait(Native Method)
  - waiting on <0xa4c04490>
  at java.lang.Object.wait(Object.java:364)
  at java.lang.Daemons$ReferenceQueueDaemon.run(Daemons.java:130)
  at java.lang.Thread.run(Thread.java:841)

"Compiler" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0xa5078260 self=0xb94c9f08
  | sysTid=1480 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1186046664
  | state=S schedstat=( 9217427 7831506 9 ) utm=0 stm=0 core=0
  #00  pc 0002ed67  /system/lib/libc.so (__futex_syscall4+23)
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=4 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0xa5078180 self=0xb94c9aa0
  | sysTid=1479 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1186006992
  | state=S schedstat=( 87784861 47188330 130 ) utm=0 stm=7 core=0
  #00  pc 0002d1e0  /system/lib/libc.so (select+32)
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=3 RUNNABLE
  | group="system" sCount=0 dsCount=0 obj=0xa5078088 self=0xb94c9638
  | sysTid=1478 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1186244424
  | state=R schedstat=( 13450535 1316670 12 ) utm=0 stm=1 core=0
  at dalvik.system.NativeStart.run(Native Method)

"GC" daemon prio=5 tid=2 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0xa5077fa8 self=0xb94c91d0
  | sysTid=1475 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=-1186040176
  | state=S schedstat=( 12677193 73095526 1316 ) utm=1 stm=0 core=0
  #00  pc 0002ed67  /system/lib/libc.so (__futex_syscall4+23)
  at dalvik.system.NativeStart.run(Native Method)

----- end 1472 -----

可以看到,日志里有这么一行:

com.example.sjr.zhenanim.MainActivity.onCreate(MainActivity.java:20)

这就是 SystemClock.sleep(30000);所在的行数了,在日常开发中ANR很容易出现,避免出现ANR只能靠程序员的开发意识了,但是当出现ANR时可以通过分析traces.txt文件去快速定位并解决问题了。


其他优化建议

这是一些对性能优化的小建议,通过这些小技巧可以在一定程度上提高性能:

  1. 避免创建过多对象;
  2. 不要过多使用枚举,因为枚举占用的内存空间比整型大两倍以上;
  3. 常量使用static final来修饰;
  4. 使用一些Android特有的数据结构,比如SparseArry和Pair等,它们都具有更好的性能;
  5. 适当使用软引用和弱引用;
  6. 采用内存缓存和磁盘缓存;
  7. 尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏。

总结

Android的性能优化是一个很大的模块,我在没写这篇文章前对某些地方也是并不注意,但是当我为了写好这篇文章查看了大量的资料之后也了解了很多我平常没注意到的东西,其实如果我们开发的项目不属于中大型项目,只是比较小型的项目时,也不需要对性能做到斤斤计较的地步,但是作为一个有文化有素养有追求的程序员,我们对性能还是要有一定追求的,保不准以后我们就参与大型的项目了呢,到时某些坏习惯养成了要改也是很麻烦的。所以虽然不至于到斤斤计较的地步但是在平常开发中我们在编写代码的时候也应该注意性能方面的优化。

时间: 2024-10-12 10:26:47

Android性能优化的一些理解的相关文章

Google 发布 Android 性能优化典范

2015年伊始,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个 方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计

[Android Pro] Android性能优化典范第一季

reference to : http://www.cnblogs.com/hanyonglu/p/4244035.html#undefined 2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议. 主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下

Android性能优化典范(一)

2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师的

Android 性能优化探究

使用ViewStub动态加载布局,避免一些不经常的视图长期握住引用: ViewStub的一些特点: 1. ViewStub只能Inflate一次,之后ViewStub对象被置空:某个被ViewStub指定的布局被Inflate后,就不会够再通过ViewStub来控制它了. 2. ViewStub只能用来Inflate一个布局文件,而不是某个具体的View,当然也可以把View写在某个布局文件中. 基于以上的特点,那么可以考虑使用ViewStub的情况有: 1. 在程序的运行期间,某个布局在Inf

Android性能优化典范

2015年伊始,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个 方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计

Android 性能优化:多线程注

前言 Android 开发中多线程的必要性 理解 Android 多线程 MessageQueue Looper Handler HandlerThread Android 中为什么只允许在主线程更新 UI Android 系统为了避免过度复杂的线程安全问题特地规定只允许在主线程中更新 UI 而开发者为了避免上述问题需要注意的是 总结 Thanks 前言 Android Performance Patterns Season 5 主要介绍了 Android 多线程环境下的性能问题.通过介绍 An

Android 性能优化

上周四参加了MDCC大会的 Android,我比较关注的5R,做一个安静的app,图片缓存以及React Native For Android,其中很大一部分的内容都是讲的性能优化,后续还会给大家带来一篇React Native ,下面就来总结一下Android性能优化方面的内容! Reduce Cache/Drawable load in demand bitmap - scale/format Reuse pools inBitmaps convertView onDraw / for Re

Android性能优化典范(二)

原文出处: 胡凯的博客(@胡凯me)   欢迎分享原创到伯乐头条 Google前几天刚发布了Android性能优化典范第2季的课程,一共20个短视频,包括的内容大致有:电量优化,网络优化,Wear上如何做优化,使用对象池来提高效率,LRU Cache,Bitmap的缩放,缓存,重用,PNG压缩,自定义View的性能,提升设置alpha之后View的渲染性能,以及Lint,StictMode等等工具的使用技巧. 下面是对这些课程的总结摘要,认知有限,理解偏差的地方请多多指教! 1)Battery

Google 发布的15个 Android 性能优化典范

2015年伊始,Google发布了关于Android性能优化典范的专题,一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App.课程专题不仅仅介绍了Android系统中有关性能问题的底层工作原理,同时也介绍了如何通过工具来找出性能问题以及提升性能的建议.主要从三个方面展开,Android的渲染机制,内存与GC,电量优化.下面是对这些问题和建议的总结梳理. 0)Render Performance 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师的