Java 基础知识

java 基础知识

成员内部类,局部内部类,匿名内部类,静态内部类

  1. 成员内部类

    在外部类内部直接定义(不在方法内部或代码块内部)的类就是成员式内部类,它可以直接使用外部类的所有变量和方法,即使是 private 的。外部类要想访问内部类的成员变量和方法,则需要通过内部类的对象来获取。
    成员式内部类如同外部类的一个普通成员。

  1. 局部内部类

    局部内部类(Local class)是定义在代码块中的类。它们只在定义它们的代码块中是可见的。作用域中的类.

    局部类有几个重要特性:

    a.仅在定义了它们的代码块中是可见的;
    b.可以使用定义它们的代码块中的任何局部 final 变量;
    c.局部类不可以是 static 的,里边也不能定义 static 成员;
    d.局部类不可以用 public、private、protected 修饰,只能使用缺省的;
    e.局部类可以是 abstract 的。

  2. 匿名内部类

     匿名内部类也就是没有名字的内部类  
     正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写  
     但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
    
  3. 静态内部类

     静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非STATIC成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
    

构造代码块和构造函数

	构造代码块:是给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块,只要对象一建立,就会调用这个代码块。

	构造函数:是给与之对应的对象进行初始化,它具有针对性。

	执行顺序:(优先级从高到低。)静态代码块>mian方法>构造代码块>构造方法。其中静态代码块只执行一次。构造代码块在每次创建对象是都会执行。

	静态代码块的作用:比如我们在调用C语言的动态库时会可把.so文件放在此处。

	构造代码块的功能:(可以把不同构造方法中相同的共性的东西写在它里面)。例如:比如不论任何机型的电脑都有开机这个功能,此时我们就可以把这个功能定义在构造代码块内。

成员变量和局部变量

1:成员变量直接定义在类中。

	局部变量定义在方法中,参数上,语句中。

2:成员变量在这个类中有效。

	局部变量只在自己所属的大括号内有效,大括号结束,局部变量失去作用域。

3:成员变量存在于堆内存中,随着对象的产生而存在,消失而消失。

	局部变量存在于栈内存中,随着所属区域的运行而存在,结束而释放。

创建一个对象时在内存中所做的事

1:先将硬盘上指定位置的Xxx.class文件加载进内存。

2:执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量p。

3:在堆内存中开辟一个实体空间,分配了一个内存首地址值。new

4:在该实体空间中进行属性的空间分配,并进行了默认初始化。

5:对空间中的属性进行显示初始化。

6:进行实体的构造代码块初始化。

7:调用该实体对应的构造函数,进行构造函数初始化。()

8:将首地址赋值给p ,p变量就引用了该实体。(指向了该对象)

成员变量和静态变量

1,成员变量所属于对象,所以也称为实例变量。
	静态变量所属于类,所以也称为类变量。

2,成员变量存在于堆内存中。
	静态变量存在于方法区中。

3,成员变量随着对象创建而存在,随着对象被回收而消失。
	静态变量随着类的加载而存在,随着类的消失而消失。

4,成员变量只能被对象所调用。
	静态变量可以被对象调用,也可以被类名调用。

所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。

设计模式

1. 单例设计模式(保证一个类在内存中的对象唯一性)
	思想:
		1.不让其他程序创建该类对象。
		2.在本类中创建一个本类对象。
		3.对外提供方法,让其他程序获取这个对象。
	饿汉式(空间换时间)  懒汉式(时间换空间)

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

	比如构建一个AlertDialog,标题、内容、取消按钮、确定按钮、中立按钮,你可能只需要单独设置几个属性即可;
3. 观察者模式

	对于这个模式的使用场景就一句话:你想在某个对象发生变化时,立刻收到通知。

	基于观察者模式的第三方框架也是非常多,比如EventBus、RxJava等等

final特点

1:这个关键字是一个修饰符,可以修饰类,方法,变量。

2:被final修饰的类是一个最终类,不可以被继承。

3:被final修饰的方法是一个最终方法,不可以被覆盖。

4:被final修饰的变量是一个常量,只能赋值一次。

重载与重写

重载的定义是:在一个类中,如果出现了两个或者两个以上的同名函数,只要它们的参数的个数,或者参数的类型不同,即可称之为该函数重载了。

如何区分重载:当函数同名时,只看参数列表。和返回值类型没关系。

重写:父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。

集合框架

Collection

**List**
	有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

	--ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。按照原数组的50%延长。构造一个初始容量为 10 的空列表

	--LinkedList:底层的数据结构是链表,线程不同步,增删元素的速度非常快。

	--Vector:底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢。按照原数组的100%延长。

**Set**
	接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。

	--HashSet:底层数据结构是哈希表,线程是不同步的。无序,高效;

   		HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。

    	当元素的hashCode值相同时,才继续判断元素的equals是否为true。
		如果为true,那么视为相同元素,不存。如果为false,那么存储。

    	如果hashCode值不同,那么不判断equals,从而提高对象比较的速度。

	--LinkedHashSet:有序,hashset的子类。
	--TreeSet:对Set集合中的元素的进行指定顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。


	对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

	对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

Map

|--Hashtable:底层是哈希表数据结构,是线程同步的。不可以存储null键,null值。  

|--HashMap:底层是哈希表数据结构,是线程不同步的。可以存储null键,null值。替代了Hashtable.

|--TreeMap:底层是二叉树结构,可以对map集合中的键进行指定顺序的排序。

hashMap原理

HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。”这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。

hashCode 相同时出现的情况(hash碰撞)
	1. (存)因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。因为HashMap使用LinkedList存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在LinkedList中。
	2. (取)HashMap会使用键对象的hashcode找到bucket位置,找到bucket位置之后,会调用keys.equals()方法去找到LinkedList中正确的节点,最终找到要找的值对象

	使用不可变的、声明作final的对象,并且采用合适的equals()和hashCode()方法的话,将会减少碰撞的发生,提高效率。不可变性使得能够缓存不同键的hashcode,这将提高整个获取对象的速度,使用String,Interger这样的wrapper类作为键是非常好的选择。

HashMap的大小超过了负载因子
	默认的负载因子大小为0.75
	当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中,这个过程叫作rehashing.
条件竞争(可能出现)
	当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在LinkedList中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在LinkedList的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。

Map集合存储和Collection区别

Collection一次存一个元素;Map一次存一对元素。

Collection是单列集合;Map是双列集合。

Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。

特点:要保证map集合中键的唯一性。

Android 基础知识

activity

1.Activity启动–>onCreate()–>onStart()–onResume();
onCreate是Activity被创建的时候调用,是生命周期的第一个方法,在这里我们可以做一些初始化操作。
onStart表明Activity正在启动,此时处于用户可见状态,但是并不能与用户交互
onResume表明Activity已经处于前台状态,可以与用户交互了。

2.点击Home键–>onPause()–>onStop()
onPause:表明Activity处于paused状态,此时Activity无法与用户交互
onStop:一般在onPause后面执行,表明Activity处于完全不可见的状态。

3.点击home后再次回到程序时–>onRestart()–>onStart()–>onResume()
onRestart:表明Activity正在重新启动,从不可见状态变为可见状态

4.退出当前Activity–onPause()–>onStop()–>onDestory()
onDestory:表明Activity正在被销毁,是整个生命周期方法中的最后一个方法,在该方法中我们可以做一些资源回收的工作。

**activity 启动模式**
1.standard
默认的启动模式,每次启动Activity的时候都会创建一个新的实例。不会复用Activity,对内存消耗较大。

2.singleTop
栈顶复用模式,如果要创建的Activity已经在栈顶的话,那么不会重新创建,直接复用,否则,仍然会重新创建。从通知栏进入activity 配置的启动模式就是singleTop.

3.singletask
栈内复用模式,如果要创建的Activity在栈内已经存在的话,不会重新创建,直接复用栈内存在的Activity,且会调用onNewIntent()方法,并且将该Activity以上的所有的Activity销毁。

4.singleInstance
单一实例,独享一个任务栈,整个手机操作系统里面只有一个实例存在。用的较少。

Fragment

fragment 加载到activity的两种模式
	1. 静态加载,在布局中直接加载
	2. 动态加载,在代码中加载,比较常用


fragment 通讯
	1.fragment调用Activity中的方法
	通过getActivity()调用

	2.在Activity中调用Fragment中的方法
	需要在fragment类中定义一个接口并在Activity中实现它。Fragment在onAttach()回调函数中获取接口的具体实现的对象。然后,fragment就可以调用接口中的方法实现与Activity的通信。

	3.在Fragment中调Fragment中的方法
	通过getSupportFragmentManager().findFragmentById()

Service

onCreate  onStart  onDestroy  onBind
1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。

4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

Service 与 Thread 的区别

没有半毛钱关系,Thread的运行时独立于Activity的,也就是说当一个 Activity 被 finish 之后
如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。
因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用,也就是你下次启动的时候,
无法控制之前创建的线程,而service则可以。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。
如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

BroadcastReceiver

Broadcast是一种广泛运用在应用程序之间传输信息的机制,Android中我们要发送的是一个intent,intent可易携带我们需要传输的数据。

广播的使用场景
	1. 同一app中不同组件之间的数据传递
	2. 不同app之间组件的数据传递

广播的种类
	1. 普通广播 Context.sendBroadcast
	2. 有序广播 Context.sendOrderedBroadcast 根据优先级传播,优先级高的接收器和一阻止继续传播活修改数据
	3. 本地广播 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); lbm.sendBroadcast(new Intent(LOCAL_ACTION)); 只会在应用内部传播,相对来说数据安全。
广播的注册方式
	1. 静态注册 在清单配置文件中声明,即使进程被杀死,该广播仍然运行
	2. 动态注册 在代码中注册,受activity生命周期的影响。

LocalBroadcastManager
	1. 使用LocalBroadcastManager发送的广播只能在app内部传播,因此数据传输很安全。
	2. 比系统广播更加高效,底层是通过Handler发送message实现的。

content provider

(1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。
(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。

Android OS体系结构

从下向上
1. Linux Kernel
	基于Linux 2.6提供核心系统服务,例如:安全、内存管理、进程管理、网络堆栈、驱动模型。
2. Android Runtime
	Android包含一个核心库的集合,提供大部分在Java编程语言核心类库中可用的功能。每一个Android应用程序是Dalvik虚拟机中的实例,运行在他们自己的进程中。
	大多数虚拟机包括JVM都是基于栈的,而Dalvik虚拟机则是基于寄存器的
3. Libraries
	Android包含一个C/C++库的集合,供Android系统的各个组件使用。这些功能通过Android的应用程序框架(application framework)暴露给开发者。(系统C库,媒体库,界面管理,LibWebCore.SGL3D.FreeType.SQlite)
4. Application Framework
	通过提供开放的开发平台,Android使开发者能够编制极其丰富和新颖的应用程序。
5. Applications
	Android的应用程序主要是用户界面(User Interface)方面的,通常以JAVA程序编 写,其中还可以包含各种资源文件(放置在res目录中)JAVA程序及相关资源经过编译后,将生成一个APK包。

Android 储存

1.	Sharepreference
	(以XML文件的形式保存在 /data/data/PACKAGE_NAME/shared_prefs 目录下)
	Preferences只能在同一个包内使用,不能在不同的包之间使用。
2.	File文件储存
	/data/data/PACKAGE_NAME/files 目录

	内部存储internal storage
		内部存储空间十分有限,因而显得可贵,另外,它也是系统本身
		和系统应用程序主要的数据存储所在地,一旦内部存储空间耗
		尽,手机也就无法使用了。所以对于内部存储空间,我们要尽量
		避免使用。Shared Preferences和SQLite数据库都是存储在
		内部存储空间上的。内部存储一般用Context来获取和操作。

		getFilesDir()获取你app的内部存储空间,相当于你的应用在内部存储上的根目录。
		安卓还为我们提供了一个简便方法 openFileOutput()来读写应用在内部存储空间上的文件

	外部存储ExternalStorage

		1. 公共文件Public files:
		文件是可以被自由访问,且文件的数据对其他应用或者用户来说都是由意义的,当应用被卸载之后,其卸载前创建的文件仍然保留。
		getExternalStoragePublicDirectory() 存放在公共文件中
		2. 私有文件Private files:
		其实由于是外部存储的原因即是是这种类型的文件也能被其他程序访问,
		只不过一个应用私有的文件对其他应用其实是没有访问价值的(恶意程序除外)。
		外部存储上,应用私有文件的价值在于卸载之后,这些文件也会被删除。
		方法是Context.getExternalFilesDir()



3.	SQLite
	数据库存放在 /data/data/PACKAGE_NAME/databases 目录下
	1.Android中通过SQLite数据库引擎来实现结构化数据存储。SQLite是一个嵌入式数据库引擎,针对内存等资源有限的设备,提供的是一种高效的数据库引擎。
	2.Android提供了创建和使用SQLite数据库的API。SQLiteDatabase代表一个数据库对象,提供了操作数据库的一些方法,另外还有一个SQLiteOpenHelper工具类提供更简洁的功能。
	3. SQLiteOpenHelper是SQliteDatabase的一个帮助类,用来管理数据库的创建和版本更新。一般用法是定义一个类继承之,并实现两个抽象方法onCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)类创建和跟新数据库。


4. 内容提供器(Content provider)方式
	用于程序间的内容共享

5.	网络储存

android 多线程的四种方式

	1. Handler+Thread
	 	Handler用法简单明了,可以将多个异步任务更新UI的代码放在一起,清晰明了
		处理单个异步任务代码略显多
	2. AsyncTask  
		AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程
	3. ThreadPoolExecutor
		ThreadPoolExecutor提供了一组线程池,可以管理多个线程并行执行。这样一方面减少了每个并行任务独自建立线程的开销,另一方面可以管理多个并发线程的公共资源,从而提高了多线程的效率。所以ThreadPoolExecutor比较适合一组任务的执行。Executors利用工厂模式对ThreadPoolExecutor进行了封装,使用起来更加方便。
			-- Executors.newFixedThreadPool()
			   创建一个定长的线程池,每提交一个任务就创建一个线程,直到达到池的最大长度,这时线程池会保持长度不再变化
			-- Executors.newCachedThreadPool()
			   创建一个可缓存的线程池,如果当前线程池的长度超过了处理的需要时,它可以灵活的回收空闲的线程,当需要增加时,
			    它可以灵活的添加新的线程,而不会对池的长度作任何限制
			-- Executors.newScheduledThreadPool()
			   创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer
			-- Executors.newSingleThreadExecutor()
			   创建一个单线程化的executor,它只创建唯一的worker线程来执行任务
	4. IntentService
		IntentService继承自Service,是一个经过包装的轻量级的Service,
		用来接收并处理通过Intent传递的异步请求。客户端通过调用startService(Intent)启动一个IntentService,
		利用一个work线程依次处理顺序过来的请求,处理完成后自动结束Service。



	public ThreadPoolExecutor(int corePoolSize,  
                          int maximumPoolSize,  
                          long keepAliveTime,  
                          TimeUnit unit,  
                          BlockingQueue<Runnable> workQueue,  
                          ThreadFactory threadFactory,  
                          RejectedExecutionHandler handler)   

	这里是7个参数(我们在开发中用的更多的是5个参数的构造方法),OK,那我们来看看这里七个参数的含义:


	corePoolSize  线程池中核心线程的数量

	maximumPoolSize  线程池中最大线程数量

	keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长

	unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等

	workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。

	threadFactory  为线程池提供创建新线程的功能,这个我们一般使用默认即可

	handler 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。

android 动画

1. View 动画(补间动画Tween)
	通过场景里的对象不断做图像变换(平移,缩放,旋转,透明度)从而产生动画效果,是一种渐近式动画,并支持自定义。

	View动画的四种变换

	名称			标签				子类						效果
	平移动画		<translate>		TranslateAnimation		移动View
	缩放动画		<scale>			ScaleAnimation			放大或缩小View
	旋转动画		<rotate>		RotateAnimation			旋转View
	透明度动画		<alpha>			AlphaAnimation			改变View的透明度


2. 帧动画(用到的类AnimationDrawable)
	帧动画其实也属于View动画。通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画效果,但图片过多过大会导致OOM

		1. 先在drawable文件下,定义一个animation-list文件
		2. 将Drawable作为View的背景播放

	帧动画使用很简单,但很容易出现OOM。尽量避免使用较大较多的图片。

3. 属性动画
	属相动画通过动态地改变对象的属性从而达到动画效果。

	ValueAnimator是整个属性动画机制当中最核心的一个类,前面我们已经提到了,属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。

	ObjectAnimator 它是可以直接对任意对象的任意属性进行动画操作的

Android应用程序启动过程

  • Launcher.startActivitySafely
  • Activity.startActivity
  • Activity.startActivityForResult
  • Instrumentation.execStartActivity
  • ActivityManagerProxy.startActivity
  • ActivityManagerService.startActivity
  • ActivityStack.startActivityMayWait
  • ActivityStack.startActivityLocked
  • ActivityStack.startActivityUncheckedLocked
  • Activity.resumeTopActivityLocked
  • ActivityStack.startPausingLocked
  • ApplicationThreadProxy.schedulePauseActivity
  • ApplicationThread.schedulePauseActivity
  • ActivityThread.queueOrSendMessage
  • H.handleMessage
  • ActivityThread.handlePauseActivity
  • ActivityManagerProxy.activityPaused
  • ActivityManagerService.activityPaused
  • ActivityStack.activityPaused
  • ActivityStack.completePauseLocked
  • ActivityStack.resumeTopActivityLokced
  • ActivityStack.startSpecificActivityLocked
  • ActivityManagerService.startProcessLocked
  • ActivityThread.main
  • ActivityManagerProxy.attachApplication
  • ActivityManagerService.attachApplication
  • ActivityManagerService.attachApplicationLocked
  • ctivityStack.realStartActivityLocked
  • ApplicationThreadProxy.scheduleLaunchActivity
  • ApplicationThread.scheduleLaunchActivity
  • ActivityThread.queueOrSendMessage
  • H.handleMessage
  • ActivityThread.handleLaunchActivity
  • ActivityThread.performLaunchActivity
  • MainActivity.onCreate

整个应用程序的启动过程要执行很多步骤,但是整体来看,主要分为以下五个阶段:

   一. Step1 - Step 11:
	Launcher通过Binder进程间通信机制通知ActivityManagerService,它要启动一个Activity;

   二. Step 12 - Step 16:
	ActivityManagerService通过Binder进程间通信机制通知Launcher进入Paused状态;

   三. Step 17 - Step 24:
	Launcher通过Binder进程间通信机制通知ActivityManagerService,它已经准备就绪进入Paused状态,于是ActivityManagerService就创建一个新的进程,用来启动一个ActivityThread实例,即将要启动的Activity就是在这个ActivityThread实例中运行;

   四. Step 25 - Step 27:
	ActivityThread通过Binder进程间通信机制将一个ApplicationThread类型的Binder对象传递给ActivityManagerService,以便以后ActivityManagerService能够通过这个Binder对象和它进行通信;

   五. Step 28 - Step 35:
	ActivityManagerService通过Binder进程间通信机制通知ActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。

Activity的四种启动模式及其应用场景

为什么要有启动模式?

任务栈的缺点:

  1. 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用,户体验差, 需要点击多次返回才可以把程序退出了。
  2. 每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)

singleTop: 栈顶复用模式,如果栈顶已经存在要启动的activity 则直接复用,如果不存在,则重新创建.用于系统消息等场景.例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。

singleTask:栈唯一模式,如果栈里面没有,就生成该activity,如果存在但是不处于栈顶,则将该activity上所有的activity移除栈.适合作为程序入口点.

singleInstance:这种启动模式比较特殊,因为它会启用一个新的栈结构,将Activity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。 例如闹铃提醒,将闹铃提醒与闹铃设置分离。

熟悉JNI/NDK开发

熟悉openGL开发

图像处理,熟悉openCV openCL android 上实现AI模型的inference

android 多线程###

handler 用于子线程对UI线程的更新,实现异步消息的处理(Looper+MessageQueue+Message+Handler)
 Message Queue
- 定义:消息队列
- 作用:用来存放通过Handler发过来的消息,按照先进先出执行

Handler
- 定义:Handler是Message的主要处理者
- 作用:负责将Message添加到消息队列&处理Looper分派过来的Message

Looper
- 定义:循环器,扮演Message Queue和Handler之间桥梁的角色

作用:主要负责消息循环:循环取出Message Queue的Message;消息派发:将取出的Message交付给相应的Handler


handler 造成的内存泄漏(当Activity结束生命周期时,Handler里的Message可能还没处理完,从而导致一系列的引用关系。)
 解决方案1:使用静态内部类+弱引用
非静态内部类和匿名类都会潜在的引用它们所属的外部类。 但是,静态内部类不会。同时,还可以加上 使用WeakReference弱引用持有Activity实例
 解决方案2:当外部类结束生命周期时清空消息队列
当Activity结束生命周期时(调用onDestroy()方法),同时清除消息队列里的所有回调消息(调用removeCallbacksAndMessages(null))

handlerThread

通过继承Thread类,快速地创建1个带有Looper对象的新工作线程
通过封装Handler类,快速创建Handler & 与其他线程进行通信

// 步骤1:创建HandlerThread实例对象

// 步骤2:启动线程
// mHandlerThread.start();

// 步骤3:创建工作线程Handler & 复写handleMessage()
// 作用:关联HandlerThread的Looper对象、实现消息处理操作 & 与其他线程进行通信
// 注:消息处理操作(HandlerMessage())的执行线程 = mHandlerThread所创建的工作线程中执行

// 步骤4:使用工作线程Handler向工作线程的消息队列发送消息
// 在工作线程中,当消息循环时取出对应消息 & 在工作线程执行相关操作

// 步骤5:结束线程,即停止线程的消息循环

AsyncTask###

作用:
实现多线程
在工作线程中执行任务,如 耗时任务
异步通信、消息传递
实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作

使用:
1. 创建 AsyncTask 子类 & 根据需求实现核心方法
2. 创建 AsyncTask子类的实例对象(即 任务实例)
3. 手动调用execute(()从而执行异步线程任务

内存泄漏与内存溢出###

一、原理

内存溢出(Out of memory):系统会给每个APP分配内存也就是Heap size值,当APP所需要的内存大于了系统分配的内存,就会造成内存溢出;通俗点就是10L桶只能装10L水,但是你却用来装11L的水,那就有1L的水就会溢出

内存泄漏(Memory leak):当一个对象不在使用了,本应该被垃圾回收器(JVM)回收,但是这个对象由于被其他正在使用的对象所持有,造成无法被回收的结果,通俗点就是系统把一定的内存值A借给程序,但是系统却收不回完整的A值,那就是内存泄漏

二、两者的关系

内存泄漏是造成内存溢出(OOM)的主要原因,因为系统分配给每个程序的内存也就是Heap size的值都是有限的,当内存泄漏到一定值的时候,最终会发生程序所需要的内存值加上泄漏值大于了系统所分配的内存额度,就是触发内存溢出

三、危害

内存溢出:会触发Java.lang.OutOfMemoryError,造成程序崩溃
内存泄漏:过多的内存泄漏会造成OOM的发送,同样也会造成相关UI的卡顿现象

四、造成的原因以及处理

A、大量的图片、音频、视频处理,当在内存比较低的系统上也容易造成内存溢出
建议使用第三方,或者JNI来进行处理

B、Bitmap对象的不正确处理(内存溢出)

不要在主线程中处理图片
使用Bitmap对象要用recycle释放
高效的处理大图,这里就不详细介绍了,建议郭神的文章

C、非静态匿名内部类Handler由于持有外部类Activity的引用所造成的内存泄漏

根据WeakReference对象,对handler使用弱引用,并且调用removeCallbacksAndMessages移除

D、线程由于匿名内部类runnable持有activity的引用,从而关闭activity,线程未完成造成内存泄漏

把线程改成静态内部类,调用WeakReference来持有外部资源

E、BraodcastReceiver、File、Cursor等资源的使用未及时关闭

在销毁activity时,应该及时销毁或者回收

F、static关键字修饰的变量由于生命周期过长,容易造成内存泄漏

尽量少使用静态变量,一定要使用要及时进行制null处理

G、单列模式造成的内存泄漏,如context的使用,单列中传入的是activity的context,在关闭activity时,activity的内存无法被回收,因为单列持有activity的引用

在context的使用上,应该传入application的context到单列模式中,这样就保证了单列的生命周期跟application的生命周期一样
单列模式应该尽量少持有生命周期不同的外部对象,一旦持有该对象的时候,必须在该对象的生命周期结束前制null
posted @ 2023-02-09 13:54  年年糕  阅读(22)  评论(0编辑  收藏  举报