Android 性能优化最佳实践

e.printStackTrace();

}

return “”;

}

}

}

我们经常会用这个方法去异步加载,然后更新数据。貌似很平常,我们开始学这个的时候就是这么写的,没发现有问题啊,但是你这么想一想,MyAscnyTask是一个非静态内部类,如果他处理数据的时间很长,极端点我们用sleep 100秒,在这期间Activity可能早就关闭了,本来Activity的内存应该被回收的,但是我们知道非静态内部类会持有外部类的引用,所以Activity也需要陪着非静态内部类MyAscnyTask一起天荒地老。好了,内存泄漏就形成了。

怎么办呢?

既然MyAscnyTask的生命周期可能比较长,那就把它变成静态,和Application玩去吧,这样MyAscnyTask就不会再持有外部类的引用了。两者也相互独立了。

public class TestActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

new MyAscnyTask().execute();

}

//改了这里 注意一下 static

static class MyAscnyTask extends AsyncTask<Void, Integer, String>{

@Override

protected String doInBackground(Void… params) {

try {

Thread.sleep(100000);

} catch (InterruptedException e) {

e.printStackTrace();

}

return “”;

}

}

}

说完非静态内部类,我再来看看匿名内部类,这个问题很常见,匿名内部类和非静态内部类有一个共同的地方,就是会只有外部类的强引用,所以这哥俩本质是一样的。但是处理方法有些不一样。但是思路绝对一样。换汤不换药。

举个灰常熟悉的栗子:

public class TestActivity extends Activity {

private TextView mText;

private Handler mHandler = new Handler(){

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

//do something

mText.setText(" do someThing");

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

mText = findVIewById(R.id.mText);

// 匿名线程持有 Activity 的引用,进行耗时操作

new Thread(new Runnable() {

@Override

public void run() {

try {

Thread.sleep(100000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

mHandler. sendEmptyMessageDelayed(0, 100000);

}

想必这两个方法是我们经常用的吧,很熟悉,也是这么学的,没感觉不对啊,老师就是这么教的,通过我们上面的分析,还这么想吗?关键是 耗时时间过长,造成内部类的生命周期大于外部类,对弈非静态内部类,我们可以静态化,至于匿名内部类怎么办呢?一样把它变成静态内部类,也就是说尽量不要用匿名内部类。完事了吗?很多人不注意这么一件事,如果我们在handleMessage方法里进行UI的更新,这个Handler静态化了和Activity没啥关系了,但是比如这个mText,怎么说?全写是activity.mText,看到了吧,持有了Activity的引用,也就是说Handler费劲心思变成静态类,自认为不持有Activity的引用了,准确的说是不自动持有Activity的引用了,但是我们要做UI更新的时候势必会持有Activity的引用,静态类持有非静态类的引用,我们发现怎么又开始内存泄漏了呢?处处是坑啊,怎么办呢?我们这里就要引出弱引用的概念了。

引用分为强引用,软引用,弱引用,虚引用,强度一次递减。

强引用 我们平时不做特殊处理的一般都是强引用,如果一个对象具有强引用,GC宁可OOM也绝不会回收它。看出多强硬了吧。

软引用(SoftReference) 如果内存空间足够,GC就不会回收它,如果内存空间不足了,就会回收这些对象的内存。

弱引用(WeakReference) 弱引用要比软引用,更弱一个级别,内存不够要回收他,GC的时候不管内存够不够也要回收他,简直是弱的一匹。不过GC是一个优先级很低的线程,也不是太频繁进行,所以弱引用的生活还过得去,没那么提心吊胆。

虚引用 用的甚少,我没有用过,如果想了解的朋友,可以自行谷歌百度。

所以我们用弱引用来修饰Activity,这样GC的时候,该回收的也就回收了,不会再有内存泄漏了。很完美。

public class TestActivity extends Activity {

private TextView mText;

private MyHandler myHandler = new MyHandler(TestActivity.this);

private MyThread myThread = new MyThread();

private static class MyHandler extends Handler {

WeakReference weakReference;

MyHandler(TestActivity testActivity) {

this.weakReference = new WeakReference(testActivity);

}

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

weakReference.get().mText.setText(“do someThing”);

}

}

private static class MyThread extends Thread {

@Override

public void run() {

super.run();

try {

sleep(100000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

mText = findViewById(R.id.mText);

myHandler.sendEmptyMessageDelayed(0, 100000);

myThread.start();

}

//最后清空这些回调

@Override

protected void onDestroy() {

super.onDestroy();

myHandler.removeCallbacksAndMessages(null);

}

4、资源未关闭造成的内存泄漏

  • 网络、文件等流忘记关闭

  • 手动注册广播时,退出时忘记 unregisterReceiver()

  • Service 执行完后忘记 stopSelf()

  • EventBus 等观察者模式的框架忘记手动解除注册

这些需要记住又开就有关,具体做法也很简单就不一一赘述了。给大家介绍几个很好用的工具: 1、leakcanary傻瓜式操作,哪里有泄漏自动给你显示出来,很直接很暴力。 2、我们平时也要多使用Memory Monitor进行内存监控,这个分析就有些难度了,可以上网搜一下具体怎么使用。 3、Android Lint 它可以帮助我们发现代码机构 / 质量问题,同时提供一些解决方案,内存泄露的会飘黄,用起来很方便,具体使用方法上网学习,这里不多做说明了。

so

优化3.0
  1. 解决各个情况下的内存泄漏,注意平时代码的规范。

4、启动速度优化

不知道大家有没有细心发现,我们的应用启动要比别的大厂的要慢,要花费更多的时间,明明他们的包体更大,app更复杂,怎么启动时间反而比我们的短呢?

但是这块的优化关注的人很少,因为App常常伴有闪屏页,所以这个问题看起来就不是问题了,但是一款好的应用是绝对不允许这样的,我加闪屏页是我的事,启动速度慢绝对不可以。

app启动分为冷启动(Cold start)、热启动(Hot start)和温启动(Warm start)三种。

冷启动(Cold start)

冷启动是指应用程序从头开始:系统的进程在此开始之前没有创建应用程序。冷启动发生在诸如自设备启动以来首次启动应用程序或自系统终止应用程序以来。

在冷启动开始时,系统有三个任务。这些任务是: 1、加载并启动应用程序 2、启动后立即显示应用程序的空白启动窗口 3、创建应用程序进程

当系统为我们创建了应用进程之后,开始创建应用程序对象。

1、启动主线程 2、创建主Activity 3、加载布局 4、屏幕布局 5、执行初始绘制

应用程序进程完成第一次绘制后,系统进程会交换当前显示的背景窗口,将其替换为主活动。此时,用户可以开始使用该应用程序。至此启动完成。

19956127-cadebe36f0a5d777.png

Application创建

当Application启动时,空白的启动窗口将保留在屏幕上,直到系统首次完成绘制应用程序。此时,系统进程会交换应用程序的启动窗口,允许用户开始与应用程序进行交互。这就是为什么我们的程序启动时会先出现一段时间的黑屏(白屏)。

如果我们有自己的Application,系统会onCreate()在我们的Application对象上调用该方法。之后,应用程序会生成主线程(也称为UI线程),并通过创建主要活动来执行任务。

从这一点开始,App就按照他的 应用程序生命周期阶段进行

Activity创建

应用程序进程创建活动后,活动将执行以下操作:

  1. 初始化值。

  2. 调用构造函数。

  3. 调用回调方法,例如 Activity.onCreate(),对应Activity的当前生命周期状态。

通常,该 onCreate()方法对加载时间的影响最大,因为它以最高的开销执行工作:加载和膨胀视图,以及初始化活动运行所需的对象。

热启动(Hot start)

应用程序的热启动比冷启动要简单得多,开销也更低。在一个热启动中,系统都会把你的Activity带到前台。如果应用程序的Activity仍然驻留在内存中,那么应用程序可以避免重复对象初始化、布局加载和渲染。

热启动显示与冷启动方案相同的屏幕行为:系统进程显示空白屏幕,直到应用程序完成呈现活动。

温启动(Warm start)

温启动包含了冷启动时发生的一些操作,与此同时,它表示的开销比热启动少,有许多潜在的状态可以被认为是温暖的开始。

场景:

  • 用户退出您的应用,但随后重新启动它。该过程可能已继续运行,但应用程序必须通过调用从头开始重新创建Activity 的onCreate()

  • 系统将您的应用程序从内存中逐出,然后用户重新启动它。需要重新启动进程和活动,但是在调用onCreate()的时候可以从Bundle(savedInstanceState)获取数据。

了解完启动过程,我们就知道哪里会影响我们启动的速度了。在创建应用程序和创建Activity期间都可能会出现性能问题。

这里是慢的定义:

  • 启动需要5秒或更长时间。

  • 启动需要2秒或更长时间。

  • 启动需要1.5秒或更长时间。

无论何种启动,我们的优化点都是: Application、Activity创建以及回调等过程

谷歌官方给的建议是: 1、利用提前展示出来的Window,快速展示出来一个界面,给用户快速反馈的体验; 2、避免在启动时做密集沉重的初始化(Heavy app initialization); 3、避免I/O操作、反序列化、网络操作、布局嵌套等。

具体做法:

针对1:利用提前展示出来的Window,快速展示出来一个界面

使用Activity的windowBackground主题属性来为启动的Activity提供一个简单的drawable。

Layout XML file:

<bitmap

android:src=“@drawable/product_logo_144dp”

android:gravity=“center”/>

Manifest file:

<activity …

android:theme=“@style/AppTheme.Launcher” />

这样在启动的时候,会先展示一个界面,这个界面就是Manifest中设置的Style,等Activity加载完毕后,再去加载Activity的界面,而在Activity的界面中,我们将主题重新设置为正常的主题,从而产生一种快的感觉。其实就是个障眼法而已,提前让你看到了假的界面。也算是一种不错的方法,但是治标不治本。

针对2:避免在启动时做密集沉重的初始化

我们审视一下我们的MyApplication里面的操作。初始化操作有友盟,百度,bugly,数据库,IM,神策,图片加载库,网络请求库,广告sdk,地图,推送,等等,这么多需要初始化,Application的任务太重了,启动不慢才怪呢。

怎么办呢?这些还都是必要的,不能不去初始化啊,那就只能异步加载了。但是并不是所有的都可以进行异步处理。这里分情况给出一些建议: 1、比如像友盟,bugly这样的业务非必要的可以的异步加载。 2、比如地图,推送等,非第一时间需要的可以在主线程做延时启动。当程序已经启动起来之后,在进行初始化。 3、对于图片,网络请求框架必须在主线程里初始化了。

同时因为我们一般会有闪屏页面,也可以把延时启动的地图,推动的启动在这个时间段里,这样合理安排时间片的使用。极大的提高了启动速度。

针对3:避免I/O操作、反序列化、网络操作、布局嵌套等。

这个不用多说了,大家应该知道如何去做了,有些上文也有说明。

so

优化4.0
  1. 利用提前展示出来的Window,快速展示出来一个界面,给用户快速反馈的体验;

  2. 避免在启动时做密集沉重的初始化(Heavy app initialization);

  3. 避免I/O操作、反序列化、网络操作、布局嵌套等。

5、包体优化

我做过两年的海外应用产品,深知包体大小对于产品新增的影响,包体小百分之五,可能新增就增加百分之五。如果产品基数很大,这个提升就更可怕了。不管怎么说,我们要减肥,要六块腹肌,不要九九归一的大肚子。

既然要瘦身,那么我们必须知道APK的文件构成,解压apk:

19956127-4b6674485ecba8b3.png

assets文件夹 存放一些配置文件、资源文件,assets不会自动生成对应的 ID,而是通过 AssetManager 类的接口获取。

res目录 res 是 resource 的缩写,这个目录存放资源文件,会自动生成对应的 ID 并映射到 .R 文件中,访问直接使用资源 ID。

META-INF 保存应用的签名信息,签名信息可以验证 APK 文件的完整性。

AndroidManifest.xml 这个文件用来描述 Android 应用的配置信息,一些组件的注册信息、可使用权限等。

classes.dex Dalvik 字节码程序,让 Dalvik 虚拟机可执行,一般情况下,Android 应用在打包时通过 Android SDK 中的 dx 工具将 Java 字节码转换为 Dalvik 字节码。

resources.arsc 记录着资源文件和资源 ID 之间的映射关系,用来根据资源 ID 寻找资源。

我们需要从代码和资源两个方面去减少响应的大小。

1、首先我们可以使用lint工具,如果有没有使用过的资源就会打印如下的信息(不会使用的朋友可以上网看一下)

res/layout/preferences.xml: Warning: The resource R.layout.preferences appears

to be unused [UnusedResources]

同时我们可以开启资源压缩,自动删除无用的资源

android {

buildTypes {

release {

shrinkResources true

minifyEnabled true

proguardFiles getDefaultProguardFile(‘proguard-android.txt’),

‘proguard-rules.pro’

}

}

无用的资源已经被删除了,接下来哪里可以在瘦身呢?

2、我们可以使用可绘制对象,某些图像不需要静态图像资源; 框架可以在运行时动态绘制图像。Drawable对象(<shape>以XML格式)可以占用APK中的少量空间。此外,XML Drawable对象产生符合材料设计准则的单色图像。

上面的话官方,简单说来就是,能自己用XML写Drawable,就自己写,能不用公司的UI切图,就别和他们说话,咱们自己造,做自己的UI,美滋滋。而且这种图片占用空间会很小。

3、重用资源,比如一个三角按钮,点击前三角朝上代表收起的意思,点击后三角朝下,代表展开,一般情况下,我们会用两张图来切换,我们完全可以用旋转的形式去改变

<?xml version="1.0" encoding="utf-8"?>

<rotate xmlns:android=“http://schemas.android.com/apk/res/android”

android:drawable=“@drawable/ic_thumb_up”

android:pivotX=“50%”

android:pivotY=“50%”

android:fromDegrees=“180” />

比如同一图像的着色不同,我们可以用android:tint和tintMode属性,低版本(5.0以下)可以使用ColorFilter

4、压缩PNG和JPEG文件 您可以减少PNG文件的大小,而不会丢失使用工具如图像质量 pngcrushpngquant,或zopflipng。所有这些工具都可以减少PNG文件的大小,同时保持感知的图像质量。

5、使用WebP文件格式 可以使用图像的WebP文件格式,而不是使用PNG或JPEG文件。WebP格式提供有损压缩(如JPEG)以及透明度(如PNG),但可以提供比JPEG或PNG更好的压缩。

可以使用Android Studio将现有的BMP,JPG,PNG或静态GIF图像转换为WebP格式。

6、使用矢量图形 可以使用矢量图形来创建与分辨率无关的图标和其他可伸缩Image。使用这些图形可以大大减少APK大小。一个100字节的文件可以生成与屏幕大小相关的清晰图像。

但是,系统渲染每个VectorDrawable对象需要花费大量时间 ,而较大的图像需要更长的时间才能显示在屏幕上。因此,请考虑仅在显示小图像时使用这些矢量图形。

不要把AnimationDrawable用于创建逐帧动画,因为这样做需要为动画的每个帧包含一个单独的位图文件,这会大大增加APK的大小。

7、代码混淆 使用proGuard 代码混淆器工具,它包括压缩、优化、混淆等功能。这个大家太熟悉了。不多说了。

android {

buildTypes {

release {

minifyEnabled true

proguardFiles getDefaultProguardFile(‘proguard-android.txt’),

‘proguard-rules.pro’

}

}

8、插件化。 比如功能模块放在服务器上,按需下载,可以减少安装包大小。

so

优化5.0
  1. 代码混淆

  2. 插件化

  3. 资源优化

6、耗电优化

我们可能对耗电优化不怎么感冒,没事,谷歌这方面做得也不咋地,5.0之后才有像样的方案,讲实话这个优化的优先级没有前面几个那么高,但是我们也要了解一些避免耗电的坑,至于更细的耗电分析可以使用这个Battery Historian

Battery Historian 是由Google提供的Android系统电量分析工具,从手机中导出bugreport文件上传至页面,在网页中生成详细的图表数据来展示手机上各模块电量消耗过程,最后通过App数据的分析制定出相关的电量优化的方法。

我们来谈一下怎么规避电老虎吧。

谷歌推荐使用JobScheduler,来调整任务优先级等策略来达到降低损耗的目的。JobScheduler可以避免频繁的唤醒硬件模块,造成不必要的电量消耗。避免在不合适的时间(例如低电量情况下、弱网络或者移动网络情况下的)执行过多的任务消耗电量。

具体功能: 1、可以推迟的非面向用户的任务(如定期数据库数据更新); 2、当充电时才希望执行的工作(如备份数据); 3、需要访问网络或 Wi-Fi 连接的任务(如向服务器拉取配置数据); 4、零散任务合并到一个批次去定期运行; 5、当设备空闲时启动某些任务; 6、只有当条件得到满足, 系统才会启动计划中的任务(充电、WIFI…);

同时谷歌针对耗电优化也提出了一个懒惰第一的法则:

减少 你的应用程序可以删除冗余操作吗?例如,它是否可以缓存下载的数据而不是重复唤醒无线电以重新下载数据?

推迟 应用是否需要立即执行操作?例如,它可以等到设备充电才能将数据备份到云端吗?

合并 可以批处理工作,而不是多次将设备置于活动状态吗?例如,几十个应用程序是否真的有必要在不同时间打开收音机发送邮件?在一次唤醒收音机期间,是否可以传输消息?

谷歌在耗电优化这方面确实显得有些无力,希望以后可以退出更好的工具和解决方案,不然这方面的优化优先级还是很低。付出和回报所差太大。

so

优化6.0
  1. 使用JobScheduler调度任务

  2. 使用懒惰法则

6、ListView和 Bitmap优化

针对ListView优化,主要是合理使用ViewHolder。创建一个内部类ViewHolder,里面的成员变量和view中所包含的组件个数、类型相同,在convertview为null的时候,把findviewbyId找到的控件赋给ViewHolder中对应的变量,就相当于先把它们装进一个容器,下次要用的时候,直接从容器中获取。

现在我们现在一般使用RecyclerView,自带这个优化,不过还是要理解一下原理的好。 然后可以对接受来的数据进行分段或者分页加载,也可以优化性能。

对于Bitmap,这个我们使用的就比较多了,很容易出现OOM的问题,图片内存的问题可以看一下我之前写的这篇文章一张图片占用多少内存

Bitmap的优化套路很简单,粗暴,就是让压缩。 三种压缩方式: 1.对图片质量进行压缩 2.对图片尺寸进行压缩 3.使用libjpeg.so库进行压缩

对图片质量进行压缩

public static Bitmap compressImage(Bitmap bitmap){

ByteArrayOutputStream baos = new ByteArrayOutputStream();

//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

int options = 100;

//循环判断如果压缩后图片是否大于50kb,大于继续压缩

while ( baos.toByteArray().length / 1024>50) {

//清空baos

baos.reset();

bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);

options -= 10;//每次都减少10

}

//把压缩后的数据baos存放到ByteArrayInputStream中

ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());

//把ByteArrayInputStream数据生成图片

Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);

return newBitmap;

}

对图片尺寸进行压缩

/**

  • 按图片尺寸压缩 参数是bitmap

  • @param bitmap

  • @param pixelW

  • @param pixelH

  • @return

*/

public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {

ByteArrayOutputStream os = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);

if( os.toByteArray().length / 1024>512) {//判断如果图片大于0.5M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出

os.reset();

bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中

}

ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

options.inPreferredConfig = Bitmap.Config.RGB_565;

BitmapFactory.decodeStream(is, null, options);

options.inJustDecodeBounds = false;

options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelW : pixelH ,pixelW * pixelH );

is = new ByteArrayInputStream(os.toByteArray());

Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);

return newBitmap;

}

/**

  • 动态计算出图片的inSampleSize

  • @param options

  • @param minSideLength

  • @param maxNumOfPixels

  • @return

*/

public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {

int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);

int roundedSize;

if (initialSize <= 8) {

roundedSize = 1;

while (roundedSize < initialSize) {

roundedSize <<= 1;

}

} else {

roundedSize = (initialSize + 7) / 8 * 8;

}

return roundedSize;

}

private static int computeInitialSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {

double w = options.outWidth;

double h = options.outHeight;

int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));

int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));

if (upperBound < lowerBound) {

return lowerBound;

}

if ((maxNumOfPixels == -1) && (minSideLength == -1)) {

return 1;

} else if (minSideLength == -1) {

return lowerBound;

} else {

return upperBound;

}

}

使用libjpeg.so库进行压缩 可以参考这篇Android性能优化系列之Bitmap图片优化: https://blog.csdn.net/u012124438/article/details/66087785)

优化7.0
  1. ListView使用ViewHolder,分段,分页加载

  2. 压缩Bitmap

8、响应速度优化

影响响应速度的主要因素是主线程有耗时操作,影响了响应速度。所以响应速度优化的核心思想是避免在主线程中做耗时操作,把耗时操作异步处理。

9、线程优化

线程优化的思想是采用线程池,避免在程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了现场的创建和销毁所带来的性能开销,同时线程池还能有效地控制线程池的最大并发数,避免大量的线程因互相抢占系统资源从而导致阻塞现象发生。

《Android开发艺术探索》对线程池的讲解很详细,不熟悉线程池的可以去了解一下。

  • 优点: 1、减少在创建和销毁线程上所花的时间以及系统资源的开销。 2、如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。

  • 需要注意的是: 1、如果线程池中的数量为达到核心线程的数量,则直接会启动一个核心线程来执行任务。 2、如果线程池中的数量已经达到或超过核心线程的数量,则任务会被插入到任务队列中标等待执行。 3、如果(2)中的任务无法插入到任务队列中,由于任务队列已满,这时候如果线程数量未达到线程池规定最大值,则会启动一个非核心线程来执行任务。 4、如果(3)中线程数量已经达到线程池最大值,则会拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的rejectedExecution方法通知调用者。

10、微优化

这些微优化可以在组合时提高整体应用程序性能,但这些更改不太可能导致显着的性能影响。选择正确的算法和数据结构应始终是我们的首要任务,以提高代码效率。

  • 编写高效代码有两个基本规则: 1、不要做你不需要做的工作 2、如果可以避免,请不要分配内存

1、避免创建不必要的对象 对象创建永远不是免费的,虽然每一个的代价不是很大,但是总归是代价的不是吗?能不创建何必要浪费资源呢?

2、首选静态(这里说的是特定情景) 如果您不需要访问对象的字段,请使您的方法保持静态。调用速度将提高约15%-20%。这也是很好的做法,因为你可以从方法签名中看出,调用方法不能改变对象的状态

3、对常量使用static final 此优化仅适用于基本类型和String常量,而不适用于 任意引用类型。尽管如此,static final尽可能声明常量是一种好习惯。

4、使用增强的for循环语法 增强for循环(for-each)可用于实现Iterable接口和数组的集合。对于集合,分配一个迭代器来对hasNext()和进行接口调用next()。使用一个 ArrayList,手写计数循环快约3倍,但对于其他集合,增强的for循环语法将完全等效于显式迭代器用法。

5、避免使用浮点数 根据经验,浮点数比Android设备上的整数慢约2倍

结尾

本文篇幅有限,性能优化的方面很多,每一项深入下去,不写个几十万字是结束不了,所以很多都是浅尝辄止,希望可以抛砖引玉,用我的拙劣的文章,给大家一些帮助。性能优化需要走的路还很远,希望能和各位同学一同前行,一起进步。

参考: Android APP 性能优化的一些思考 https://www.cnblogs.com/cr330326/p/8011523.html

谷歌官方 http://developer.android.com/topic/performance/

Android性能优化系列之Bitmap图片优化 https://blog.csdn.net/u012124438/article/details/66087785

Android 内存泄漏总结 https://yq.aliyun.com/articles/3009

文末

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究

**对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。**整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。(以下是一小部分,获取更多其他精讲进阶架构视频资料可以简信我【学习】获取免费领取方式)

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

以下是今天给大家分享的一些独家干货:

Android学习PDF+架构视频+面试文档+源码笔记

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。

技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
1712824301679)]
[外链图片转存中…(img-vF6GqoC2-1712824301679)]
[外链图片转存中…(img-jKdiZ48q-1712824301679)]
[外链图片转存中…(img-6MKFTQg2-1712824301680)]
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-JinV1Ik0-1712824301680)]

最后

都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。

技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

[外链图片转存中…(img-RznQAk5y-1712824301681)]

[外链图片转存中…(img-FRvy3Mbd-1712824301681)]

[外链图片转存中…(img-isvls9wc-1712824301681)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。祝大家2021年万事大吉。

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-qNTWYKDM-1712824301682)]

  • 30
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值