<span style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.2;"><span style="font-size:32px;">1、</span></span><span style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; font-size: 2.25em; line-height: 1.2;">对Application类的理解</span>

其实说对什么的理解,就是考察你对这个东西会不会用,重点是有没有什么坑!


首先,Application在一个Dalvik虚拟机里面只会存在一个实例,所以你不要傻傻的去弄什么单例模式,来静态获取Application了,你把Application构造函数设置成privete都不可能实现(我年轻的时候就这么傻傻的试过,想着如果可以通过Singleton.getInstance()就能获取到Application对象,多爽呀~)。

那么为什么强调说是一个Dalvik虚拟机,而不是说一个App呢?

因为一个App有可能有多个Dalvik虚拟机,也就是传说中的多进程模式。在这种模式下,每一个Dalvik都会存在一个Application实例,他们之间没有关系,在A进程Application里面保存的数据不能在B进程的Application获取,因为他们根本不是一个对象,而且被隔离在了两个进程里面,所以这里强调是一个Dalvik虚拟机,而不是一个App。


其次,Application的实质是一个Context,它继承自ContextWrapper。

  android.content.Context
       ↳  android.content.ContextWrapper
        ↳ android.app.Application     

ContextWrapper是什么玩意?就是对Context的一个包装而已。

Application有两个子类,一个是MultiDexApplication,如果你遇到了"65535"问题,可以选择继承自他,完成多Dex打包配置的相关工作。

另外一个是在TDD(测试用例驱动)开发模式中使用Moke进行测试的时候用到的,可以来代替Application的Moke类MockApplication。


在应用启动的时候,会首先调用Application.attach(),当然,这个方法是隐藏的,开发者能接触到的第一个被调用的方法其实是Application.onCreate(),我们通常会在这个方法里面完成各种初始化,比如图片加载库、Http请求库的默认配置初始化操作等等。但是最好别在这个方法里面进行太多耗时操作,因为这会影响App的启动速度,所以对于不必要的操作可以使用异步操作、懒加载、延时加载等策略来减少对UI线程的影响。


除此之外,由于在Context中可以通过getApplicationContext()获取到Application对象,或者是通过Activity.getApplication()Service.getApplication()获取到Application,所以可以在Application保存全局的数据,供所有的Activity或者是Service使用。

PS:使用上面的三种方法获取到的都是同一个Application对象,getApplicationContext()是在Context的实现类ContextImpl中具体实现的,而getApplication()则是在Activity和Service中单独实现的,所以他们的作用域不同,但是获取到的都是同一个Application对象,因为一个Dalvik虚拟机只有一个Application对象。

但是这里存在着一个坑,那就是在低内存情况下,Application有可能被销毁,从而导致保存在Application里面的数据信息丢失,最后程序错乱,甚至是Crash。

所以当你想在Application保存数据的时候,请做好为空判断,或者是选择其他方式保存你的数据信息。


另外,在Application中存在着几个有用的方法,比如onLowMemory()和onTrimMemory(),在这两个方法里面,我们可以实现自己的内存回收逻辑,比如关闭数据库连接、移除图片内存缓存等操作来降低内存消耗,从而降低被系统回收的风险。


最后,就是要注意Application的生命周期,他和Dalvik虚拟机生命周期一样长,所以在进行单例或者是静态变量的初始化操作时,一定要用Application作为Context进行初始化,否则会造成内存泄露的发生。使用Dialog的时候一般使用Activity作为Context,但是也可以使用Application作为上下文,前提是你必须设置Window类型为TYPE_SYSTEM_DIALOG,并且申请相关权限。这个时候弹出的Dialog是属于整个Application的,弹出这个Dialog的Activity销毁时也不会回收Dialog,只有在Application销毁时,这个Dialog才会自动消失。

2、 对Activity生命周期的理解

首先,为什么Activity要有生命周期呢?我觉得这是移动设备的硬件特性和交互特性决定的。

首先从硬件上来说,移动设备不像是PC平台,它的CPU、内存、电量都受到了限制,特别是电量,你必须要保证开发出的App既能够满足用户需求,又不能很随意的去使用CPU进行高密度计算工作,所以如果用户临时不使用App的时候,要有一个机制来告诉我们开发的App:用户现在暂时不用了,不要做一些影响用户使用其他功能的事情。

除了电量之外,内存也是一个需要考虑的因素,Android基于Java开发,所以也有Java语言的一些特性,比如说在内存管理上,使用垃圾回收器来自动回收内存,虽然说减轻了开发者的工作量,但是GC发生的时间是不确定的,所以你不能够很好的保证用户使用App的时候,是否有足够的内存来分配对象。

除了硬件特性之外,手机这种设备的交互特性(比如屏幕大小、触摸操作),决定了你顶多只有一个App的界面会在前台,所以说,要有这样一个机制来告诉开发者:我们的App现在不在前台和用户交互了,优先级没有那么高,在后台不能做一些占用CPU和内存的事情影响到前台App的用户使用。

而解决这些问题的一个机制,就是Activity的生命周期。我们可以在onStop()发生的时候,暂停CPU密集运算、取消正在进行的网络连接、停止轮播图、退出死循环、去掉动画等一些列操作,来保证正在和用户交互的Activity拥有更多的硬件资源,来完成相关工作。

其次,从架构设计的角度来说,这也是一种很有效也很成熟的管理方式。其实不光Android上有生命周期,在网页开发中也会有生命周期,那么这种生命周期的方法有什么好处呢?

其实这种生命周期的方法,属于一种设计模式——模板设计模式。我认为,模板设计模式的实质,就是为了确定流程,而对每个流程的具体实现并不关心,你只要按照我这个顺序走就OK了,具体你想做什么,自己去实现吧!我们再来看一下Android,Activity是用来显示界面的,而显示界面的功能无非下面几个问题:

  1. 要显示那个界面?
  2. 界面里面的元素有什么行为?
  3. 界面被部分遮挡住怎么办?
  4. 界面看不见,被销毁怎么办?

我们在对应的看一下Activity的简单(无onRestart())生命周期:

  1. onCreate(),通过setContent()设置要显示哪个界面,并定义界面元素的行为(设置点击事件等)
  2. onStart(),一般很少处理
  3. onResume(),界面即将可见,在这之后用户就可以与界面交互了,在这里可以注册一些接收者
  4. onPause(),界面即将不可见或者是部分可见,在这里可以注销接受者
  5. onStop(),界面不可见,我们可以取消轮播图的滚动
  6. onDestory(),Activity即将销毁,我们要做好对象回收,避免内存泄露

所以说,我们只要按照每一个生命周期的方法,完成对应的工作,就可以让我们的Activity完整的显示出来。我们不需要考虑界面怎么画出来,点击事件怎么传递到按钮上去的,销毁回收工作是如何进行的,简简单单的完成生命周期的方法,就可以实现我们想要的效果。这就像是模板方法的典型应用场景——算法实现——所要达到的效果。我就想查找一个数,我定义一个方法给子类去重写,子类具体是二分法查找还是快速排序查找,我都不关心,你只要给我找出这个数,我就能按照流程完成相应的工作。对比一下你就会发现,这两者没有本质上的差别。

所以说,从手机设备的硬件特性、交互形式、架构设计来说,Activity的生命周期都是非常合适,而且非常必要的!

3、 android中常见的设计模式有哪些?

建造者模式

建造者模式最明显的标志就是Build类,而在Android中最常用的就是Dialog的构建,Notification的构建也是标准的建造者模式。

建造者模式很好理解,如果一个类的构造需要很多参数,而且这些参数并不都是必须的,那么这种情况下就比较适合Builder。

比如构建一个AlertDialog,标题、内容、取消按钮、确定按钮、中立按钮,你可能只需要单独设置几个属性即可;另外在我的OkHttpPlus项目中,构造一个Http请求也是这样的,有可能你只需要设置URL,有可能需要添加请求参数、Http Header等,这个时候建造者模式也是比较合适的。

单例模式

单例在Android开发中经常用到,但是表现形式可能不太一样。

以ActivityManager等系统服务来说,是通过静态代码块的形式实现单例,在首次加载类文件时,生成单例对象,然后保存在Cache中,之后的使用都是直接从Cache中获取。

class ContextImpl extends Context {

    static {
        registerService(ACTIVITY_SERVICE, new ServiceFetcher() {
                public Object createService(ContextImpl ctx) {
                    return new ActivityManager(ctx.getOuterContext(),       ctx.mMainThread.getHandler());
                }});
    }
}
<p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">当然,还有更加明显的例子,比如AccessibilityManager内部自己也保证了单例,使用getInstance获取单例对象。</p><pre style="font-size: 14px; box-sizing: border-box; overflow: auto; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; margin-top: 0px; margin-bottom: 16px; line-height: 1.45; padding: 16px; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-wrap: normal; color: rgb(51, 51, 51);"><code style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; padding: 0px; margin: 0px; background-color: transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; border: 0px; display: inline; line-height: inherit; word-wrap: normal;"> public static AccessibilityManager getInstance(Context context) {
        synchronized (sInstanceSync) {
            if (sInstance == null) {

               ......

                IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
                IAccessibilityManager service = iBinder == null
                        ? null : IAccessibilityManager.Stub.asInterface(iBinder);
                sInstance = new AccessibilityManager(context, service, userId);
            }
        }
        return sInstance;
    }
</code>

除此之外,还有一些伪单例,比如Application,默认情况下在一个进程中只存在一个实例,但是Application不能算是单例,因为它的构造方法未私有,你可以生成多个Application实例,但是没有用,你没有通过attach()绑定相关信息,没有上下文环境。

public Application() {
        super(null);
    }

单例的使用场景也很简单,就是一个App只需要存在一个类实例的情况,或者是类的初始化操作比较耗费资源的情况。在很多开源框架中,我们只需要一个对象即可完成工作,比如各种网络框架和图片加载库。

除此之外,因为单例的实现方式很多,比如懒汉式、饿汉式、静态内部类、双重锁检查、枚举等方式,所以要清楚每种实现方式的主要特点和使用场景。

原型模式

原型模式在开发中使用的并不多,但是在源码中却有所体现。

书中以Intent介绍了原型模式,是通过实现Cloneable接口来做的

public class Intent implements Parcelable, Cloneable {
    @Override
        public Object clone() {
         return new Intent(this);
        }
    }

其实这样来看的话,原型模式也比较好理解,就是你想更快的获取到一个相同属性的对象,那么就可以使用原型模式,比如这里就获取到了一个Intent对象,Intent里面的属性与被clone的相同,但是两者并无关联,可以单独使用。

除了实现Cloneable接口,你完全可以自己定义一个方法,来获取一个对象。我这里以PhoneLayoutInflater为例子介绍。

PhoneLayoutInflater是LayoutInflater的子类,如果我们在Activity中获取LayoutInflate的话,是通过下面方法

 @Override public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

可以看到,如果为null,就会调用cloneInContext(),这个方法在LayoutInflate是抽象方法,具体实现在PhoneLayoutInflater中

  public LayoutInflater cloneInContext(Context newContext) {
        return new PhoneLayoutInflater(this, newContext);
    }

可以看到,这也是一个原型模式,所以我们不要太纠结于形式,更重要的是理解这样做的好处。

除了在源码中可以看到原型模式,在开源框架中也可以看到,比如OkHttpClient中就存在着下面的方法

/** Returns a shallow copy of this OkHttpClient. */
  @Override public OkHttpClient clone() {
    return new OkHttpClient(this);
  }

可以看到,实现和前面的完全相同,也是new了一个对象返回,因为OkHttpClient的构造过程比较复杂,参数众多,所以用这种方式来直接生成新对象,成本很低,而且能保留之前对象的参数设置。

工厂方法模式

书中对于工厂方法模式的一个观点很新奇,就是Activity.onCreate()可以看做是工厂方法模式,来生成不同的View对象填充界面。

但是我对这个说法不太苟同,原因有两点:一是这种形式不太符合工厂方法,没有抽象,没有实现,不符合一般格式,也不是静态方法,不可看做是静态工厂方法;二是没有以生成对象为结果,即不是return view来生成对象,只是通过setContentView()来设置了属性而已。这就像是给一个Activity设置了背景颜色一样。当然,设计模式这东西一个人有一个人的看法。

静态工厂方法在Android中比较明显的例子应该就是BitmapFactory了,通过各种decodeXXX()就可以从不同渠道获得Bitmap对象,这里不再赘述。

策略模式

在书中策略模式讲得非常好,结合动画的插值器用法,我们可以很好的理解策略模式的形式和用法。

在我看来,策略模式就相当于一个影碟机,你往里面插什么碟子,就能放出什么电影。

同样,在OkHttpPlus的封装中,为了对网络返回值进行解析,我使用了策略模式。当然我写代码的时候还不知道策略模式,是写完了之后突然想到,这就是策略模式啊!

策略模式的精髓就在于,你传入一个类,后面的处理就能按照这个类的实现去做。以动画为例,设置不同的插值器对象,就可以得到不同的变化曲线;以返回值解析为例,传入什么样的解析器,就可以把二进制数据转换成什么格式的数据,比如String、Json、XML。

责任链模式

书中对于责任链模式选取的例子非常有代表性,那就是Android的触摸机制,这个看法让我从另一个维度去理解Android中的触摸事件传递。

我在这里提到这个模式,并不想说太多,只是简单的推荐你读一下这一章的内容,相信你也会有收获的。

观察者模式

Android中的观察者模式应该是用的非常频繁的一种模式了,对于这个模式的使用场景就一句话:你想在某个对象发生变化时,立刻收到通知。

书中介绍观察者模式使用的是ListView的Adapter为例子,我之前知道Adapter属于适配器模式,不知道这里还有观察者模式的身影,学到了。

Android里面的各种监听器,也都属于观察者模式,比如触摸、点击、按键等,ContentProvider和广播接收者也有观察者模式的身影,可以说是无处不在。

除此之外,现在很多基于观察者模式的第三方框架也是非常多,比如EventBus、RxJava等等,都是对观察者模式的深入使用,感兴趣的同学可以研究一下。

模板方法模式

这个模式我之前见的比较少,但是理解之后,就会发现这个模式很简单。

我觉得,模板方法模式的使用场景也是一句话:流程确定,具体实现细节由子类完成。

这里要关注一下『流程』这个关键字,随便拿一个抽象类,都符合"具体实现细节由子类完成"的要求,关键就在于是否有流程,有了流程,就叫模板方法模式,没有流程,就是抽象类的实现。

书中讲这个模式用的是AsyncTask,各个方法之间的执行符合流程,具体实现由我们完成,非常经典。

另外一个方面,Activity的生命周期方法可以看做是模板方法模式,各个生命周期方法都是有顺序的,具体实现我们可以重写,是不是和前面的要求很符合?关于这方面的理解,可以参考我的这篇文章:对Activity生命周期的理解

除了Android里面的模板方法模式,在其他开源项目中也存在着这个模式的运用。比如鸿洋的OkHttp-Utils项目,就是模板方法模式的典型实现。将一个Http请求的过程分割成几部分,比如获取URL,获取请求头,拼接请求信息等步骤,这几个步骤之前有先后顺序,就可以这样来做。

代理模式和装饰器模式

之所以把这两个放在一起说,是因为这两种模式很像,所以这里简单介绍下他们之间的区别,主要有两点。

  1. 装饰器模式关注于在一个对象上动态的添加方法,而代理模式关注于控制对对象的访问
  2. 代理模式,代理类可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。而当我们使用装饰器模式的时候,通常的做法是将原始对象作为一个参数传给装饰者的构造器

这两句话可能不太好理解,没关系,下面看个例子。

代理模式会持有被代理对象的实例,而这个实例一般是作为成员变量直接存在于代理类中的,即不需要额外的赋值。

比如说WindowManagerImpl就是一个代理类,虽然名字上看着不像,但是它代理的是WindowManagerGlobal对象。从下面的代码中就可以看出来。

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;

    ......

    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    @Override
    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public Display getDefaultDisplay() {
        return mDisplay;
    }
}

从上面的代码中可以看出,大部分WindowManagerImpl的方法都是通过WindowManagerGlobal实现的,而WindowManagerGlobal对象不需要额外的赋值,就存在于WindowManagerImpl中。另外,WindowManagerGlobal中其实有大量的方法,但是通过WindowManagerImpl代理之后,都没有暴露出来,对开发者是透明的。

我们再来看一下装饰器模式。装饰器模式的目的不在于控制访问,而是扩展功能,相比于继承基类来扩展功能,使用装饰器模式更加的灵活。

书中是以Context和它的包装类ContextWrapper讲解的,也非常的典型,我这里就不在赘述了,贴出一些代码来说明装饰器模式的形式。

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
}

但是还有一个问题,就是在ContextWrapper中,所有方法的实现都是通过mBase来实现的,形式上是对上号了,说好的扩展功能呢?

功能扩展其实是在ContextWrapper的子类ContextThemeWrapper里面。

在ContextWrapper里面,获取系统服务是直接通过mBase完成的

@Override
    public Object getSystemService(String name) {
        return mBase.getSystemService(name);
    }

但是在ContextThemeWrapper里面,对这个方法进行了重写,完成了功能扩展

@Override public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

当然,如果不存在功能扩展就不算是装饰器模式了吗?其实设计模式本来就是『仁者见仁,智者见智』的事情,只要你能理解这个意思就好。

外观模式

外观模式可能看到的比较少,但是其实不经意间你就用到了。

这里以我的一个开源项目KLog来说吧,在最开始写这个类的时候,就只有KLog这一个类,完成基本的Log打印功能,后来又添加了JSON解析、XML解析、Log信息存储等功能,这个时候一个类就不太合适了,于是我把JSON、XML、FILE操作相关的代码抽取到单独的类中,比如JSON打印的代码

public class JsonLog {

    public static void printJson(String tag, String msg, String headString) {

        String message;

        try {
            if (msg.startsWith("{")) {
                JSONObject jsonObject = new JSONObject(msg);
                message = jsonObject.toString(KLog.JSON_INDENT);
            } else if (msg.startsWith("[")) {
                JSONArray jsonArray = new JSONArray(msg);
                message = jsonArray.toString(KLog.JSON_INDENT);
            } else {
                message = msg;
            }
        } catch (JSONException e) {
            message = msg;
        }

        Util.printLine(tag, true);
        message = headString + KLog.LINE_SEPARATOR + message;
        String[] lines = message.split(KLog.LINE_SEPARATOR);
        for (String line : lines) {
            Log.d(tag, "║ " + line);
        }
        Util.printLine(tag, false);
    }
}

代码很简单,就一个方法,但是在使用的时候,无论打印哪种格式,都是这样使用的

//普通打印
 KLog.d(LOG_MSG);
 //JSON格式打印
 KLog.json(JSON);
 //XML格式打印
 KLog.xml(XML);

可以看到,虽然功能不同,但是都通过KLog这个类进行了封装,用户只知道用KLog这个类能完成所有需求即可,完全不需要知道代码实现是几个类完成的。

实际上,在KLog内部,是多个类共同完成打印功能的。

 private static void printLog(int type, String tagStr, Object... objects) {

        if (!IS_SHOW_LOG) {
            return;
        }

        String[] contents = wrapperContent(tagStr, objects);
        String tag = contents[0];
        String msg = contents[1];
        String headString = contents[2];

        switch (type) {
            case V:
            case D:
            case I:
            case W:
            case E:
            case A:
                BaseLog.printDefault(type, tag, headString + msg);
                break;
            case JSON:
                JsonLog.printJson(tag, msg, headString);
                break;
            case XML:
                XmlLog.printXml(tag, msg, headString);
                break;
        }
    }

但是通过外观模式,这些细节对用户隐藏了,这样如果以后我想更换JSON的解析方式,用户的代码不需要任何改动,这也是这个设计模式的优势所在。

4、 SharedPreference在使用过程中有什么注意点?c ommit()和apply()的区别
 
<span style="font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.2;"></span><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';">返回值</h2><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">apply()没有返回值,而commit()返回boolean表明修改是否提交成功。</p><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-操作效率" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/09.SharedPreference%E5%9C%A8%E4%BD%BF%E7%94%A8%E8%BF%87%E7%A8%8B%E4%B8%AD%E6%9C%89%E4%BB%80%E4%B9%88%E6%B3%A8%E6%84%8F%E7%82%B9%EF%BC%9F.md#操作效率" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>操作效率</h2><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">apply()是将修改数据原子提交到内存, 而后异步真正提交到硬件磁盘,。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">而commit()是同步的提交到硬件磁盘。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">因此,在多并发commit()的时候,会等待正在处理的commit保存到磁盘后再操作,从而降低了效率。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">而apply只是原子的提交到内容,后面有调用apply的函数的将会直接覆盖前面的内存数据,从一定程度上提高了效率。</p><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-建议" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/09.SharedPreference%E5%9C%A8%E4%BD%BF%E7%94%A8%E8%BF%87%E7%A8%8B%E4%B8%AD%E6%9C%89%E4%BB%80%E4%B9%88%E6%B3%A8%E6%84%8F%E7%82%B9%EF%BC%9F.md#建议" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>建议</h2><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">如果对提交的结果不关心的话,建议使用apply(),如果需要确保提交成功且有后续操作的话,还是需要用commit()。</p><h1 style="font-size: 2.25em; box-sizing: border-box; margin: 1em 0px 16px; line-height: 1.2; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-多进程表现" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/09.SharedPreference%E5%9C%A8%E4%BD%BF%E7%94%A8%E8%BF%87%E7%A8%8B%E4%B8%AD%E6%9C%89%E4%BB%80%E4%B9%88%E6%B3%A8%E6%84%8F%E7%82%B9%EF%BC%9F.md#多进程表现" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>多进程表现</h1><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">是为了回答这个<a target=_blank href="https://github.com/android-cn/android-discuss/issues/135" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none;">问题</a>。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">一句话:在多进程中,如果要交换数据,不要使用SharedPreference,因为在不同版本表现不稳定,推荐使用ContentProvider替代。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">在有的文章中,有提到在多进程中使用SharedPreference添加下面标志位就可以了。</p><pre style="font-size: 14px; box-sizing: border-box; overflow: auto; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; margin-top: 0px; margin-bottom: 16px; line-height: 1.45; padding: 16px; background-color: rgb(247, 247, 247); border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-wrap: normal; color: rgb(51, 51, 51);"><code style="box-sizing: border-box; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; padding: 0px; margin: 0px; background-color: transparent; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; word-break: normal; border: 0px; display: inline; line-height: inherit; word-wrap: normal;">MODE_MULTI_PROCESS
</code>

但是在官方文档中这样提到:

 This was the legacy (but undocumented) behavior in and
   before Gingerbread (Android 2.3) and this flag is implied when targetting such releases.  
   For applications targetting SDK versions greater than Android 2.3, 
   this flag must be explicitly set if desired.

@deprecated MODE_MULTI_PROCESS does not work reliably in some versions of Android, 
and furthermore does not provide any mechanism for reconciling concurrent modifications 
across processes.  Applications should not attempt to use it.  Instead, they should use an explicit 
cross-process data management approach such as ContentProvider

简单解释下,就是这个标志位在2.3之前是默认支持的,但是在2.3之后,如果需要多进程访问的情景,就需要显示的声明出来。

现在这个标志位被废弃了,因为在某些版本上表现不稳定。我们开发者不应该尝试去使用它,因为他没有提供任何并发机制,我们应该使用一种明确支持跨进程访问的机制,比如ContentProvider。

使用细节

  • ContextImpl中有一个静态的ArrayMap变量sSharedPrefs,无论有多少个ContextImpl对象实例,系统都共享这一个sSharedPrefs的Map,应用启动以后首次使用SharePreference时创建,系统结束时才可能会被垃圾回收器回收,所以如果我们一个App中频繁的使用不同文件名的SharedPreferences很多时这个Map就会很大,也即会占用移动设备宝贵的内存空间。所以我们应用中应该尽可能少的使用不同文件名的SharedPreferences,取而代之的是合并他们,减小内存使用
  • SharedPreferences在实例化时首先会从sdcard异步读文件,然后缓存在内存中;接下来的读操作都是内存缓存操作而不是文件操作。
  • 在SharedPreferences的Editor中如果用commit()方法提交数据,其过程是先把数据更新到内存,然后在当前线程中写文件操作,提交完成返回提交状态;如果用的是apply()方法提交数据,首先也是写到内存,接着在一个新线程中异步写文件,然后没有返回值。
  • 在写操作commit时有三级锁操作,效率很低,所以当我们一次有多个修改写操作时等都批量put完了再一次提交确认,这样可以提高效率。
5、 ART、JIT、AOT、Dalvik之间有什么关系?
 
<span style="font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.2;"></span><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';">JIT与Dalvik</h2><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">JIT是"Just In Time Compiler"的缩写,就是"即时编译技术",与Dalvik虚拟机相关。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">怎么理解这句话呢?这要从Android的一些特性说起。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">JIT是在2.2版本提出的,目的是为了提高Android的运行速度,一直存活到4.4版本,因为在4.4之后的ROM中,就不存在Dalvik虚拟机了。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">我们使用Java开发android,在编译打包APK文件时,会经过以下流程</p><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">Java编译器将应用中所有Java文件编译为class文件</li><li style="box-sizing: border-box;">dx工具将应用编译输出的类文件转换为Dalvik字节码,即dex文件</li></ul><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">之后经过签名、对齐等操作变为APK文件。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">Dalvik虚拟机可以看做是一个Java VM,他负责解释dex文件为机器码,如果我们不做处理的话,每次执行代码,都需要Dalvik将dex代码翻译为微处理器指令,然后交给系统处理,这样效率不高。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">为了解决这个问题,Google在2.2版本添加了JIT编译器,当App运行时,每当遇到一个新类,JIT编译器就会对这个类进行编译,经过编译后的代码,会被优化成相当精简的原生型指令码(即native code),这样在下次执行到相同逻辑的时候,速度就会更快。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">当然使用JIT也不一定加快执行速度,如果大部分代码的执行次数很少,那么编译花费的时间不一定少于执行dex的时间。Google当然也知道这一点,所以JIT不对所有dex代码进行编译,而是只编译执行次数较多的dex为本地机器码。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">有一点需要注意,那就是dex字节码翻译成本地机器码是发生在应用程序的运行过程中的,并且应用程序每一次重新运行的时候,都要做重做这个翻译工作,所以这个工作并不是一劳永逸,每次重新打开App,都需要JIT编译。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">另外,Dalvik虚拟机从Android一出生一直活到4.4版本,而JIT在Android刚发布的时候并不存在,在2.2之后才被添加到Dalvik中。</p><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-art与aot" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/10.ART%E3%80%81JIT%E3%80%81AOT%E3%80%81Dalvik%E4%B9%8B%E9%97%B4%E6%9C%89%E4%BB%80%E4%B9%88%E5%85%B3%E7%B3%BB%EF%BC%9F.md#art与aot" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>ART与AOT</h2><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">AOT是"Ahead Of Time"的缩写,指的就是ART(Anroid RunTime)这种运行方式。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">前面介绍过,JIT是运行时编译,这样可以对执行次数频繁的dex代码进行编译和优化,减少以后使用时的翻译时间,虽然可以加快Dalvik运行速度,但是还是有弊病,那就是将dex翻译为本地机器码也要占用时间,所以Google在4.4之后推出了ART,用来替换Dalvik。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">在4.4版本上,两种运行时环境共存,可以相互切换,但是在5.0+,Dalvik虚拟机则被彻底的丢弃,全部采用ART。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">ART的策略与Dalvik不同,在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。之后打开App的时候,不需要额外的翻译工作,直接使用本地机器码运行,因此运行速度提高。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">当然ART与Dalvik相比,还是有缺点的。</p><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">ART需要应用程序在安装时,就把程序代码转换成机器语言,所以这会消耗掉更多的存储空间,但消耗掉空间的增幅通常不会超过应用代码包大小的20%</li><li style="box-sizing: border-box;">由于有了一个转码的过程,所以应用安装时间难免会延长</li></ul><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">但是这些与更流畅的Android体验相比而言,不值一提。</p><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-总结" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/10.ART%E3%80%81JIT%E3%80%81AOT%E3%80%81Dalvik%E4%B9%8B%E9%97%B4%E6%9C%89%E4%BB%80%E4%B9%88%E5%85%B3%E7%B3%BB%EF%BC%9F.md#总结" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>总结</h2><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">通过前面背景知识的介绍,我终于可以更简单的介绍这四个名词之间的关系了:</p><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">JIT代表运行时编译策略,也可以理解成一种运行时编译器,是为了加快Dalvik虚拟机解释dex速度提出的一种技术方案,来缓存频繁使用的本地机器码</li><li style="box-sizing: border-box;">ART和Dalvik都算是一种Android运行时环境,或者叫做虚拟机,用来解释dex类型文件。但是ART是安装时解释,Dalvik是运行时解释</li><li style="box-sizing: border-box;">AOT可以理解为一种编译策略,即运行前编译,ART虚拟机的主要特征就是AOT</li></ul><span style="font-size:18px;">5、</span><span style="font-size:24px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.2;">缓存文件可以放在哪里?它们各自的特点是什么?</span>
<span style="font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.2;"></span><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">在Android手机里面,缓存的位置分为两类,一类是Internal Storage,即内部存储,另外一类是External Storage,即外部存储。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">比较老的手机,有一个手机内部存储,还有一个SD卡存储,就是分别对应这两种存储位置,因为以前的SD卡是可以扩展的,即可拆卸的,所以可以用是否可拆卸作为内外存储的分类标准。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">但是现在最新的设备,比如小米、锤子、华为等,都取消了可拆卸的SD卡,直接与机身焊接在一起,分为16G、32G版本,所以现在内外存储的分类不再以是否可拆卸作为标准,而是以下面的几方面作为新的标准:</p><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-内部存储" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#内部存储" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>内部存储</h2><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">总是可用的</li><li style="box-sizing: border-box;">这里的文件默认是只能被你的app所访问的。</li><li style="box-sizing: border-box;">当用户卸载你的app的时候,系统会把internal里面的相关文件都清除干净。</li><li style="box-sizing: border-box;">Internal是在你想确保不被用户与其他app所访问的最佳存储区域。</li></ul><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-外部存储" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#外部存储" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>外部存储</h2><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">并不总是可用的,因为用户可以选择把这部分作为USB存储模式,这样就不可以访问了。</li><li style="box-sizing: border-box;">是大家都可以访问的,因此保存到这里的文件是失去访问控制权限的。</li><li style="box-sizing: border-box;">当用户卸载你的app时,系统仅仅会删除external根目录(getExternalFilesDir())下的相关文件。</li><li style="box-sizing: border-box;">External是在你不需要严格的访问权限并且你希望这些文件能够被其他app所共享或者是允许用户通过电脑访问时的最佳存储区域。<br style="box-sizing: border-box;" /></li></ul><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-文件位置" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#文件位置" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>文件位置</h2><h3 style="font-size: 1.5em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.43; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-内部存储-1" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#内部存储-1" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1.2;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>内部存储</h3><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">getFileDir() 通过此方法可以获取到你的APP内部存储的文件,路径为/data/data/pacgage_name/files</li><li style="box-sizing: border-box;">getCacheDir() 通过此方法可以获取到你的APP内部存储的文件,路径为/data/data/package_name/cache</li><li style="box-sizing: border-box;">openFileOutput() 通过此方法,我们可以获取到一个输出流,输出流的保存路径是/data/data/package_name/files ,和getFileDir()的路径一致</li></ul><h3 style="font-size: 1.5em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.43; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-外部存储-1" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#外部存储-1" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1.2;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>外部存储</h3><h4 style="font-size: 1.25em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.4; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-私有存储" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#私有存储" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1.2;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>私有存储</h4><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">Context.getExternalCacheDir()</li><li style="box-sizing: border-box;">Context.getExternalFilesDir()</li></ul><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">创建的私有文件的地址是/sdcard/Android/date/package_name下面,Android文件夹是隐藏文件夹,用户无法操作。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">如果我们想缓存图片等比较耗空间的文件,推荐放在getExternalCacheDir()所在的文件下面,这个文件和getCacheDir()很像,都可以放缓存文件,在APP被卸载的时候,都会被系统删除,而且缓存的内容对其他APP是相对私有的。</p><h4 style="font-size: 1.25em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.4; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-公共存储" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#公共存储" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1.2;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>公共存储</h4><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">你的APP产生的文件不需要隐藏,即对用户是可见的,那么你可以把文件放在外部的公共存储文件下面。这个方法不是Context的方法,而是Environment的两个方法,第一个方法获取到的其实是外部存储的根目录,而第二个方法获取到得则是外部存储的公共目录。其实在访问权限上是没有区别的,不同点是getExternalStoragePublicDirectory()在运行的时候,会需要你带有一个特定的参数来指定这些public的文件类型,以便于与其他public文件进行分类。</p><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">Environment.getExternalStorageDirectory()<br style="box-sizing: border-box;" /></li><li style="box-sizing: border-box;">Environment.getExternalStoragePublicDirectory()<br style="box-sizing: border-box;" /></li></ul><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-表现" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#表现" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>表现</h2><h3 style="font-size: 1.5em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.43; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-内部存储-2" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#内部存储-2" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1.2;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>内部存储</h3><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">你的app的internal storage 目录是以你的app的包名作为标识存放在Android文件系统的特定目录下[data/data/com.example.xx]。 从技术上讲,如果你设置文件为可读的,那么其他app就可以读取你的internal文件。然而,其他app需要知道你的包名与文件名。若是你没有设置为可读或者可写,其他app是没有办法读写的。因此只要你使用MODE_PRIVATE ,那么这些文件就不可能被其他app所访问。</p><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">另外记住一点,内部存储在你的APP卸载的时候,会一块被删除,因此,我们可以在cache目录里面放置我们的图片缓存,而且cache与files的差别在于,如果手机的内部存储空间不够了,会自行选择cache目录进行删除,因此,不要把重要的文件放在cache文件里面,可以放置在files里面,因为这个文件只有在APP被卸载的时候才会被删除。还有要注意的一点是,如果应用程序是更新操作,内部存储不会被删除,区别于被用户手动卸载。</p><h3 style="font-size: 1.5em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.43; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-外部存储-2" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#外部存储-2" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1.2;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>外部存储</h3><p style="font-size: 16px; box-sizing: border-box; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;">不管你是使用 getExternalStoragePublicDirectory() 来存储可以共享的文件,还是使用 getExternalFilesDir() 来储存那些对于你的app来说是私有的文件,有一点很重要,那就是你要使用那些类似DIRECTORY_PICTURES 的API的常量。那些目录类型参数可以确保那些文件被系统正确的对待。例如,那些以DIRECTORY_RINGTONES 类型保存的文件就会被系统的media scanner认为是ringtone而不是音乐。</p><h2 style="font-size: 1.75em; box-sizing: border-box; margin-top: 1em; margin-bottom: 16px; line-height: 1.225; padding-bottom: 0.3em; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(238, 238, 238); color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';"><a target=_blank id="user-content-清除数据清除缓存的区别" class="anchor" href="https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/06.%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%E5%8F%AF%E4%BB%A5%E6%94%BE%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E5%AE%83%E4%BB%AC%E5%90%84%E8%87%AA%E7%9A%84%E7%89%B9%E7%82%B9%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.md#清除数据清除缓存的区别" aria-hidden="true" style="box-sizing: border-box; background-color: transparent; color: rgb(64, 120, 192); text-decoration: none; display: inline-block; padding-right: 2px; margin-left: -18px; line-height: 1;"><span class="octicon octicon-link" style="box-sizing: border-box; font-weight: normal; font-size: 16px; line-height: 1; font-family: octicons; display: inline-block; text-rendering: auto; -webkit-font-smoothing: antialiased; -webkit-user-select: none; color: rgb(0, 0, 0); vertical-align: middle; visibility: hidden;"></span></a>清除数据、清除缓存的区别</h2><ul style="font-size: 16px; box-sizing: border-box; padding: 0px 0px 0px 2em; margin-top: 0px; margin-bottom: 16px; color: rgb(51, 51, 51); font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 25px;"><li style="box-sizing: border-box;">清除数据主要是清除用户配置,比如SharedPreferences、数据库等等,这些数据都是在程序运行过程中保存的用户配置信息,清除数据后,下次进入程序就和第一次进入程序时一样</li><li style="box-sizing: border-box;">缓存是程序运行时的临时存储空间,它可以存放从网络下载的临时图片,从用户的角度出发清除缓存对用户并没有太大的影响,但是清除缓存后用户再次使用该APP时,由于本地缓存已经被清理,所有的数据需要重新从网络上获取。为了在清除缓存的时候能够正常清除与应用相关的缓存,请将缓存文件存放在getCacheDir()或者 getExternalCacheDir()路径下。</li></ul><span style="font-size:24px;">6、</span><span style="font-size:24px; font-family: 'Helvetica Neue', Helvetica, 'Segoe UI', Arial, freesans, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; line-height: 1.2;">Android中View的触摸事件涉及到哪些方法?他们之间有什么关系?</span>

在Android中的View触摸事件处理主要涉及以下三个方法

  • dispatchTouchEvent()
  • onInterceptTouchEvent()
  • onTouchEvent()

View的触摸事件传递,从前到后按照以下顺序

  1. Activity
  2. PhoneWindow
  3. DecorView
  4. 子View

他们之间的关系如下

  • 当触摸事件来临时,dispatchTouchEvent()会被调用,它的返回值由onInterceptTouchEvent()和child.dispatchTouchEvent()共同决定
  • 在dispatchTouchEvent()中,onInterceptTouchEvent()会被调用,onInterceptTouchEvent()决定的是当前ViewGroup是否对触摸事件进行拦截,如果返回true,则触摸事件由当前ViewGroup的onTouchEvent()处理,否则触摸事件会被分发到子View,由子View进行处理。
  • onTouchEvent()中对具体的触摸事件进行处理。

他们之间的关系可以用伪代码标示,如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) {
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

另外,ViewGroup.onInterceptTouchEvent()默认返回false,即对触摸事件不进行拦截,都会分发到子View。

如果View设置了onTouchListener(),则onTouchListener()的处理在onTouchEvent()之前,如果onTouchListener()返回true,则View.onTouchEvent()不会被调用,即onTouchListener()的优先权高于onTouchEvent()

Android中默认认为View是消耗onTouchEvent()的,即返回值为true,除非它是不可点击的,即clickable和longClickable都为false。

7、 Android中IPC通信的方式有哪些?使用场景是什么?


这一部分你可以阅读《Android开发艺术探索》第二章 IPC机制,任主席写的真是非常好,我也推荐两年以上的开发者买一本读一下。

8、Android为什么要设计出Bundle而不是直接使用HashMap来进行数据传递?

  • Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。
  • 另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。

以上内容来自于对这个问题的思考和总结







Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐