Android之引用其它已安裝或未安裝apk檔案的資源
阿新 • • 發佈:2019-01-25
Android應用有時候會涉及到面板的更換問題,在這裡,我用一種引用其它已安裝或未安裝apk檔案的資源來說明。
其核心思想就是利用反射來獲取。
a、引用其它未安裝apk檔案的資源來說明
1、首先建立一個application(StyleClient),將其打包稱APK並拷貝到/data/目錄下
2、在StyleClient裡的Drawable資料夾下隨便拷貝進去一張圖片,OK。
3、再建立一個application(StyleHost),編寫程式碼,下面附上。
b、引用其它已安裝apk檔案的資源來說明
基本步驟同上,只是,要把StyleClient安裝一下,不用拷貝。
核心程式碼:
package org.zqy.stylehost; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import dalvik.system.DexClassLoader; import android.os.Bundle; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.RelativeLayout; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final RelativeLayout tre = (RelativeLayout) findViewById(R.id.root); Button btn_1 = (Button) findViewById(R.id.btn_1);//獲取未安裝的圖片 Button btn_2 = (Button) findViewById(R.id.btn_2);//獲取已安裝的圖片 btn_2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { tre.setBackground(getTestBContext().getResources() .getDrawable( getId(getTestBContext().getResources(), "drawable", "xiangbi"))); } catch (NotFoundException e) { e.printStackTrace(); } catch (NameNotFoundException e) { e.printStackTrace(); } } }); btn_1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 要操作的apk的位置 String dexPath = "/data/StyleClient.apk"; // 把apk解壓到data\data\當前應用程式 String dexOuputPath = getApplicationInfo().dataDir; // C/C++類庫位置 String libPath = null; DexClassLoader localDexClassLoader = new DexClassLoader( dexPath, dexOuputPath, libPath, this.getClass() .getClassLoader()); try { // 反射drawable內部類 Class<?> localClass1 = localDexClassLoader .loadClass("org.zqy.styleclient.R$drawable"); // 反射內部類的屬性 Field[] fields = localClass1.getDeclaredFields(); for (Field f : fields) { // 將其屬性設定為可讀 f.setAccessible(true); System.out.println(f.getName()); if (f.getName().equals("shui")) { int id = f.getInt(new R.id()); // 設定背景 tre.setBackground(getPackageResource().getDrawable( id)); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } // 構建apk裡的資源 private Resources getPackageResource() { try { // 反射出資源管理器 Class<?> class_AssetManager = Class .forName("android.content.res.AssetManager"); // 建立類的例項 Object assetMag = class_AssetManager.newInstance(); // 宣告方法,因為addAssetPath是被隱藏的,所以只能通過反射呼叫 Method method_addAssetPath = class_AssetManager.getDeclaredMethod( "addAssetPath", String.class); // 執行方法 method_addAssetPath.invoke(assetMag, "/data/StyleClient.apk"); // 為下一行傳遞引數用的 Resources res = getResources(); // 確定用哪個建構函式 Constructor<?> constructor_Resources = Resources.class .getConstructor(class_AssetManager, res.getDisplayMetrics() .getClass(), res.getConfiguration().getClass()); // 執行建構函式 res = (Resources) constructor_Resources.newInstance(assetMag, res.getDisplayMetrics(), res.getConfiguration()); // 返回/data/StyleCilent.apk的resource例項 return res; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 獲取TestB的Context * * @return * @throws NameNotFoundException */ private Context getTestBContext() throws NameNotFoundException { return createPackageContext("org.zqy.styleclient", Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE); } // 獲取已安裝APK資源的id private int getId(Resources testb, String resType, String resName) { return testb.getIdentifier(resName, resType, "org.zqy.styleclient"); } }
程式碼地址: