上一篇我们介绍了加载已安装apk,本篇将介绍加载未安装apk。
未安装apk由于系统毫不知情,因此无法通过系统api获取其上下文,自然也就无法获取其资源。接下来,我们将另辟蹊径来解决这个问题。
步骤如下:
一、采用上一篇中的Android Host工程,仍然导出插件接口类
二、新建Android插件工程,导入插件接口类,编译成apk,并push到手机
三、运行Android Host,加载插件
Android Host工程如下
public class MainActivity extends Activity {
private Button mBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = (Button) findViewById(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
loadPlugin();
}
});
}
private void loadPlugin() {
File jarFile = new File(getExternalCacheDir(), "AndroidPlugin.apk");
File outputDir = getDir("plugin", 0);
FileUtils.deleteDirectory(outputDir);
DexClassLoader loader = new DexClassLoader(jarFile.getAbsolutePath(),
outputDir.getAbsolutePath(), null, getClass().getClassLoader());
try {
Class<?> clazz = loader.loadClass("com.example.androidplugin.MainActivity");
IPlugin plugin = (IPlugin) clazz.newInstance();
String text = String.format("getInt: %d, getString: %s", plugin.getInt(),
plugin.getString());
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Android插件工程
public class MainActivity extends Activity implements IPlugin {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public int getInt() {
// TODO Auto-generated method stub
return 100;
}
@Override
public String getString() {
// TODO Auto-generated method stub
return "Hello, This is Plugin!";
}
}
有一点需要注意:插件工程中不能直接导入插件接口jar,如下
不能选择Add JARs,而是应该选择Add External JARs,否则会报运行时错误如下
我们注意到插件工程中插件的接口jar放在libs_ex目录中,而不是libs目录中,就是为避免eclipse自动将libs中的jar直接添加到工程中了。
运行Host工程,可以成功地加载插件apk中的MainActivity,并调用其插件接口getString返回了字符串,然而这里MainActivity只是被当做了一个普通java类被调起而已,并不是真正意义上的Android组件,我们可以尝试着在getString时返回Android字符串资源。如下:
public class MainActivity extends Activity implements IPlugin {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public int getInt() {
// TODO Auto-generated method stub
return 100;
}
@Override
public String getString() {
// TODO Auto-generated method stub
return getString(R.string.hello_world);
}
}
运行Host调起插件,结果会崩溃,日志如下:
可见插件中的Activity只是一个普通的Java类,无法获取其系统资源,要获取系统资源必须获取插件的Resources,Host中修改如下:
public class MainActivity extends Activity {
private Button mBtn;
private Resources mPluginResources;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = (Button) findViewById(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
loadPlugin();
}
});
}
private void loadPlugin() {
File jarFile = new File(getExternalCacheDir(), "AndroidPlugin.apk");
File outputDir = getDir("plugin", 0);
FileUtils.deleteDirectory(outputDir);
DexClassLoader loader = new DexClassLoader(jarFile.getAbsolutePath(),
outputDir.getAbsolutePath(), null, getClass().getClassLoader());
try {
Class<?> clazz = loader.loadClass("com.example.androidplugin.MainActivity");
IPlugin plugin = (IPlugin) clazz.newInstance();
loadResources(jarFile.getAbsolutePath());
int resId = mPluginResources.getIdentifier("hello_world", "string",
"com.example.androidplugin");
String text = mPluginResources.getString(resId);
Toast.makeText(this, plugin.getInt() + text, Toast.LENGTH_SHORT).show();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void loadResources(String dexPath) {
AssetManager assetManager = null;
try {
assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath);
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Resources resources = getResources();
mPluginResources = new Resources(assetManager, resources.getDisplayMetrics(),
resources.getConfiguration());
mPluginResources.newTheme().setTo(getTheme());
}
}