編輯:關於Android編程
代碼如下:
package net.mobctrl.hostapk;
import java.io.File;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
/**
* @Author Zheng Haibo
* @PersonalWebsite http://www.mobctrl.net
* @version $Id: LoaderResManager.java, v 0.1 2015年12月11日 下午7:58:59 mochuan.zhb
* Exp $
* @Description 動態加載資源的管理器
*/
public class BundlerResourceLoader {
private static AssetManager createAssetManager(String apkPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
try {
AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(
assetManager, apkPath);
} catch (Throwable th) {
System.out.println("debug:createAssetManager :"+th.getMessage());
th.printStackTrace();
}
return assetManager;
} catch (Throwable th) {
System.out.println("debug:createAssetManager :"+th.getMessage());
th.printStackTrace();
}
return null;
}
/**
* 獲取Bundle中的資源
* @param context
* @param apkPath
* @return
*/
public static Resources getBundleResource(Context context){
AssetsManager.copyAllAssetsApk(context);
File dir = context.getDir(AssetsManager.APK_DIR, Context.MODE_PRIVATE);
String apkPath = dir.getAbsolutePath()+"/BundleApk.apk";
System.out.println("debug:apkPath = "+apkPath+",exists="+(new File(apkPath).exists()));
AssetManager assetManager = createAssetManager(apkPath);
return new Resources(assetManager, context.getResources().getDisplayMetrics(), context.getResources().getConfiguration());
}
}
注意:我們使用Resources對象,獲取資源時,傳遞的ID必須是離線apk中R文件對應的資源的ID。如果使用getIdentifier方法,第一個參數是資源名稱,第二個參數是資源類型,第三個參數是離線apk的包名,切記第三個參數。
Resources resources = BundlerResourceLoader.getBundleResource(getApplicationContext());
imageView = (ImageView)findViewById(R.id.image_view_iv);
if(resources != null){
String str = resources.getString(resources.getIdentifier("test_str", "string", "net.mobctrl.normal.apk"));
String strById = resources.getString(0x7f050001);//注意,id參照Bundle apk中的R文件
System.out.println("debug:"+str);
Toast.makeText(getApplicationContext(),strById, Toast.LENGTH_SHORT).show();
Drawable drawable = resources.getDrawable(0x7f020000);//注意,id參照Bundle apk中的R文件
imageView.setImageDrawable(drawable);
}
上述代碼是加載離線apk中的字符串和Drawable資源,那麼layout資源呢?
我們使用LayoutInflate對象,一般使用方法如下:
View view = LayoutInflater.from(context).inflate(R.layout.main_fragment, null);
其中,R.layout.main_fragment我們可以通過上述方法獲取其ID,那麼關鍵的一步就是如何生成一個context?直接傳入當前的context是不行的。
解決方案有2個:
- 1.創建一個自己的ContextImpl,Override其方法。
- 2.通過反射,直接替換當前context的mResources私有成員變量。<>br
當然,我們是使用第二種方案:
@Override
protected void attachBaseContext(Context context) {
replaceContextResources(context);
super.attachBaseContext(context);
}
/**
* 使用反射的方式,使用Bundle的Resource對象,替換Context的mResources對象
* @param context
*/
public void replaceContextResources(Context context){
try {
Field field = context.getClass().getDeclaredField("mResources");
field.setAccessible(true);
field.set(context, mBundleResources);
System.out.println("debug:repalceResources succ");
} catch (Exception e) {
System.out.println("debug:repalceResources error");
e.printStackTrace();
}
}
我們在Activity的attachBaseContext方法中,對Context的mResources進行替換,這樣,我們就可以加載離線apk中的布局了。
如果想要做到插件化,需要了解Android資源文件的打包過程,這樣可以為每一個插件進行編號,然後按照規則生成R文件。例如,以攜程DynamicAPK為例,它將插件的R文件按照如下規則:
1.R文件為int型,前8位代表插件的Id,其中兩個特殊的Id:Host是0x7f,android系統自帶的是以0x01開頭. 2.緊跟著的8位是區分資源類型的,比如layout,id,string,dimen等 3.後面16位是資源的編號按照上述規則生成對應的插件apk。然後在運行時,我們可以寫一個ResourceManager類,它繼承自Resource對象,然後所有的Activity,都將其context的mResource成員變量修改為ResourceManager類,然後Override其方法,然後在加載資源時,根據不同的id的前綴,查找對應插件的Resource即可。也就是說,用一個類做分發。
ContentProvider為Android四大組件之一,主要用來應用程序之間的數據共享,也就是說一個應用程序用ContentProvider將自己的數據暴露出來,其他
近日,微信悄然上線了個人公眾號改名功能,據悉,微信在原賬號名稱下方會有一行“個人類賬號一年內可主動修改一次名稱”的標注,每年只有一次
借助SQLiteOpenHelper幫助類實現對數據庫的創建和升級。MainActivity.javapackage com.example.databasetest;i
申明: 下面實現如何通過應用層支持多點觸控操作,對於常規的控件觸控操實現onTouchEvent()方法來處理。同時對onTouchEvent方法的參數Moti