浅谈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性能优化之内存优化的资料请关注我们其它相关文章!

时间: 2021-06-06

Android Bitmap详解及Bitmap的内存优化

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

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

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

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

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

android内存优化之图片优化

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

Android内存优化杂谈

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

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

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

详解Android的内存优化--LruCache

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

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

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

Android开发实现从相册中选择照片功能详解

本文实例讲述了Android开发实现从相册中选择照片功能.分享给大家供大家参考,具体如下: 实际效果图: 代码实现: 1. 权限配置 2. 点击事件绑定 3. 相册访问 4. 根据路径设置图片 5. 其他方法 权限 首先,现在 mainfest.xml 文件中添加以下权限: <!--获取照片权限--> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <us

Android开发教程之调用摄像头功能的方法详解

本文实例讲述了Android调用摄像头功能的方法.分享给大家供大家参考,具体如下: 我们要调用摄像头的拍照功能,显然 第一步必须加入调用摄像头硬件的权限,拍完照后我们要将图片保存在SD卡中,必须加入SD卡读写权限,所以第一步,我们应该在Android清单文件中加入以下代码 摄像头权限: <uses-permission android:name="android.permission.CAMERA"/> SD卡读写权限: <uses-permission androi

Android开发中的几种网络请求方式详解

Android应用经常会和服务器端交互,这就需要手机客户端发送网络请求,下面介绍四种常用网络请求方式,我这边是通过Android单元测试来完成这四种方法的,还不清楚Android的单元测试的同学们请看Android开发技巧总结中的Android单元测试的步骤一文. Java.NET包中的HttpURLConnection类 Get方式: // Get方式请求 public static void requestByGet() throws Exception { String path = "h

Android开发之图形图像与动画(五)LayoutAnimationController详解

首先需要先介绍下LayoutAnimationController: * 1.LayoutAnimationController用于为一个layout里面的控件,或者是一个ViewGroup * 里面的控件设置动画效果(即整个布局) * 2.每一个控件都有相同的动画效果 * 3.这些控件的动画效果在不同的实现显示出来 * 4.LayoutAnimationController可以在xml文件当中设置,也可以在代码中进行设置 本文就针对两种实现LayoutAnimationController的方

Android开发笔记之:消息循环与Looper的详解

Understanding LooperLooper是用于给一个线程添加一个消息队列(MessageQueue),并且循环等待,当有消息时会唤起线程来处理消息的一个工具,直到线程结束为止.通常情况下不会用到Looper,因为对于Activity,Service等系统组件,Frameworks已经为我们初始化好了线程(俗称的UI线程或主线程),在其内含有一个Looper,和由Looper创建的消息队列,所以主线程会一直运行,处理用户事件,直到某些事件(BACK)退出.如果,我们需要新建一个线程,并

解决Fedora14下eclipse进行android开发,ibus提示没有输入窗口的方法详解

好不容易搭建好了开发环境,可是不管怎么按Ctr + space,ibus就是不弹出来.用鼠标点吧,上面提示没有输入窗口.真是操蛋!google了一圈也没有解决办法,我是第一个遇到这问题的人么??无奈下,干脆换输入法!将系统自带的ibus换成fcitx输入法,安装步骤为:首先切换到root1,yum install fcitx2, alternatives --config xinputrc会出来提示,会提示共有 4 个程序提供"xinputrc".选择    命令-----------

Android开发笔记之:ListView刷新顺序的问题详解

背景一个典型的ListView,每个Item显示一个TextView,代表一个Task,需要实现二个编辑方式:一个是用CheckBox来标识任务已经完成,另一个要实现的编辑是删除任务.对于完成的CheckBox就直接放在布局中就可,但对于删除不想使用ContextMenu来实现编辑,对于像iOS中那样的列表,它的删除都是通过对列表中每个项目的手势来触发.这个实现起来并不难,可以用一个ViewSwitcher,Checkbox和删除按扭是放入其中,让ViewSwitcher来控制显示哪一个,正常情

Android Drawable和Bitmap的转换实例详解

Android Drawable和Bitmap的转换实例详解 通常我们需要通过代码去设置图片,就需要设置图片Bitmap和Drawable的转换,下面整理了几种方式 一.Bitmap转Drawable Bitmap bm=xxx; //xxx根据你的情况获取 BitmapDrawable bd=new BitmapDrawable(bm);//因为BtimapDrawable是Drawable的子类,最终直接使用bd对象即可. 二. Drawable转Bitmap Drawable d=xxx;