Usar fragmento / actividad de aplicación externa dentro de la aplicación

¿Es posible usar un fragmento / actividad de una aplicación externa y usarlo si está embebido? Por ejemplo, inserte un fragmento de lector de PDF desde una aplicación de lector de PDF.

No, no puede “reutilizar” el código de otras aplicaciones. La única forma oficial es usar Intent para invocar toda la Actividad.

Puede ser un poco tarde, pero aún así sentir que se puede agregar y puede ayudar a otros.

Para la actividad, realmente no tiene sentido que esté incrustado, hay una forma conveniente de usar otras actividades de aplicaciones: iniciarlo con intención. Para los fragmentos, podría tener sentido en caso de implementación algún tipo de funcionalidad ‘plug-in’ dentro de la aplicación.

Hay una forma oficial de usar el código de otras aplicaciones (o cargar el código de la red) en el Blog de Android ‘Carga de clase personalizada en Dalvik’ . Tenga en cuenta que el Android no es muy diferente de otras plataformas / entornos, por lo que ambas partes (su aplicación y fragmento desea cargar en su aplicación) deben admitir algún tipo de contrato. Eso significa que no puede cargar ningún componente desde ninguna aplicación, lo cual es bastante común y existen varias razones para que sea así.

Entonces, aquí hay un pequeño ejemplo de la implementación. Se compone de 3 partes:

  1. Proyecto de interfaces: este proyecto contiene definiciones de interfaces que la aplicación principal debe cargar para usar clases externas:

     package com.example.test_interfaces; import android.app.Fragment; /** * Interface of Fragment holder to be obtained from external application */ public interface FragmentHolder { Fragment getFragment(); } 

    Para este ejemplo, solo necesitamos una única interfaz para demostrar cómo cargar el fragmento.

  2. Aplicación de complemento, que contiene el código que necesita cargar; en nuestro caso, es un fragmento. Tenga en cuenta que este proyecto en su IDE debe depender de la interfaz uno utilizando el tipo ‘proporcionado’ y sin exportar, ya que será importado por la aplicación principal.

    Fragmento, vamos a cargar PlugInFragment :

     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; } } 

    Y es diseño fragment_layout.xml :

        
  3. Aplicación principal que quiere cargar el fragmento desde otra aplicación. Debería haber importado el proyecto Interface:

    La actividad misma MyActivity :

     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(); } } } 

    Y es layout main.xml :

         

Y finalmente podemos observar lo siguiente en el dispositivo real:

enter image description here

Posible manejo de problemas (gracias a @MikeMiller para la actualización):

  1. Si obtiene el siguiente error en la llamada a classLoader.loadClass :

java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

Asegúrese de que los módulos de fragmentos estén incluidos en la aplicación principal (como ‘comstackdo’)

  1. Si obtiene una NameNotFoundException en la llamada a context.getPackageManager().getApplicationInfo(packageName,0).sourceDir , asegúrese de que el fragmento esté en una APLICACIÓN instalada (no solo una dependencia de la biblioteca). Siga los pasos a continuación para asegurarse de que ese sea el caso:

    1) En el build.gradle de la aplicación principal, cambie el apply plugin: 'android-library' para apply plugin: 'android' y asegúrese de que haya un archivo Java de actividad ficticia. En la aplicación principal, elimine la dependencia del módulo de fragmento (no está especificado en el paso 3, pero tuve que agregar una dependencia en el módulo de fragmento a la aplicación principal. Pero el módulo de fragmento ahora es una aplicación de actividad, y usted puede ‘ t tiene dependencias) o obtendrá esto: Error:Dependency unspecified on project resolves to an APK archive which is not supported as a comstacktion dependency.

    2) Ejecuta el módulo de fragmento (que puedes hacer ahora, porque es una aplicación de actividad). Eso lo instala de una manera que la llamada a getApplicationInfo puede encontrarlo Revertir build.gradle y agregar la dependencia a la aplicación principal (como una dependencia de ‘comstackción’) Todo debería funcionar ahora. Cuando realice actualizaciones del código de fragmento, no tendrá que volver a realizar este proceso. Lo hará, sin embargo, si desea ejecutar en un nuevo dispositivo o si agrega un nuevo módulo de fragmento. Espero que esto pueda salvar a alguien el tiempo que pasé tratando de resolver los errores anteriores.

Android L

Parece que, según el soporte multidex normal con Android L, los pasos anteriores no son necesarios, porque la carga de clases es diferente. En lugar del Android Blog ‘Custom Class Loading en Dalvik’ , se puede utilizar Approach, descrito en soporte multidex, porque establece claramente que:

Nota: La guía provista en este documento reemplaza la guía dada en la publicación de blog de Desarrolladores de Android Custom Class Loading en Dalvik.

Probablemente, cambios en android.support.multidex puedan ser necesarios para reutilizar ese enfoque.