Android 常见的问题整理 2022

Android 语音TTS 识别全链路过程

  • 本地录音 =》 ASR识别 =》请求后台语义 =》语义落域分发返回 =》 本地仲裁处理落域分发 =》 TTS播报
  • 下面是语音链路的一些基本思路
  1. 录音 :Android基本录音为48K的采样率 语音这边需要做降采样处理 降采样为16K。通过Android原生录音将音频给到引擎
  2. 唤醒:一般唤醒都是做本地唤醒,所有wakeup唤醒引擎。也可以通过唤醒引擎做一些免唤醒功能
  3. 语音识别,识别分为两种:
    3.1 离线识别,走本地识别引擎,
    优点:识别快
    缺点:需要精准识别,并不能做太多泛化处理。对音频要求比较高
    3.2 在线识别,走云端识别引擎
    优点 :可以模糊匹配,多泛化
    缺点 :网路查的情况下识别很慢
  4. 云端与离线云端技能分发
  5. TTS播报:由云端或本地接收文本进行语音音频合成。进行播报

Android 屏幕适配相关,方案

    1. 通过dp加上自适应布局可以基本解决屏幕碎片化的问题。也是Android推荐使用的屏幕兼容性适配方案。
    1. 根据ui设计图的宽度dp值,算出当前屏幕每dp占当前屏幕多少像素值(也就是density)。
    1. 根据ui设计图的宽度dp值,算出当前屏幕分成ui设计图的宽高度dp份后,每dp占当前屏幕实际多少dp,然后这个实际dp值再根dpi转换成具体的像素值。
    1. 自定义像素适配,以美工的设计尺寸为原始尺寸,根据不同设备的密度 计算出宽和高 参考UIAdapter。如果想显示屏幕的1/3的话就是360了宽度,是根据设计师给出来的宽度进行设置
    1. 百分比适配。这是Google 提出来的一个解决适配方案,想要使用必须添加依赖
 implementation 'com.android.support:percent:28.0.0'

主要就是两个类
PercentRelativeLayout PercentFrameLayout

多线程,线程池 相关

  1. 线程的创建,线程创建的常用方法
  • 1.继承Thread重写run方法
  • 2.实现Runnable重写run方法
  • 3.实现Callable重写call方法
    1.3 实现Callable重写call方法
    实现Callable和实现Runnable类似,但是功能更强大
    可以在任务结束后提供一个返回值,Runnable不行
    call方法可以抛出异常,Runnable的run方法不行
    可以通过运行Callable得到的Fulture对象监听目标线程调用call方法的结果,得到返回值,(fulture.get(),调用后会阻塞,直到获取到返回值)
  1. Android中的四类线程池
    1. Android中最常见的四类具有不同特性的线程池分别为FixThreadPool、CachedThreadPool、ScheduleThreadPool和SingleThreadExecutor
    1. FixThreadPool(一堆人排队上公厕)
public static ExecutorService newFixThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
//使用
Executors.newFixThreadPool(5).execute(r);
  1. 从配置参数来看,FixThreadPool只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。
  2. FixThreadPool其实就像一堆人排队上公厕一样,可以无数多人排队,但是厕所位置就那么多,而且没人上时,厕所也不会被拆迁
  3. 由于线程不会回收,FixThreadPool会更快地响应外界请求,这也很容易理解,就好像有人突然想上厕所,公厕不是现用现建的
    1. SingleThreadPool(公厕里只有一个坑位)
public static ExecutorService newSingleThreadPool (int nThreads){
    return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (1, 1, 0, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );
}
//使用
Executors.newSingleThreadPool ().execute(r);
  1. 从配置参数可以看出,SingleThreadPool只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。
  2. 可以把SingleThreadPool简单的理解为FixThreadPool的参数被手动设置为1的情况,即Executors.newFixThreadPool(1).execute(r)。所以SingleThreadPool可以理解为公厕里只有一个坑位,先来先上。为什么只有一个坑位呢,因为这个公厕是收费的,收费的大爷上年纪了,只能管理一个坑位,多了就管不过来了(线程同步问题)
    1. CachedThreadPool(一堆人去一家很大的咖啡馆喝咖啡)
public static ExecutorService newCachedThreadPool(int nThreads){
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());
}
//使用
Executors.newCachedThreadPool().execute(r);
  1. CachedThreadPool只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务。
  2. 任务队列SynchronousQueue相当于一个空集合,导致任何任务都会被立即执行。
  3. CachedThreadPool就像是一堆人去一个很大的咖啡馆喝咖啡,里面服务员也很多,随时去,随时都可以喝到咖啡。但是为了响应国家的“光盘行动”,一个人喝剩下的咖啡会被保留60秒,供新来的客人使用,哈哈哈哈哈,好恶心啊。如果你运气好,没有剩下的咖啡,你会得到一杯新咖啡。但是以前客人剩下的咖啡超过60秒,就变质了,会被服务员回收掉。
  4. 比较适合执行大量的耗时较少的任务。喝咖啡人挺多的,喝的时间也不长
    1. ScheduledThreadPool(4个里面唯一一个有延迟执行和周期重复执行的线程池)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());
}
//使用,延迟1秒执行,每隔2秒执行一次Runnable r
Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS);
  1. 核心线程数固定,非核心线程(闲着没活干会被立即回收)数没有限制。
  2. 从上面代码也可以看出,ScheduledThreadPool主要用于执行定时任务以及有固定周期的重复任务。

Handler 介绍

  • 由于Android中主线程是不能进行耗时操作的,子线程是不能进行更新UI的。所以就有了handler,它的作用就是实现线程之间的通信。 handler整个流程中,主要有四个对象,handlerMessage,MessageQueue, Looper。当应用创建的时候,就会在主线程中创建handler对象, 我们通过要传送的消息保存到Message中,handler.post handler通过调用sendMessage方法将Message发送到MessageQueue中,Looper对象就会不断的调用loop()方法 不断的从MessageQueue中取出Message交给handler进行处理。从而实现线程之间的通信

说下 handler 原理

  • Handler,Message,looper 和 MessageQueue 构成了安卓的消息机制,handler创建后可以通过 sendMessage 将消息加入消息队列,然后 looper不断的将消息从 MessageQueue 中取出来,回调到 Hander 的 handleMessage方法,从而实现线程的通信。
  1. 在UI线程创建Handler,此时我们不需要手动开启looper,因为在应用启动时,在ActivityThread的main方法中就创建了一个当前主线程的looper,并开启了消息队列,消息队列是一个无限循环,为什么无限循环不会ANR ? 因为应用的整个生命周期就是运行在这个消息循环中的,安卓是由事件驱动的,Looper.loop不断的接收处理事件,每一个点击触摸或者Activity每一个生命周期都是在Looper.loop的控制之下的,looper.loop一旦结束,应用程序的生命周期也就结束了。我们可以想想什么情况下会发生ANR,第一,事件没有得到处理
  2. 事件正在处理,但是没有及时完成,而对事件进行处理的就是looper,所以只能说事件的处理如果阻塞会导致ANR,而不能说looper的无限循环会ANR。
    另一种情况就是在子线程创建Handler,此时由于这个线程中没有默认开启的消息队列,所以我们需要手动调用looper.prepare(),并通过looper.loop开启消息
  3. 主线程Looper从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因此loop的循环并不会对CPU性能有过多的消耗

Activity A跳转Activity B,再按返回键,生命周期执行的顺序?

  • A.onPause() B.onCreate() B.onStart() B.onResume() A.onStop()
  • 另外 如果Activity B是透明的 或者Activity B 并未完全遮住Activity A,那么上述操作点击Activity A 跳转 Activity B 生命周期中A.onStop()是不会被调用的,因为Activity A还可见,所以Activity A不能被停止

View 的绘制流程

  • Activity、Window、DecorView之间关系
 public void setContentView(@LayoutRes int layoutResID) {
        //  将xml布局传递到Window当中
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
  1. 从代码可以看出,Activity的setContentView实质是将View传递到Window的setContentView()方法中,Window的setContenView会在内部调用installDecor()方法创建DecorView
  2. View的绘制是从ViewRootImpl的performTraversals()方法开始,从最顶层的View(ViewGroup)开始逐层对每个View进行绘制操作
  • measure:为测量宽高过程,如果是ViewGroup还要在onMeasure中对所有子View进行measure操作。
  • layout:用于摆放View在ViewGroup中的位置,如果是ViewGroup要在onLayout方法中对所有子View进行layout操作。
  • draw:往View上绘制图像。
  1. View 的绘制流程是 measure -> layout -> draw

View 事件分发机制

  1. View 的事件分发机制主要涉及到以下几个方法
  • dispatchTouchEvent ,这个方法主要是用来分发事件的
  • onInterceptTouchEvent,这个方法主要是用来拦截事件的(需要注意的是 ViewGroup 才有这个方法,View 没有 onInterceptTouchEvent 这个方法
  • onTouchEvent 这个方法主要是用来处理事件的
  • requestDisallowInterceptTouchEvent(true),这个方法能够影响父View是否拦截事件,true 表示父 View 不拦截事件,false 表示父 View 拦截事件
  1. 当触摸事件发生时,首先 Activity 将 TouchEvent 传递给最顶层的 View,TouchEvent最先到达最顶层 view 的 dispatchTouchEvent,然后由 dispatchTouchEvent方法进行分发,

如果dispatchTouchEvent返回true 消费事件,事件终结。
如果dispatchTouchEvent返回 false ,则回传给父View的onTouchEvent事件处理;

如果dispatchTouchEvent返回super的话,默认会调用自己的onInterceptTouchEvent方法。

默认的情况下onInterceptTouchEvent回调用super方法,super方法默认返回false,所以会交给子View的onDispatchTouchEvent方法处理
如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,
如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。

Activity 四种启动模式
  1. standard : 默认启动模式,每开启一个activity就在任务栈中创建一个新的实例.Top single 顶部只有一个 不允许存在两个相同的Activity.使用场景:基本绝大多数地方都可以用。
  2. singleTop: 如果在任务的栈顶正好存有该 Activity的实例,则会通过调用 onNewIntent() 方法进行重用,否则就会同 standard 模式一样,创建新的实例并放入栈顶。即便栈中已经存在了该 Activity 的实例,也会创建新的实例
    当且仅当启动的 Activity 和上一个 Activity 一致的时候才会通过调用 onNewIntent()方法重用 Activity 。使用场景:资讯阅读类 APP 的内容界面。
  3. singleTask : 在同一个任务栈中,如果要启动的目标Activity已经在栈中,则会复用该Activity,并调用其onNewIntent()方法,并且该Activity上面的Activity会被清除,如果栈中没有,则创建新的实例。使用场景:浏览器的主页面,或者大部分 APP 的主页面。
  4. singleInstance: 指定为singleInstance模式的活动会启用一个新的返回栈来管理这个活动。在一个新栈中创建该 Activity 的实例,并让多个应用共享该栈中的该 Activity 实例。一旦该模式的 Activity 实例已经存在于某个栈中,任何应用再激活该 Activity 时都会重用该栈中的实例,是的,依然是调用 onNewIntent()方法。其效果相当于多个应用共享一个应用,不管是谁激活,该 Activity 都会进入同一个应用中。但值得引起注意的是:singleInstance 不要用于中间页面,如果用户中间页面,跳转会出现很难受的问题。 这个在实际开发中我暂未遇到过, Android 系统的来电页面,多次来电都是使用的同一个 Activity

Service启动方式

  1. startService. startService() 启动一个 Service。一旦启动,Service 将一直运行在后台,即使启动这个 Service 的组件已经被销毁。通常一个被 start 的 Service 会在后台执行单独的操作,也并不需要给启动它的组件返回结果。只有当 Service 自己调用 stopSelf() 或者其它组件调用 stopService() 才会终止。
  2. **bindService **. bindService() 来绑定一个 Service。这种方式会让 Service 和启动它的组件绑定在一起,当启动它的组件销毁的时候,Service 也会自动进行 unBind 操作。同一个 Service 可以被多个组件绑定,只有所有绑定它的组件都进行了 unBind 操作,这个 Service 才会被销毁
  3. Service 的生命周期
  • 当调用 startService() 去 start 一个 Service 后,仍然可以 bind 这个 Service。比如:当播放音乐的时候,需要调用 startService() 启动指定的音乐,当需要获取该音乐的播放进度的时候,又需要调用 bindService(),在这种情况下,除非 Service 被 unbind,此前调用 stopService() 和 stopSelf() 都不能停止该 Service

  • 完整生命周期(entire lifetime):从 onCreate() 被调用,到 onDestroy() 返回。和 Activity 类似,一般在 onCreate() 方法中做一些初始化的工作,在 onDestroy() 中做一些资源释放的工作。如,若 Service 在后台播放一个音乐,就需要在 onCreate() 方法中开启一个线程启动音乐,并在 onDestroy() 中结束线程。

  • 活动生命周期(activity lifetime):从 onStartCommand() 或 onBind() 回调开始,由相应的 startService() 或 bindService() 调用。start 方式的活动生命周期结束就意味着完整证明周期的结束,而 bind 方式,当 onUnbind() 返回后,Service 的活动生命周期结束。

  • 是 startService() 还是 bindService() 启动 Service,onCreate() 和 onDestroy() 均会被回调

Service 的 onCreate() 可以执行耗时操作吗?

  • Service 运行在主线程中,它并不是一个新的线程,也不是新的进程,所以并不能执行耗时操作。

如果要在 Service 中执行耗时操作,怎么做?

  1. 使用 AysncTask 或 HandlerThread 来替代 Thread 创建线程。
  2. IntentService 继承于 Service,若 Service 不需要同时处理多个请求,那么使用 IntentService 将是最好选择。只需要重写 onHandleIntent() 方法,该方法接收一个回调的 Intent 参数, 在方法内进行耗时操作,因为它默认开启了一个子线程,操作执行完成后也无需手动调用 stopSelf() 方法,onHandleIntent() 将会自动调用该方法

Service 与 IntentService区别

  1. Service 不是运行在独立的线程,所以不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。
  2. IntentService
  • 可用于执行后台耗时的任务,任务执行后会自动停止。
  • 具有高优先级,适合高优先级的后台任务,且不容易被系统杀死。
  • 可以多次启动,每个耗时操作都会以工作队列的方式在IntentService的onHandleIntent回调方法中执行

Serializable 和Parcelable的区别

  • 平台区别。Serializable是属于 Java 自带的,表示一个对象可以转换成可存储或者可传输的状态,序列化后的对象可以在网络上进行传输,也可以存储到本地。
    Parcelable 是属于 Android 专用。不过不同于Serializable,Parcelable实现的原理是将一个完整的对象进行分解。而分解后的每一部分都是Intent所支持的数据类型。
  • 编写上的区别。Serializable代码量少,写起来方便
    Parcelable代码多一些,略复杂
  • 选择的原则
  1. 如果是仅仅在内存中使用,比如activity、service之间进行对象的传递,强烈推荐使用Parcelable,因为Parcelable比Serializable性能高很多。因为Serializable在序列化的时候会产生大量的临时变量, 从而引起频繁的GC。
  2. 如果是持久化操作,推荐Serializable,虽然Serializable效率比较低,但是还是要选择它,因为在外界有变化的情况下,Parcelable不能很好的保存数据的持续性。
  • 本质的区别
  1. Serializable的本质是使用了反射,序列化的过程比较慢,这种机制在序列化的时候会创建很多临时的对象,比引起频繁的GC、
  2. Parcelable方式的本质是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的类型,这样就实现了传递对象的功能了

Serializable中为什么要设置UID,设置UID与不设置UID值的区别和影响 ?

  • serialVersionUID 是用来辅助序列化和反序列化的过程。 序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID一致才能成功的反序列化
  • serialVersionUID 适用于java序列化机制。简单来说,JAVA序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID于本地相应实体类的serialVersionUID进行比较。如果相同说明是一致的,可以进行反序列化,否则会出现反序列化版本一致的异常,即是InvalidCastException。
  • 具体序列化的过程 :序列化操作时会把系统当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会自动检测文件中的serialVersionUID,判断它是否与当前类中的serialVersionUID一致。如果一致说明序列化文件的版本与当前类的版本是一样的,可以反序列化成功,否则就失败;
  • serialVersionUID有两种显示的生成方式:
  1. 默认的1L,比如:private static final long serialVersionUID = 1L;
  2. 根据包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等诸多因子计算得出的,极度复杂生成的一个64位的哈希字段。基本上计算出来的这个值是唯一的。比如:private static final long serialVersionUID = xxxxL;
    注意:显示声明serialVersionUID可以避免对象不一致

内存泄漏的场景和解决办法

  1. 非静态内部类的静态实例
    非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类
  2. 多线程相关的匿名内部类和非静态内部类
    匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面退出时结束线程中的任务
  3. Handler内存泄漏
    Handler导致的内存泄漏也可以被归纳为非静态内部类导致的,Handler内部message是被存储在MessageQueue中的,有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,如果handler是非静态的,就会导致它的外部类无法被回收,解决办法是1.使用静态handler,外部类引用使用弱引用处理2.在退出页面时移除消息队列中的消息
  4. Context导致内存泄漏
    根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收
  5. 静态View导致泄漏
    使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收,解决办法是在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity)
  6. WebView导致的内存泄漏
    WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题,通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉
  7. 资源对象未关闭导致
    如Cursor,File等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为null
  8. 集合中的对象未清理
    集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的
  9. Bitmap导致内存泄漏
    bitmap是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态变量持有大的bitmap对象
  10. 监听器未关闭
    很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也需要及时移除

EventBus原理

  1. 主要是维护了几个数组,然后根据对应的key找到对应的注册对象,通过放射的方式调用对应的方法。
  2. EventBus 2.x 是采用反射的方式对整个注册的类的所有方法进行扫描来完成注册,当然会有性能上的影响。EventBus 3.0 中EventBus提供了EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析、处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快
/注册事件
EventBus.getDefault().register(this);
//注册方法
@Subscribe
public void event(BaseEventBusBeaan message) {
  LogUtils.d("EventBusActivity event");
}
 
//发送事件
EventBus.getDefault().post(new BaseEventBusBeaan("123", new Bundle()));
 
//反注册
EventBus.getDefault().unregister(this);

总结一下大概的流程

  1. 通过apt在编译期将所有被 @Subscribe注解的函数添加到MyEventBusIndex对象中。
  2. register过程中生成subscriptionsByEventType的数据。
  3. post过程中通过subscriptionsByEventType数据查找对应的函数,然后再通过反射的方式调用。

优先级的问题

这个问题也十分简单,只需要在插入数据的时候,做下优先级判断即可。

Android 热更新 流程和原理

  • 一个完整的项目应该有如下分支:
    develop分支---- 这个分支中放的是线上的发布版本。
    bugfix分支---- 这个是热更新分支,由develop中迁出。
    master分支---- 这个是开发中的分支
  • 热更新应当按照如下步骤进行:
  1. 线上检测到严重的crash
  2. 从develop中拉出一个线上的最新版,在bugfix分支上进行问题的修复
  3. jenkins构建和补丁的生成
  4. app通过推送或主动拉取补丁文件
  5. 将bugfix代码合到master上,保证以后不会出现该问题
  • 主流热更新框架介绍
  1. Dexposed :该框架是阿里巴巴开源的一个Android平台下的无侵入的运行时AOP(面向方向编程)框架。基于以前开源的一个Xposed框架实现的。Dexposed框架基于Hook技术实现功能,不仅可以hook你自己的程序代码,也可以hook你的应用程序中调用的Android框架中的函数。基于动态类加载技术,运行中的app可以加载一小段经过编译的Java的代码,而且在不需要重写APP的前提下,就可以实现修改APP
  2. AndFix:该框架同样出自阿里。与Dexposed不是同一个团队。AndFix也是基于Xposed思想。而相比第一个,它是一个更纯粹的热修复的框架。
  3. Nuwa:它其实是基于类加载器ClassLoader加载Dex文件。如果多个Dex文件存在相同的类,那么排在前面的Dex文件就将优先被选择。这是热更新最主要的思想,它通过不断地去轮询,去遍历Dex的一个数组,然后把我们需要修改的类的dex文件加到最前面,这样当轮询遍历时就不会加载有问题的那个类。
  • 热更新的原理
  1. Android的类加载机制 :Android的类加载器主要有这样两个:PathClassLoader和DexClassLoader。PathClassLoader主要用于加载系统的类和应用类,DexClassLoader主要加载Dex文件,Jar文件,apk文件等。
  2. 热修复机制:在BaseClassLoader中会创建一个dexElements数组,然后我们会通过ClassLoader遍历这个数组,加载这个数组中的dex文件。这样当BaseClassLoader加载到正确的类以后,就不会去加载有Crash的那个类。因此我们就将这个有问题修复后的类放入Dex文件当中,让这个Dex文件排在dexElements前面。这样BaseClassLoader就不会加载到处于后面的那个Dex文件。这样就完成了整个热修复过程。

Android线程间通信四种方式:

    1. 通过Handler机制
      主线程中定义Handler,子线程发消息,通知Handler完成UI更新,Handler对象必须定义在主线程中,如果是多个类直接互相调用,就不是很方便,需要传递content对象或通过接口调用。另外Handler机制与Activity生命周期不一致的原因,容易导致内存泄漏,不推荐使用。
    1. runOnUiThread方法,用Activity对象的runOnUiThread方法更新,在子线程中通过runOnUiThread()方法更新UI,强烈推荐使用。
  • 3.View.post(Runnable r)这种方法更简单,但需要传递要更新的View过去,推荐使用
    1. AsyncTask,即异步任务,是Android给我们提供的一个处理异步任务的类.通过此类,可以实现UI线程和后台线程进行通讯,后台线程执行异步任务,并把结果返回给UI线程

Android中有哪些进程间通信方式?

  • Binder 简单易用 只能传输Bundle支持的数据类型 四大组件间的进程间通信
    文件共享 简单易用 不适用高并发场景,并且无法做到进程间即时通信 适用于无关发的情况下,交换简单的数据,对实时性要求不高的场景。
  • AIDL功能强大,支持一对多实时并发通信 使用稍复杂,需要处理好线程间的关系 一对多通信且有RPC需求
  • Messenger功能一般,支持一对多串行通信,支持实时通信 不能很好地处理高并发的情形,不支持RPC,由于数据通过Message传输,因此只能传输Bundle支持的数据类型 低并发的一对多实时通信,无RPC需求,或者无需要返回结果的RPC需求
    -ContentProvider支持一对多的实时并发通信,在数据源共享方面功能强大,可通过Call方法扩展其它操作 可以理解为受约束的AIDL,主要提供对数据源的CRUD操作 一对多的进程间数据共享
  • BroadcastReceiver操作简单,对持一对多实时通信 只支持数据单向传递,效率低且安全性不高 一对多的低频率单向通信
  • Socket功能强大,可通过网络传输字节流,支持一对多实时并发通信 实现细节步骤稍繁琐,不支持直接的RPC 网络间的数据交换

websocket 和 http相关

    1. 什么是 websocket
      websocketHTML5的一种新协议,允许服务器想客户端传递信息,实现浏览器和客户端双工通信。
    1. websocket特点
      (1)与http协议有良好的兼容性;
      (2)建立在TCP协议之上,和http协议同属于应用层;
      (3)数据格式比较轻量,性能开销小,通信高效;
      (4)可以发送文本,也可以发送二进制;
      (5)没有同源限制,可以与任意服务器通信。
    1. httpwebsocket的区别
      3.1 http协议是短链接,因为请求之后,都会关闭连接,下次请求需要重新打开链接。
      3.2 websocket协议是一种长连接,只需要通过一次请求来初始化连接,然后所有请求和响应都是通过TCP链接进行通信。
    1. 特点
      最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
      其他特点包括:
      (1)建立在 TCP 协议之上,服务器端的实现比较容易。
      (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
      (3)数据格式比较轻量,性能开销小,通信高效。
      (4)可以发送文本,也可以发送二进制数据。
      (5)没有同源限制,客户端可以与任意服务器通信。
      (6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL

持续更新中 ...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容