• android 如何给strings.xml文件内容加密?


    序:本篇文章只是交流,如需上线还需要自己多完善
    各位同学可想过如何给strings.xml里面的内容进行加密,然后在使用的地方进行解密呢?
    我们想一想如果要做加密,应该要解决什么问题?

    什么时候给strings.xml进行加密?
    activity和xml里面的内容,怎么解密?

    第一个问题
    我们可以在编译成apk的时候进行任务拦截,然后处理合并之后的strings.xml文件内容,注意,在清单文件或anim等非常规xml文件中使用的不能加密
    伪代码如下
    void attachObs(Project pj) {
    pj.afterEvaluate(new Action() {
    @Override
    void execute(Project project) {

                Map> allTasks = project.getAllTasks(true)
                for (Map.Entry> projectSetEntry : allTasks.entrySet()) {
                    Set value = projectSetEntry.getValue()
                    for (Task task : value) {
    
                            if(task.name.matches("^merge\S*ReleaseResources\$")) {
                               
                                String channel = task.name
                                channel = task.name.substring(5, channel.length() - 16)
                                task.doFirst { t ->
                                    println("-------mergeReleaseResources---------" + channel)
    
                                    for (File file : t.getInputs().getFiles().getFiles()) {
                                        if (file.isDirectory()) {
                                            getStringFile(file, project, channel.toLowerCase())
                                        }
                                    }
    
                                    modifyAppStringXmlFile(project)
    
                                }
                                
                            }
    
                    }
                }
            }
        }
    }
    
    
    /***
     * 给string.xml进行加密
     * @param file
     * @param project
     * @param channel
     */
    void getStringFile(File file, Project project, String channel) {
        for (File cfile : file.listFiles()) {
            if (cfile.isDirectory()) {
                getStringFile(cfile, project, channel)
            } else if (cfile.absolutePath.contains("values") && cfile.name.endsWith(".xml")) {
                println("values file->" + cfile.absolutePath)
                // 处理所需要加密的values.xml文件,因为strings.xml是一个xml文件,所以我们完全可以使用XmlParser来解析,而里面的内容,其实就是一个一个node节点,我们把要加密的node节点,添加到app这个module的strings.xml文件中,这样打包会覆盖其原来的内容
                
                
            }
        }
    }
    
    • 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

    复制代码
    下边是加密伪代码
    boolean isAppStringFile = false;
    if(absolutePath.contains(“values”+File.separator+“strings.xml”)) {
    // 说明是app下的values下的strings.xml
    Utils.println(“获取到app下的values下的strings.xml”);
    appStringFile = xmlFile;
    isAppStringFile = true;
    }
    try {
    XmlParser xmlparser = new XmlParser();
    Node xml = xmlparser.parse(xmlFile.getPath());
    if(xml == null) {
    return;
    }
    Iterator iterator = xml.iterator();

    while(iterator.hasNext()) {
        Object next = iterator.next();
        if(next instanceof Node) {
            Node node = (Node) next;
            String nodeText = node.text();
            if(node.name().equals("string")) {
                if(nodeText != null && nodeText.length() > 0) {
                    String nodeNameAttr = node.attribute("name").toString();
                    Utils.println("string--- node=" + nodeText + "  name=" + node.name() + "  text=" + node.text() +
                            " nodeNameAttr=" + nodeNameAttr + " node.attribute="+node.attributes() );
                    // 这里就可以对你的strings.xml的node节点做加密
                    if(nodeNameAttr.equals(你的strings.xml里面的要加密的name的名字)){
                        // 这里保存你的node节点,到modifyAppStringXmlFile方法去修改app下的strings.xml文件
                    }
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    } catch (ParserConfigurationException e) {
    e.printStackTrace();
    } catch (SAXException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    复制代码

    /***
    * 修改app下的strings.xml文件
    * @param project
    */
    void modifyAppStringXmlFile(Project project) {

        removeStringXmlSameNode(project,appStringFile,needEntryNodeList) // 这个方法是为了在jenkins上打包的时候,先删除掉上一次加密过的strings.xml里面的节点,demo中可注释
        addNodeToStringXml(project,appStringFile,needEntryNodeList)
    
    }
    
    /***
     * 添加node节点到strings.xml文件中
     * @param project
     * @param appStringFile
     * @param needEntryNodeList
     */
    void addNodeToStringXml(Project project,File appStringFile,List needEntryNodeList) {
        def xml = project.file(appStringFile)
        def appStringxml = new XmlParser().parseText(xml.getText())
        if(appStringxml != null) {
            List appendNodeList = new ArrayList<>()
    
            for(Node node1:needEntryNodeList) {
                String needEntryText = node1.text();
                needEntryText = Utils.getRandomString(5) + needEntryText
                String str = AESUtil.encrypt(needEntryText,"hello");
                println("string---加密后"+str)
                Node node2 = new Node(node1.parent(),"string",node1.attributes(),str)
                appendNodeList.add(node2)
                appStringxml.append(node2)
            }
            // 保存修改后的strings.xml文件
            def serialize = groovy.xml.XmlUtil.serialize(appStringxml)
            xml.withWriter {writer->
                writer.write(serialize)
            }
    
           
        }
    }
    
    • 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

    复制代码
    上边是加密的核心代码,现在再来回顾一下逻辑

    遍历所有的strings.xml文件(有的string写到了values.xml中)
    找到要加密的strings.xml文件中要加密的node节点
    将要加密的node节点写入到app下的strings.xml文件中,以便可以用加密后的节点覆盖加密前的节点

    下边我们再来说一说如何解密?
    其实当时做的时候,我是有一些纠结的,因为我不知道在哪里去对R.string.xxx去进行解密,而且如果布局xml文件中有使用的话,那又该怎么做解密呢?
    其实所有的资源文件都是通过resources类来做解析的,这里的原理跟换肤有点类似,伪代码如下

    public class WxjResources extends Resources {

    private static final String TAG = "BaseActivity";
    
    private Resources mResources;
    
    public WxjResources(Resources resources) {
        super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration());
        mResources = resources;
    }
    
    
    @NonNull
    @Override
    public CharSequence getText(int id) throws NotFoundException {
        //CharSequence value = mResources.getResourceEntryName(id);
        CharSequence value = mResources.getText(id);
    
        value = jiemiStr2(value);
        Log.d(TAG, "getText 2222222222 value===="+value+"id="+id);
        return value;
    }
    
    @Override
    public CharSequence getText(int id, CharSequence def) {
        CharSequence value = mResources.getText(id, def);
        value = jiemiStr2(value);
        Log.d(TAG, "getText 2222222222 value===="+value+"id="+id);
        return value;
    }
    
    private CharSequence jiemiStr2(CharSequence value) {
        if(AESUtil.checkHexString(value.toString())) {
            String decryptValue = null;
            try {
                decryptValue = AESUtil.decrypt(value.toString(), "hello");
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.d(TAG, "jiemistr2 decryptValue===="+decryptValue);
            if(TextUtils.isEmpty(decryptValue)) {
                return value;
            }
            decryptValue = decryptValue.substring(5);
    
            return decryptValue;
        }
        Log.d(TAG, "jiemistr2 value===="+value);
        return value;
    }
    
    private String jiemiStr(String value) {
        if(AESUtil.checkHexString(value)) {
            String decryptValue = null;
            try {
                decryptValue = AESUtil.decrypt(value, "hello");
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.d(TAG, "jiemiStr decryptValue===="+decryptValue);
            if(TextUtils.isEmpty(decryptValue)) {
                return value;
            }
            decryptValue = decryptValue.substring(5);
    
            return decryptValue;
        }
        Log.d(TAG, "jiemistr value===="+value);
        return value;
    }
    
    @NonNull
    @Override
    public String getString(int id, Object... formatArgs) throws NotFoundException {
        String value = mResources.getString(id, formatArgs);
        if(TextUtils.isEmpty(value)) {
            return value;
        }
        Log.d(TAG, "getString 111111111111 value===="+value);
        value = jiemiStr(value);
        return value;
    }
    
    @NonNull
    @Override
    public String getString(int id) throws NotFoundException {
        String value = mResources.getString(id);
        if(TextUtils.isEmpty(value)) {
            return value;
        }
        value = jiemiStr(value);
        Log.d(TAG, "getString 2222222222 value===="+value+"id="+id);
        return value;
    }
    
    
    
    
    
    
    @RequiresApi(api = Build.VERSION_CODES.O)
    @NonNull
    @Override
    public Typeface getFont(int id) throws NotFoundException {
        return mResources.getFont(id);
    }
    
    @NonNull
    @Override
    public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
        return mResources.getQuantityText(id, quantity);
    }
    
    @NonNull
    @Override
    public String getQuantityString(int id, int quantity, Object... formatArgs) throws NotFoundException {
        return mResources.getQuantityString(id, quantity, formatArgs);
    }
    
    @NonNull
    @Override
    public String getQuantityString(int id, int quantity) throws NotFoundException {
        return mResources.getQuantityString(id, quantity);
    }
    
    @NonNull
    @Override
    public CharSequence[] getTextArray(int id) throws NotFoundException {
        return mResources.getTextArray(id);
    }
    
    @NonNull
    @Override
    public String[] getStringArray(int id) throws NotFoundException {
        return mResources.getStringArray(id);
    }
    
    @NonNull
    @Override
    public int[] getIntArray(int id) throws NotFoundException {
        return mResources.getIntArray(id);
    }
    
    @NonNull
    @Override
    public TypedArray obtainTypedArray(int id) throws NotFoundException {
        return mResources.obtainTypedArray(id);
    }
    
    @Override
    public float getDimension(int id) throws NotFoundException {
        return mResources.getDimension(id);
    }
    
    @Override
    public int getDimensionPixelOffset(int id) throws NotFoundException {
        return mResources.getDimensionPixelOffset(id);
    }
    
    @Override
    public int getDimensionPixelSize(int id) throws NotFoundException {
        return mResources.getDimensionPixelSize(id);
    }
    
    @Override
    public float getFraction(int id, int base, int pbase) {
        return mResources.getFraction(id, base, pbase);
    }
    
    @Override
    public Drawable getDrawable(int id) throws NotFoundException {
        return mResources.getDrawable(id);
    }
    
    @Override
    public Drawable getDrawable(int id, @Nullable Theme theme) throws NotFoundException {
        return mResources.getDrawable(id, theme);
    }
    
    @Nullable
    @Override
    public Drawable getDrawableForDensity(int id, int density) throws NotFoundException {
        return mResources.getDrawableForDensity(id, density);
    }
    
    @Nullable
    @Override
    public Drawable getDrawableForDensity(int id, int density, @Nullable Theme theme) {
        return mResources.getDrawableForDensity(id, density, theme);
    }
    
    @Override
    public Movie getMovie(int id) throws NotFoundException {
        return mResources.getMovie(id);
    }
    
    @Override
    public int getColor(int id) throws NotFoundException {
        return mResources.getColor(id);
    }
    
    @Override
    public int getColor(int id, @Nullable Theme theme) throws NotFoundException {
        return mResources.getColor(id, theme);
    }
    
    @NonNull
    @Override
    public ColorStateList getColorStateList(int id) throws NotFoundException {
        return mResources.getColorStateList(id);
    }
    
    @NonNull
    @Override
    public ColorStateList getColorStateList(int id, @Nullable Theme theme) throws NotFoundException {
        return mResources.getColorStateList(id, theme);
    }
    
    @Override
    public boolean getBoolean(int id) throws NotFoundException {
        return mResources.getBoolean(id);
    }
    
    @Override
    public int getInteger(int id) throws NotFoundException {
        return mResources.getInteger(id);
    }
    
    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    public float getFloat(int id) {
            return mResources.getFloat(id);
    }
    
    @NonNull
    @Override
    public XmlResourceParser getLayout(int id) throws NotFoundException {
        return mResources.getLayout(id);
    }
    
    @NonNull
    @Override
    public XmlResourceParser getAnimation(int id) throws NotFoundException {
        return mResources.getAnimation(id);
    }
    
    @NonNull
    @Override
    public XmlResourceParser getXml(int id) throws NotFoundException {
        return mResources.getXml(id);
    }
    
    @NonNull
    @Override
    public InputStream openRawResource(int id) throws NotFoundException {
        return mResources.openRawResource(id);
    }
    
    @NonNull
    @Override
    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
        return mResources.openRawResource(id, value);
    }
    
    @Override
    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
        return mResources.openRawResourceFd(id);
    }
    
    @Override
    public void getValue(int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException {
        mResources.getValue(id, outValue, resolveRefs);
    }
    
    @Override
    public void getValueForDensity(int id, int density, TypedValue outValue, boolean resolveRefs) throws NotFoundException {
        mResources.getValueForDensity(id, density, outValue, resolveRefs);
    }
    
    @Override
    public void getValue(String name, TypedValue outValue, boolean resolveRefs) throws NotFoundException {
        mResources.getValue(name, outValue, resolveRefs);
    }
    
    @Override
    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
        return mResources.obtainAttributes(set, attrs);
    }
    
    @Override
    public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
        mResources.updateConfiguration(config, metrics);
    }
    
    @Override
    public DisplayMetrics getDisplayMetrics() {
        return mResources.getDisplayMetrics();
    }
    
    @Override
    public Configuration getConfiguration() {
        return mResources.getConfiguration();
    }
    
    @Override
    public int getIdentifier(String name, String defType, String defPackage) {
        return mResources.getIdentifier(name, defType, defPackage);
    }
    
    @Override
    public String getResourceName(int resid) throws NotFoundException {
        return mResources.getResourceName(resid);
    }
    
    @Override
    public String getResourcePackageName(int resid) throws NotFoundException {
        return mResources.getResourcePackageName(resid);
    }
    
    @Override
    public String getResourceTypeName(int resid) throws NotFoundException {
        return mResources.getResourceTypeName(resid);
    }
    
    @Override
    public String getResourceEntryName(int resid) throws NotFoundException {
        return mResources.getResourceEntryName(resid);
    }
    
    @Override
    public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle) throws IOException, XmlPullParserException {
        mResources.parseBundleExtras(parser, outBundle);
    }
    
    @Override
    public void parseBundleExtra(String tagName, AttributeSet attrs, Bundle outBundle) throws XmlPullParserException {
        mResources.parseBundleExtra(tagName, attrs, outBundle);
    }
    
    @Override
    public void addLoaders(@NonNull ResourcesLoader... loaders) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            mResources.addLoaders(loaders);
        }
    }
    
    @Override
    public void removeLoaders(@NonNull ResourcesLoader... loaders) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            mResources.removeLoaders(loaders);
        }
    }
    
    • 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
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350

    }
    复制代码
    在所有的activity中都需要去使用

    public class ResEntryLifecycle implements Application.ActivityLifecycleCallbacks {

    private static final String TAG = "ResEntryLifecycle";
    
    @Override
    public void onActivityPreCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
        try {
            Resources res = activity.getResources();
            WxjResources wxjResources = new WxjResources(res);
    
            Field mResources = ContextThemeWrapper.class.getDeclaredField("mResources");
            mResources.setAccessible(true);
            mResources.set(activity,wxjResources);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        if(activity instanceof AppCompatActivity) {
            installLayoutFactory(activity);
        } else {
            installLayoutFactoryByActivity(activity);
        }
    }
    
    @Override
    public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
    
    }
    
    private void installLayoutFactoryByActivity(Activity activity) {
        LayoutInflater layoutInflater = activity.getLayoutInflater();
        LayoutInflaterCompat.setFactory2(layoutInflater, new LayoutInflater.Factory2() {
            @Nullable
            @Override
            public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
                Log.d(TAG, "installLayoutFactoryByActivity parent = " + parent + " name="+name + " attrs="+attrs.toString());
                LayoutInflater inflater = LayoutInflater.from(context);
                try {
                    View view = null;
                    if (name.indexOf('.') > 0) { //表明是自定义View
                        view = inflater.createView(name, null, attrs);
                    } else {
                        String prefix = "android.widget.";
                        if(name.equals("ViewStub")) {
                            prefix = "android.view.";
                        }
                        view = inflater.createView(name, prefix, attrs);
                    }
                    Log.d(TAG, "报错类找不到"+view);
                    if(view instanceof TextView) {
                        int[] set = {
                                android.R.attr.text        // idx 0
                        };
                        // 不需要recycler,后面会在创建view时recycle的
                        @SuppressLint("Recycle")
                        TypedArray a = context.obtainStyledAttributes(attrs, set);
                        int resourceId = a.getResourceId(0, 0);
                        if (resourceId != 0) {
                            // 在这里进行解析
                            String value = activity.getResources().getString(resourceId);
                            Log.d(TAG, "installLayoutFactoryByActivity解密后value:"+value);
    
                            ((TextView) view).setText(value);
    
                            return view;
                        }
                    } else {
                        Log.d(TAG, "view 不是textview");
                    }
    
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                return null;
            }
    
            @Nullable
            @Override
            public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
                return null;
            }
        });
    }
    
    private void installLayoutFactory(Activity activity) {
        LayoutInflater layoutInflater = activity.getLayoutInflater();
        LayoutInflaterCompat.setFactory2(layoutInflater, new LayoutInflater.Factory2() {
            @Nullable
            @Override
            public View onCreateView(@Nullable View parent, @NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
                LayoutInflater inflater = LayoutInflater.from(context);
                AppCompatActivity activity = null;
                if (parent == null) {
                    if (context instanceof AppCompatActivity) {
                        activity = ((AppCompatActivity)context);
                    }
                } else if (parent.getContext() instanceof AppCompatActivity) {
                    activity = (AppCompatActivity) parent.getContext();
                }
                if (activity == null) {
                    return null;
                }
    
                AppCompatDelegate delegate = activity.getDelegate();
                int[] set = {
                        android.R.attr.text        // idx 0
                };
    
                // 不需要recycler,后面会在创建view时recycle的
                @SuppressLint("Recycle")
                TypedArray a = context.obtainStyledAttributes(attrs, set);
                View view = delegate.createView(parent, name, context, attrs);
                if (view == null && name.indexOf('.') > 0) { //表明是自定义View
                    try {
                        view = inflater.createView(name, null, attrs);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                }
    
                if (view instanceof TextView) {
                    int resourceId = a.getResourceId(0, 0);
                    if (resourceId != 0) {
                        // 在这里进行解析
                        String value = activity.getResources().getString(resourceId);
                        Log.d(TAG, "解密后value:"+value);
    
                        ((TextView) view).setText(value);
                    }
                }
    
                return view;
            }
    
            @Nullable
            @Override
            public View onCreateView(@NonNull String name, @NonNull Context context, @NonNull AttributeSet attrs) {
                return null;
            }
        });
    }
    
    
    
    
    @Override
    public void onActivityStarted(@NonNull Activity activity) {
    
    }
    
    @Override
    public void onActivityResumed(@NonNull Activity activity) {
    
    }
    
    @Override
    public void onActivityPaused(@NonNull Activity activity) {
    
    }
    
    @Override
    public void onActivityStopped(@NonNull Activity activity) {
    
    }
    
    @Override
    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
    
    }
    
    @Override
    public void onActivityDestroyed(@NonNull Activity activity) {
    
    }
    
    • 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
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171

    }
    复制代码
    下边就是使用了
    public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        ResEntryLifecycle resEntryLifecycle = new ResEntryLifecycle();
        registerActivityLifecycleCallbacks(resEntryLifecycle);
    }
    
    
    @Override
    public Resources getResources() {
        Resources res = super.getResources();
        return new WxjResources(res);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    }

    复制代码
    到这里所有的逻辑代码已经完成了,其实上边遗留了一个问题,我们项目是在jenkins上打包,然后当打包失败的时候,很有可能strings.xml里面已经存在加密后的node了,如果此时再次打包,那么就会有两个相同的node,这个问题其实也可以通过加密之前先把strings.xml里面的节点都干掉,然后再添加,虽然暴力,但是有效,还有很多不完善的地方,敬请谅解,只是提供一个思路!

  • 相关阅读:
    力扣刷题day39|70. 爬楼梯(进阶版)、322零钱兑换、279完全平方数
    【Pytorch】进阶学习:深入解析 sklearn.metrics 中的 classification_report 函数---分类性能评估的利器
    c语言必背100代码,C语言代码大全(c语言必背项目代码)
    flutter3-weos手机OS系统|Flutter3.22+Getx仿ios桌面管理OA应用
    K3S系列文章-使用AutoK3s在腾讯云上安装高可用K3S集群
    C语言斐波那契数列的前N项 循环和递归斐波那契数列规律如下:1, 1, 2, 3, 5, 8, 13
    Vega Prime入门教程12.10:DevToolCRO与部署
    Java8实战-总结23
    物联网的应用——环境监测
    wirehark数据分析与取证hack.pcapng
  • 原文地址:https://blog.csdn.net/u010648159/article/details/128203545