Android 启动模式

1/standard: 标准模式

特点: 无论栈内是否存在, 都会重新创建一个新的 Activity
使用场景: 详情页面

2/singleTop: 栈顶复用模式

特点: 如果栈顶存在就复用, 否则就重新创建
使用场景: 消息推送页面, 详情页面

3/singleTask: 栈内复用模式

特点: 如果栈内存在就复用, 并清理掉之上的所有页面
使用场景: app 主页

4/singleInstance: 单实例模式

特点: 当前模式创建的页面会存放在单独的栈内, 创建时会寻找是否存在单纯的栈并且目标在栈内, 存在就复用, 不存在就创建一个新的栈和新的页面, 并将页面放至新的栈内
使用场景: 这个经常使用于系统中的应用,比如Launch、锁屏键的应用等等,整个系统中仅仅有一个!所以在我们的应用中一般不会用到。了解就可以。

5/复用 Activity 关键点

singleTop/singleTask/singleInstance 模式下复用 Activity 时, 被复用的 Activity 会先调用 onNewIntent 方法, 此方法有一个需要注意的地方就是调用 setIntent() 方法更新 intent , 否则 getIntent() 获取到的就是旧的.

屏幕适配

1/使得布局元素自适应屏幕尺寸

在常用的布局中尽量使用 wrap_content/match_parent 还有”线性布局”和”约束布局”里的”权重”.
尽量使用”约束布局”来优化减少布局层级

2/根据屏幕的配置来加载相应的UI布局

使用限定符来辅助适配.
限定符类型: 尺寸限定符/最小宽度限定符/布局别名/屏幕方向限定符
常用方式: 尺寸限定符/最小宽度限定符

  • 尺寸限定符: 根据目标硬件的具体尺寸匹配 res/**-高度x宽度 , 对于没有匹配到的会使用默认的文件 , 所需匹配文件相对较多
  • 最小宽度限定符: 根据目标硬件的对应的最大宽度 dp 匹配 res/**-sw最大宽度dp , 对于没有匹配到的会就近向下匹配 , 所需匹配文件相对较少

    3/关于图片适配

    Android SDK会根据屏幕密度自动选择对应的资源文件进行渲染加载(自动渲染)
    假设你只在xhpdi文件夹下有对应的图片资源文件(mdpi文件夹是空的),那么SDK会去xhpdi文件夹找到相应的图片资源文件,然后将原有大像素的图片自动缩放成小像素的图片,于是大像素的图片照样可以在小像素分辨率的手机上正常显示。
    所以理论上来说只需要提供一种分辨率规格的图片资源就可以了, 根据市场统计 xhdpi 应该是首选.

    4/主流屏幕适配框架(AndroidAutoSize)

    原理: 主要是通过替换 DisplayMetrics 类中的三个主管缩放的属性, 来匹配设计稿的设计尺寸.

  • density : 逻辑密度

  • densityDpi : 每英寸的屏幕密度
  • scaledDensity : 字体的缩放密度

三者通常都是相同的. AAS 框架主要就是通过计算和修改这三个参数来进行屏幕适配.
优点:

  • 使用成本非常低,操作非常简单
  • 侵入性非常低,该方案和项目完全解耦
  • 可适配三方库的控件和系统的控件

缺点:

Android 消息机制

Android 消息机制涉及的类包含 Handler/Message/MessageQueue/Looper/ThreadLocal
Handler 关联 Message 和 MessageQueue, 通过 Handler 将 Message 发送到 MessageQueue, 由 MessageQueue 进行排序管理, 后由 looper 轮询从 MessageQueue 处获取 Message. looper 的获取跟 ThreadLocal 关联, ThreadLocal 会为每个线程创建 map 用来进行线程间数据隔离, 获取 looper 的时候通过 ThreadLocal 判断所在线程, 获取所在线程的 looper.

Android 消息机制关联的四大概念:

  • ThreadLocal
  • MessageQueue
  • Looper
  • Handler

    1/ThreadLocal

    作用: 线程间数据隔离, ThreadLocal 中存储的数据只能在当前线程中获取
    使用示例: ```java import java.util.concurrent.atomic.AtomicInteger;

public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0);

  1. // Thread local variable containing each thread's ID
  2. private static final ThreadLocal<Integer> threadId =
  3. new ThreadLocal<Integer>() {
  4. @Override protected Integer initialValue() {
  5. return nextId.getAndIncrement();
  6. }
  7. };
  8. // Returns the current thread's unique ID, assigning it if necessary
  9. public static int get() {
  10. return threadId.get();
  11. }

}

  1. 关键方法:
  2. - set/get: 设置/获取
  3. - initialValue: 设置默认值
  4. - remove: 清空本地存储
  5. <a name="PpmRP"></a>
  6. ### 2/MessageQueue
  7. 作用: 用来管理 Message, Message 之间是单链表结构存储的<br />关键方法:
  8. - next: 取出 message 链表的下一个值
  9. - quit: 退出, 销毁消息链表
  10. - enqueueMessage: 排序插入 message 到链表
  11. - remove**: 从消息链表中删除 message
  12. <a name="hzZqr"></a>
  13. ### 3/Looper
  14. 作用: 用于通过 MessageQueue 循环获取 Message, 通过 Message.target.dispatchMessage 回调, Message 交于 Handler 处理. _**Looper会在没有消息的时候阻塞当前线程,释放CPU资源,等到有消息到来的时候,再唤醒主线程**_<br />使用示例:
  15. ```java
  16. class LooperThread extends Thread {
  17. public Handler mHandler;
  18. public void run() {
  19. Looper.prepare();
  20. mHandler = new Handler() {
  21. public void handleMessage(Message msg) {
  22. // process incoming messages here
  23. }
  24. };
  25. Looper.loop();
  26. }
  27. }

关键方法:

  • prepare: 初始化方法, 创建一个 looper, 并将 looper 与所在线程通过 ThreadLocal 关联起来
  • loop: 启动循环获取 message

    4/Handler

    作用: 发送/接收处理 Message
    使用示例:

    1. class TestActivity extends AppCompatActivity {
    2. public Handler mHandler;
    3. public void xxx() {
    4. mHandler = new Handler() {
    5. public void handleMessage(Message msg) {
    6. // process incoming messages here
    7. }
    8. };
    9. }
    10. }

    关键方法:

  • obtainMessage: 创建 Message

  • sendXXX: 发送 Message

    5/Handler 如何处理线程切换的?

    handler 创建于子线程之外, 属于共享变量, handler 可在子线程里发送消息, 在 looper 的 loop 方法里将 message 分发给 handler 的 handleMessage 方法. 由于 handleMessage 方法在主线程内, 所以就自然完成了子线程到主线程的线程切换.

    Bitmap

    1/内存计算方式

    Android 中的图片会根据屏幕密度对图片进行对应的缩放, 相同的图片在 res 中不同的 dpi 文件夹中被加载到内存后所占的大小都不相同. 关键在于计算出图片在 Android 中进行缩放后的实际加载尺寸和加载图片所用的格式.

像素点大小:
常见的像素点有

  • ARGB_8888:4个字节
  • ARGB_4444、ARGB_565:2个字节

资源文件位置:
不同dpi对应存放的文件夹 整理 - 图1

实际尺寸计算方式:
横向像素点 = 图片宽度 (设备独立像素密度/对应 dpi 文件夹的最大值) + 0.5f
纵向像素点 = 图片高度
(设备独立像素密度/对应 dpi 文件夹的最大值) + 0.5f
内存大小 = 横向像素点 纵向像素点 像素点所占大小

2/图片压缩方式

2.1/质量压缩

特点: 质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位深及透明度等,来达到压缩图片的目的,这也是为什么该方法叫质量压缩方法。那么,图片的长,宽,像素都不变,那么bitmap所占内存大小是不会变的。

  1. /**
  2. * 质量压缩方法
  3. * @param image
  4. * @return
  5. */
  6. public static Bitmap compressQuality(Bitmap image) {
  7. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  8. // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
  9. image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
  10. int quality = 90;
  11. // 循环判断如果压缩后图片是否大于100kb,大于继续压缩
  12. while (baos.toByteArray().length / 1024 > 100) {
  13. // 重置baos即清空baos
  14. baos.reset();
  15. // 这里压缩options%,把压缩后的数据存放到baos中
  16. image.compress(Bitmap.CompressFormat.JPEG, quality, baos);
  17. // 每次都减少10
  18. quality -= 10;
  19. }
  20. image.recycle();
  21. // 把压缩后的数据baos存放到ByteArrayInputStream中
  22. ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
  23. // 把ByteArrayInputStream数据生成图片
  24. Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
  25. return bitmap;
  26. }

2.2/采样率压缩

特点: 采样率压缩是改变了图片的像素,他是通过先读取图片的边,然后在自己设定图片的边,然后根据设定,读取图片的像素。在读取的时候,并不是所有的像素都读取,而是由选择的。所以这种方式减少了像素的个数,能改变图片在内存中的占用大小。

  1. /**
  2. * 采样率压缩
  3. */
  4. public void compressInSampleSize() {
  5. BitmapFactory.Options options = new BitmapFactory.Options();
  6. // 根据实际宽高计算出的采样率, 一般为 2 的次幂
  7. options.inSampleSize = 2;
  8. Bitmap bm = BitmapFactory.decodeFile("文件 path", options);
  9. }

2.3/缩放压缩

特点: 将 bitmap 通过 Matrix 设置缩放参数, 之后进行缩放

  1. Matrix matrix = new Matrix();
  2. matrix.setScale(0.5f, 0.5f);
  3. bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(),
  4. bit.getHeight(), matrix, true);
  5. Log.i("wechat", "压缩后图片的大小" + (bm.getByteCount() / 1024 / 1024)
  6. + "M宽度为" + bm.getWidth() + "高度为" + bm.getHeight());

2.4/RGB 压缩

  1. BitmapFactory.Options options2 = new BitmapFactory.Options();
  2. options2.inPreferredConfig = Bitmap.Config.RGB_565;
  3. bm = BitmapFactory.decodeFile(Environment
  4. .getExternalStorageDirectory().getAbsolutePath()
  5. + "/DCIM/Camera/test.jpg", options2);
  6. Log.i("wechat", "压缩后图片的大小" + (bm.getByteCount() / 1024 / 1024)
  7. + "M宽度为" + bm.getWidth() + "高度为" + bm.getHeight());

3/图片高效加载方式

Bitmap的高效加载在Glide中也用到了,思路:

  1. 获取需要的长和宽,一般获取控件的长和宽。
  2. 设置BitmapFactory.Options中的inJustDecodeBounds为true,可以帮助我们在不加载进内存的方式获得Bitmap的长和宽。
  3. 对需要的长和宽和Bitmap的长和宽进行对比,从而获得压缩比例,放入BitmapFactory.Options中的inSampleSize属性。
  4. 设置BitmapFactory.Options中的inJustDecodeBounds为false,将图片加载进内存,进而设置到控件中。

链接:https://juejin.cn/post/6844904138019438605