Android 14 系统修改第三方应用的DPI

文章讲述了如何通过修改Android系统中的Activity.java文件,针对特定的第三方浏览器应用(如Firefox)调整其屏幕密度,以解决网页布局不正确的问题。重点在于在onCreate()方法中定位并修改DensityMetrics属性以确保正确显示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题描述】

使用浏览器访问某些网页布局不正确

【问题分析】

首先,这个网页不允许缩放,无法通过手动修改网页比例解决。

通过adb, wm density修改全局屏幕密度,能够让网页显示正常。

说明这个可以通过修改系统来解决问题,但要只针对第三方浏览器应用,不影响其他应用的显示。

不难得出,解决这个问题的措施是单独修改浏览器的DPI

DisplayMetrics

densityDpi 屏幕密度。 每英寸屏幕中包含的像素数量,密度越大越清晰

density 逻辑密度。 可以理解为dp换算为像素的比例(即1个dp等于几个像素)。标准的屏幕密度为160,它的 密度比例就是1,即1个dp就等于1个像素。如果你手机的densityDpi为320,则它是标准屏幕密度的两倍(320 / 160 = 2),则density = 2,表示1个dp就等于2个像素。举个例子,比如你手机的densityDpi为320,然后你设置了一个控件的宽为60dp,则它显示到屏幕上的实际宽度为120像素,因为density = 2,所以60 * 2px = 120px。

在android系统中,density 和densityDpi 通常是一一对应的,遵循一个基本的计算规则。

densityDpi 是屏幕密度的绝对值,以每英寸像素数(dpi)表示,而density 是相对于标准密度(160dpi)的缩放因子

这个关系可以用以下公式表示:

density = denistyDpi / 160

那么我们只需要针对这个应用修改DisplayMetrics这两个属性,问题应该就迎刃而解。

并且修改密度应该在WindowManager.addView() 之前,否则修改密度就不起作用,而addView是在onResume方法之后,因此我们需要在onResume()前更改应用的DPI

void handleResumeActivity(){
     ...
	r = performResumeActivity(token, clearHide, reason); // 调用onResume方法
     ...
    wm.addView(decor, 1); // WindowManager 添加DecorView
     ...
}

我们选择修改Activity.java的onCreate() 方法,通过筛选包名的方式修改指定第三方应用的分辨率

【修改对策】

这边基于android 14更新了最新的对策

framework/base/core/java/android/window/WindowMetricsController.java



/**
 * A controller to handle {@link android.view.WindowMetrics} related APIs, which are
@@ -56,6 +59,13 @@ import java.util.function.Supplier;
public final class WindowMetricsController {
    private final Context mContext;

    //add begin
    private final String TAG = "WindowManagerImpl";
    private static final String PROP_RESOLUTION_CONVERT = "persist.sys.resolution.need_convert";
    private static final String PROP_CONVERT_PACKAGES = "persist.sys.resolution.need_convert_pkg";
    private static final String PROP_CONVERT_DPR = "persist.sys.resolution.need_convert_dpr";
    //add end

    public WindowMetricsController(@NonNull Context context) {
        mContext = context;
    }
@@ -78,7 +88,7 @@ public final class WindowMetricsController {
     */
    private WindowMetrics getWindowMetricsInternal(boolean isMaximum) {
        final Rect bounds;
        final float density;
        float density;
        final boolean isScreenRound;
        final int activityType;
        synchronized (ResourcesManager.getInstance()) {
@@ -89,6 +99,23 @@ public final class WindowMetricsController {
            // the scaling factor for the Density Independent Pixel unit, which is the same unit
            // as DisplayMetrics#density
            density = config.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;

            //add begin
            if (isMaximum && shouldConvertResolution(mContext)) {
                float dprVal = 2.0;		//根据自己需求改
                
                ensity = dprVal;   
                mContext.getResources().getDisplayMetrics().density = dprVal;
                
            }
            //add end
            isScreenRound = config.isScreenRound();
            activityType = winConfig.getActivityType();
        }
@@ -171,4 +198,16 @@ public final class WindowMetricsController {
        }
        return maxMetrics;
    }

    //add begin
    private boolean shouldConvertResolution(Context context) {
    return isPackageInConvertList(context.getBasePackageName());
    }

    private boolean isPackageInConvertList(String packageName) {
        String convertPackages = PkgList;        //PkgList根据自己需求改
        return Arrays.asList(convertPackages.split(",")).contains(packageName);
    }
    //add end
}

【总结】

对策修改其实就是几行代码,难点在于定位修改的位置。如果对于系统源码不够熟悉,会需要花费相当多的时间进行调试。系统层去修改第三方应用的DPI等资源是一个十分实用的适配无源码的第三方应用的技巧;

在碰到一些视频画面比例不对,或者View被放大/缩小的问题等,都可以尝试通过修改屏幕密度去解决。

修改时,需要注意修改值符合 公式:density = denistyDpi / 160,否则可能会存在显示不正确,触摸错位等问题。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值