【问题标题】:Load fragment with layout from another APK从另一个 APK 加载带有布局的片段
【发布时间】:2020-04-13 12:05:57
【问题描述】:

我有两个 APK。第一个 APK 是加载第二个 APK 的主 APK。

MainActivity.java(第一个 APK): 对象 mainFragment 被 DexClassLoader 提前加载

setContentView(R.layout.activity_main);

LinearLayout fragContainer = findViewById(R.id.main_fragment);

LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.setId(View.generateViewId());

getSupportFragmentManager().beginTransaction().add(ll.getId(), mainFragment, "mainFragment").commit();

fragContainer.addView(ll);

activity_main.xml(第一个 APK):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >
    <LinearLayout
            android:layout_height="match_parent"
            android:layout_width="match_parent"
            android:id="@+id/main_fragment"
            android:orientation="vertical"
            />

</LinearLayout>

MainFragment.java(第二个 APK):

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_main, viewGroup, false);

    view.findViewById(R.id.emailSignInButton).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {

        }
    });

    return view;
}

fragment_main.xml(第二个 APK):

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <Button
            android:text="@string/sign_in"
            android:layout_width="88dp"
            android:layout_height="wrap_content"
            android:id="@+id/emailSignInButton"

            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" android:layout_marginStart="32dp"
            android:layout_marginEnd="32dp" android:layout_marginTop="8dp"
            tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

我不明白,为什么要排队

View view = inflater.inflate(R.layout.fragment_main, viewGroup, false);

view 始终为 NULL。我需要像 mainFragment 那样将 R.layout.fragment_main 加载到内存中吗?

【问题讨论】:

  • 资源不存在。 DexClassLoader 加载类,而不是资源。
  • 尝试使用应用程序上下文,而不是 viewGroup 上下文,例如View.inflate(getApplicationContext(), R.layout.fragment_main, viewGroup) 或 LayoutInflater.from(getApplicationContext()).inflate(R.layout.fragment_main, viewGroup, false)
  • @VytautasBerankis 它没有帮助

标签: java android android-fragments layout dynamic-loading


【解决方案1】:

@CommonsWare 已经回答您:您正在加载类,而不是资源。

在 Android 的包中,您同时拥有类(如 R)和资源(例如,预处理的 XML 文件,如布局)。 ClassLoader 能够读取和加载第一个类,因此,加载另一个 APK R 类是可能的。但是 R 的索引只指向资源文件,它们不包含所述资源的实际值。因此,您侧载的 R 类不提供到有效资源的路由。如果 R 可以做到这一点,那么 APK/AAR/APKLib 格式将不存在;常规的 JAR 文件就足够了。但它们不会,因为 android 资源文件与类文件不同,因此,需要某种“链接”以使它们对您的类可见:R 类。

如果您想让您当前的代码正常工作,您需要将 fragment_main.xml 替换为编码的视图组实例。创建一个类/方法,提供一个包含您的按钮的约束布局实例(例如 MyViewFactory.createConstraintLayoutWithButton),通过代码设置属性,然后替换

    @Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_main, viewGroup, false);

    view.findViewById(R.id.emailSignInButton).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {

        }
    });

    return view;
}

类似的东西

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle savedInstanceState) {
    View view = MyViewFactory.createConstraintLayoutWithButton(getContext)

    view.findViewById(R.id.emailSignInButton).setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {

        }
    });

    return view;
}

只要你不使用资源,你会没事的。也就是说,这种方法非常脆弱,所以最好在 APK 之间创建 Intent 过滤器,以使 APK 2 能够适用于 APK 1。

编辑: 您可以使用 R 常量作为代码生成视图的 id,只要您先手动设置它们。例如:

public static ConstraintLayout createConstraintLayoutWithButton(Context context){

  Constraintlayout mylayout = new ConstraintLayout(context);
  //set up of the layout
  Button myButton = new Button(context);
  //set up of the button
  button.setId(R.id.emailSignInButton);
  myLayout.addView(button);
  return mylayout;
}

然后你就可以用 id 找到那个按钮了。

【讨论】:

  • 感谢您的解决方案。 1. 我认为 CommonsWare 没有回答我的问题,所以我不能将他的评论作为解决方案。 2. 我尝试了您的解决方案,它对我有用(我将其标记为正确答案)。 3. 我的目的是从主 APK 创建动态加载代码/资源,我需要它来静默更新我的应用程序(由于某些原因我不能使用 Google Play)。那么在您的解决方案中,所有资源都必须进行硬编码吗?例如, R.id.emailSignInButton 它会在 MyViewFactory.emailSignInButton 之类的东西上发生变化?我没听错吧?
  • 嗨!在这种情况下,您必须将所有内容公开为一个类,从类加载器中看不到 XML。所以 XML 布局是不可能的。但是对于像图像这样的物理资产,您可以在 APK 2 中有一个服务,将这些文件复制到一个共享目录,APK 1 可以从中读取它们。您仍然可以使用 R 常量作为 ID,但您必须在代码生成的视图中手动设置它们。
猜你喜欢
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-06
相关资源
最近更新 更多