3

我的應用叫做MyNiceApp。 MyNiceApp大多隻是一個核心,在MainActivity onCreate中加載一個名爲coreView的視圖。 coreView通過用戶根據需要下載的其他插件的視圖來填充。我定義了核心視圖上的各個區域,這些區域可以通過MyNiceApp中的Interfaces插件填充。如何加載視圖並將其從插件傳遞到coreView如何將視圖添加到其他應用的應用

我被告知,RemoteViews是一個不錯的選擇,但我不知道如何實現它。還有什麼其他選擇?

RemoteViews是最好的方式嗎?即使不是最好的方法,我也願意嘗試任何可行的方法。黑客會做。目前,任何可以提供這種功能的東西都已足夠。稍後可以改進。

謝謝大家提前。

UPDATE

我想有他們的託管我的私人服務器上。他們將下載到稱爲/data/app/com.myniceapp.plugins

我想這將是更好的組織,如果我有下/data/app/com.myniceapp創建的文件夾專用文件夾。/plugins,然後讓DexClassLoader爲下載的插件抓取/data/app/com.myniceapp/plugins,然後我可以調用我的Class實現,並在運行時動態地將插件視圖加載到核心視圖。

臨時更新

嗨@lelloman,和大家一樣。我一直在努力讓您的解決方案發揮作用,但迄今爲止我一直未能成功。

我創建了一個名爲的新項目測試視圖。它有一個XML佈局,我嘗試膨脹並傳送到核心觀點如下:

package rev.ca.testview; 

import android.content.Context; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.widget.Button; 

import ca.rev.libs.core.MyViewCreator; 

public class TestView implements MyViewCreator { 
    @Override 
    public View createMyView(Context context) { 
     LayoutInflater revInfl = LayoutInflater.from(context); 
     View toolBarItemsLL = revInfl.inflate(R.layout.layout, null, false); 

     Button button = (Button) toolBarItemsLL.findViewById(R.id.testButton); 
     return button; 
    } 
} 

但這不起作用。下面是它的其餘部分:

在MainActivity認爲,這是應該從插件獲取的觀點:

NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 
navigationView.setNavigationItemSelectedListener(this); 

String dexPath = "/data/app/rev.ca.testview"; 
String optimizedDirectory = this.getCacheDir().getAbsolutePath(); 
String libraryPath = null; 

DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, null, ClassLoader.getSystemClassLoader()); 
DexFile dexFile = null; 
try { 
    dexFile = DexFile.loadDex(dexPath, File.createTempFile("opt", "dex", this.getCacheDir()).getPath(), 0); 

    for (Enumeration<String> classNames = dexFile.entries(); classNames.hasMoreElements();) { 
     String className = classNames.nextElement(); 
     Class myClass = dexClassLoader.loadClass(className); 
     if (myClass.isAssignableFrom(MyViewCreator.class)) { 
      MyViewCreator creator = (MyViewCreator) myClass.getConstructor().newInstance(); 
      View myView = creator.createMyView(this); 
        // add myView wherever you want 
      navigationView.addView(myView); 
     } 
    } 
} catch (IOException e) { 
    e.printStackTrace(); 
} catch (InstantiationException e) { 
    e.printStackTrace(); 
} catch (InvocationTargetException e) { 
    e.printStackTrace(); 
} catch (NoSuchMethodException e) { 
    e.printStackTrace(); 
} catch (IllegalAccessException e) { 
    e.printStackTrace(); 
} catch (ClassNotFoundException e) { 
    e.printStackTrace(); 
} 

抽屜佈局實現發出更新

嗨@lelloman再次。我一直在嘗試將您的解決方案實施到具有抽屜佈局的主項目中。它突破final View toolBarItemsLL = revInfl.inflate(R.layout.activity_main, null, false);

爲什麼它不適用於抽屜佈局。如果您將導航抽屜活動android-plugins/MyNiceApps/app/src/main/:新建 - >活動 - >導航抽屜活動)添加進來,那就是當它全部崩潰時。希望你能幫助。

這裏是堆棧跟蹤:

08-14 21:44:27.564 13390-13390/rev.ca.revcore W/ResourceType: For resource 0x7f0b005e, entry index(94) is beyond type entryCount(9) 
08-14 21:44:27.564 13390-13390/rev.ca.revcore W/ResourceType: Failure getting entry for 0x7f0b005e (t=10 e=94) (error -75) 
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: For resource 0x7f0a002c, entry index(44) is beyond type entryCount(5) 
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: Failure getting entry for 0x7f0a002c (t=9 e=44) (error -75) 
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: For resource 0x7f060022, entry index(34) is beyond type entryCount(1) 
08-14 21:44:27.565 13390-13390/rev.ca.revcore W/ResourceType: Failure getting entry for 0x7f060022 (t=5 e=34) (error -75) 
08-14 21:44:27.565 13390-13390/rev.ca.revcore D/AndroidRuntime: Shutting down VM 
08-14 21:44:27.566 13390-13390/rev.ca.revcore E/AndroidRuntime: FATAL EXCEPTION: main 
                   Process: rev.ca.revcore, PID: 13390 
                   android.view.InflateException: Binary XML file line #7: Binary XML file line #7: Error inflating class TextView 
                   Caused by: android.view.InflateException: Binary XML file line #7: Error inflating class TextView 
                   Caused by: java.lang.UnsupportedOperationException: Can't convert to ComplexColor: type=0x1 
                    at android.content.res.ResourcesImpl.loadComplexColorForCookie(ResourcesImpl.java:879) 
                    at android.content.res.ResourcesImpl.loadComplexColorFromName(ResourcesImpl.java:756) 
                    at android.content.res.ResourcesImpl.loadColorStateList(ResourcesImpl.java:835) 
                    at android.content.res.Resources.loadColorStateList(Resources.java:1002) 
                    at android.content.res.TypedArray.getColorStateList(TypedArray.java:531) 
                    at android.widget.TextView.<init>(TextView.java:1076) 
                    at android.widget.TextView.<init>(TextView.java:704) 
                    at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:62) 
                    at android.support.v7.widget.AppCompatTextView.<init>(AppCompatTextView.java:58) 
                    at android.support.v7.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:103) 
                    at android.support.v7.app.AppCompatDelegateImplV9.createView(AppCompatDelegateImplV9.java:1029) 
                    at android.support.v7.app.AppCompatDelegateImplV9.onCreateView(AppCompatDelegateImplV9.java:1087) 
                    at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:47) 
                    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:769) 
                    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727) 
                    at android.view.LayoutInflater.rInflate(LayoutInflater.java:858) 
                    at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821) 
                    at android.view.LayoutInflater.inflate(LayoutInflater.java:518) 
                    at android.view.LayoutInflater.inflate(LayoutInflater.java:426) 
                    at rev.ca.revbags.MyViewCreator.createView(MyViewCreator.java:24) 
                    at rev.ca.revcore.rev_plugin_loader.RevPluginLoader.revLoadView(RevPluginLoader.java:36) 
                    at rev.ca.revcore.RevCoreMainActivity$1.handleMessage(RevCoreMainActivity.java:21) 
                    at android.os.Handler.dispatchMessage(Handler.java:102) 
                    at android.os.Looper.loop(Looper.java:154) 
                    at android.app.ActivityThread.main(ActivityThread.java:6119) 
                    at java.lang.reflect.Method.invoke(Native Method) 
                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 
+0

用戶如何下載插件? – lelloman

+0

我正在考慮在我的私人服務器上託管插件。 @lelloman –

+0

我的意思是,這些視圖是什麼格式,它們是實際的視圖編譯成Dex文件?定義視圖結構的Json模型?你是否在你的應用中管理下載過程? – lelloman

回答

1

第一件事情,似乎你並不需要RemoteViews可言,你可以實例化視圖的過程,並將其連接到活動。

然後你需要澄清一件事:你需要在運行時加載類?如果你想在你的應用中使用新的類而不更新它,就會出現這種情況。這不是微不足道的,你將被迫在你的應用中使用一些已經定義好的接口,或者通過反射來抓取你的方式。這將是一個痛苦。

另一個更簡單的選擇是爲每個視圖下載一個xml佈局,並將其與可能描述某些行爲的配置文件相關聯。如果你決定從外部插件加載運行時類,你可以走了這條路:

  • 定義庫
  • 內ViewCreator類,當你想創建你需要它包含一個APK插件一個這些ViewCreator類
  • 在你的應用程序,你再與DexClassLoader加載apk文件(或更多?),找到ViewCreator類實例化它
  • 的ViewCreator實例即可生成您查看

這種方法爲您提供了巨大的力量,遠遠超出了視圖實例化的範圍,但也帶來了很多複雜性。如果你是爲了好玩而做這件事的話,那麼我認爲你是在正確的軌道上,但我不會推薦這樣做的商業項目。我創建了一個最小工作示例here的示例存儲庫。

這種方法的主要目的是創建一個「插件」庫,它將包含您的應用程序和每個插件的通用接口。在我的示例中,庫只包含一個類:

public abstract class AbstractViewCreator { 

    private final Context context; 

    public AbstractViewCreator(Context context) { 
     this.context = context; 
    } 

    public abstract View createView(); 

    protected Context getContext() { 
     return context; 
    } 
} 

然後,您需要創建一個「插件」應用程序,示例中爲PluginA。這個應用程序必須包含AbstractViewCreator的一個實現。然後NiceApp需要下載插件apk,在示例中,apk被複制到assets文件夾中。之後,你需要加載APK:

DexClassLoader dexClassLoader = new DexClassLoader(apkPath, codeCachePath, librariesPath, parentClassLoader); 

,那麼你需要加載一個類,你可以得到你要加載的類的名稱,例如:

String className = "com.lelloman." + assetsFileName.replace(".apk", "").toLowerCase() + ".MyViewCreator"); 
Class fooClass = dexClassLoader.loadClass(className); 

然後得到的構造函數,通過反射實例化ViewCreator

Constructor constructor = myClass.getConstructor(Context.class); 
AbstractViewCreator creator = (AbstractViewCreator) constructor.newInstance(context); 

,那麼你可以創建你View

View viewFromPlugin = creator.createView(); 
+0

Hi @lelloman。我一直在努力讓您的解決方案發揮作用,但迄今爲止我一直未能成功。我已經添加了迄今爲止我一直在嘗試的更新。能否請你告訴我失敗的原因是什麼?謝謝。 –

+0

正如我在之前的評論中所說的,請定義您的要求。我提出的解決方案實施起來非常痛苦,你很有可能不需要走這麼遠。如何下載你的意見的XML佈局文件?那會是一個選擇嗎? – lelloman

+0

這些插件將爲應用程序帶來各自的功能。他們將擁有自己的持久化實現的形式,這意味着他們自己的獨立動作和按鈕等等。所以我認爲完全合格的獨立模塊看起來是合適的,除非有其他方法。你的方法似乎是最好的,非常可行的,只要我能加載Dex文件,我覺得我很接近。它將處理我需要完成的大部分工作。 –

相關問題