Android 基于Web Html实现webrtc 直播 调取Native操作
选择控件
原生Webview
X5webView (腾讯内核)
XWalkView(因特尔浏览器内核-----已停止维护)
我的结果
原生Webview(失败)
X5webView (失败—但是使用QQ打开后可以使用)
XWalkView(成功----目前只是用在Android5.1.1、Android6.0.1两版本测试成功;在Android10.0上不知道是什么原因失败------会增加apk体积40M或者20M)下载链接
代码
权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" tools:ignore="ProtectedPermissions" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
a.引入依赖
//在app层级下build
implementation 'org.xwalk:xwalk_core_library:23.53.589.4'
//在工程的build
maven { url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'}
//例如下面:
repositories { google() jcenter() maven { url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'} } allprojects { repositories { google() jcenter() maven { url 'https://download.01.org/crosswalk/releases/crosswalk/android/maven2'} } }
引入上面的依赖必须要配置证书(如果技术强大,自己编译一个跳过证书的)
b.可以下载aar包,引入经过更改跳过证书验证的aar。下载链接
代码
xml <?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" tools:context=".WalkerXActivity"> <org.xwalk.core.XWalkView android:id="@+id/xwalkView" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
activity
package com.example.myapplication import android.annotation.SuppressLint import android.content.Intent import android.os.Bundle import org.xwalk.core.* class WalkerXActivity : XWalkActivity() { private var webView: XWalkView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_wal2) webView = findViewById(R.id.xwalkView) } override fun onXWalkReady() { initSettings() webView?.setUIClient(XWUIClient(webView)) webView?.setResourceClient(XWResourceClient(webView)) //用来交互**加粗样式** //webView?.addJavascriptInterface(new AppShell(this), AppShell.TAG); webView?.loadUrl("http://www.baidu.com"); } override fun onPointerCaptureChanged(hasCapture: Boolean) {} override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) if (isXWalkReady) webView?.onNewIntent(intent) } override fun onPause() { super.onPause() if (isXWalkReady) { webView?.pauseTimers() webView?.onHide() } } override fun onResume() { super.onResume() if (isXWalkReady) { webView?.resumeTimers() webView?.onShow() } } override fun onDestroy() { super.onDestroy() if (isXWalkReady) { webView?.onDestroy() } } override fun onBackPressed() { if (isXWalkReady) { val history = webView?.navigationHistory if (history?.canGoBack()!!) { history.navigate(XWalkNavigationHistory.Direction.BACKWARD, 1) } else { super.onBackPressed() } } else { super.onBackPressed() } } /** * 没有允许定位的设置 */ @SuppressLint("SetJavaScriptEnabled") fun initSettings() { val webSettings = webView?.settings //启用JavaScript webSettings?.javaScriptEnabled = true //允许js弹窗alert等,window.open方法打开新的网页,默认不允许 webSettings?.javaScriptCanOpenWindowsAutomatically = true //localStorage和sessionStorage webSettings?.domStorageEnabled = true //Web SQL Databases webSettings?.databaseEnabled = true //是否可访问Content Provider的资源,默认值 true webSettings?.allowContentAccess = true /* 是否允许访问文件系统,默认值 true file:///androMSG_asset和file:///androMSG_res始终可以访问,不受其影响 */webSettings?.allowFileAccess = true //是否允许通过file url加载的Javascript读取本地文件,默认值 false webSettings?.allowFileAccessFromFileURLs = true //是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false webSettings?.allowUniversalAccessFromFileURLs = true //设置是否支持缩放 webSettings?.setSupportZoom(false) //设置内置的缩放控件 webSettings?.builtInZoomControls = false /* 当该属性被设置为false时,加载页面的宽度总是适应WebView控件宽度; 当被设置为true,当前页面包含viewport属性标签,在标签中指定宽度值生效,如果页面不包含viewport标签,无法提供一个宽度值,这个时候该方法将被使用。 */webSettings?.useWideViewPort = false //缩放至屏幕大小 webSettings?.loadWithOverviewMode = true //支持多窗口 webSettings?.setSupportMultipleWindows(true) /* 缓存模式 LOAD_CACHE_ONLY 不使用网络,只读取本地缓存 LOAD_DEFAULT 根据cache-control决定是否从网络上获取数据 LOAD_NO_CACHE 不使用缓存,只从网络获取数据 LOAD_CACHE_ELSE_NETWORK 只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据 */webSettings?.cacheMode = XWalkSettings.LOAD_DEFAULT //设置是否加载图片 webSettings?.loadsImagesAutomatically = true //允许远程调试 XWalkPreferences.setValue("enable-javascript", true) XWalkPreferences.setValue(XWalkPreferences.REMOTE_DEBUGGING, true) } }
自定义XWalkUIClient
package com.example.myapplication import android.graphics.Bitmap import android.net.Uri import android.os.Message import android.util.Log import android.view.KeyEvent import android.view.View import android.webkit.ValueCallback import org.xwalk.core.CustomViewCallback import org.xwalk.core.XWalkJavascriptResult import org.xwalk.core.XWalkUIClient import org.xwalk.core.XWalkView class XWUIClient(view: XWalkView?) : XWalkUIClient(view) { override fun onPageLoadStarted(view: XWalkView, url: String) { Log.i(TAG, "onPageLoadStarted $url") super.onPageLoadStarted(view, url) } override fun onPageLoadStopped( view: XWalkView, url: String, status: LoadStatus ) { Log.i( TAG, "onPageLoadStopped $url, $status" ) super.onPageLoadStopped(view, url, status) } override fun onJsAlert( view: XWalkView, url: String, message: String, result: XWalkJavascriptResult ): Boolean { Log.i(TAG, "onJsAlert $url, $message") return super.onJsAlert(view, url, message, result) } override fun onJsConfirm( view: XWalkView, url: String, message: String, result: XWalkJavascriptResult ): Boolean { Log.i(TAG, "onJsConfirm $url, $message") return super.onJsConfirm(view, url, message, result) } override fun onJsPrompt( view: XWalkView, url: String, message: String, defaultValue: String, result: XWalkJavascriptResult ): Boolean { Log.i(TAG, "onJsPrompt $url, $message") return super.onJsPrompt(view, url, message, defaultValue, result) } override fun onConsoleMessage( view: XWalkView, message: String, lineNumber: Int, sourceId: String, messageType: ConsoleMessageType ): Boolean { Log.i( TAG, "onConsoleMessage $message, $lineNumber, $sourceId, $messageType" ) return super.onConsoleMessage(view, message, lineNumber, sourceId, messageType) } override fun onShowCustomView( view: View, requestedOrientation: Int, callback: CustomViewCallback ) { Log.i(TAG, "onShowCustomView $requestedOrientation") super.onShowCustomView(view, requestedOrientation, callback) } override fun onShowCustomView( view: View, callback: CustomViewCallback ) { Log.i(TAG, "onShowCustomView") super.onShowCustomView(view, callback) } override fun onCreateWindowRequested( view: XWalkView, initiator: InitiateBy, callback: ValueCallback<XWalkView> ): Boolean { Log.i(TAG, "onCreateWindowRequested") return super.onCreateWindowRequested(view, initiator, callback) } override fun onJavascriptModalDialog( view: XWalkView, type: JavascriptMessageType, url: String, message: String, defaultValue: String, result: XWalkJavascriptResult ): Boolean { Log.i( TAG, "onJavascriptModalDialog $type, $url, $message, $defaultValue" ) return super.onJavascriptModalDialog(view, type, url, message, defaultValue, result) } override fun onFullscreenToggled( view: XWalkView, enterFullscreen: Boolean ) { Log.i(TAG, "onFullscreenToggled $enterFullscreen") super.onFullscreenToggled(view, enterFullscreen) } override fun onHideCustomView() { Log.i(TAG, "onHideCustomView") super.onHideCustomView() } override fun onIconAvailable( view: XWalkView, url: String, startDownload: Message ) { Log.i( TAG, "onIconAvailable $url, $startDownload" ) super.onIconAvailable(view, url, startDownload) } override fun onJavascriptCloseWindow(view: XWalkView) { Log.i(TAG, "onJavascriptCloseWindow") super.onJavascriptCloseWindow(view) } override fun onReceivedIcon( view: XWalkView, url: String, icon: Bitmap ) { Log.i(TAG, "onReceivedIcon $url") super.onReceivedIcon(view, url, icon) } override fun onReceivedTitle(view: XWalkView, title: String) { Log.i(TAG, "onReceivedTitle $title") super.onReceivedTitle(view, title) } override fun onRequestFocus(view: XWalkView) { Log.i(TAG, "onRequestFocus") super.onRequestFocus(view) } override fun onScaleChanged( view: XWalkView, oldScale: Float, newScale: Float ) { Log.i(TAG, "onScaleChanged $oldScale, $newScale") super.onScaleChanged(view, oldScale, newScale) } override fun onUnhandledKeyEvent( view: XWalkView, event: KeyEvent ) { Log.i( TAG, "onUnhandledKeyEvent " + event.action + ", " + event.keyCode ) super.onUnhandledKeyEvent(view, event) } override fun openFileChooser( view: XWalkView, uploadFile: ValueCallback<Uri>, acceptType: String, capture: String ) { Log.i( TAG, "openFileChooser $acceptType, $capture" ) super.openFileChooser(view, uploadFile, acceptType, capture) } override fun shouldOverrideKeyEvent( view: XWalkView, event: KeyEvent ): Boolean { Log.i( TAG, "shouldOverrideKeyEvent " + event.action + ", " + event.keyCode ) return super.shouldOverrideKeyEvent(view, event) } override fun getBridge(): Any { val obj = super.getBridge() if (obj != null) { Log.i( TAG, "getBridge " + obj.javaClass.simpleName ) } else { Log.i(TAG, "getBridge()") } return obj } companion object { private const val TAG = "XWalkUIClient" } }
自定义XWResourceClient
package com.example.myapplication; import android.net.http.SslError; import android.util.Log; import android.webkit.ValueCallback; import android.webkit.WebResourceResponse; import org.json.JSONObject; import org.xwalk.core.ClientCertRequest; import org.xwalk.core.XWalkHttpAuthHandler; import org.xwalk.core.XWalkResourceClient; import org.xwalk.core.XWalkView; import org.xwalk.core.XWalkWebResourceRequest; import org.xwalk.core.XWalkWebResourceResponse; import java.io.InputStream; import java.util.Arrays; import java.util.Map; public class XWResourceClient extends XWalkResourceClient { private static final String TAG = "XWalkUIClient"; public XWResourceClient(XWalkView view) { super(view); } @Override public void onLoadStarted(XWalkView view, String url) { Log.i(TAG, "onLoadStarted " + url); super.onLoadStarted(view, url); } @Override public void onLoadFinished(XWalkView view, String url) { Log.i(TAG, "onLoadFinished " + url); super.onLoadFinished(view, url); } @Override public void onProgressChanged(XWalkView view, int progressInPercent) { Log.i(TAG, "onProgressChanged " + progressInPercent); super.onProgressChanged(view, progressInPercent); } @Override public boolean shouldOverrideUrlLoading(XWalkView view, String url) { Log.i(TAG, "shouldOverrideUrlLoading " + url); return super.shouldOverrideUrlLoading(view, url); } @Override public WebResourceResponse shouldInterceptLoadRequest(XWalkView view, String url) { Log.i(TAG, "shouldInterceptLoadRequest " + url); return super.shouldInterceptLoadRequest(view, url); } @Override public XWalkWebResourceResponse shouldInterceptLoadRequest(XWalkView view, XWalkWebResourceRequest request) { Log.i(TAG, "shouldInterceptLoadRequest " + request.isForMainFrame() + ", " + request.getUrl() + ", " + new JSONObject(request.getRequestHeaders()).toString()); return super.shouldInterceptLoadRequest(view, request); } @Override public void onReceivedSslError(XWalkView view, ValueCallback<Boolean> callback, SslError error) { Log.i(TAG, "onReceivedSslError " + error.toString()); callback.onReceiveValue(true); } @Override public void onReceivedLoadError(XWalkView view, int errorCode, String description, String failingUrl) { Log.i(TAG, "onReceivedLoadError " + errorCode + ", " + description + ", " + failingUrl); super.onReceivedLoadError(view, errorCode, description, failingUrl); } @Override public void onDocumentLoadedInFrame(XWalkView view, long frameId) { Log.i(TAG, "onDocumentLoadedInFrame " + frameId); super.onDocumentLoadedInFrame(view, frameId); } @Override public void onReceivedClientCertRequest(XWalkView view, ClientCertRequest handler) { Log.i(TAG, "onReceivedClientCertRequest " + handler.getHost() + ", " + handler.getPort() + ", " + Arrays.toString(handler.getKeyTypes())); super.onReceivedClientCertRequest(view, handler); // handler.proceed(); } @Override public void onReceivedHttpAuthRequest(XWalkView view, XWalkHttpAuthHandler handler, String host, String realm) { Log.i(TAG, "onReceivedHttpAuthRequest " + host + ", " + realm); super.onReceivedHttpAuthRequest(view, handler, host, realm); } @Override public void onReceivedResponseHeaders(XWalkView view, XWalkWebResourceRequest request, XWalkWebResourceResponse response) { Log.i(TAG, "onReceivedResponseHeaders " + request.isForMainFrame() + ", " + request.getUrl() + ", " + new JSONObject(request.getRequestHeaders()).toString()); super.onReceivedResponseHeaders(view, request, response); } @Override public XWalkWebResourceResponse createXWalkWebResourceResponse(String mimeType, String encoding, InputStream data) { Log.i(TAG, "createXWalkWebResourceResponse " + mimeType + ", " + encoding); return super.createXWalkWebResourceResponse(mimeType, encoding, data); } @Override public XWalkWebResourceResponse createXWalkWebResourceResponse(String mimeType, String encoding, InputStream data, int statusCode, String reasonPhrase, Map<String, String> responseHeaders) { Log.i(TAG, "createXWalkWebResourceResponse " + mimeType + ", " + encoding + ", " + statusCode + ", " + reasonPhrase + ", " + new JSONObject(responseHeaders)); return super.createXWalkWebResourceResponse(mimeType, encoding, data, statusCode, reasonPhrase, responseHeaders); } @Override public void doUpdateVisitedHistory(XWalkView view, String url, boolean isReload) { Log.i(TAG, "doUpdateVisitedHistory " + url + ", " + isReload); super.doUpdateVisitedHistory(view, url, isReload); } @Override protected Object getBridge() { Object obj = super.getBridge(); if (obj != null) { Log.i(TAG, "getBridge " + obj.getClass().getSimpleName()); } else { Log.i(TAG, "getBridge()"); } return obj; } }