如何获取手机中其他应用中的字符串? 由『网络链接较慢』的提示说起

★ 起因

这里写图片描述

『网络链接较慢,建议检查网络设置或稍后重试。』这个字符串可能很多人都不陌生。我的手机里在一段时间内一直在弹这个提示,而且是在任意界面中都会提示,样子是这样的:
(为了显示完整的字符串,我截了个横屏的图)

这里写图片描述

我选择在WPS office中截图,但这个提示显然不是WPS office提示的(因为在WPS office完全不运行时也会提示这个界面)。

总是提示这个界面,确实挺烦人的。搜了一下必应,遇到此问题的人很多,各种手机型号都有,所以初步排除了是某个手机厂商开发的应用导致的,必应搜索结果如下(部分结果):

这里写图片描述

心急的,可以直接点此看结果

★ 思路

对于我的user版的手机来说,我自己的小应用是第三方应用,我的手机没有root权限。
所以,能够利用的接口必须是第三方应用可以使用的。
注:本文中『接口』是指双方约定好的可以调用的函数或方法,是宽泛的概念。如果提到『Java Interface接口』会以『Java接口』来指代。

createPackageContext()

此接口可以根据package name 获取其他应用的代码和资源。

getInstalledPackages()

此接口获取手机中所有安装的应用的信息。

♦ 反射的方法 获取字符串的ID

一个应用中的字符串资源会编译到R.java中,所以我的目标是如何获取R.java中的string内部类的信息。

通常,字符串资源会在 pkgName + ".R$string"类中。

还有一些应用是加固过的,所以不遵守这个规则,例如某app的com.cmbchina.ccd.pluto.cmbActivity.R$string这个类是没有的,因为它加固了,取而代之的是com.secneo.apkwrapper.R$stringcom.tencent.txproxy.R$string。但是这两个类中已经没有什么字符串了,应用原始的字符串已经被保护起来了。

能看到的字符串,如下:

R class name: com.secneo.apkwrapper.R$string
id (hex): 7f050001, id: 2131034113
str: res/anim/abc_fade_out.xml
id (hex): 7f050000, id: 2131034112
str: res/anim/abc_fade_in.xml
id (hex): 7f050002, id: 2131034114
str: res/anim/abc_grow_fade_in_from_bottom.xml

R class name: com.tencent.txproxy.R$string
id (hex): 7f030000, id: 2130903040
str: res/mipmap-xxhdpi-v4/ic_launcher.png

为了获取 com.secneo.apkwrapper.R$stringcom.tencent.txproxy.R$string 这类的与包名不一致的R$string类,我选择了通过反射的方法。

废话少说,上代码。

★ 代码

完整代码在: https://github.com/galian123/Samples/blob/master/app/src/main/java/com/galian/samples/CheckStringsInAppsActivity.java

这里只贴出关键代码:

    void checkStringsAsync() {
        PackageManager pm = getPackageManager();

        ArrayList<PackageInfo> thirdPartyAppList = new ArrayList<>();

        List<PackageInfo> pkgList = pm.getInstalledPackages(0);
        if (pkgList != null && pkgList.size() > 0) {
            for (PackageInfo pi : pkgList) {
                Log.e(TAG, "pkg name: " + pi.packageName);
                Log.e(TAG, "dir: " + pi.applicationInfo.publicSourceDir);
                // 我使用的是坚果Pro,所以把系统app都排除掉了。
                if (pi.applicationInfo.publicSourceDir.startsWith("/data/app/")
                        && !pi.packageName.startsWith("com.smartisanos.")) {
                    thirdPartyAppList.add(pi);
                }
            }
        }

        ArrayList<String> RClassNameList = new ArrayList<>();

        if (thirdPartyAppList.size() > 0) {

            for (PackageInfo pi : thirdPartyAppList) {
                String pkgName = pi.packageName;

                try {
                    // 获取其他apk的代码和资源
                    Context context = createPackageContext(pkgName, CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY);
                    ClassLoader pathClassLoader = context.getClassLoader();

                    Class baseDexClassLoaderCls = Class.forName("dalvik.system.BaseDexClassLoader");
                    Field pathListField = baseDexClassLoaderCls.getDeclaredField("pathList");
                    pathListField.setAccessible(true);
                    Object dexPathListObj = pathListField.get(pathClassLoader);

                    Class dexPahtListCls = dexPathListObj.getClass();
                    Field dexElementsField = dexPahtListCls.getDeclaredField("dexElements");
                    dexElementsField.setAccessible(true);
                    Object elementArrayObj = dexElementsField.get(dexPathListObj);

                    int len = Array.getLength(elementArrayObj);

                    RClassNameList.clear();

                    for (int i = 0; i < len; i++) {
                        Object elementObj = Array.get(elementArrayObj, i);

                        Class ElementCls = elementObj.getClass();
                        Field dexFileField = ElementCls.getDeclaredField("dexFile");
                        dexFileField.setAccessible(true);
                        Object dexFileObj = dexFileField.get(elementObj);
                        if (dexFileObj != null) {
                            Class dexFileCls = dexFileObj.getClass();
                            Method DexFile_entries = dexFileCls.getMethod("entries");
                            Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFileObj);
                            while (e.hasMoreElements()) {
                                String className = e.nextElement();
                    // 通过反射的方式,获取所有R$string类
                                if (className.contains(".R$string")) {
                                    Log.e(TAG, className);
                                    RClassNameList.add(className);
                                }
                            }
                        }
                    }

                    Resources resources = context.getResources();
                    if (RClassNameList.size() > 0) {
                        for (String RClassName : RClassNameList) {
                            String Rclass = RClassName; //pkgName + ".R$string";
                            if (Rclass.equals("android.support.v7.appcompat.R$string")) {// 可以排除一些系统的类
                                continue;
                            }
                            Log.e(TAG, "R class name: " + Rclass);
                            Class RClassObj = context.getClassLoader().loadClass(Rclass);
                            ReflectUtils reflectUtils = new ReflectUtils(RClassObj);

                            List<Integer> ids = reflectUtils.getAllIntValues();
                            if (ids != null && ids.size() > 0) {
                                for (Integer id : ids) {
                                    Log.e(TAG, "id (hex): " + Integer.toHexString(id) + ", id: " + id);
                                    try {
                                        String str = resources.getString(id);
                                        Log.e(TAG, "str: " + str);
                                        if (str.contains("网络链接较慢,建议检查网络设置或稍后重试。")) {
                                            Log.e(TAG, pi.toString());
                                            Log.e(TAG, "+++++ package: " + pi.packageName);                                            
                                        }
                                    } catch ...
                                }
                            }
                        }
                    }
                } catch ...
            }
        }
    }

反射相关的代码在: https://github.com/galian123/Samples/blob/master/app/src/main/java/com/galian/samples/ReflectUtils.java

    public List<Integer> getAllIntValues() {
        List<Integer> ids = new ArrayList<Integer>();
        Field[] fields = mClassObj.getDeclaredFields();
        if (fields != null && fields.length > 0) {

            for (Field field : fields) {
                String name = field.getName();
                try {
                    int val = field.getInt(null);                    
                    ids.add(val);
                } catch ...
            }
            return ids;
        } else {
            Log.d(LOG_TAG, "No fields.");
            return ids;
        }
    }

★ 结果

id (hex): 7f0b23dd, id: 2131436509
str: 网络链接较慢,建议检查网络设置或稍后重试。
PackageInfo{23e574e com.tencent.mobileqq}
+++++ package: com.tencent.mobileqq

QQ 弹的提示界面。
我用的QQ一直没有更新,是7.0.0.676版本。

versionCode: '676'
versionName: 7.0.0

更新QQ后,到目前为止,不再弹出那个烦人的提示界面了。
执行上面的代码,也没有匹配到任何应用含有『网络链接较慢,建议检查网络设置或稍后重试。』这个字符串。

新版QQ 7.7.0.884 :

  versionCode: '884'
  versionName: 7.7.0
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值