【案例分享】屏幕适配导致的一个crash的问题。

问题背景

学而思应用在设备A上必现crash,在B设备上没有问题。A和B是相同的厂商相同的系统版本。

问题原因

首先看crash的日志

 W/Bitmap: Called getByteCount() on a recycle()'d bitmap! This is undefined behavior!
 ? W/System.err: java.lang.IllegalStateException: Can't compress a recycled bitmap
 ? W/System.err:     at android.graphics.Bitmap.checkRecycled(Bitmap.java:514)
 ? W/System.err:     at android.graphics.Bitmap.compress(Bitmap.java:1560)
 ? W/System.err:     at com.tal.xes.app.share.WeixinUtil.bmpToByteArray(WeixinUtil.java:37)
 ? W/System.err:     at com.tal.xes.app.share.XesShareTool.sendWXMessage(XesShareTool.java:364)
 ? W/System.err:     at com.tal.xes.app.share.XesShareTool.shareLink(XesShareTool.java:316)
 ? W/System.err:     at com.xes.jazhanghui.activity.mvp.web.AppWebViewActivity.lambda$showShare$74$AppWebViewActivity(AppWebViewActivity.java:3513)
 ? W/System.err:     at com.xes.jazhanghui.activity.mvp.web.-$$Lambda$AppWebViewActivity$ky7U6HBCmrioBHyzGwtb6oeVh-8.onClick(Unknown Source:2)
 ? W/System.err:     at com.tal.xes.app.share.XesShareTool$4.onClick(XesShareTool.java:513)
 ? W/System.err:     at android.view.View.performClick(View.java:7603)
 ? W/System.err:     at android.view.View.performClickInternal(View.java:7577)
 ? W/System.err:     at android.view.View.access$3800(View.java:865)
 ? W/System.err:     at android.view.View$PerformClick.run(View.java:29384)
 ? W/System.err:     at android.os.Handler.handleCallback(Handler.java:955)
 ? W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
 ? W/System.err:     at android.os.Looper.loopOnce(Looper.java:206)
 ? W/System.err:     at android.os.Looper.loop(Looper.java:296)
 ? W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:8985)
 ? W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
 ? W/System.err:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:569)
 ? W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:976)

反编译学而思代码:

在这里插入图片描述

代码流程分析:

1。在屏幕密度为400dpi(A)的设备上,Bitmap.createScaledBitmap传入的参数bitmap被decodeResource后的大小已经是目前指定的150,150,createScaledBitmap最终走到createBitmap,createBitmap判断如果传入的长宽大小和source一致,则返回source本身。
2。bitmap被释放。createScaledBitmap=bitmap 导致createScaledBitmap也是被释放的。
3。 createScaledBitmap 也是被释放的。所以导致了第三步的异常。
在这里插入图片描述

BitmapFactory.decodeResource

上节中Bitmap source是通过decodeResource加载进来的。

    Bitmap bimap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background);

android不同的drawable目录,是针对不同的屏幕密度的。
反编译可以看到原图是在xxhdpi目录下,原始的图片大小是180*180,xxhdpi是针对480屏幕密度的。

BitmapFactory加载图片的时候,有两个重要的属性值。
inTargetDensity当前手机的densityDpi,即当前的手机运行环境的手机密度
**inDensity **是把从资源中读取的density赋值给它,其实就是我们的图片资源所在drawable对应的density,也就是下面表格的对应的值:
在这里插入图片描述

举例原始图片在不同屏幕密度上都显示60dp的显示效果:

  • 原始图片180*180 在xxhdpi inDensity = 480 inTargetDensity = 480 1dp=3px 180/3=60dp
  • A设备 150*150 inDensity = 480 inTargetDensity = 400 1dp=2.5px 150/2.5 = 60 显示60DP的尺寸
    每英寸 276.462像素 60dp =150/276.46 = 0.54英寸
  • B设备 105*105 inDensity = 480 inTargetDensity = 280 1dp=1.75px 105/1.75=60 显示60DP的尺寸
    每英寸 195.384像素 60dp =105/195.38 = 0.537英寸

px = dp * (dpi / 160)。
因为原始图片是180*180并且Dpi是480的图片,所以要显示在60dp的长宽高上面。所以在A设备和B设备上也要转换到60DP的长度。
Dpi表示每英寸的像素数,decodeResource也会针对不同的inTargetDensity做相应的大小转换。

所以decodeResource会把图片,进行480/400的缩放,缩放1.2倍。刚好等于150;导致createScaledBitmap时将返回原始的source。进而导致第三步bitmap被释放

屏幕像素密度(dpi)

dpi是 屏幕每英寸显示的像素点数,在同一尺寸下,分辨率越高,屏幕越清晰,dpi越高。得到屏幕像素密度的计算方式如下

dpi不同

屏幕密度不同,即设备分辨率的不同,如在同尺寸的屏幕中,不同分辨率会使屏幕密度不同。如下图示例
在这里插入图片描述

在上面两个图中,屏幕的尺寸、比例,都是一样的,但是分辨率是不一样的,这就造成屏幕密度不一样。这样画一条 360px 的直线,在图5中是满屏,在图6中就只有三分之一,在图6中可以显示的内容,在图5中就有可能超过底部边缘,显示不下。这种情况也是造成需要适配的原因。

Android 屏幕适配原因

总结一下适配的 2 个主要原因

1.屏幕像素密度不一样
2.屏幕比例不同(屏幕尺寸不一样)

其实适配,主要是针对这两点进行适配的。理解了屏幕的概念,知道了适配屏幕的原因,这样处理问题就有了方向。下面说说几种官方推荐的主流适配方式,与民间使用比较多的适配方式。

官方推荐适配方式----dp

Dpi表示每英寸的像素数

屏幕适配原理可以参考

https://blog.csdn.net/xiaojinlai123/article/details/103542363?spm=1001.2014.3001.5502

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
下面是一个简单的案例,演示如何在Vue2项目中进行PC端屏幕适配: 1. 安装并引入CSS预处理器(可选):如果你想使用CSS预处理器(如Sass、Less等),可以先根据需求安装相应的依赖,并在项目中引入。 2. 创建全局样式文件:在项目的`src/assets`目录下创建一个全局样式文件,比如命名为`global.css`。 3. 编写全局样式:在`global.css`文件中,你可以使用CSS媒体查询、Flexbox等方式编写适应不同屏幕尺寸的样式。以下是一个简单的示例: ```css /* global.css */ /* PC端屏幕适配 */ @media (min-width: 1024px) { /* 在宽度大于等于1024px的屏幕上,设置容器宽度为960px */ .container { width: 960px; margin: 0 auto; /* 居中显示 */ } } /* Flexbox布局 */ .container { display: flex; justify-content: center; /* 水平居中 */ align-items: center; /* 垂直居中 */ } ``` 4. 在主入口文件中引入全局样式:在主入口文件(一般是`src/main.js`)中,引入全局样式文件。 ```javascript // main.js import Vue from 'vue'; import App from './App.vue'; import './assets/global.css'; Vue.config.productionTip = false; new Vue({ render: h => h(App), }).$mount('#app'); ``` 5. 在组件中使用适配样式:在需要适配的组件中,使用对应的类名或选择器来应用适配样式。 ```html <template> <div class="container"> <!-- 内容 --> </div> </template> <script> export default { // 组件逻辑 } </script> <style scoped> /* 组件样式 */ .container { /* 组件样式 */ } </style> ``` 这是一个简单的案例,你可以根据实际需求和项目情况进行调整和扩展。记得根据具体的屏幕尺寸和需求,编写相应的媒体查询规则和样式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值