Tencent / Shadow

零反射全动态Android插件框架
BSD 3-Clause "New" or "Revised" License
7.46k stars 1.3k forks source link

有关cordova框架作为插件接入,报错:Faild create webview #1175

Closed senda58 closed 1 year ago

senda58 commented 1 year ago

详细的报错信息如下:

Caused by: java.lang.RuntimeException: java.lang.RuntimeException: Failed to create webview. at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:159) at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:85) at android.app.Activity.performCreate(Activity.java:7802) at android.app.Activity.performCreate(Activity.java:7791) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)  at android.os.Handler.dispatchMessage(Handler.java:107)  at android.os.Looper.loop(Looper.java:214)  at android.app.ActivityThread.main(ActivityThread.java:7356)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)  Caused by: java.lang.RuntimeException: Failed to create webview. at org.apache.cordova.CordovaWebViewImpl.createEngine(CordovaWebViewImpl.java:84) at org.apache.cordova.CordovaActivity.makeWebViewEngine(CordovaActivity.java:209) at org.apache.cordova.CordovaActivity.makeWebView(CordovaActivity.java:205) at org.apache.cordova.CordovaActivity.init(CordovaActivity.java:149) at org.apache.cordova.CordovaActivity.loadUrl(CordovaActivity.java:227) at com.example.hello.MainActivity.onCreate(MainActivity.java:39) at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:156) at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:85)  at android.app.Activity.performCreate(Activity.java:7802)  at android.app.Activity.performCreate(Activity.java:7791)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)  at android.os.Handler.dispatchMessage(Handler.java:107)  at android.os.Looper.loop(Looper.java:214)  at android.app.ActivityThread.main(ActivityThread.java:7356)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)  Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Constructor.newInstance0(Native Method) at java.lang.reflect.Constructor.newInstance(Constructor.java:343) at org.apache.cordova.CordovaWebViewImpl.createEngine(CordovaWebViewImpl.java:82) at org.apache.cordova.CordovaActivity.makeWebViewEngine(CordovaActivity.java:209)  at org.apache.cordova.CordovaActivity.makeWebView(CordovaActivity.java:205)  at org.apache.cordova.CordovaActivity.init(CordovaActivity.java:149)  at org.apache.cordova.CordovaActivity.loadUrl(CordovaActivity.java:227)  at com.example.hello.MainActivity.onCreate(MainActivity.java:39)  at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:156)  at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:85)  at android.app.Activity.performCreate(Activity.java:7802)  at android.app.Activity.performCreate(Activity.java:7791)  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245)  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409)  at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)  at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)  at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)  at android.os.Handler.dispatchMessage(Handler.java:107)  at android.os.Looper.loop(Looper.java:214)  at android.app.ActivityThread.main(ActivityThread.java:7356)  at java.lang.reflect.Method.invoke(Native Method)  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)  Caused by: java.lang.ClassCastException: android.webkit.WebViewClient cannot be cast to org.apache.cordova.engine.SystemWebViewClient at org.apache.cordova.engine.SystemWebView.setWebViewClient(SystemWebView.java:70) at com.tencent.shadow.core.runtime.ShadowWebView.init(ShadowWebView.java:82) at com.tencent.shadow.core.runtime.ShadowWebView.(ShadowWebView.java:61)

senda58 commented 1 year ago

大概分析了一下原因,在cordova 框架中 public class SystemWebView extends WebView ,而插件在编译时,将WebView 替换了ShadowWebview 导致初始化时,转化类型出错。这个怎么改呢?

@shifujun 能给讲讲,宿主加载weview 资源文件的原理么? 插件编译时将WebView 替换了ShadowWebview 意义在于什么呢?

shifujun commented 1 year ago

webview的处理原因可以blame一下shadowwebview那个文件看到提交记录。

这里的bug应该是 ebb33a85 处理时没考虑到子类可能需要对应的client子类。

解决这个bug需要再review一下init时需要初始化client的原因。以及如何确定client子类,如何构造client子类对象。

senda58 commented 1 year ago

这个是cordova框架自带的。还是没明白该咋改, 我想知道插件编译时将WebView 替换了ShadowWebview 意义在于什么呢? @shifujun

shifujun commented 1 year ago

你翻阅ShadowWebview的提交记录和实现就会发现它和从assets中加载url的关系了。

senda58 commented 1 year ago

@shifujun 为什么不能 使用ShdowWebView子类自己的setWebviewClient 呢? 也就是说 使用插件自己webview的逻辑呢?

下面是cordova框架 自带的webview子类,麻烦大佬给分析一下,谢谢了

        public class SystemWebView extends WebView implements CordovaWebViewEngine.EngineView {
            private SystemWebViewClient viewClient;
            SystemWebChromeClient chromeClient;
            private SystemWebViewEngine parentEngine;
            private CordovaInterface cordova;

            public SystemWebView(Context context) {
                this(context, null);
            }

            public SystemWebView(Context context, AttributeSet attrs) {
                super(context, attrs);
            }

            // Package visibility to enforce that only SystemWebViewEngine should call this method.
            void init(SystemWebViewEngine parentEngine, CordovaInterface cordova) {
                this.cordova = cordova;
                this.parentEngine = parentEngine;
                if (this.viewClient == null) {
                    setWebViewClient(new SystemWebViewClient(parentEngine));
                }

                if (this.chromeClient == null) {
                    setWebChromeClient(new SystemWebChromeClient(parentEngine));
                }
            }

            @Override
            public CordovaWebView getCordovaWebView() {
                return parentEngine != null ? parentEngine.getCordovaWebView() : null;
            }

            @Override
            public void setWebViewClient(WebViewClient client) {
                viewClient = (SystemWebViewClient)client;
                super.setWebViewClient(client);
            }

            @Override
            public void setWebChromeClient(WebChromeClient client) {
                chromeClient = (SystemWebChromeClient)client;
                super.setWebChromeClient(client);
            }

            @Override
            public boolean dispatchKeyEvent(KeyEvent event) {
                Boolean ret = parentEngine.client.onDispatchKeyEvent(event);
                if (ret != null) {
                    return ret.booleanValue();
                }
                return super.dispatchKeyEvent(event);
            }
        }
shifujun commented 1 year ago

肯定是可以的,也应该使用原本业务定义的子类的。你理解错了。我们就是把WebView换成了ShadowWebView,加了一层逻辑而已。

senda58 commented 1 year ago

@shifujun 好,谢谢 大佬,代码可以了,太厉害了!