列出strings.xml中的值

时间:2014-03-13 13:25:07

标签: android

我需要列出strings.xml中的所有值(对于给定的语言环境), 基本上获取应用程序中所有字符串的动态列表。

我的目的是列出我(最根本的)手机中所有应用的所有字符串,以加快翻译工作。

访问其他应用的AssetManager没问题:

- 获取我使用的所有应用的列表:

List<PackageInfo> packs = getPackageManager().getInstalledPackages(0);

- 要访问我使用的包管理器:

PackageManager manager = getPackageManager();
Resources mApk1Resources = manager.getResourcesForApplication(pname);
AssetManager a = mApk1Resources.getAssets();

但我不太确定从哪里开始。

显然这不是出于生产目的,只是为了帮助我的客户的翻译团队(你知道中国的原始设备制造商......),所以欢迎使用手提钻的解决方案:-P(反射,动态的外国环境,实时dex加载,炸药等...)

谢谢!

编辑1:我已经知道B.A.R.T它不适合我的需要我需要在实时手机(不是压缩ROM)上进行,例如“实时翻译检查应用”。特别是,我没有立即访问该应用程序的源代码,因为我必须检查大量的手机,有些是几年。如果需要,我可以花时间根除所有这些,但不多。

编辑2:我真的需要在不需要电脑的情况下在现场手机上运行的东西。我不能修改单个应用程序的源代码,我无法反编译ROM或使用像B.A.R.T这样的外部工具。我需要一个全Java解决方案。

4 个答案:

答案 0 :(得分:1)

我可以看到:通常应用程序标签的字符串将获得资源字符串中的第一个id。 (尚未找到关于此的官方文件)

所以计划是:获取应用程序标签的id,每次增加id以获取下一个资源字符串,直到得到Resources.NotFoundException

try {
    List<ApplicationInfo> apps = getPackageManager()
            .getInstalledApplications(0);
    for (ApplicationInfo appInfo : apps) {
        Resources mApk1Resources = getPackageManager()
                .getResourcesForApplication(appInfo.packageName);
        int id = appInfo.labelRes;
        try {
            Log.e("Test", "*******************************************");
            Log.e("Test", apps.get(0).packageName);
            while (true) {
                Log.e("Test",
                        "String resource: "
                            + mApk1Resources.getString(id));
                id++;
            }
        } catch (Resources.NotFoundException e) {
            // Handle exception
        }
    }
} catch (NameNotFoundException e) {
    // Handle exception
}

答案 1 :(得分:0)

看到你说你接受&#34;手提钻解决方案&#34;,试一试:

Strings.xml中,使用<string-array>标记包围所有字符串值,并且内部的所有string标记都更改为item标记,就像这样(使用简单查找 - 和 - 替换以替换标签名称):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World!</string>
    <string name="app_name">YourAppNAme</string>
     <string-array name="yourTitle">
        <item>item1</item> //used to be <string name"blabla">....
        <item>item2</item>
        <item>item3</item>
    </string-array>
</resources>

在您拥有ListView的xml布局文件中,输入:

<ListView
        android:id=”@+id/yourListView”
        android:layout_width=”match_parent”
        android:layout_height=”wrap_content”
        android:entries="@array/yourTitle"
    />

现在,你的Activity有你的List,只需调用ListView,它应该已经填充了Strings.xml中的项目

一旦完成,不要忘记恢复Strings.xml中的更改(并且备份总是一个好主意)!

了解更多Here

希望这有帮助!

答案 2 :(得分:0)

由于您已经根据手机设置了手机并可以访问手机中的每个APK,如何对它们进行反编译并以此方式获取字符串,请参阅此处的教程:http://www.miui-au.com/add-ons/apktool/

答案 3 :(得分:0)

好的,我想我找到了。反思就行了! 甚至根本不需要Root。很难知道手机上的任何应用都可以看到所有其他的资源...

    public void listAllStrings(){
    List<PackageInfo> packs = getPackageManager().getInstalledPackages(0);
    for(int i=0;i<packs.size();i++) {
        PackageInfo p = packs.get(i);
        String pname = p.packageName;

        Resources packageResources=null;
        Context packageContext=null;
        try
        {   

            packageResources = MainActivity.this.getPackageManager().getResourcesForApplication(pname);
            packageContext = MainActivity.this.createPackageContext(pname, Context.CONTEXT_INCLUDE_CODE + Context.CONTEXT_IGNORE_SECURITY);                
         }
         catch(NameNotFoundException excep)
         {
             // the package does not exist. move on to see if another exists.
             Log.e(pname, "Package not found!");
         }

         Class<?> stringClass=null;
         try
         {
               // using reflection to get the string class inside the R class of the package
               stringClass = packageContext.getClassLoader().loadClass(pname + ".R$string");
         }
         catch (ClassNotFoundException excep1)
         {
             // Less chances that class won't be there.
             Log.e(pname, "R.string not found!");
         }
         if(stringClass!=null){
          //For every fields of the string class
          for( Field stringID : stringClass.getFields() )
          {
            try
            {
                //We get the id
                int id = stringID.getInt(stringClass);
                //We get the string value itself
                String xmlResourceLayout = packageResources.getString(id); 

                Log.v(pname, xmlResourceLayout);
            }
            catch (Exception excep)
           {
            Log.e(pname, "Can't access : "+stringID.getName());
            continue;
           }
         }
        }

    }
}

希望这可以帮助他人: - )

编辑:对于某些Apks,R类不在包的根目录下,所以我不得不编写一个find-R函数

    String findR(Context packageContext){
    try {
        //First case, the R class is easy to find !
        packageContext.getClassLoader().loadClass(packageContext.getPackageName() + ".R");
        return packageContext.getPackageName() + ".R";
    } catch (Exception e) {
        //Second case, it is not at the root of the package, we will list all classes...
        Log.v(packageContext.getPackageName(),"R not found... Continue searching !");
        try {
         final PackageManager pm = getApplicationContext().getPackageManager();
         ApplicationInfo ai = pm.getApplicationInfo(packageContext.getPackageName(), 0);

         DexFile dx = DexFile.loadDex(ai.sourceDir, File.createTempFile("opt", "dex",
                    getCacheDir()).getPath(), 0);
         String pathToR=null;
            // Search inside each and every class in the dex file
            for(Enumeration<String> classNames = dx.entries(); classNames.hasMoreElements();) {
                String className = classNames.nextElement();
                //for every single class, we will see if one of the fields is called app_name
                //Log.v(className, "Class detail : "+className);
                if(className.contains("R$string")) pathToR=className;


            }
          if(pathToR==null) throw new ClassNotFoundException();
          pathToR=pathToR.replaceAll("$string", "");
          Log.v(packageContext.getPackageName(), "R FOUND ! "+packageContext.getPackageName());
          return pathToR;
        } catch (Exception exc) {
            Log.e(packageContext.getPackageName(), "ERROR ! R NOT FOUND ...");
            return null;
        }

    }

}

但它仍不适用于所有情况。所以我结束了劫持apktools库以便它可以在手机上运行...(我使用了1.5.2 http://code.google.com/p/android-apktool/downloads/detail?name=apktool1.5.2.tar.bz2&can=2&q=的jar)这是我被劫持的类:

public class Decoder {
    public void decode(ResTable resTable, ExtFile apkFile, File outDir)
            throws AndrolibException {
        Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
        ResFileDecoder fileDecoder = duo.m1;
        ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder();

        attrDecoder.setCurrentPackage(resTable.listMainPackages().iterator()
                .next());

        Directory inApk, in = null, out;
        try {
            inApk = apkFile.getDirectory();
            out = new FileDirectory(outDir);

            Log.v(MainActivity.SMALL_TAG,"Decoding AndroidManifest.xml with resources...");

            fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
                    "AndroidManifest.xml");

            // fix package if needed
            adjust_package_manifest(resTable, outDir.getAbsolutePath()
                    + "/AndroidManifest.xml");

            if (inApk.containsDir("res")) {
                in = inApk.getDir("res");
            }
            out = out.createDir("res");
        } catch (DirectoryException ex) {
            throw new AndrolibException(ex);
        }

        ExtMXSerializer xmlSerializer = getResXmlSerializer();
        for (ResPackage pkg : resTable.listMainPackages()) {
            attrDecoder.setCurrentPackage(pkg);

            //Log.v(MainActivity.SMALL_TAG,"Decoding file-resources...");
            //for (ResResource res : pkg.listFiles()) {
            //  fileDecoder.decode(res, in, out);
            //}

            Log.v(MainActivity.SMALL_TAG,"Decoding values */* XMLs...");
            for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
                generateValuesFile(valuesFile, out, xmlSerializer);
            }
            generatePublicXml(pkg, out, xmlSerializer);
            Log.v(MainActivity.SMALL_TAG,"Done.");
        }

        AndrolibException decodeError = duo.m2.getFirstError();
        if (decodeError != null) {
            throw decodeError;
        }
    }

    public Duo<ResFileDecoder, AXmlResourceParser> getResFileDecoder() {
        ResStreamDecoderContainer decoders = new ResStreamDecoderContainer();
        decoders.setDecoder("raw", new ResRawStreamDecoder());
        //decoders.setDecoder("9patch", new Res9patchStreamDecoder());
        //TODO THIS DECODER CREATES ALL PROBLEMS !
        AXmlResourceParser axmlParser = new AXmlResourceParser();
        axmlParser.setAttrDecoder(new ResAttrDecoder());
        decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser,
                getResXmlSerializer()));

        return new Duo<ResFileDecoder, AXmlResourceParser>(new ResFileDecoder(
                decoders), axmlParser);
    }
    public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
    public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
    public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
    public ExtMXSerializer getResXmlSerializer() {
        ExtMXSerializer serial = new ExtMXSerializer();
        serial.setProperty(PROPERTY_SERIALIZER_INDENTATION,
                "    ");
        serial.setProperty(PROPERTY_SERIALIZER_LINE_SEPARATOR,
                System.getProperty("line.separator"));
        serial.setProperty(PROPERTY_DEFAULT_ENCODING, "utf-8");
        serial.setDisabledAttrEscape(true);
        return serial;
    }

    public void adjust_package_manifest(ResTable resTable, String filePath)
            throws AndrolibException {

        // check if packages different, and that package is not equal to
        // "android"
        Map<String, String> packageInfo = resTable.getPackageInfo();
        if ((packageInfo.get("cur_package").equalsIgnoreCase(
                packageInfo.get("orig_package")) || ("android"
                .equalsIgnoreCase(packageInfo.get("cur_package")) || ("com.htc"
                .equalsIgnoreCase(packageInfo.get("cur_package")))))) {

            Log.v(MainActivity.SMALL_TAG,"Regular manifest package...");
        } else {
            try {

                Log.v(MainActivity.SMALL_TAG,"Renamed manifest package found! Fixing...");
                DocumentBuilderFactory docFactory = DocumentBuilderFactory
                        .newInstance();
                DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
                Document doc = docBuilder.parse(filePath.toString());

                // Get the manifest line
                Node manifest = doc.getFirstChild();

                // update package attribute
                NamedNodeMap attr = manifest.getAttributes();
                Node nodeAttr = attr.getNamedItem("package");
                mPackageRenamed = nodeAttr.getNodeValue();
                nodeAttr.setNodeValue(packageInfo.get("cur_package"));

                // re-save manifest.
                TransformerFactory transformerFactory = TransformerFactory
                        .newInstance();
                Transformer transformer = transformerFactory.newTransformer();
                DOMSource source = new DOMSource(doc);
                StreamResult result = new StreamResult(new File(filePath));
                transformer.transform(source, result);

            } catch (ParserConfigurationException ex) {
                throw new AndrolibException(ex);
            } catch (TransformerException ex) {
                throw new AndrolibException(ex);
            } catch (IOException ex) {
                throw new AndrolibException(ex);
            } catch (SAXException ex) {
                throw new AndrolibException(ex);
            }
        }
    }

    private void generatePublicXml(ResPackage pkg, Directory out,
            XmlSerializer serial) throws AndrolibException {
        try {
            OutputStream outStream = out.getFileOutput("values/public.xml");
            serial.setOutput(outStream, null);
            serial.startDocument(null, null);
            serial.startTag(null, "resources");

            for (ResResSpec spec : pkg.listResSpecs()) {
                serial.startTag(null, "public");
                serial.attribute(null, "type", spec.getType().getName());
                serial.attribute(null, "name", spec.getName());
                serial.attribute(null, "id",
                        String.format("0x%08x", spec.getId().id));
                serial.endTag(null, "public");
            }

            serial.endTag(null, "resources");
            serial.endDocument();
            serial.flush();
            outStream.close();
        } catch (IOException ex) {
            throw new AndrolibException("Could not generate public.xml file",
                    ex);
        } catch (DirectoryException ex) {
            throw new AndrolibException("Could not generate public.xml file",
                    ex);
        }
    }

    private void generateValuesFile(ResValuesFile valuesFile, Directory out,
            ExtXmlSerializer serial) throws AndrolibException {
        try {
            OutputStream outStream = out.getFileOutput(valuesFile.getPath());
            serial.setOutput((outStream), null);
            serial.startDocument(null, null);
            serial.startTag(null, "resources");

            for (ResResource res : valuesFile.listResources()) {
                if (valuesFile.isSynthesized(res)) {
                    continue;
                }
                ((ResValuesXmlSerializable) res.getValue())
                        .serializeToResValuesXml(serial, res);
            }

            serial.endTag(null, "resources");
            serial.newLine();
            serial.endDocument();
            serial.flush();
            outStream.close();
        } catch (IOException ex) {
            throw new AndrolibException("Could not generate: "
                    + valuesFile.getPath(), ex);
        } catch (DirectoryException ex) {
            throw new AndrolibException("Could not generate: "
                    + valuesFile.getPath(), ex);
        }
    }

    private String mPackageRenamed = null;

}

以下是我如何使用它:

    String pname = p.packageName;

    Context packageContext = MainActivity.this.createPackageContext(pname, Context.CONTEXT_INCLUDE_CODE + Context.CONTEXT_IGNORE_SECURITY);    
    ApplicationInfo ai = packageContext.getApplicationInfo();

    Log.v(TAG,"Analysing : "+pname+" "+ai.sourceDir);
    if(new File(destination.getAbsolutePath()+"/"+pname+".strings.xml").exists()){
        Log.v(TAG,"Already translated...");
        continue;
    }

    ApkDecoder decoder = new ApkDecoder();
    decoder.setApkFile(new File(ai.sourceDir));

    File directory = new File(Environment.getExternalStorageDirectory()+File.separator+"tempApk");
    directory.mkdirs();
    DeleteRecursive(directory);
    directory.mkdirs();

    decoder.setOutDir(directory);

    decoder.setForceDelete(true);

    File frmwrk = new File(Environment.getExternalStorageDirectory()+File.separator+"framework");
    frmwrk.mkdirs();

    decoder.setFrameworkDir(Environment.getExternalStorageDirectory()+File.separator+"framework");

    decoder.setDecodeSources((short)0x0000);

    decoder.setKeepBrokenResources(true);

    try{
    //decoder.decode();
        new Decoder().decode(decoder.getResTable(), new ExtFile(new File(ai.sourceDir)), directory);
    } catch (Throwable e) {
        e.printStackTrace();
    }

少数弦乐带来很多麻烦; - )