개발 관련/Android

안드로이드 바이너리 XML 의 구조.

snoworca 2015. 3. 23. 00:12

  APK 파일의 압축을 풀면 classes.dex 파일과 그리고 String 과 drawable 과 같은 리소스 맵핑 정보를 담고 있는 resources.arsc 파일등과 AndroidManifest.xml 파일 외에 res 폴더 내에 여러 XML 파일등이 존재한다. 하지만 이 XML 파일은 우리가 흔히 아는 형식으로 되어있지 않다

  왜 APK 파일 내부에 있는 AndroidManifast.xml 을 비롯한 XML 파일들은 우리가 알고 있는 XML 형식이 아닌걸까? 아마도 공간을 정약하고 런타임 환경에서 빨리 리소스 정보를 읽어들일 수 있도록 바이너리 타입으로 만들었을 것이다. 


  안드로이드에서 사용하는 바이너리 타입의 XML 포맷에 대하여 아래 사이트에서 자세하게 설명하고 있다. 

  https://justanapplication.wordpress.com/category/android/android-binary-xml/


  위의 링크를 타고 들어가서 언급되는 내용을 보면 알겠지만, 안드로이드 바이너리 XML 의 구조는 잘 들여다보면 이해하기 쉽고 단순한 구조로 되어있다. 



  위 그림에서 나온 구조를 정리해 보자면...


     1. Header 

         - 2byte. xml 바이너리의  사이즈를  담고 있다.

     2. StringPool

         - 0x08 위치부터 시작한다. 

          - XML 내부의 String 값들에 대한 배열을 갖고 있다. 외부에서 이 String 배열의 인덱스 값을 참고하여 접근한다.

     3. XMLResouceMap 

         - 리소스 맵에 대한 정보를 갖고 있다. 

     4. Namespace  - EndNamesapce

         - Start 와 end 타입으로 분류된다

         - Start 와 End 사이에 있는 Element 들은 자식 노드(Element)가 된다.

     5.Element 

         - 엘리먼트. 내부에 Attribute 에 대한 배열을 갖고 있다.  
         - NameSpace 와 마찬가지로 Start 와 End 타입으로 분류되고 그 사이에는 자식 노드(Element)가 들어간다.

       

 

  와 같은 구조로 간단하게 되어있다.

  

  중요한 점은 Namespace 와 Element 에 대한 Chunk 는 Start 와 End 값을 갖고 있으며 그 사이에 들어가는 값들은 자식 노드가 된다.

  또, 각 data 들의 String 값은 integer 타입으로 되어 있는데, StringPool 내부의 String 배열에 대한 인덱스 값을 나타내기 때문이다. 즉 StringPool 의 배열로부터 인덱스 값을 이용하여 실제 String 데이터를 찾을 수 있다. 



  구조를 좀 더 확실하게 이해하기 위하여 

  https://justanapplication.wordpress.com/category/android/android-binary-xml/

  에 나온 설명을 이용하여 파서를 만들어 보았다. 

   

  코드 주소 :    https://github.com/ice3x2/AndroidXMLReader


  퍼포먼스는 전~~~혀 고려하지 않고, 그냥 구조를 확인하기 위하여 코드만 작성했는데 정신차려보니 파서가 완성되어 있었다. ㅡ , , ㅡ;;

  애초에 실제 구조만 확인하려고 만든 것이기 때문에 테스트 케이스 같은 것은 안중에도 없었다. 혹시나 버그가 있을지도 모르니 참고하기 바란다...  (물론 앞으로 계속 개선하여 좀 쓸만한 모듈로 만들 생각이다;;)



  어쨌든 이 코드는 안드로이드 APK 내부의 바이너리 타입 XML  구조를 확인하는데 여러분들께 큰 도움이 될 것 이다.


   이 것을 이용하여 JAVA 코드 상에서 APK 내부의 AndroidManifest.XML  내용을 읽어오는 방법은 다음과 같다. 


AndroidXML androidXML = AndroidXMLReader.readAndroidManifest("test.apk");
			Element rootElement =  androidXML.getRootElement();
			
String packageName = rootElement.findAttribute("package")[0].getValue();
String versionName = rootElement.findAttribute("versionName")[0].getValue();
String versionCode = rootElement.findAttribute("versionCode")[0].getValue();
Element[] userPermissions = rootElement.findElements("uses-permission");
			
System.out.println("Package : " + packageName);
System.out.println("Version name : " + versionName);
System.out.println("Version code : " + versionCode);
System.out.println("uses-permission");
for(Element element : userPermissions) {
	Attribute attribute = element.getAttribute(0);
	System.out.println("\t" + attribute.getName() + " : " + attribute.getValue());
}



이 시리즈의 다음 포스팅에선 resources.arsc  의 내부 구조와 파싱 방법에 대하여 알아보겠다.