利用createPackageContext()方法實現囊中探物

Context有個createPackageContext方法,可以創建另外一個包的上下文,這個實例不同於它本身的Context實例,但是功能是一樣的。


      這個方法有兩個參數:
1.packageName  包名,要得到Context的包名
2.flags  標誌位,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY兩個選項。

              CONTEXT_INCLUDE_CODE的意思是包括代碼,也就是說可以執行這個包裏面的代碼。

              CONTEXT_IGNORE_SECURITY的意思是忽略安全警告,如果不加這個標誌的話,有些功能是用不了的,會出現安全警告。


利用以上特性,可以實現從別的apk包中獲取Resource資源

新建一個目標資源項目,例子的apk包名爲"com.test.resource",在src/value/string.xml有一下內容

[html] view plaincopy
  1. <resources>  
  2.   
  3.     <string name="app_name">GetOtherResource</string>  
  4.     <string name="hello_world">Hello world!</string>  
  5.     <string name="title_activity_main">MainActivity</string>  
  6.       
  7.     <string name="message">這個是資源項目的專屬string資源</string>  
  8.   
  9. </resources>  

編譯運行一下這個簡單的項目(相當於在設備上安裝該項目的apk)

       好了,現在設備上已經存在包名爲“com.test.resource”的apk,此爲前提準備,接下來就是怎樣獲取資源的過程

新建一個測試項目,在main.xml有個顯示結果的TextView和一個觸發事件的Button,Activity的代碼如下:

[html] view plaincopy
  1. public class GetResourceActivity extends Activity {  
  2.   
  3.     private TextView txvA;  
  4.   
  5.     @SuppressLint("ParserError")  
  6.     @Override  
  7.     public void onCreate(Bundle savedInstanceState) {  
  8.         super.onCreate(savedInstanceState);  
  9.         setContentView(R.layout.main);  
  10.   
  11.         txvA = (TextView) findViewById(R.id.txvA);  
  12.   
  13.         ((Button) findViewById(R.id.btnA)).setOnClickListener(new OnClickListener() {  
  14.   
  15.             @Override  
  16.             public void onClick(View v) {  
  17.                   
  18.                 Context context;  
  19.                 try {  
  20.                     context = createPackageContext("com.test.resource", Context.CONTEXT_INCLUDE_CODE  
  21.                             | Context.CONTEXT_IGNORE_SECURITY);  
  22.                     txvA.setText(context.getResources().getText(R.string.message));  
  23.                 } catch (NameNotFoundException e) {  
  24.                     // TODO Auto-generated catch block  
  25.                     e.printStackTrace();  
  26.                 }  
  27.             }  
  28.         });  
  29.     }  
  30. }  

這樣,就可以在包名爲“com.test.resource”的apk存在的情況下獲取/res/values/string.xml裏面message標籤的內容展示在txvA上了。

更進一部探討,就會發現項目B要獲取項目A的資源,在上面的方法中B項目的R.string.message必須跟項目A的一樣才能正常編譯通過,在很多情況下並不知道A有什麼資源,或者在B中存在隱藏的界面並且在某種情況下將其展現,顯然是不夠靈活的。

進一步利用java的反射機制,通過利用資源名稱反推出資源的ID號,利用一下方法可以實現

[html] view plaincopy
  1. /***  
  2.      * 通過資源名稱反推出資源ID號  
  3.      *   
  4.      * @param clazz 目標資源的R.java  
  5.      * @param className R.java的內部類,如layout,string,drawable...  
  6.      * @param name 資源名稱  
  7.      * @return  
  8.      */  
  9.     private int getResourseIdByName(Class clazz, String className, String name) {  
  10.         int id = 0;  
  11.         try {  
  12.   
  13.             Class[] classes = clazz.getClasses(); // 獲取R.java裏的所有靜態內部類  
  14.             Class desireClass = null;  
  15.   
  16.             for (int i = 0; i < classes.length; i++) {  
  17.                 if (classes[i].getName().split("\\$")[1].equals(className)) { // 查找指定的靜態內部類  
  18.                     desireClass = classes[i];  
  19.                     break;  
  20.                 }  
  21.             }  
  22.             if (desireClass != null)  
  23.                 id = desireClass.getField(name).getInt(desireClass); // 從指定的靜態內部類獲得資源編號  
  24.         } catch (IllegalArgumentException e) {  
  25.             e.printStackTrace();  
  26.         } catch (SecurityException e) {  
  27.             e.printStackTrace();  
  28.         } catch (IllegalAccessException e) {  
  29.             e.printStackTrace();  
  30.         } catch (NoSuchFieldException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.   
  34.         return id;  
  35.     }  
  36. }  

這樣Button按鈕裏面的寫法應該這樣

[html] view plaincopy
  1. ((Button) findViewById(R.id.btnA)).setOnClickListener(new OnClickListener() {  
  2.   
  3.             @Override  
  4.             public void onClick(View v) {  
  5.   
  6.                 Context context;  
  7.                 try {  
  8.                     context = createPackageContext("com.test.resource", Context.CONTEXT_INCLUDE_CODE  
  9.                             | Context.CONTEXT_IGNORE_SECURITY);  
  10.                     Class cls = context.getClassLoader().loadClass("com.test.resource.R"); // 獲得目標apk的R類  
  11.                     txvA.setText(context.getResources().getText(getResourseIdByName(cls, "string", "message")));  
  12.                 } catch (NameNotFoundException e) {  
  13.                     e.printStackTrace();  
  14.                 } catch (ClassNotFoundException e) {  
  15.                     e.printStackTrace();  
  16.                 }  
  17.             }  
  18.         });  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章