1.onCreate()
这个方法在每一个Activity类都会有,当我们新建一个Activity类时,一定会重写父类的onCreate方法,onCreate方法会在Activity第一次被创建时调用。我们应该在这个方法中完成Activity的初始化操作,比如说加载布局,初始化布局控件,绑定按钮事件等。2.onStart()
这个方法在Activity由不可见变为可见时调用。3.onResume()
这个方法在Activity准备好喝用户交互的时候调用。此时的Activity一定位于返回栈的栈顶,并且处于运行状态。4.onPause()
这个方法在系统准备去启动或者恢复另一个Activity的时候调用。5.onStop()
这个方法在Activity完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动的新Activity是一个对话框式的activity,那么,onPause()方法会得到执行,而onStop()方法并不会执行。6.onDestory()
这个方法在Activity被销毁之前调用,之后Activity的状态将变为销毁状态。7.onRestart()
这个方法在Activity由停止状态变为运行状态之前调用,也就是Activity被重新启动了。 启动模式: 标准模式(Standard) 栈顶复用模式(SingleTop) 栈内复用模式(SingleTask) 单例模式(SingleInstance) 3 activity启动流程 我们可以从Context的startActivity说起,其实现是ContextImpl的startActivity,然后内部会通过Instrumentation来尝试启动Activity,这是一个跨进程过程,它会调用ams的startActivity方法,当ams校验完activity的合法性后,会通过ApplicationThread回调到我们的进程,这也是一次跨进程过程,而applicationThread就是一个binder,回调逻辑是在binder线程池中完成的,所以需要通过Handler H将其切换到ui线程,第一个消息是LAUNCH_ACTIVITY,它对应handleLaunchActivity,在这个方法里完成了Activity的创建和启动,接着,在activity的onResume中,activity的内容将开始渲染到window上,然后开始绘制直到我们看见 service 1 service启动方式和区别 startService 启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService; bindService 启动的服务:该方法启动的服务可以进行通信。停止服务使用unbindService; 1 )采用start的方式开启服务 使用Service的步骤: 1.定义一个类继承Service 2.在Manifest.xml文件中配置该Service 3.使用Context的startService(Intent)方法启动该Service 4.不再使用时,调用stopService(Intent)方法停止该服务 使用这种start方式启动的Service的生命周期如下: 生命周期:onCreate()--->onStartCommand()(onStart()方法已过时) ---> onDestory() 说明:如果服务已经开启,不会重复的执行onCreate(), 而是会调用onStart()和onStartCommand()。 服务停止的时候调用 onDestory()。服务只会被停止一次。 特点:一旦服务开启跟调用者(开启者)就没有任何关系了。 开启者退出了,开启者挂了,服务还在后台长期的运行。 开启者不能调用服务里面的方法。 2)采用bind的方式开启服务 使用Service的步骤: 1.定义一个类继承Service 2.在Manifest.xml文件中配置该Service 3.使用Context的bindService(Intent, ServiceConnection, int)方法启动该Service 4.不再使用时,调用unbindService(ServiceConnection)方法停止该服务 使用这种start方式启动的Service的生命周期如下: 生命周期:onCreate() --->onBind()--->onunbind()--->onDestory() 注意:绑定服务不会调用onstart()或者onstartcommand()方法 特点:bind的方式开启服务,绑定服务,调用者挂了,服务也会跟着挂掉。 绑定者可以调用服务里面的方法。 2 service与activity交互与通信方式 活动是需要和服务进行交互的,比如音乐播放界面,用户可以根据播放进度条掌握播放的进度,用户也可以自己根据歌词的进度选择调整整首歌的进度 1) 创建一个专门的Binder类来进行管理 当Activity通过调用bindService(Intent service, ServiceConnection conn,int flags),我们可以得到一个Service的一个对象实例,然后我们就可以访问Service中的方法 2 ) 通过broadcast(广播)的形式 当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新ProgressBar, 3 service与线程的区别 1.) 服务不是单一的进程。服务没有自己的进程,应用程序可以不同,服务运行在相同的进程中。 2.) 服务不是线程。可以在线程中工作。 一.在应用中,如果是长时间的在后台运行,而且不需要交互的情况下,使用服务。 同样是在后台运行,不需要交互的情况下,如果只是完成某个任务,之后就不需要运行,而且可能是多个任务,需需要长时间运行的情况下使用线程。 二.如果任务占用CPU时间多,资源大的情况下,要使用线程。 servie是系统的组件,它由系统进程托管(servicemanager);它们之间的通信类似于client和server,是一种轻量级的ipc通信,这种通信的载体是binder,它是在linux层交换信息的一种ipc。而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 1 静态与动态广播区别,静态广播如何启动 1)动态注册的广播永远要快于静态注册的广播,不管静态注册的优先级设置的多高,不管动态注册的优先级有多低 2)生存期,静态广播的生存期可以比动态广播的长很多,因为静态广播很多都是用来对系统时间进行监听,比如我们可以监听手机开机。而动态广播会随着context的终止而终止 3)动态广播无需在AndroidManifest.xml中声明即可直接使用,也即动态;而静态广播则需要,有时候还要在AndroidManifest.xml中加上一些权限的声明 4)动态注册的广播会受Activity的生命周期的影响, 当Activity销毁的时候,广播就失效了。 而静态注册的广播,即使Activity销毁了,仍然可以收到广播。更牛掰的是即使杀死进程,仍然可以收到广播 静态广播如何启动: 静态注册(清单文件注册) 2 本地广播实现原理 使用LocalBroadcastManager有如下好处: 1. 发送的广播只会在自己App内传播,不会泄露给其他App,确保隐私数据不会泄露 2. 其他App也无法向你的App发送该广播,不用担心其他App会来搞破坏比系统全局广播更加高效 先通过 LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this); 1 获取实例,然后通过函数 registerReceiver来注册监听器 lbm.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Handle the received local broadcast } }, new IntentFilter(LOCAL_ACTION)); 通过 sendBroadcast 函数来发送广播 lbm.sendBroadcast(new Intent(LOCAL_ACTION)); 总结: LocalBroadcastManager 的核心实现实际上还是Handler,只是利用到了IntentFilter的match功能,至于BroadcastReceiver换成其他接口也无所谓,顺便利用了现成的类和概念而已 contentprovider 1 contentprovider的使用 进程间 进行数据交互 & 共享,即跨进程通信 ContentProvider的底层原理 = Android中的Binder机制 首先,如果要访问一个应用程序通过ContentProvider共享的数据,一定要借助ContentResolver类,我们可以通过Context类的getContentResolver()方法获取ContentResolver类的对象, 之后,我们就可以通过ContentResolver类提供的一些方法对数据进行 增加、更改、删除、查询 操作(和数据库的4个操作类似)。 和数据库操作不同的是,ContentResolver类的这些操作都不是通过数据库表名进行的,而是通过传入ContentProvider共享数据的Uri(统一资源标识符,通过它可以找到任何文件所处的位置)对象来标识和操作共享的数据 2 数据库的使用升级1 创建一个接口,定义了一个名为CONTENT_URL,并且是public static final的Uri类型的类变量,必须为其指定一个唯一的字符串值,最好的方案是类的全称,和数据列的名称
2.实现SQLiteOpenHelper
3.创建一个继承了ContentProvider父类的类
4.在AndroidManifest.xml中使用标签来设置调用ContentProvider。
5 更新数据 性能 1 内存优化 : 如果一个无用对象(不需要再使用的对象)仍然被其他对象持有引用,造成该对象无法被系统回收,以致该对象在堆中所占用的内存单元无法被释放而造成内存空间浪费,这中情况就是内存泄露 1) 单例导致内存泄露---- 单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露 解决方案: 将context参数改为全局的上下文--- 全局的上下文Application Context就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏 2) 静态变量导致内存泄露--- 静态变量存储在方法区,它的生命周期从类加载开始,到整个进程结束。一旦静态变量初始化后,它所持有的引用只有等到进程结束才会释放 Info作为Activity的静态成员,并且持有Activity的引用,但是sInfo作为静态变量,生命周期肯定比Activity长。所以当Activity退出后,sInfo仍然引用了Activity,Activity不能被回收,这就导致了内存泄露 解决方案:静态持有很多时候都有可能因为其使用的生命周期不一致而导致内存泄露,所以我们在新建静态持有的变量的时候需要多考虑一下各个成员之间的引用关系,并且尽量少地使用静态持有的变量,以避免发生内存泄露。当然,我们也可以在适当的时候讲静态量重置为null,使其不再持有引用,这样也可以避免内存泄露 3) 非静态内部类导致内存泄露--- 非静态内部类(包括匿名内部类)默认就会持有外部类的引用,当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露 非静态内部类导致的内存泄露在Android开发中有一种典型的场景就是使用Handler 熟悉Handler消息机制的都知道,mHandler会作为成员变量保存在发送的消息msg中,即msg持有mHandler的引用,而mHandler是Activity的非静态内部类实例,即mHandler持有Activity的引用,那么我们就可以理解为msg间接持有Activity的引用。msg被发送后先放到消息队列MessageQueue中,然后等待Looper的轮询处理(MessageQueue和Looper都是与线程相关联的,MessageQueue是Looper引用的成员变量,而Looper是保存在ThreadLocal中的)。那么当Activity退出后,msg可能仍然存在于消息对列MessageQueue中未处理或者正在处理,那么这样就会导致Activity无法被回收,以致发生Activity的内存泄露 解决方案: 通常在Android开发中如果要使用内部类,但又要规避内存泄露,一般都会采用静态内部类+弱引用的方式;mHandler通过弱引用的方式持有Activity,当GC执行垃圾回收时,遇到Activity就会回收并释放所占据的内存单元。这样就不会发生内存泄露了。上面的做法确实避免了Activity导致的内存泄露,发送的msg不再已经没有持有Activity的引用了,但是msg还是有可能存在消息队列MessageQueue中,所以更好的是在Activity销毁时就将mHandler的回调和发送的消息给移除掉 4) 未取消注册或回调导致内存泄露 我们在Activity中注册广播,如果在Activity销毁后不取消注册,那么这个广播会一直存在系统中,同上面所说的非静态内部类一样持有Activity引用,导致内存泄露。因此注册广播后在Activity销毁后一定要取消注册 5) Timer和TimerTask导致内存泄露--- Timer和TimerTask在Android中通常会被用来做一些计时或循环任务,比如实现无限轮播的ViewPager 当我们Activity销毁的时,有可能Timer还在继续等待执行TimerTask,它持有Activity的引用不能被回收,因此当我们Activity销毁的时候要立即cancel掉Timer和TimerTask,以避免发生内存泄漏 6) 集合中的对象未清理造成内存泄露----如果一个对象放入到ArrayList、HashMap等集合中,这个集合就会持有该对象的引用。当我们不再需要这个对象时,也并没有将它从集合中移除,这样只要集合还在使用(而此对象已经无用了),这个对象就造成了内存泄露。并且如果集合被静态引用的话,集合里面那些没有用的对象更会造成内存泄露了。所以在使用集合时要及时将不用的对象从集合remove,或者clear集合,以避免内存泄漏 7) 资源未关闭或释放导致内存泄露--- 在使用IO、File流或者Sqlite、Cursor等资源时要及时关闭。这些资源在进行读写操作时通常都使用了缓冲,如果及时不关闭,这些缓冲对象就会一直被占用而得不到释放,以致发生内存泄露。因此我们在不需要使用它们的时候就及时关闭,以便缓冲能及时得到释放,从而避免内存泄露 8) 属性动画造成内存泄露--动画同样是一个耗时任务,比如在Activity中启动了属性动画(ObjectAnimator),但是在销毁的时候,没有调用cancle方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity,这就造成Activity无法正常释放。因此同样要在Activity销毁的时候cancel掉属性动画,避免发生内存泄漏。 9) WebView造成内存泄露--- 因为WebView在加载网页后会长期占用内存而不能被释放,因此我们在Activity销毁后要调用它的destory()方法来销毁它以释放内存 解决方法: 在销毁WebView之前需要先将WebView从父容器中移除,然后在销毁WebView 总结:构造单例的时候尽量别用Activity的引用; 静态引用时注意应用对象的置空或者少用静态引用; 使用静态内部类+软引用代替非静态内部类; 及时取消广播或者观察者注册; 耗时任务、属性动画在Activity销毁时记得cancel; 文件流、Cursor等资源及时关闭; Activity销毁时WebView的移除和销毁。 启动优化 影响启动速度的原因 高耗时任务 数据库初始化、某些第三方框架初始化、大文件读取、MultiDex加载等,导致CPU阻塞 复杂的View层级 使用的嵌套Layout过多,层级加深,导致View在渲染过程中,递归加深,占用CPU资源,影响Measure、Layout等方法的速度 类过于复杂 Java对象的创建也是需要一定时间的,如果一个类中结构特别复杂,new一个对象将消耗较高的资源,特别是一些单例的初始化,需要特别注意其中的结构 主题及Activity配置 有一些App是带有Splash页的,有的则直接进入主界面,由于主题切换,可能会导致白屏,或者点了Icon,过一会儿才出现主界面 功耗优化:耗电量? 内存泄漏工具3. 检测、分析内存泄漏的工具
MemoryMonitor:随时间变化,内存占用的变化情况 MAT:输入HRPOF文件,输出分析结果 Histogram:查看不同类型对象及其大小 DominateTree:对象占用内存及其引用关系 MAT使用教程 LeakCanary:实时监测内存泄漏的库(LeakCanary原理) 内存溢出强引用,软引用和弱引用
释放强引用,使用软引用和弱引用;大量的图片、音频、视频处理,当在内存比较低的系统上也容易造成内存溢出
建议使用第三方,或者JNI来进行处理;Bitmap对象的处理
不要在主线程中处理图片 使用Bitmap对象要用recycle释放 控制图片的大小,压缩大图,高效处理,加载合适属性的图片。 当我们有些场景是可以显示缩略图的时候,就不要调用网络请求加载大图,例如在RecyclerView中,我们在上下滑动的时候,就不要去调用网络请求,当监听到滑动结束的时候,才去加载大图,以免上下滑动的时候产生卡顿现象 窗口泄漏 产生原因: 我们知道Android的每一个Activity都有个WindowManager窗体管理器,同样,构建在某个Activity之上的对话框、PopupWindow也有相应的WindowManager窗体管理器。因为对话框、PopupWindown不能脱离Activity而单独存在着,所以当某个Dialog或者某个PopupWindow正在显示的时候我们去finish()了承载该Dialog(或PopupWindow)的Activity时,就会抛Window Leaked异常了,因为这个Dialog(或PopupWindow)的WindowManager已经没有谁可以附属了,所以它的窗体管理器已经泄漏了。 解决方法: 关闭(finish)某个Activity前,要确保附属在上面的Dialog或PopupWindow已经关闭(dismiss)了 anr的原因和解决方案 首先ANR的发生是有条件限制的,分为以下三点: 1.只有主线程才会产生ANR,主线程就是UI线程; 2.必须发生某些输入事件或特定操作,比如按键或触屏等输入事件,在BroadcastReceiver或Service的各个生命周期调用函数; 3.上述事件响应超时,不同的context规定的上限时间不同 a.主线程对输入事件5秒内没有处理完毕 b.主线程在执行BroadcastReceiver的onReceive()函数时10秒内没有处理完毕 c.主线程在Service的各个生命周期函数时20秒内没有处理完毕。 那么导致ANR的根本原因是什么呢?简单的总结有以下两点: 1.主线程执行了耗时操作,比如数据库操作或网络编程 2.其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。 细分的话,导致ANR的原因有如下几点: 1.耗时的网络访问 2.大量的数据读写 3.数据库操作 4.硬件操作(比如camera) 5.调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候 6.service binder的数量达到上限 7.system server中发生WatchDog ANR 8.service忙导致超时无响应 9.其他线程持有锁,导致主线程等待超时 10.其它线程终止或崩溃导致主线程一直等待 那么如何避免ANR的发生呢或者说ANR的解决办法是什么呢? 1.避免在主线程执行耗时操作,所有耗时操作应新开一个子线程完成,然后再在主线程更新UI。 2.BroadcastReceiver要执行耗时操作时应启动一个service,将耗时操作交给service来完成。 3.避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现 oom 1.数据库的cursor没有关闭。 2.构造adapter没有使用缓存contentview。 3.调用registerReceiver()后未调用unregisterReceiver(). 4.未关闭InputStream/OutputStream。 5.Bitmap使用后未调用recycle()。 6.Context泄漏。 7.static关键字等 稳定性 稳定性问题比较杂,且很多是概率性问题,没有统一处理方式,需要针对具体的问题,具体分析, 必现的问题较易解决,针对当前代码添加各种调试log,一步步debug去定位,过程虽然可能慢点,但一般都会解决。 但针对偶发性的概率问题,则较为麻烦,依赖于大量的测试复现,然后统计 分析当前抓取到的 events、system 等log中,找到复现的步骤,然后去定位。 且针对与这种概率问题,最好能够拿到当时的现场,所以有时候需要将 tombstone 或者anr、crash 转为anr 去处理。 稳定性问题分类 ANR Watchdog Crash、界面、流程异常 Tombstone Panic ANR 系统中发生的ANR 大概分为三类: 1. 输入事件响应超时(主要类型):主线程 (“事件处理线程” / “UI线程”) 在5秒内没有响应输入事件 2. 广播接收处理超时:BroadcastReceiver 没有在10秒内完成返回 备注: 前台广播为10s, 后台广播为 60s, 默认发送的为后台广播 发送广播时,携带Intent.FLAG_RECEIVER_FOREGROUND将广播设置为前台广播 3. Service服务处理超时(小概率事件):Service在20秒(前台5s, 后台service 为200 秒,默认为后台服务)内没有执行完成应用发起的service请求 备注: 前台服务:超时时间为5s, 通过startForegroundService 将指定服务指定为前台服务,会在notification中存在提示信息(像音乐类应用) 被前台进程binder的服务: service 服务被前台进程binder, 那么此服务的超时时间为 20 秒 被后台进程binder的服务: service 服务被后台进程binder,那么此服务的超时时间为 200 秒 以上三类ANR产生的原因可能是各种各样的,但常见的原因可以分为: 1. 程序自身主线程有问题引起的anr,此类问题往往可以直接通过 ANR的 backtrace 定位,此类异常常见的为: a、主进程进行IO文件操作 b、主进程进行大量频繁的数据库操作 c、主进程进入死循环 d、主进程进行联网操作(应该已经限制不允许了) view 1 自定义控件 自定义控件两种方式: 1. 继承ViewGroup 例如:ViewGroup、LinearLayout、FrameLayout、RelativeLayout等。 2. 继承View 例如:View、TextView、ImageView、Button等。 自定义控件基本绘制原理: View的绘制基本上由measure()、layout()、draw()这个三个函数完成1.)测量-Measure过程是计算视图大小,View measure过程相关方法主要有三个:
2.)布局-Layout过程用于设置视图在屏幕中显示的位置,View layout过程相关方法主要要三个:
3.)绘制-draw过程主要用于利用前两步得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。
2自定义控件的回调时序 转存失败重新上传取消 3 view事件分发机制 事件分发的对象是谁- 点击事件 定义: 当用户触摸屏幕时,将产生点击事件(touch事件) touch事件的相关细节被封装成motionevent 事件类型有四种 motionevent.ACTION_DOWN 按下view(所有事件的开始) motionevent.ACTIONUP 抬起view (与DOWN对应) motionevent.ACTION_MOVE 滑动view motionevent.ACTION_CANCEL结束事件(非人为原因) 事件列 从手指接触屏幕至手指离开屏幕,这个过程产生的一系列事件 一般情况下,事件列都是以DOWN事件开始,UP事件结束,中间有无数的MOVE事件。即当一个点击事件motionevent产生后,系统需要把这个事件传递给一个具体的view去处理 事件分发的本质: 将点击事件motionevent传递到某个具体的view与处理的整个过程 即事件的传递过程=分发过程 事件在哪些对象之间进行传递 -- activity,viewgroup,view 事件的分发顺序 -- activity-viewgroup-view 事件分发过程由哪些方法协作完成 --dispatchtouchevent(),onInterceptTouchEvent和ontouchevent 4 surfaceview的ondraw方法会不会执行 在继承SurfaceView的类中即使重写了onDraw()方法也是没有用的,因为SurfaceView虽然继承自View,但并没 重写onDraw(),其子类可以重写onDraw()但并不能自动调用。 解决办法: 首先SurfaceView的子类XXX要 implements SurfaceHolder.Callback,然后定义private SurfaceHolder sh = null; 多线程 1 handler中post与sendmessage的区别 handler.post和handler.sendMessage本质上是没有区别的,都是发送一个消息到消息队列中,只不过post使用方式更简单 2 多线程,线程状态 1. NEW 新建状态:还没有启动,对应代码: Thread thread1 = new Thread(); 此时thread的状态就是new 2. RUNNABLE 运行状态:可能当前已经被cpu调度,也可能正在等待cpu调度,对应代码: Thread thread1 = new Thread(); thread1.start(); 3. BLOCKED 阻塞状态:当前线程等待monitor锁资源进入synchronized方法。 4. WAITING 等待状态,包含三种场景: Object.wait,不带timeout参数 Thread.join,不带timeout参数 LockSupport.park() 5. TIMED_WAITING 等待状态,包含以下场景: Thread.sleep Object.wait,带timeout参数 Thread.join,带timeout参数 LockSupport.parkNanos LockSupport.parkUntil 6. TERMINATED 终结状态:run方法执行结束 3 主线程与子线程hanndler区别---? 4 线程同步问题 什么是线程同步? 当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题 实现同步机制有两个方法: 1。同步代码块: synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据 2。同步方法: public synchronized 数据返回类型 方法名(){} 就是使用 synchronized来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是 this也就是该对象的本身(这里指的对象本身有点含糊,其实就是调用该同步方法的对象)通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征: 1,该类的对象可以被多个线程安全的访问。 2,每个线程调用该对象的任意方法之后,都将得到正确的结果。 3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。 注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。 实现同步机制注意以下几点: 安全性高,性能低,在多线程用。性能高,安全性低,在单线程用。 1,不要对线程安全类的所有方法都进行同步,只对那些会改变共享资源方法的进行同步。 2,如果可变类有两种运行环境,当线程环境和多线程环境则应该为该可变类提供两种版本:线程安全版本和线程不安全版本(没有同步方法和同步块)。在单线程中环境中,使用线程不安全版本以保证性能,在多线程中使用线程安全版本. 线程通讯: 为什么要使用线程通讯? 当使用synchronized 来修饰某个共享资源时(分同步代码块和同步方法两种情况),当某个线程获得共享资源的锁后就可以执行相应的代码段,直到该线程运行完该代码段后才释放对该共享资源的锁,让其他线程有机会执行对该共享资源的修改。当某个线程占有某个共享资源的锁时,如果另外一个线程也想获得这把锁运行就需要使用wait() 和notify()/notifyAll()方法来进行线程通讯了。 Java.lang.object 里的三个方法wait() notify() notifyAll() wait方法导致当前线程等待,直到其他线程调用同步监视器的notify方法或notifyAll方法来唤醒该线程。 wait(mills)方法 都是等待指定时间后自动苏醒,调用wait方法的当前线程会释放该同步监视器的锁定,可以不用notify或notifyAll方法把它唤醒。 notify() 唤醒在同步监视器上等待的单个线程,如果所有线程都在同步监视器上等待,则会选择唤醒其中一个线程,选择是任意性的,只有当前线程放弃对该同步监视器的锁定后,也就是使用wait方法后,才可以执行被唤醒的线程。 notifyAll()方法 唤醒在同步监视器上等待的所有的线程。只用当前线程放弃对该同步监视器的锁定后,才可以执行被唤醒的线程。 5 handler机制和工作原理 andriod提供了Handler 和 Looper 来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。 1)Looper: 一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。 2)Handler: 你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从Message Queue取出)所送来的消息。 3) Message Queue(消息队列):用来存放线程放入的消息。 4)线程:UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue。 1.Handler创建消息每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。消息的创建流程如图所示。
2.Handler发送消息 UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。 Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。 3.Handler处理消息 UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。 6 handler实现流程 7 消息机制流程 在应用启动的时候,会执行程序的入口函数main(),main()里面会创建一个Looper对象,然后通过这个Looper对象开启一个死循环,这个循环的工作是,不断的从消息队列MessageQueue里面取出消息即Message对象,并处理。然后看下面两个问题: 循环拿到一个消息之后,如何处理? 是通过在Looper的循环里调用Handler的dispatchMessage()方法去处理的,而dispatchMessage()方法里面会调用handleMessage()方法,handleMessage()就是平时使用Handler时重写的方法,所以最终如何处理消息由使用Handler的开发者决定。 MessageQueue里的消息从哪来? 使用Handler的开发者通过调用sendMessage()方法将消息加入到MessageQueue里面。 上面就是Android中消息机制的一个整体流程,也是 “Android中Handler,Looper,MessageQueue,Message有什么关系?” 的答案。通过上面的流程可以发现Handler在消息机制中的地位,是作为辅助类或者工具类存在的,用来供开发者使用。 8 消息机制的原理 首先Looper.prepare()创建Looper并初始化Looper持有的消息队列MessageQueue,创建好后将Looper保存到ThreadLocal中方便Handler直接获取。 然后Looper.loop()开启循环,从MessageQueue里面取消息并调用handler的 dispatchMessage(msg) 方法处理消息。如果MessageQueue里没有消息,循环就会阻塞进入休眠状态,等有消息的时候被唤醒处理消息。 再然后我们new Handler()的时候,Handler构造方法中获取Looper并且拿到Looper的MessageQueue对象。然后Handler内部就可以直接往MessageQueue里面插入消息了,插入消息即发送消息,这时候有消息了就会唤醒Looper循环去处理消息。处理消息就是调用dispatchMessage(msg) 方法,最终调用到我们重写的Handler的handleMessage()方法。 进程间通信 1 binder机制,多线程间相互通信 相当于短信或者BB机 Client端发起通信请求–>服务代理Proxy通过Parcel打包数据–>内核空间Binder驱动实现共享数据–>Server端解包Parcel获取数据。这样就实现了Client端到Server端的跨进程通信。 java 1 java虚拟机和android虚拟机区别,dex与class区别 1.Java虚拟机运行的是Java字节码,Dalvik虚拟机运行的是Dalvik字节码。 2.Dalvik可执行文件体积更小 3.Java虚拟机与Dalvik虚拟机架构不同。 Java虚拟机基于栈结构,Dalvil虚拟机基于寄存器架构,数据的访问通过寄存器直接传递,这样的访问方式比基于栈方式要快很多 2 对象序列化 1、序列化是干什么的? 简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。 2、什么情况下需要序列化 a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候; b)当你想用套接字在网络上传送对象的时候; c)当你想通过RMI传输对象的时候; 3、当对一个对象实现序列化时,究竟发生了什么? 在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如: 3 静态方法锁与方法锁区别, static方法调用方式是通过class.fun,而非static方法调用是先new出这个对象,再调用。 static synchronized是类锁,synchronized是对象锁。 对象锁(又称实例锁,synchronized):该锁针对的是该实例对象(当前对象)。 synchronized是对类的当前实例(当前对象)进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”, 类的两个不同实例就没有这种约束了。 每个对象都有一个锁,且是唯一的。 类锁(又称全局锁,static synchronized):该锁针对的是类,无论实例出多少个对象,那么线程依然共享该锁。 static synchronized是限制多线程中该类的所有实例同时访问该类所对应的代码块。(实例.fun实际上相当于class.fun) 两个对象带锁的方法是否可以同时调用? 不能, 发现jvm在执行 方法以前,如果发现该方法前面有对象的synchronized关键字,就现在该对象的ID上加锁,当其他线程执行同时执行这个方法时,会检测改对象ID上是否加锁,如果加锁时就等待锁释放。 4 单例模式与线程安全 单例模式就是说系统中对于某类的只能有一个对象,不可能出来第二个 因此一个类实现单例模式时需要具备以下3个条件: 1)类的内部定义一个该类的静态私有成员变量; 2)构造方法为私有; 3)提供静态工厂方法,供外部获取类的实例; 1.多线程安全单例模式实例一(不使用同步锁) 1 public class Singleton { 2 private static Singleton sin=new Singleton(); ///直接初始化一个实例对象3 private Singleton(){ ///private类型的构造函数,保证其他类对象不能直接new一个该对象的实例4 } 5 public static Singleton getSin(){ ///该类唯一的一个public方法 6 return sin; 7 } 8 } 上述代码中的一个缺点是该类加载的时候就会直接new 一个静态对象出来,当系统中这样的类较多时,会使得启动速度变慢 。现在流行的设计都是讲“延迟加载”,我们可以在第一次使用的时候才初始化第一个该类对象。所以这种适合在小系统。 2.多线程安全单例模式实例二(使用同步方法) 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton (){ 4 5 } 6 public static synchronized Singleton getInstance(){ //对获取实例的方法进行同步 7 if (instance == null) 8 instance = new Singleton(); 9 return instance; 10 } 11 } 上述代码中的一次锁住了一个方法, 这个粒度有点大 ,改进就是只锁住其中的new语句就OK。就是所谓的“双重锁”机制。 3.多线程安全单例模式实例三(使用双重同步锁) 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton (){ 4 } 5 public static Singleton getInstance(){ //对获取实例的方法进行同步 6 if (instance == null){ 7 synchronized(Singleton.class){ 8 if (instance == null) 9 instance = new Singleton(); 10 } 11 } 12 return instance; 13 } 14 15 } crash原因? 逻辑出错了 怎么拿到message? 1 new 2 looper 3 obtain 进程交互方式: binder aidl 广播 contentprovider sp 线程交互---handler 线程保活? 1 像素1*1 activity 2 不死服务 3 广播拉起 4 发通知 view事件分发? inputevent inputreader inputdispatch 运行时 focusapp focuswindow ->null 5s anr inputchannel 没注册上 anr dectorview viewgroup view oom? 1 大图片 --》 需要压缩 2 大文件 3 读流不关闭 4 数据库不关闭 存储方式: 文件 网络 sp 数据库 anr分析---dropbox 查看堆栈,内存信息,包名,进程号,cpu 1 anr时间点 2 什么anr了 ,到底是什么超时了 3 分析代码 good to go作者:qq601517284