Android Studio 开发笔记

2019-03-07  本文已影响0人  Killshadow

0x00 ArrayAdapter & ArrayList

ArrayAdapter:adapter即适配器,这个数组适配器一般用于列表中,如:ListView、Spinner。
ArrayList:List接口的可调整大小数组实现。实现所有可选的列表操作,并允许所有元素,包括null。除了实现List接口之外,该类还提供了一些方法来控制用于内部存储列表的数组大小。 (这个类大致相当于Vector,除了它是不同步的。)



ArrayList包含了两个重要的对象:elementData 和 size。
(01) elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。
(02) size 则是动态数组的实际大小。

0x01 metadata

元数据是用来描述数据的数据(Data that describes other data)。
我再举一个例子。在电影数据库IMDB上可以查到每一部电影的信息。IMDB本身也定义了一套元数据,用来描述每一部电影。下面是它的一级元数据,每一级下面又列出了二级元数据,总共加起来,可以从100多个方面刻画一部电影:

Cast and Crew(演职人员)、Company Credits(相关公司)、Basic Data(基本情况)、Plot & Quotes(情节和引语)、Fun Stuff(趣味信息)、Links to Other Sites(外部链接)、Box Office and Business(票房和商业开发)、Technical Info(技术信息)、Literature(书面内容)、Other Data(其他信息)。

元数据最大的好处是,它使信息的描述和分类可以实现格式化,从而为机器处理创造了可能。

0x02 @param注释

注释格式:
1、单行(single-line)注释:“//……”
2、块(block)注释:“/……/”
3、文档注释:“/*……/”
4、javadoc 注释标签语法

@author 对类的说明 标明开发该类模块的作者
@version 对类的说明 标明该类模块的版本
@see 对类、属性、方法的说明 参考转向,也就是相关主题
@param 对方法的说明 对方法中某参数的说明
@return 对方法的说明 对方法返回值的说明
@exception 对方法的说明 对方法可能抛出的异常进行说明

0x03 intent

Intent是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

0x04 单例模式

单例模式的定义是它应该保证一个类仅有一个实例,同时这个类还必须提供一个访问该类的全局访问点。如下图是单例模式的结构图:


实现单例模式有以下几个关键点:
(1)其构造函数不对外开放,一般为private;
(2)通过一个静态方法或者枚举返回单例类对象;
(3)确保单例类的对象有且只有一个,尤其要注意多线程的场景;
(4)确保单例类对象在反序列化时不会重新创建对象;
通过将单例类的构造函数私有化,使得客户端不能通过new的形式手动构造单例类的对象。单例类会主动暴露一个公有的静态方法,客户端调用这个静态的方法获取到单例类的唯一实例。在获取这个单例类的时候需要确保这个过程是线程安全的。
引自:https://blog.csdn.net/happy_horse/article/details/51164262

0x05 Serializable

https://www.jianshu.com/p/fb903a1f8831

0x06 关键字 new

作用:https://blog.csdn.net/ljheee/article/details/52235915
声明一个变量来指向一个对象,即引用;
实例化一个类对象;
初始化一个类对象.

0x07 UUID

UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。UUID具有以下涵义:来源

经由一定的算法机器生成:为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。
非人工指定,非人工识别:UUID是不能人工指定的,除非你冒着UUID重复的风险。UUID的复杂性决定了“一般人“不能直接从一个UUID知道哪个对象和它关联。
在特定的范围内重复的可能性极小:UUID的生成规范定义的算法主要目的就是要保证其唯一性。但这个唯一性是有限的,只在特定的范围内才能得到保证,这和UUID的类型有关(参见UUID的版本)。
UUID是16字节128位长的数字,通常以36字节的字符串表示,示例如下:
3F2504E0-4F89-11D3-9A0C-0305E82C3301
其中的字母是16进制表示,大小写无关。

0x08 Fragment 生命周期

1 生命周期两张图

Fragment生命周期图:

image

Fragment与Activity生命周期对比图:

image

2 生命周期分析

1. 当一个fragment被创建的时候,它会经历以下状态.

2. 当这个fragment对用户可见的时候,它会经历以下状态。

3. 当这个fragment进入“后台模式”的时候,它会经历以下状态。

4. 当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。

5. 就像activitie一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象。

6. fragments的大部分状态都和activitie很相似,但fragment有一些新的状态。

一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activity在resumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的。

0x09 inflater.inflate

很多人在网上问LayoutInflater类的用法,以及inflate()方法参数的含义,现解释如下:
inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件。

  1. 获取LayoutInflater对象有三种方法
LayoutInflater inflater=LayoutInflater.from(this);
LayoutInflater inflater=getLayoutInflater();
LayoutInflater inflater=(LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
  1. 关于LayoutInflater类inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义

resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。
root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个参数attachToRoot为true,就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附加到resource对象的根布局对象上,也就是布局文件resource的最外层的View上,比如是一个LinearLayout或者其它的Layout对象。
attachToRoot:是否将root附加到布局文件的根视图上

0x0A Builder模式

一、 概述

1.1 定义

将一个复杂对象的构建与表示分离开来,使得同样的构建过程可以创建不同的表示。

1.2 使用场景

1.3 分析

Builder模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,从而降低两者耦合度。

优点:

缺点:

二、 实现

2.1 示例

Builder模式非常易于上手,我们通过分析一个简单的demo来感受Builder模式的魅力。

EventBus 框架中的 EventBusBuilder 就采用 Builder 模式实现,我们来分析Builder模式的实现,EventBus 3.0 源码解析见 EventBus3.源码解析

EventBusBuilderEventBus 框架中的个性化配置类,从类名就可以看出这是一个 Builder 模式,通过Builder 对象来组装个性化设置 EventBus 的各项参数配置,包括 是否通过Log输出异常信息logSubscriberExceptions 等。下面看看 EventBusBuilder 的相关源码。

public class EventBusBuilder {
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();

    boolean logSubscriberExceptions = true;//是否通过Log输出异常信息
    boolean logNoSubscriberMessages = true;//是否通过Log输出发送事件无订阅者信息
    boolean sendSubscriberExceptionEvent = true;//是否将内部异常信息通过SubscriberExceptionEvent发送出去
    boolean sendNoSubscriberEvent = true;//是否将无订阅者的时间通过NoSubscriberEvent发送出去
    boolean throwSubscriberException;//是否将内部异常信息通过EventBusException抛出
    boolean eventInheritance = true;//发送事件是否支持调用所有父类及实现的接口
    boolean ignoreGeneratedIndex;//是否忽略生成被观察者订阅的方法(通过反射)
    boolean strictMethodVerification;//是否开启严格的方法验证,(public,只有一个参数,不为static及abstract),非法则均抛出异常
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;//发送事件的线程池
    List<Class<?>> skipMethodVerificationForClasses;//对类忽略方法校验,目前未实现
    List<SubscriberInfoIndex> subscriberInfoIndexes;//通过annotation preprocessor生成的订阅者方法list

    EventBusBuilder() {
    }

    /**
     * 设置 logSubscriberExceptions,默认为true
     * @param logSubscriberExceptions
     * @return
     */
    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }

    /**
     * 设置 logNoSubscriberMessages,默认为 true
     * @param logNoSubscriberMessages
     * @return
     */
    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }

    /**
     * 设置 sendSubscriberExceptionEvent,默认为 true
     * @param sendSubscriberExceptionEvent
     * @return
     */
    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }

    /**
     * 设置 sendNoSubscriberEvent,默认为 true
     * @param sendNoSubscriberEvent
     * @return
     */
    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }

    /**
     * 设置 throwSubscriberException,默认为 false
     * @param throwSubscriberException
     * @return
     */
    public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
        this.throwSubscriberException = throwSubscriberException;
        return this;
    }

    /**
     * 设置 eventInheritance,默认为 true
     * @param eventInheritance
     * @return
     */
    public EventBusBuilder eventInheritance(boolean eventInheritance) {
        this.eventInheritance = eventInheritance;
        return this;
    }

    /**
     * 设置 executorService
     * @param executorService
     * @return
     */
    public EventBusBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

    /**
     * 设置 skipMethodVerificationForClasses
     * @param clazz
     * @return
     */
    public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
        if (skipMethodVerificationForClasses == null) {
            skipMethodVerificationForClasses = new ArrayList<>();
        }
        skipMethodVerificationForClasses.add(clazz);
        return this;
    }

    /**
     * 设置 ignoreGeneratedIndex
     * @param ignoreGeneratedIndex
     * @return
     */
    public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
        return this;
    }

    /**
     * 设置 strictMethodVerification
     * @param strictMethodVerification
     * @return
     */
    public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
        this.strictMethodVerification = strictMethodVerification;
        return this;
    }

    /**
     * 设置 subscriberInfoIndexes
     * @param index
     * @return
     */
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if(subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

    /**
     * 实现修改 EventBus 默认 EventBusBuilder(EventBus.defaultInstance)
     * @return
     */
    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }

    /**
     * 通过 build 方式实现 EventBus 初始化
     * @return
     */
    public EventBus build() {
        return new EventBus(this);
    }

}

上述代码中,EventBusBuilder 类可以设置 EventBus 中的 * logSubscriberExceptions、logNoSubscriberMessages、sendSubscriberExceptionEvent* 等参数,这些参数统一存储在 EventBusBuilder 中。在调用 EventBusBuilder 类的 build() 函数时会创建 EventBus,并且将各项配置用于到 EventBus 中去。

EventBusBuilder 采用 Builder 模式主要是初始化 EventBus 配置项参数复杂,并且很多参数具有默认值,在使用时可能只需要对部分参数进行修改,故采用 Builder 模式方便修改初始化。

2.2 小结

通过 2.1节 可以看出 Builder 模式使用非常方便,并且对于多参数属性初始化来说,极大的简化了工作。

在项目中合理的加入 Builder 模式吧~

三、小结

Builder 模式在 Android 开发中较为常用,通常作为配置类的构造器将配置的构建和表示分离开来,同时也实现了将配置类从目标类中抽离出来,避免了过多的 setter 方法。

Builder 模式比较常见的实现方式是通过调用链实现,这样使得代码简单易懂。

0x0B 单例模式

一、 概述

1.1 定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

1.2 使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源;或者某种类型的对象应该有且只有一个。

eg:创建一个对象需要消耗的资源过多,如访问IO和数据库资源。

1.3 关键点

构造函数不对外开放,一般为 private ;
通过一个静态方法或者枚举返回单例类对象;
确保单例类的对象有且只有一个,尤其是在 多线程 环境下;
确保单例类对象在反序列化时不会重新构建对象。

二、实现方式

2.1 懒汉模式

声明一个静态对象,并且在用户第一次调用 getInstance 时进行初始化。

2.1.1 分析
synchronized 关键字用于在多线程情况下保证单例对象唯一性

优点:单例只有在使用时才会被实例化,在一定程度上节约了资源

缺点:
    每一次加载时需要及时进行实例化,响应速度稍慢
    每次调用 getInstance() 都进行同步,造成不必要的同步开销

一般不建议使用
2.1.2 源码

public class Singleton {
private static Singleton instance;

private Singleton() {

}

public static synchronized Singleton getInstance() {
    if (null == instance) {
        instance = new Singleton();//加载时进行实例化
    }
    return instance;
}

}

2.2 饿汉模式

声明静态对象时就已经初始化。

2.2.1 分析
静态对象在声明的时候就已经初始化,从而保证了单例对象唯一性

优点: 每次调用 getInstance() 直接取出静态对象,不需要同步锁,响应速度快

缺点:初始化声明对象造成了一定资源的闲置浪费

2.2.2 源码

public class Singleton {
private static Singleton instance = new Singleton();

private Singleton() {

}

public static Singleton getInstance() {
    return instance;
}

}

2.3 Double Check Lock (DCL) 模式

2.3.1 分析
优点:
    资源利用率高
    既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用 getInstance() 不进行同步锁

缺点:
    第一次加载时响应稍慢
    由于Java内存模型的原因偶尔会失败
        instance = new Singleton(); 这句代码并不是一个原子操作,由于 Java 编译器允许处理器乱序执行汇编指令以及 JDK1.5 之前的 JVM (Java Memory Model, Java 内存模型) 中Cache、寄存器到主内存回写顺序的规定,该语句转换的汇编指令无法确保顺序执行
        在 JDK1.5 之后,具体化了 volatile 关键字,因此可以直接定义成 private volatile static Singleton instance = null; ,就可以保证 instance 对象每次都是从主内存中读取
2.3.2 源码

public class Singleton {
private volatile static Singleton instance = null;

private Singleton() {

}

public static Singleton getInstance() {
    if (null == instance) {
        synchronized (Singleton.class) {
            if (null == instance) {
                instance = new Singleton();
            }
        }
    }
    return instance;
}

}

2.4 静态内部类单例模式

2.4.1 分析

强烈推荐使用

优点:
    第一次加载 Singleton 类时并不会初始化 instance ,只有在第一次调用 getInstance() 时才会初始化
    既能保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化
2.4.2 源码

public class Singleton {
private Singleton() {

}

public static Singleton getInstance() {
    return SingletonHolder.instance;
}

/**
 * 静态内部类
 */
private static class SingletonHolder {
    private static final Singleton instance = new Singleton();
}

}

2.5 枚举单例

2.5.1 分析

枚举单例模式最大的优点是写法简单,枚举在 Java 中与普通类是一样的,不仅能够有字段,还能够有自己的方法。最重要的是默认枚举实例的创建时线程安全的,并且在任何情况下它都是一个单例。

在上述的几种单例模式中,反序列化 的时候会出现重新创建对象的情况。**

上述示例中如果要杜绝单利对象在被反序列化时重新生成对象,则必须加入如下方法:

private Object readResolve() throws ObjectStreamException {
return instance;
}

2.5.2 源码

public enum Singleton {

INSTANCE;

public void doSomething() {
    // ... do something
}

}

2.6 使用容器实现单例模式

2.6.1 分析

在程序初始化的时候,将多种单例类型注入到一个统一的管理类中,在使用时根据 key 获取对象对应类型的对象。

这种方式使得我们可以管理多种类型的单例,并且在使用时候可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

2.6.2 源码

public class SingletonManager {
private static Map<String, Object> data = new HashMap<>();

public SingletonManager() {
}

public static void register(String key, Object instance) {
    if (!data.containsKey(key)) {
        data.put(key, instance);
    }
}

public static Object get(String key) {
    return data.get(key);
}

}

三、小结

所有的单例模式核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例。

需要注意的是在获取实例的过程中保证线程安全、防止反序列化导致重新生成实例对象等问题。

具体选择哪种方式实现单例模式还需要结合项目业务逻辑。

0X0C AndroidManifest之activity

<activity android:allowEmbedded=["true" | "false"]
          android:allowTaskReparenting=["true" | "false"]
          android:alwaysRetainTaskState=["true" | "false"]
          android:autoRemoveFromRecents=["true" | "false"]
          android:banner="drawable resource"
          android:clearTaskOnLaunch=["true" | "false"]
          android:configChanges=["mcc", "mnc", "locale",
                                 "touchscreen", "keyboard", "keyboardHidden",
                                 "navigation", "screenLayout", "fontScale",
                                 "uiMode", "orientation", "screenSize",
                                 "smallestScreenSize"]
          android:documentLaunchMode=["intoExisting" | "always" |
                                  "none" | "never"]
          android:enabled=["true" | "false"]
          android:excludeFromRecents=["true" | "false"]
          android:exported=["true" | "false"]
          android:finishOnTaskLaunch=["true" | "false"]
          android:hardwareAccelerated=["true" | "false"]
          android:icon="drawable resource"
          android:label="string resource"
          android:launchMode=["multiple" | "singleTop" |
                              "singleTask" | "singleInstance"]
          android:maxRecents="integer"
          android:multiprocess=["true" | "false"]
          android:name="string"
          android:noHistory=["true" | "false"]  
          android:parentActivityName="string" 
          android:permission="string"
          android:process="string"
          android:relinquishTaskIdentity=["true" | "false"]
          android:screenOrientation=["unspecified" | "behind" |
                                     "landscape" | "portrait" |
                                     "reverseLandscape" | "reversePortrait" |
                                     "sensorLandscape" | "sensorPortrait" |
                                     "userLandscape" | "userPortrait" |
                                     "sensor" | "fullSensor" | "nosensor" |
                                     "user" | "fullUser" | "locked"]
          android:stateNotNeeded=["true" | "false"]
          android:taskAffinity="string"
          android:theme="resource or theme"
          android:uiOptions=["none" | "splitActionBarWhenNarrow"]
          android:windowSoftInputMode=["stateUnspecified",
                                       "stateUnchanged", "stateHidden",
                                       "stateAlwaysHidden", "stateVisible",
                                       "stateAlwaysVisible", "adjustUnspecified",
                                       "adjustResize", "adjustPan"] >   
    . . .
</activity>

0x0D 使TextView中的文本滑动

首先在textView组件中设置scrollbars属性为vertical:

<TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_weight="2.89"
        android:scrollbars="vertical"
        android:textColor="#ffffffff"
        android:text=" "/>

然后在java里实例化TextView的时候:

// obj表示实例化的TextView组件
obj.setMovementMethod(ScrollingMovementMethod.getInstance());

0x0E 圆角按钮

res/drawable/目录下添加一个这样的xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">

    <item android:state_pressed="true">
        <shape>
            <solid android:color="@color/light_goldenrod_yellow"/>
            <corners android:radius="8dp"/>
        </shape>
    </item>
    <!--<item android:state_pressed="false">-->
        <!--<shape>-->
            <!--<solid android:color="@android:color/holo_blue_dark"/>-->
            <!--<corners android:radius="5dp"/>-->
        <!--</shape>-->
    <!--</item>-->

    <item>
        <shape android:shape="rectangle">
            <corners android:radius="8dp"/>
            <solid android:color="@android:color/transparent"/>
            <stroke android:color="@color/dark_slate_gray"
                android:width="2dp"/>
        </shape>
    </item>
</selector>

一些Tips

  1. layout_gravity 和 gravity的区别

android:gravity sets the gravity of the contents (i.e. its subviews) of the View it's used on.
android:layout_gravity sets the gravity of the View or Layout relative to its parent.

  1. scrollview

放在根视图, 可以使整个activity滚动.

  1. TimePicker DatePicker可以弹出时间/日期选择器.

  2. edittext添加输入字数限制:
    xml中:

<EditText  android:layout_width = "fill_parent"  
    android:layout_height = "wrap_content"  
    android:id = "@+id/mEdit"  
    android:maxLength = "10"/>  

代码中:

view plain
EditText mEdit = (EditText)findViewById(R.id.mEdit);  
InputFilter[] filters = {new LengthFilter(10)};  
mEdit.setFilters(filters);  
上一篇下一篇

猜你喜欢

热点阅读