1. 程式人生 > >Android之引用其它已安裝或未安裝apk檔案的資源

Android之引用其它已安裝或未安裝apk檔案的資源

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");
	}

}

程式碼地址: