Mark的私人博客

数风流人物,还看今朝

一、Activity启动模式

  • 1、standard 标准模式
  • 2、singleTop 栈顶复用模式 (例如:推送点击消息页面)
  • 3、singleTask 栈内复用模式 (例如: 首页)
  • 4、singleInstance 单例模式 (单独位于一个任务栈中,例如:拨打电话页面)
    1. singleInstancePerTask android12的时候新加入

二、序列化 Serializable 和 Parcelable

  • Serializable: Java 序列化方式,适用于存储和网络传输,serializableUID 用于确定反序列化和类版本是否一致,不一致时反序列化失败
  • Parcelable: Android 序列化方式,适用于组件通信数据传输,性能高

Java中的序列化方式Serializable效率比较低,重要是以下原因:

  • 1、Serializable在序列化过程中会创建大量的临时变量,这样会造成大量的GC
  • 2、Serializable使用了大量的反射,而反射操作耗时
  • 3、Serializable使用了大量的IO操作,也影响耗时

所以Android重新设计了一种序列化的方式,结合Binder的方式,对上述三点进行了优化,一定程度上提高了序列化和返序列化的效率。

三、进程知识点

IPC进程通信

  • Intent、Bundle:要求传输数据能被序列化,实现 Parcelable、 Serializable,适用于四大组件通信
  • 文件共享:适用于交换简单的数据实时性不高的场景
  • AIDL:AIDL 接口实质上是系统提供给我们可以方便实现Binder的工具
  • Messenger:基于 AIDL 实现,服务端的串行处理,主要用于传递消息,适用于低并发一对多的通信
  • ContentProvider: 基于 Binder 实现,适用于一对多的进程间的数据共享。(通讯录、短信等)
  • Socket:TCP、UDP,适用于网络数据交换

进程保活

进程被杀的原因:

  • 切换到后台内存不足时被杀

  • 切换到后台厂商省电机制杀死

  • 用户主动清理

    保活方式:

  • 1、 Activity提权:挂一个 1像素 Activity将进程优先级提高到前台进程

  • 2、 Service提权: 启动一个前台服务,(API>18会有正在运行的通知栏)

  • 3、 广播拉活。(监听 开机 等系统广播)

  • 4、 Service拉活

  • 5、 JobScheduler 定时任务拉活。(Android 高版本不行)

  • 6、 双进程保活

  • 7、 监听其他大厂广播 (如tx baidu 全家桶互相拉活)

4、内存泄露

  • 构造单列的时候尽量别用Activity的引用;
  • 静态引用时注意应用对象的置空或者少用静态引用;
  • 使用静态内部类+软引用代替非静态内部类;
  • 即使取消广播或者观察者注册、耗时任务、属性动画在Activity销毁时记得cancel;
  • 文件流、Cursor等资源操作即使关闭;
  • Activity销毁时WebView的移除和销毁。

5、Android进程间的通信

  • bundle:由于Activity、service、receiver都是可以通过 Intent 来携带Bundle数据传输的,所以我们在一个进程中通过Intent将携带数据的Bundle发送到另一个进程的组件;(bundle只能传输三种数据类型,一是键值对的形式,二是键为String的类型,三是值为Parcelable类型)
  • ContentProvider: contentprovicer是安卓四大组件之一,以表格 的形式来存储数据,提供给外界,及ContentProvider 可以跨进程访问其他应用程序中的数据。
  • 文件:两个进程可以到同一个文件去交流数据,我们不仅可以保存文本文件,还可以将对象持久化到文件中,从另一个文件恢复。要注意的是,当并发读/写时可能出现并发的问题。
  • Broadcast:Broadcast可以想Android系统中所有的应用程序发送广播,需要跨进程通信的应用程序可以监听这些广播。
  • AIDL: AIDL通过定义服务端暴露的接口,以提供给客户端来通用,AIDL使用服务器可以并行处理。
  • Messenger:Messenger封装了AIDL之后只能串行运行,所以Messenger一般作用消息传递
  • Socket:

6、Android 线程通信

Handler 和 AsyncTask (AsyncTask:异步任务,内部封装了Handler)

Handler线程间通信
作用:

线程之间的消息通信

流程:

主线程默认实现了Looper (调用loop.prepare方法 向sThreadLocal中set一个新的looper对象, looper构造方法中又创建了MsgQueue) 手动创建Handler ,调用 sendMessage 或者 post (runable) 发送Message 到 msgQueue ,如果没有Msg 这添加到表头,有数据则判断when时间 循环next 放到合适的 msg的next 后。Looper.loop不断轮训Msg,将msg取出 并分发到Handler 或者 post提交的 Runable 中处理,并重置Msg 状态位。回到主线程中 重写 Handler 的 handlerMessage 回调的msg 进行主线程绘制逻辑。

问题:

1、Handler 同步屏障机制:通过发送异步消息,在msg.next 中会优先处理异步消息,达到优先级的作用。

2、Looper.loop 为什么不会卡死:为了app不挂掉,就要保证主线程一直运行存在,使用死循环代码阻塞在msgQueue.next()中的nativePollOnce()方法里 ,主线程就会挂起休眠释放cpu,线程就不会退出。Looper死循环之前,在ActivityThread.main()中就会创建一个 Binder 线程(ApplicationThread),接收系统服务AMS发送来的事件。当系统有消息产生(其实系统每 16ms 会发送一个刷新 UI 消息唤醒)会通过epoll机制 向pipe管道写端写入数据 就会发送消息给 looper 接收到消息后处理事件,保证主线程的一直存活。只有在主线程中处理超时才会让app崩溃 也就是ANR。

3、Messaage复用:将使用完的Message清除附带的数据后, 添加到复用池中 ,当我们需要使用它时,直接在复用池中取出对象使用,而不需要重新new创建对象。复用池本质还是Message 为node 的单链表结构。所以推荐使用Message.obation获取 对象。

剩余链接

MVVM是什么

是 Model-View-ViewModel 的简写。MVVM与MVP的结构还是很相似的,就是将Presenter升级为ViewModel。在MVVM中,View层和Model层进行了双向绑定(即Data Binding),所以Model数据的更改会表现在View上,反之亦然。ViewModel就是用来根据具体情况处理View或Model的变化。

Android中的MVVM含义

  • Model:实体类(数据的获取、存储、数据状态变化)。
  • View:布局文件+Activity。
  • ViewModel: 关联层,将Model和View进行绑定,Model或View更改时,实时刷新对方。

工作原理

  • 1.View 接收用户交互请求
  • 2.View 将请求转交给ViewModel
  • 3.ViewModel 操作Model数据更新
  • 4.Model 更新完数据,通知ViewModel数据发生变化
  • 5.ViewModel 更新View数据

MVVM的优点

  • 1.提高可维护性。Data Binding可以实现双向的交互,使得视图和控制层之间的耦合程度进一步降低,分离更为彻底,同时减轻了Activity的压力。
  • 2.简化测试。因为同步逻辑是交由Binder做的,View跟着Model同时变更,所以只需要保证Model的正确性,View就正确。大大减少了对View同步更新的测试。
  • 3.ViewModle易于单元测试。

MVVM的缺点

  • 1.对于简单的项目,使用MVVM有点大材小用。
  • 2.对于过大的项目,数据绑定会导致内存开销大,影响性能。
  • 3.ViewModel和View的绑定,使页面异常追踪变得不方便。有可能是View出错,也有可能是ViewModel的业务逻辑有问题,也有可能是Model的数据出错。

MVP和MVC的最大区别

在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的交互都发生在Presenter内部,而在MVC中View直接从Model中读取数据而不是通过 Controller。

如何选取框架

适合自己的才是最好的

原文链接

观察者模式

指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。


介绍

  • 优点

    • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
    • 目标与观察者之间建立了一套触发机制。
  • 缺点

    • 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
    • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
  • 使用场景

    • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
    • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
    • 一个对象必须通知其他对象,而并不知道这些对象是谁。
    • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
  • 注意事项

    • 1、JAVA 中已经有了对观察者模式的支持类。
    • 2、避免循环引用。
    • 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

实现

观察者模式的主要角色如下。

  • 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
  • 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
  • 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
  • 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

观察者模式的实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class ObserverPattern {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}

//抽象目标
abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();

//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}

//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}

public abstract void notifyObserver(); //通知观察者方法
}

//具体目标
class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");

for (Object obs : observers) {
((Observer) obs).response();
}

}
}

//抽象观察者
interface Observer {
void response(); //反应
}

//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}

//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}

建造者模式

建造者模式(Builder Pattern)使用多个简单的对象构建一个复杂的对象,这种类型的设计模式就属于创建型模式,它提供了一种创建对象的最佳方式。
一个建造者(Builder)类会一步一步构造最终的对象,该 Builder 类是独立于其他的对象的

阅读全文 »

场景描述:产品故意整事,狂点一个页面跳转按钮,然而那个页面的展示时需要初始化一个第三方的SDK,导致一个启动模式为 android:launchMode="singleTop"的页面Activity重复新建了多次。

1、解决思路查看源码:startActivity(intent);

阅读全文 »

[toc]

ConnectionFactory的作用

  • 利用工厂模式提升代码的额重要性
  • 封装注册数据库的驱动和获得数据库的连接
  • 利用配置文件减少硬编码,便于维护

ConnectionFactory的开发

  • 配置文件 jdbcinfo.properties

    1
    2
    3
    4
    oracle.driver=oracle.jdbc.driver.OracleDriver  //数据库驱动
    oracle.url=jdbc:oracle:thin:@localhost:1521:helowin //数据库地址 helowin:数据库的名字
    oracle.user=mark //管理账户
    oracle.password=chenyunlin //密码
    阅读全文 »

JDBC的API结构和使用流程

1
2
3
4
5
6
7
8
9
10
graph TB
A0[DriverManager] --> B0[Driver]
A0[DriverManager] --> B1[Driver]
B0[Driver] --> C0[Connection]
B1[Driver] -->C1[Connection]
B1[Driver] -->C2[Connection]
C0[Connection]-->D0[Statement]
C0[Connection]-->D1[Statement]-->E0[ResultSet]
C0[Connection]-->D2[Statement]-->E1[ResultSet]
C1[Connection]-->D3[Statement]-->E2[ResultSet]
阅读全文 »

原文地址:大自然的流风


Mac电脑maven安装与配置

  1. 下载:http://maven.apache.org/download.cgi

  2. 安装:解压下载好的maven的文件,解压到你想要的文件夹下。

  3. 配置:打开终端输入命令 sudo vim ~/.bash_profile (编辑环境变量配置文件)

    1
    2
    export MAVEN_HOME=maven文件夹路径
    export PATH=$PATH:$MAVEN_HOME/bin

    小技巧

    复制文件夹路径方法:点击文件夹,然后使用组合快捷键:command + option + c 就会把路径复制到粘贴板了。
    或者把文件夹拖放到控制台也可以显示出来
    让mac文件夹显示文件夹和文件路径,执行命令:defaults write com.apple.finder _FXShowPosixPathInTitle -bool YES

  4. :wq退出并保存当前文件,source .bash_profile,按下Enter键使bash_profile生效。
    mvn -v查看是否成功

    1
    2
    3
    4
    5
    6
    @Markxiansheng ~ % mvn -v
    Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
    Maven home: /Users/mark/Library/maven/apache-maven-3.8.1
    Java version: 11.0.11, vendor: Amazon.com Inc., runtime: /Users/mark/Library/Java/JavaVirtualMachines/corretto-11.0.11/Contents/Home
    Default locale: zh_CN_#Hans, platform encoding: UTF-8
    OS name: "mac os x", version: "10.15.7", arch: "x86_64", family: "mac"