siren4
siren4
singleTask什么时候开启新栈? 当启动的activity的启动模式为singleTask时,会首先检测manifest中的taskAffinity属性,如果taskAffinity存在并且和已有的taskAffinity不同时(默认是包名),则创建新的任务栈,并把目标activity放入其中,否则不会创建新栈。
首先明确一点,内存泄漏和内存溢出是不同的,但是过多的内存泄漏会导致内存溢出。 内存溢出的根本原因: 1.应用申请内存的速度超过了gc内存回收的速度。 2.应用申请的内存大小超过了手机的可用内存。 常见的内存溢出的原因: 1.加载超大的图片、文件 2.创建大量的对象 内存优化的方法: 1.申请更大的内存,比如多进程、设置manifest中的largeHeap=true等。 2.减少内存使用 ①使用优化后的集合对象,分场景使用SpaseArray和HashMap; ②使用微信的mmkv替代sharedpreference; ③使用StringBuilder替代String拼接 ④统一带有缓存的基础库,特别是图片库,如果用了两套不一样的图片加载库就会出现2个图片各自维护一套图片缓存 ⑤给ImageView设置合适尺寸的图片,列表页显示缩略图,查看大图显示原图 ⑥优化业务架构设计,比如省市区数据分批加载,需要加载省就加载省,需要加载市就加载失去,避免一下子加载所有数据 3.避免内存泄漏
1.Context本身是一个抽象类。 2.ContextWrapper和ContextImpl是它的直接子类,ContextWrapper进行功能的封装,内部委派ContextImpl去实现。 3.在attachBaseContext(context)方法中,对mBase进行赋值委派。 4.在android中,有3种context,分别是application、service、activity。通常情况下,这3种context的能力都是相同的,但是在有些场景下需要注意,比如启动activity和dialog;因为系统不允许凭空出现activity和dialog(一个启动另一个以此来形成返回栈),所以必须使用activity类型的context. 5.Context的数量=activity的数量+service的数量+1,一个应用只有一个application类型的context. 6.getApplication()和getApplicationContext()返回的都是Application对象本身,但是2者的作用域不一样。getApplication()的作用域比较小,一般只能在activity和service中调用。 7.getBaseContext()方法返回的是ContextImpl对象。 8.Application的方法调用顺序:构造函数->attachBaseContext->onCreate.因此在attachBaseContext方法之前调用context的方法会报空指针,因为这时候mBase==null. 9.不能直接new一个Application对象,因为它是系统组件,它需要系统去创建和维护,否则它不具备context的各种能力。
1.界面上的任何一个view的刷新请求最终都是调用ViewRootImpl的scheduleTraversals()来实现的。 2.scheduleTraversals() 会先过滤掉同一帧内的重复调用,确保同一帧内只需要安排一次遍历绘制 View 树的任务. 3.scheduleTraversals() 会往主线程的消息队列中发送一个同步屏障,发完同步屏障后 scheduleTraversals() 将 doTraversal() 封装到 Runnable 里面,然后将这个 Runnable 任务以当前时间戳放进一个待执行的队列里,并且向底层订阅下一个屏幕刷新信号Vsync. 4.当下一个屏幕刷新信号发出时,底层就会回调取出之前放进待执行队列里的任务来执行,也就是ViewRootImpl的doTraversal() 操作。 5.doTraversal()中首先移除同步屏障,再会调用performTraversals() 方法根据当前状态判断是否需要执行performMeasure() 测量、perfromLayout() 布局、performDraw() 绘制流程,在这几个流程中都会去遍历 View 树来刷新需要更新的View。 6.等到下一个Vsync信号到达,将上面计算好的数据渲染到屏幕上,同时如果有必要开始下一帧的数据处理。
相同点: 1.抽象类和接口都不能直接实例化 2.抽象类的子类和接口的实现类都必须实现所有的抽象方法。 不同点: 1.抽象类可以有非抽象的方法,但接口不行(JDK8以后可以有) 2.单继承,多实现 3.接口的成员变量只能是静态常量,没有构造函数,也没有代码块,但抽象类都可以有。 4.抽象类强调的是重用,接口强调的是解耦。
1.自定义静态的Handler 2.非静态的Handler,可以加个弱引用(针对回调) 3.在onDestory时,调用removecallbacksandmessages(null)去清除Message和Runnable.
MVC: Model:主要用于网络请求、数据库、业务逻辑处理等操作。 View:用于展示UI,一般采用XML文件进行界面的描述。 Controller:控制层的重任落在了众多Activity上,Activity需要交割业务逻辑至Model层处理。 事件从View层流向Controller层,经过Model层处理,然后通知View层更新。 缺点: a.Controller层,也就是activity承担了部分View层的职责,导致该层过于臃肿。 b.在MVC中,Model层处理完数据后,直接通知View层更新,因此MV耦合性强。 MVP: Model:数据处理层。 View:视图显示层。 Presenter:业务逻辑处理层。 通过接口,V和P间接相互持有引用,P和M间接相互持有引用,但是V和M完全解耦,而且把业务逻辑从activity中移到P中,减轻了activity的压力。 缺点: 当View层足够复杂的时候,View接口会变得很庞大。 MVVM: Model:同上。 View:同上。 ViewModel:视图模型,通过框架实现View层和Model层的双向绑定。 优点: a.View和Model双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。 b.不需要findViewById也不需要butterknife,不需要拿到具体的View去设置数据绑定监听器等等,这些都可以用DataBinding完成。 c.View和Model的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle完成。 d.不会像MVC一样导致Activity中代码量巨大,也不会像MVP一样出现大量的View和Presenter接口,项目结构更加低耦合。 缺点: 由于数据和视图的双向绑定,导致出现问题时不好定位来源,有可能数据问题导致,也有可能业务逻辑中对视图属性的修改导致。 选择: 1、如果项目简单,没什么复杂性,未来改动也不大的话,那就不要用设计模式或者架构方法,只需要将每个模块封装好,方便调用即可,不要为了使用设计模式或架构方法而使用。 2、对于偏向展示型的app,绝大多数业务逻辑都在后端,app主要功能就是展示数据,交互等,建议使用mvvm。 3、对于工具类或者需要写很多业务逻辑app,使用mvp或者mvvm都可。
AsyncTask的实现原理: 1.AsyncTask是一个抽象类,主要由Handler+2个线程池构成,SERIAL_EXECUTOR是任务队列线程池,用于调度任务,按顺序排列执行,THREAD_POOL_EXECUTOR是执行线程池,真正执行具体的线程任务。Handler用于工作线程和主线程的异步通信。 2.AsyncTask,其中Params是doInBackground()方法的参数类型,Result是doInBackground()方法的返回值类型,Progress是onProgressUpdate()方法的参数类型。 3.当执行execute()方法的时候,其实就是调用SERIAL_EXECUTOR的execute()方法,就是把任务添加到队列的尾部,然后从头开始取出队列中的任务,调用THREAD_POOL_EXECUTOR的execute()方法依次执行,当队列中没有任务时就停止。 4.AsyncTask只能执行一次execute(params)方法,否则会报错。但是SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR线程池都是静态的,所以可以形成队列。 Q:AsyncTask只能执行一次execute()方法,那么为什么用线程池队列管理 ? 因为SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR线程池都是静态的,所有的AsyncTask实例都共享这2个线程池,因此形成了队列。 Q:AsyncTask的onPreExecute()、doInBackground()、onPostExecute()方法的调用流程? AsyncTask在创建对象的时候,会在构造函数中创建mWorker(workerRunnable)和mFuture(FutureTask)对象。 mWorker实现了Callable接口的call()方法,在call()方法中,调用了doInBackground()方法,并在最后调用了postResult()方法,也就是通过Handler发送消息给主线程,在主线程中调用AsyncTask的finish()方法,决定是调用onCancelled()还是onPostExecute(). mFuture实现了Runnable和Future接口,在创建对象时,初始化成员变量mWorker,在run()方法中,调用mWorker的call()方法。 当asyncTask执行execute()方法的时候,会先调用onPreExecute()方法,然后调用SERIAL_EXECUTOR的execute(mFuture),把任务加入到队列的尾部等待执行。执行的时候调用THREAD_POOL_EXECUTOR的execute(mFuture).