您的位置:首页 > 移动开发 > Android开发

Android插件技术——(四)加载未安装apk

2015-08-19 17:03 483 查看
上一篇虽然研究了如何加载未安装apk的资源,然而由于只是当做普通的Java类来加载,没有Android系统上下文,所以无法启动Activity,展开布局,进行各种界面交互,本篇将研究如何解决这些问题。

首先介绍一下核心思路,未安装的apk是没有系统上下文的,所以导致上述一系列问题,由此可见,解决问题的关键是给插件赋予一个系统上下文。而这个上下文如何生成呢?一种可行的方案是采用主App的上下文做代理,以主App的代理为壳,行插件之实。例如主App的Activity是受生命周期控制的,在生命周期各个回调如onCreate,onResume内,我们调用插件的onCreate,onResume。至于如何获取插件资源上一篇已讨论过了,这里就不再赘述。

接下来介绍一下各个工程,一共分Host,Plugin,PluginLib,Host和Plugin就不用多说了,PluginLib是一个插件功能库,包括一些基础接口和类。Host和Plugin都要导入该库,然而区别是Host中该库是作为普通jar导入的,而PluginLib中是作为External Jar导入的,这点切记。

Host工程主要有两个类很重要,PluginManager和PluginProxyActivity,PluginManager用于加载插件,PluginProxyActivity即为插件的代理Activity。废话不说了,直接上代码:



public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
startPlugin();
}
});
}

private void startPlugin() {

Intent intent = new Intent();
intent.setClass(this, PluginProxyActivity.class);

File apkFile = new File(getExternalCacheDir(), "AndroidPlugin.apk");
PluginManager.loadPluginApk(this, apkFile.getAbsolutePath());

intent.putExtra(PluginProxyActivity.EXTRA_APK, apkFile.getAbsolutePath());
intent.putExtra(PluginProxyActivity.EXTRA_CLASS, "com.example.androidplugin.MainActivity");

startActivity(intent);
}
}


public class PluginManager {

private static HashMap<String, Plugin> mPluginMap = new HashMap<String, Plugin>();

public static Plugin getPlugin(String apkPath) {
return mPluginMap.get(apkPath);
}

public static void loadPluginApk(Context context, String apkPath) {
String dexOutputPath = context.getDir("plugin", 0).getAbsolutePath();

FileUtils.deleteDirectory(dexOutputPath);

DexClassLoader dexClassLoader = new DexClassLoader(apkPath, dexOutputPath,
null, PluginManager.class.getClassLoader());

AssetManager assetManager = createAssetManager(apkPath);
Resources resources = createResources(context, assetManager);

Plugin plugin = new Plugin(dexClassLoader, assetManager, resources);

mPluginMap.put(apkPath, plugin);
}

private static AssetManager createAssetManager(String dexPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod(
"addAssetPath", String.class);
addAssetPath.invoke(assetManager, dexPath);
return assetManager;
} catch (Exception e) {
e.printStackTrace();
return null;
}

}

private static Resources createResources(Context context, AssetManager assetManager) {
Resources superRes = context.getResources();
Resources resources = new Resources(assetManager,
superRes.getDisplayMetrics(), superRes.getConfiguration());
return resources;
}

public static class Plugin {
public Resources resources;
public AssetManager assetManager;
public DexClassLoader dexClassLoader;

public Plugin(DexClassLoader dexClassLoader, AssetManager assetManager, Resources resources) {
this.resources = resources;
this.assetManager = assetManager;
this.dexClassLoader = dexClassLoader;
}
}
}


public class PluginProxyActivity extends FragmentActivity implements IProxy {

public static final String EXTRA_APK = "extra_apk";
public static final String EXTRA_CLASS = "extra_class";

private String mApkPath;
private Plugin mPlugin;
private Theme mTheme;

private PluginBaseActivity mPluginActivity;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);

Intent intent = getIntent();

mApkPath = intent.getStringExtra(EXTRA_APK);
String className = intent.getStringExtra(EXTRA_CLASS);

mPlugin = PluginManager.getPlugin(mApkPath);

launchActivity(intent, className);
}

private void launchActivity(Intent intent, String className) {
try {
Class<?> localClass = mPlugin.dexClassLoader.loadClass(className);
Constructor<?> localConstructor = localClass
.getConstructor(new Class[] {});
Object instance = localConstructor.newInstance(new Object[] {});

mPluginActivity = (PluginBaseActivity) instance;

handleActivityTheme();

mPluginActivity.attach(this);
mPluginActivity.setIntent(intent);
mPluginActivity.onCreate(intent.getExtras());

} catch (Exception e) {
finish();
return;
}

}

private void handleActivityTheme() {
Theme superTheme = super.getTheme();
mTheme = mPlugin.resources.newTheme();
mTheme.setTo(superTheme);
}

@Override
public Theme getTheme() {
if (mTheme == null) {
return super.getTheme();
} else {
return mTheme;
}
}

@Override
public AssetManager getAssets() {
if (mPlugin == null) {
return super.getAssets();
} else {
return mPlugin.assetManager;
}
}

@Override
public ClassLoader getClassLoader() {
if (mPlugin == null) {
return super.getClassLoader();
} else {
return mPlugin.dexClassLoader;
}
}

@Override
public Resources getResources() {
if (mPlugin == null) {
return super.getResources();
}
return mPlugin.resources;
}

@Override
public FragmentActivity getProxyActivity() {
// TODO Auto-generated method stub
return this;
}

@Override
public void startActivityForResult(Intent bundle, String className, int requestCode) {
// TODO Auto-generated method stub
Intent intent = new Intent(this, PluginProxyActivity.class);

if (bundle != null) {
intent.putExtras(bundle);
}

intent.putExtra(EXTRA_APK, mApkPath);
intent.putExtra(EXTRA_CLASS, className);

startActivityForResult(intent, requestCode);
}
}


接下来要说的是PluginLib,主要包括两个接口IPlugin和IProxy,此外还有PluginBaseActivity,插件中的Activity都要继承自这个类,里面封装了与Host代理Activity进行交互的一些操作。



public interface IPlugin {
public void attach(IProxy proxy);
}


public interface IProxy {

public FragmentActivity getProxyActivity();

public void startActivityForResult(Intent intent,
String className, int requestCode);

}


public class PluginBaseActivity extends FragmentActivity implements IPlugin {

private IProxy mProxy;
private FragmentActivity mProxyActivity;

@Override
public void attach(IProxy proxy) {
// TODO Auto-generated method stub
mProxy = proxy;
mProxyActivity = proxy.getProxyActivity();
}

@Override
public void onCreate(Bundle savedInstanceState) {

}

@Override
public void setContentView(int layoutResID) {
// TODO Auto-generated method stub
mProxyActivity.setContentView(layoutResID);
}

@Override
public View findViewById(int id) {
return mProxyActivity.findViewById(id);
}

@Override
public Intent getIntent() {
return mProxyActivity.getIntent();
}

@Override
public void setIntent(Intent newIntent) {
mProxyActivity.setIntent(newIntent);
}

@Override
public Resources getResources() {
return mProxyActivity.getResources();
}

public void startActivity(Intent intent) {
startActivity(intent, intent.getComponent().getClassName());
}

public void startActivity(Intent intent, Class<?> clazz) {
startActivity(intent, clazz.getName());
}

public void startActivity(Intent intent, String className) {
startActivityForResult(intent, className, -1);
}

public void startActivityForResult(Intent intent, String className,
int requestCode) {
mProxy.startActivityForResult(intent, className, requestCode);
}
}


接下来介绍插件工程,由于只是为了演示,这个插件就做的很简单,只包括了加载布局和资源,插件内跳转Activity。



public class MainActivity extends PluginBaseActivity {

private Button mBtn;

@Override
public 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
startTestActivity();
}
});
}

private void startTestActivity() {
Intent intent = new Intent();
startActivity(intent, TestActivity.class);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: