浅谈Android性能优化之内存优化

1、Android内存管理机制

1.1 Java内存分配模型

先上一张JVM将内存划分区域的图

程序计数器:存储当前线程执行目标方法执行到第几行。

栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应一个被调用的方法。栈帧包括局部标量表,
操作数栈。

本地方法栈:本地方法栈主要是为执行本地方法服务的。而Java栈是为执行Java方法服务的。
方法区:该区域被线程共享。主要存储每个类的信息(类名,方法信息,字段信息等)、静态变量,常量,以及编译器编译后的代码等。

堆:Java中的堆是被线程共享的,且JVM中只有一个堆内存,主要存储对象本身及数组

1.2 Dalvik和ART介绍

Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。它可以支持已转换为.dex格式的Java应用程序的运行,.dex格式是专门为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统,Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机实例,并且每一个Dalvik应用做为独立的Linux进程执行,独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

ART:ART表示Android Runtime,Dalvik是依靠一个just-In -Time编译器去解释字节码,运行时编译后的应用都需要通过一个解释器在用户的设备上运行,这一机制并不是特别高效,但是能让应用更容易在不同的硬件和架构上运行。ART则是完全改变了这种做法,在安装应用的时候就预编译字节码到机器语言,这一机制叫预编译。在移除解释代码这一过程,应用程序执行将更有效率,启动速度更快。

ART优点:

1.系统性能更高

2.应用启动速度,运行更快,体验更好,触感反馈更加及时。

3.更长的电池续航能力

4.支持更低的硬件

ART缺点:

1.储存空间占用更大。

2.应用安装时间更长。

Dalvik与ART区别

1.Dalvik每次都要编译在运行,art只会安装时启动编译

2.art占用的空间比Dalvik要大,就是用空间换时间

3.art减少编译,减少CPU使用频率,使用明显改善电池续航

4.art启动,运行更快,体验更好,触感反馈更及时。

1.3 为什么要进行内存优化

1.减少oom,提高应用的稳定性
2.减少卡顿,体验更好
3.减少内存占用,应用存活率更高
4.提前处理掉一些异常的隐患

2、Java内存回收算法

2.1判断Java中对象是否存活的算法

2.1.1 引用计数算法

堆内存的每个对象都有一个引用计数器,当对象被引用的时候,计数器+1,当引用失效时计数器-1,当计数器的值为0时,说明该对象没有被引用,就会被认为是垃圾对象,系统将会将其回收内存重新分配。

优点:引用计数器执行简单,判定效率高。

缺点:对于循环引用的对象难以判断出来,同时引用计数器增加了程序执行的开销,在jdk1.1后,就不在使用了。

2.1.1 根搜索法

GC Roots的对象做为起点,然后向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则该对象不可达,也就是说该对象为为垃圾对象,可以被回收。
在Java中,可以做为GC Roots的对象包括一下四种:

1.虚拟机栈中引用的对象

2.方法区中的类静态属性引用的对象

3.方法区中常量引用的对象

4.本地方法栈中JNI的引用对象

2.2 JVM垃圾回收算法

2.2.1 标记清除法

最基础的垃圾收集算法,算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象。

缺点:效率低,其次会产生大量的不连续的内存碎片,导致提前触发另一次垃圾收集动作。

2.2.2 复制回收算法

复制回收算法是将可用内存按容量分成大小相等的两块,每次只使用其中的一块,当这块内存使用完了,就将存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉,这样使得每都次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况。

缺点:可使用内存降为原来的一半。

2.2.3 标记整理法

标记-整理算法在标记-清除算法的基础上做了改进,标记阶段将可回收的对象标记出来,标记完成后不是直接对可回收的对象进行清理,而是让所有存活的对象都向一端移动,在移动的过程中清理掉可回收的对象。

优点:相比于标记清除法来说,标记整理法不会大量产生不连续内存碎片问题。

缺点:如果是在对象存活率较高的情况下会执行较多的复制操作,效率将会降低很多,而在存活率较低的情况下,效率会大大提高。

2.2.4 分代收集回收算法

当前商业虚拟机都是采用的是分代收集算法,根据对象存活的周期不同将内存划分为几块,一般是将java堆分为年轻代,老年代和永久代。然后根据各个年代的特点来采取不同收集算法,年轻代存活率较低,采用复制回收算法,老年代对象存活率较高,采用标记清除法或者是标记整理法来进行回收。

3、内存问题表现形式

3.1 内存抖动

内存波动图呈锯齿状,gc频繁导致卡顿。

3.2 内存泄漏

内存泄露简单来说就是系统分配出去的内存由于某种原因导致没法释放,内存会越来越小,最终导致oom。

3.3 内存溢出

即OOM,OOM时会导致程序异常。Android设备出厂以后,java虚拟机对单个应用的最大内存分配就确定下来了,超出这个值就会OOM。

4、内存优化常用工具

4.1 Memory Profiler

Memory Profiler是Android studio自带的工具,实时图表形式展示应用内存使用的情况,可以用来识别内存泄露,抖动等
注意:如果在控制台中没有找到Profiler,可View -----> Tool Windows ---> Profiler 进行打开

优点:方便直观,便于线下使用

4.2 Memory Analyzer(MAT)

1、强大的java heap分析工具,查找内存泄露及内存占用

2、生成整体报告,便于分析问题

3、可以在线下深入使用

MAT使用:

MAT下载地址:https://www.eclipse.org/mat/downloads.php

获取hprof文件

导出来的Dump是没法直接使用mat打开的,Android SDK自带了一个转换工具在SDK的platform-tools下,其中转换语句为:

cd D:\aa\sdk\platform-tools

hprof-conv aaa.hprof  bbb.hprof

注:aaa.hprof表示从profiler中导出来的dump文件,bbb.hprof 表示转化出来的dump文件

使用mat打开转化出来的dump

MAT视图

在MAT窗口上,OverView是一个总体概览,显示总体的内存消耗情况和疑似问题。

1、Histogram:列出内存中的所有实例对象和个数以及大小,在顶部regex区域支撑正则表达式查找

2、Dominator Tree:列出最大的对象及其依赖存活的Object,相比于Histogram,能更方便的看出引用关系。

3、Top Consumers:通过图像列出最大的Object

4、Leak Suspects:通过MAT自动分析内存泄露的原因和泄露的一份总体报告

其中分析内存情况,我们基本用到的就是Histogram和Dominator Tree

Class Name:类名。

Objects:对象实例个数。

Shallow Heap:对象自身占用内存大小,不包括它引用的对象

Retained Heap:是当前对象大小和直接或者间接引用到的对象大小总和,包括递归释放的。

查找内存泄露方式

步骤一:在Regex通过包名进行匹配,当然也可以通过其他方式进行匹配

步骤二:右键选中怀疑对象,List objects --> with incoming references

注 with outgoing references 他引用了那些对象

with incoming references 那些对象引用了他

步骤三:选择当前的一个 Path to GC Roots/Merge to GC Roots 的 exclude All 弱软虚引用。

图标的左下角出现这个,则表示出现了内存泄露。然后回调代码中分析即可。

4.3 LeakCanary

使用

implementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'

application中

public class App extends Application {

    private RefWatcher mRefWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
     mRefWatcher = LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        App application = (App) context.getApplicationContext();
        return application.mRefWatcher;
    }
 }

在activity或者fragment中的onDestory()方法调用

RefWatcher refWatcher = App.getRefWatcher(getActivity());
refWatcher.watch(this);

原理

主要是通过WeakReference + ReferenceQueue来判断对象是否被系统GC回收,WeakReference创建时,传入一个ReferenceQueue对象,当WeakReference引用的对象生命周期结束后,会被添加到ReferenceQueue中,当GC过后,对象一直没有被添加进入到ReferenceQueue,可能就会存在内存泄露,再次触发GC,二次确认。

5、常见的内存泄露

1、资源性对象未关闭

对于资源性对象不再使用时,应该立即调用它的close()函数,将其关闭,然后再置为null。例如Bitmap等资源未关闭会造成内存泄漏,此时我们应该在Activity销毁时及时关闭。

2、注册对象未注销

例如BraodcastReceiver、EventBus未注销造成的内存泄漏,我们应该在Activity销毁时及时注销。

3、类的静态变量持有大数据对象

尽量避免使用静态变量存储数据,特别是大数据对象,建议使用数据库存储。

4、单例造成的内存泄漏

优先使用Application的Context,如需使用Activity的Context,可以在传入Context时使用弱引用进行封装,然后,在使用到的地方从弱引用中获取Context,如果获取不到,则直接return即可。

5、非静态内部类的静态实例

该实例的生命周期和应用一样长,这就导致该静态实例一直持有该Activity的引用,Activity的内存资源不能正常回收。此时,我们可以将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,尽量使用Application Context,如果需要使用Activity Context,就记得用完后置空让GC可以回收,否则还是会内存泄漏。

6、Handler临时性内存泄漏

Message发出之后存储在MessageQueue中,在Message中存在一个target,它是Handler的一个引用,Message在Queue中存在的时间过长,就会导致Handler无法被回收。如果Handler是非静态的,则会导致Activity或者Service不会被回收。并且消息队列是在一个Looper线程中不断地轮询处理消息,当这个Activity退出时,消息队列中还有未处理的消息或者正在处理的消息,并且消息队列中的Message持有Handler实例的引用,Handler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。解决方案如下所示:

  • 1、使用一个静态Handler内部类,然后对Handler持有的对象(一般是Activity)使用弱引用,这样在回收时,也可以回收Handler持有的对象。
  • 2、在Activity的Destroy或者Stop时,应该移除消息队列中的消息,避免Looper线程的消息队列中有待处理的消息需要处理。需要注意的是,AsyncTask内部也是Handler机制,同样存在内存泄漏风险,但其一般是临时性的。对于类似AsyncTask或是线程造成的内存泄漏,我们也可以将AsyncTask和Runnable类独立出来或者使用静态内部类。

7、容器中的对象没清理造成的内存泄漏

在退出程序之前,将集合里的东西clear,然后置为null,再退出程序

8、WebView

WebView都存在内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。我们可以为WebView开启一个独立的进程,使用AIDL与应用的主进程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,达到正常释放内存的目的。

9、使用ListView时造成的内存泄漏

在构造Adapter时,使用缓存的convertView。

6、优化内存空间的方式

6.1、java对象的引用

强引用:我们平时开发写的代码,基本百分之九十九的都是强引用。

软引用:如果一个对象具有软引用,那么当内存不足时,就会回收它。

弱引用:GC时,只要发现有弱引用,那么就会回收它,当然,有可能存在GC多次才发现

虚引用:虚引用必须要和引用队列关联起来使用。任何时候都有可能被垃圾回收器回收。一般可以用来判断GC的频率,GC频率过高,那么说明内存出了问题。同时也可以监听某个重要的对象是否被回收。

所以,在平时我们编写代码的时候,适当的使用软引用,弱引用,对我们的内存优化也能起到重要的作用。

6.2、减少不必要的内存开销

1、AutoBoxing

自动装箱的核心是吧基础数据类型转换成对应的包装类,比如int 类型只是占用4字节,但是Integer对象占用16字节。

2、内存复用

资源复用:通用的字符串,颜色定义,简单页面布局的复用

视图复用:进行布局复用

3、使用优化过的数据类型

如 SparseArray、SparseBooleanArray、LongSparseArray,使用这些API可以让我们的程序更加高效。HashMap 工具类会相对比较 低效,因为它 需要为每一个键值对都提供一个对象入口,而 SparseArray 就 避免 掉了 基本数据类型转换成对象数据类型的时间。

4、项目中少用枚举

枚举占用内存是常量三倍。

5、在应用可以内存过低时主动释放内存

在application中的 onTrimMemory/onLowMemory,内存紧张时会回调该方法,我们可以在这个方法中释放掉图片缓存,静态缓存来避免被kill。

6、避免创建一些不必要的对象

如在字符串拼接时不要用“+”来进行拼接,而是使用StringBuffer,StringBuilder来替代。因为String 内部是被final修饰的,不可继承,使用+进行拼接是会产生一个新的对象,而占用内存。

7、尽量不要在一些循环的地方创建对象。

如自定义的时候在onDraw()方法。

7、优雅的检测大图

项目中会经常遇到这样的情况,我们的布局中,控件的宽高可能只是50 * 50 但是从服务器给过来的图片或者是UI给过来的图片往往会大很多,而如果图片在资源文件下还好,可以直接查看宽高,但是如果从服务器上获取到的呢,这是我们经常会忽略的。而图片过大,占用的内存就更多,这是没有必要的。那么我们怎么检测出服务器给过来的图片过大的呢?

7.1、继承ImageView 重新实现onDraw()

这种方法我们可以重新测量图片的宽高,超过一定的范围,我们就可以输出警告。但是这种方法对代码侵入性很强。如果是有新同学加入,容易造成代码混乱。

7.2、ARTHook

Hook的意思是钩子,也就是在消息过去之前可以把消息勾住,不让其传递,能够针对不同的消息或者api在执行之前,先执行我们自己的操作。

这里推荐使用Epic 框架:https://github.com/tiann/epic

添加依赖

implementation 'me.weishu:epic:0.3.6'

创建一个ImageHook类

package com.optimize.performance.memory;

import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;

import com.optimize.performance.utils.LogUtils;
import com.taobao.android.dexposed.XC_MethodHook;

public class ImageHook extends XC_MethodHook {

    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        // 实现我们的逻辑
        ImageView imageView = (ImageView) param.thisObject;
        checkBitmap(imageView,((ImageView) param.thisObject).getDrawable());
    }

    private static void checkBitmap(Object thiz, Drawable drawable) {
        if (drawable instanceof BitmapDrawable && thiz instanceof View) {
            final Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();
            if (bitmap != null) {
                final View view = (View) thiz;
                int width = view.getWidth();
                int height = view.getHeight();
                if (width > 0 && height > 0) {
                    // 图标宽高都大于view带下的2倍以上,则警告
                    if (bitmap.getWidth() >= (width << 1)
                            && bitmap.getHeight() >= (height << 1)) {
                        warn(bitmap.getWidth(), bitmap.getHeight(), width, height, new RuntimeException("Bitmap size too large"));
                    }
                } else {
                    final Throwable stackTrace = new RuntimeException();
                    view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                        @Override
                        public boolean onPreDraw() {
                            int w = view.getWidth();
                            int h = view.getHeight();
                            if (w > 0 && h > 0) {
                                if (bitmap.getWidth() >= (w << 1)
                                        && bitmap.getHeight() >= (h << 1)) {
                                    warn(bitmap.getWidth(), bitmap.getHeight(), w, h, stackTrace);
                                }
                                view.getViewTreeObserver().removeOnPreDrawListener(this);
                            }
                            return true;
                        }
                    });
                }
            }
        }
    }

    private static void warn(int bitmapWidth, int bitmapHeight, int viewWidth, int viewHeight, Throwable t) {
        String warnInfo = new StringBuilder("Bitmap size too large: ")
                .append("\n real size: (").append(bitmapWidth).append(',').append(bitmapHeight).append(')')
                .append("\n desired size: (").append(viewWidth).append(',').append(viewHeight).append(')')
                .append("\n call stack trace: \n").append(Log.getStackTraceString(t)).append('\n')
                .toString();

        LogUtils.i(warnInfo);
    }

}

在application中

DexposedBridge.hookAllConstructors(ImageView.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                DexposedBridge.findAndHookMethod(ImageView.class, "setImageBitmap", Bitmap.class, new ImageHook());
            }
        });

这样在开发者调用setImageBitmap 来设置图片的时候,都会进行对图片的宽高进行比如,如果超出一定的范围则进行提示。

以上就是浅谈Android性能优化之内存优化的详细内容,更多关于Android性能优化之内存优化的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android 中对于图片的内存优化方法

    1. 对图片本身进行操作 尽量不要使用 setImageBitmap.setImageResource. BitmapFactory.decodeResource 来设置一张大图,因为这些方法在完成 decode 后,最终都是通过 Java 层的 createBitmap 来完成的,需要消耗更多内存.因此,改用先通过 BitmapFactory.decodeStream 方法,创建出一个 bitmap,再将其设为 ImageView 的 source,decodeStream 最大的秘密在于其直

  • 浅谈Android应用的内存优化及Handler的内存泄漏问题

    一.Android内存基础 物理内存与进程内存 物理内存即移动设备上的RAM,当启动一个Android程序时,会启动一个Dalvik VM进程,系统会给它分配固定的内存空间(16M,32M不定),这块内存空间会映射到RAM上某个区域.然后这个Android程序就会运行在这块空间上.Java里会将这块空间分成Stack栈内存和Heap堆内存.stack里存放对象的引用,heap里存放实际对象数据. 在程序运行中会创建对象,如果未合理管理内存,比如不及时回收无效空间就会造成内存泄露,严重的话可能导致

  • android内存优化之图片优化

    对图片本身进行操作.尽量不要使用setImageBitmap.setImageResource.BitmapFactory.decodeResource来设置一张大图,因为这些方法在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存.因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的source,decodeStream最大的秘密在于其直接调用JNI>>nativeDeco

  • 解析Android开发优化之:对Bitmap的内存优化详解

    1) 要及时回收Bitmap的内存 Bitmap类有一个方法recycle(),从方法名可以看出意思是回收.这里就有疑问了,Android系统有自己的垃圾回收机制,可以不定期的回收掉不使用的内存空间,当然也包括Bitmap的空间.那为什么还需要这个方法呢? Bitmap类的构造方法都是私有的,所以开发者不能直接new出一个Bitmap对象,只能通过BitmapFactory类的各种静态方法来实例化一个Bitmap.仔细查看BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通

  • Android Bitmap详解及Bitmap的内存优化

    Android Bitmap详解及Bitmap的内存优化 一.Bitmap: Bitmap是Android系统中的图像处理的最重要类之一.用它可以获取图像文件信息,进行图像剪切.旋转.缩放等操作,并可以指定格式保存图像文件. 常用方法: public void recycle() // 回收位图占用的内存空间,把位图标记为Dead public final boolean isRecycled() //判断位图内存是否已释放 public final int getWidth() //获取位图的

  • 详解Android的内存优化--LruCache

    概念: LruCache 什么是LruCache? LruCache实现原理是什么? 这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 LruCache 的实现原理:Lru的全称是Least Recently Used ,近期最少使用的!所以我们可以推断出 LruCache 的实现原理:把近期最少使用的数据从缓存中移除,保留使用最频繁的数据,那具体代码要怎么实现呢,我们进入到源码中看看. LruCache源码分析 public class LruCache<

  • Android内存优化杂谈

    Android内存优化是我们性能优化工作中比较重要的一环,这里其实主要包括两方面的工作: 1.优化RAM,即降低运行时内存.这里的目的是防止程序发生OOM异常,以及降低程序由于内存过大被LMK机制杀死的概率.另一方面,不合理的内存使用会使GC大大增多,从而导致程序变卡. 2.优化ROM,即降低程序占ROM的体积.这里主要是为了降低程序占用的空间,防止由于ROM空间不足导致程序无法安装. 本文的着重点为第一点,总结概述降低应用运行内存的技巧.在这里我们不再细述PSS.USS等概念与Android应

  • 总结Android App内存优化之图片优化

    前言 在Android设备内存动不动就上G的情况下,的确没有必要去太在意APP对Android系统内存的消耗,但在实际工作中我做的是教育类的小学APP,APP中的按钮.背景.动画变换基本上全是图片,在2K屏上(分辨率2048*1536)一张背景图片就会占用内存12M,来回切换几次内存占用就会增涨到上百兆,为了在不影响APP的视觉效果的前提下,有必要通过各种手段来降低APP对内存的消耗. 通过DDMS的APP内存占用查看工具分析发现,APP中占用内存最多的是图片,每个Activity中图片占用内存

  • 浅谈Android性能优化之内存优化

    1.Android内存管理机制 1.1 Java内存分配模型 先上一张JVM将内存划分区域的图 程序计数器:存储当前线程执行目标方法执行到第几行. 栈内存:Java栈中存放的是一个个栈帧,每个栈帧对应一个被调用的方法.栈帧包括局部标量表, 操作数栈. 本地方法栈:本地方法栈主要是为执行本地方法服务的.而Java栈是为执行Java方法服务的. 方法区:该区域被线程共享.主要存储每个类的信息(类名,方法信息,字段信息等).静态变量,常量,以及编译器编译后的代码等. 堆:Java中的堆是被线程共享的,

  • 浅谈android性能优化之启动过程(冷启动和热启动)

    本文介绍了浅谈android性能优化之启动过程(冷启动和热启动) ,分享给大家,具体如下: 一.应用的启动方式 通常来说,启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动. 2.热启动:当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热

  • 浅谈webpack性能榨汁机(打包速度优化)

    最近对项目的本地开发环境进行了打包速度优化,原有项目,网上能搜到的优化方案基本都加了,在16年低配mac pro 上打包时间为25秒多,但我发现细节做一些调整可能大大降低打包时间,最终优化到7秒多 dll 原有项目是线上和本地公用一套dll配置,因为antd这类ui库需要按需加载所以不能放到dll中,这时可以单独写一个dll配置,将所有第三方库添加到dll中. 这时因为.babelrc中添加了babel-plugin-import插件会导致优化不生效,所以需要对开发环境单独配置babel opt

  • 浅谈PHP性能优化之php.ini配置

    内存 默认设置 memory_limit = 128M 单个进程可使用的内存最大值,这个值的设定可以从以下几点考虑: 应用的类型.如果是内存集中型应用,可增加该值: 单个 PHP 进程平均消耗的内存,该值可通过多次运行同一个脚本来计算平均值: 能负担多少个 php-fpm 进程:该值等于分配的总内存除以单个 PHP 进程平均消耗的内存: 文件上传 默认设置 file_uploads = On max_file_uploads = 20 upload_max_filesize = 2M max_e

  • 浅谈vue首屏加载优化

    本文介绍了浅谈vue首屏加载优化,分享给大家,具体如下: 库使用情况 vue vue-router axios muse-ui material-icons vue-baidu-map 未优化前 首先我们在正常情况下build 优化 1. 按需加载 当前流行的UI框架如iview,muse-ui,Element UI都支持按需加载,只需稍微改动一下代码. 修改前: import MuseUI from 'muse-ui' import 'muse-ui/dist/muse-ui.css' imp

  • 浅谈Android Studio3.6 更新功能

    前言 下载google CodeLab的程序时,提示要更新3.6版本才能运行程序,于是更新了一下,看看有什么新功能. 界面设计工具 这次更新了一些设计工具,比如Layout Editor 和 Resource Manager. 现在,在XML或设计工具的颜色选择器中,Android Studio会在您的应用程序中填充颜色资源,以便您快速选择和替换颜色资源值. 拆分视图并放大设计编辑器 设计编辑器(例如,布局编辑器和导航编辑器)现在提供一个拆分视图,使您可以同时查看UI的"设计"视图和&

  • 浅谈C++性能榨汁机之伪共享

    前言 在多核并发编程中,如果将互斥锁的争用比作"性能杀手"的话,那么伪共享则相当于"性能刺客"."杀手"与"刺客"的区别在于杀手是可见的,遇到杀手时我们可以选择战斗.逃跑.绕路.求饶等多种手段去应付,但"刺客"却不同,"刺客"永远隐藏在暗处,伺机给你致命一击,防不胜防.具体到我们的并发编程中,遇到锁争用影响并发性能情况时,我们可以采取多种措施(如缩短临界区,原子操作等等)去提高程序性能,

  • 浅谈Android插件化

    目录 一.认识插件化 1.1 插件化起源 1.2 插件化优点 1.3 与组件化的区别 二.插件化的技术难点 三.ClassLoader Injection 3.1 java 中的 ClassLoader 3.2 android 中的 ClassLoader 3.3 双亲委派机制 3.4 如何加载插件中的类 3.5 执行插件类的方法 四.Runtime Container 4.1 为什么没有注册的 Activity 不能和系统交互 4.2 运行时容器技术 4.3 字节码替换 五.Resource

  • 浅谈Android Studio 4.1 更新内容

    概览 Android Studio 4.1 目前已经发布,该版本共修复了2370 个 bug 以及 275 个 issue,主要包含如下新增功能: 设计 Material Design 组件库的更新 开发 Database Inspector 功能 直接在 Android Studio 中运行模拟器 Dagger 导航支持 使用 TensorFlow Lite 模型 构建与测试 Android 模拟器支持折叠屏 Apply Changes 更新 从 AAR 中导出 C/C++ 中的依赖 Nati

  • 浅谈Android手机的抢红包插件

    前语 最近,Android手机上的手机管家更新了新版本,提供了红包闹钟功能,只要有微信红包或者QQ红包,就会自动提醒.恰逢最近又在做UI自动化的工作,使用到UI Automator框架.几行代码,就可以让手机自动完成某些操作,很有意思,今天就来扒一扒这背后的原理. UI Automator 传统的手工测试,我们需要点击一些控件元素,来查看输出的结果是否符合预期.比如在登录界面,输入正确的用户名和密码,点击登录按钮后,就可以正常登录. 如果这些操作,每一次都需要手工执行的话,是需要大量的人力成本的

随机推荐