是否可以使用来自外部应用程序的 fragment/Activity 并在嵌入时使用?
例如:嵌入来自 PDF 阅读器应用程序的 PDF 阅读器 fragment 。
最佳答案
可能有点晚了,但还是觉得可以加,可能对别人有帮助。
对于 Activity ,嵌入它真的没有意义,有一种方便的方式来使用其他应用程序 Activity - 用 Intent 启动它。对于在应用程序内实现某种“插件”功能的情况下的 fragment 可能有意义。
Android Blog 'Custom Class Loading in Dalvik' 中有一个官方方法可以使用来自其他应用程序的代码(或从网络加载代码) .请注意,android 与其他平台/环境没有太大区别,因此两个部分(您的应用程序和您想要加载到您的应用程序中的 fragment )都应该支持某种契约(Contract)。这意味着您不能从任何应用程序中加载任何组件,这很常见,并且有很多原因会导致这种情况。
所以,这里有一些实现的小例子。它由3部分组成:
package com.example.test_interfaces;
import android.app.Fragment;
/**
* Interface of Fragment holder to be obtained from external application
*/
public interface FragmentHolder {
Fragment getFragment();
}
对于这个例子,我们只需要一个接口(interface)来演示如何加载 fragment 。
fragment ,我们要加载 插件 fragment :
package com.sandrstar.plugin;
import com.example.test_interfaces.FragmentHolder;
public class PlugInFragment extends Fragment implements FragmentHolder {
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
// Note that loading of resources is not the same as usual, because it loaded actually from another apk
final XmlResourceParser parser = container.getContext().getPackageManager().getXml("com.sandrstar.plugin", R.layout.fragment_layout, null);
return inflater.inflate(parser, container, false);
}
@Override
public Fragment getFragment() {
return this;
}
}
它的布局 fragment 布局.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/black">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is from fragment"
android:textColor="@android:color/white"/>
</LinearLayout>
Activity 本身 我的 Activity :
public class MyActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
Class<?> requiredClass = null;
final String apkPath = getPackageManager().getApplicationInfo("com.sandrstar.plugin",0).sourceDir;
final File dexTemp = getDir("temp_folder", 0);
final String fullName = "com.sandrstar.plugin.PlugInFragment";
boolean isLoaded = true;
// Check if class loaded
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
if (!isLoaded) {
final DexClassLoader classLoader = new DexClassLoader(apkPath,
dexTemp.getAbsolutePath(),
null,
getApplicationContext().getClassLoader());
requiredClass = classLoader.loadClass(fullName);
}
if (null != requiredClass) {
// Try to cast to required interface to ensure that it's can be cast
final FragmentHolder holder = FragmentHolder.class.cast(requiredClass.newInstance());
if (null != holder) {
final Fragment fragment = holder.getFragment();
if (null != fragment) {
final FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.add(R.id.fragmentPlace, fragment, "MyFragment").commit();
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
它的布局 main.xml :
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clipChildren="false"
android:id="@+id/root">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/down_image" />
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fragmentPlace"
android:layout_centerInParent="true" />
</RelativeLayout>
最后我们能够在真实设备上观察到以下内容:
可能的问题处理 (感谢 @MikeMiller 的更新):
classLoader.loadClass
时遇到以下错误: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
确保 fragment 模块包含在主应用程序中(作为“已编译”)
NameNotFoundException
在调用 context.getPackageManager().getApplicationInfo(packageName,0).sourceDir
,然后确保该 fragment 位于已安装的 APPLICATION 中(而不仅仅是库依赖项)。请按照以下步骤确保是这种情况:1) 在主应用程序的 build.gradle 中,更改
apply plugin: 'android-library'
至apply plugin: 'android'
并确保有一个虚拟 Activity java 文件。在主应用中,移除对fragment模块的依赖(步骤3中没有指定,但是我必须在主应用中添加对fragment模块的依赖。但是fragment模块现在是一个 Activity 应用程序,你可以'不依赖这些),否则你会得到:Error:Dependency unspecified on project resolves to an APK archive which is not supported as a compilation dependency.
2)运行 fragment 模块(你现在可以这样做,因为它是一个 Activity 应用程序)。这以 getApplicationInfo 调用可以找到它的方式安装它 Revert build.gradle 并将依赖项添加回主应用程序(作为“编译”依赖项)现在一切都应该工作了。当您对 fragment 代码进行更新时,您无需再次执行此过程。但是,如果您想在新设备上运行或添加新的 fragment 模块,您将会这样做。我希望这能够节省我试图解决上述错误的时间。
安卓L
看来,基于正常multidex support使用 Android L,则不需要上述步骤,因为类加载不同。可以使用 multidex 支持中描述的方法来代替 Android Blog 'Custom Class Loading in Dalvik' ,因为它明确指出:
Note: The guidance provided in this document supersedes the guidance given in the Android Developers blog post Custom Class Loading in Dalvik.
可能,
android.support.multidex
的变化可能需要重用该方法。
关于android - 在应用程序内使用外部应用程序 fragment/Activity ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7726702/