Использование возможностей веб-приложений в родном Android
Когда дело доходит до разработки мобильных приложений для Android или iOS, существует 3 разных подхода: разработка собственных приложений, разработка мобильных веб-приложений, более известных как прогрессивные веб-приложения (PWA), и разработка гибридных приложений. Среди этих трех вариантов разработка собственных приложений по-прежнему остается на первом месте, поскольку она обеспечивает полный доступ к аппаратному обеспечению устройства и его функциям, таким как камера, GPS, Bluetooth и т. Д., Тогда как прогрессивные веб-приложения - это веб-приложения, которые предоставляют нативное приложение, подобное приложению. пользовательский интерфейс для кроссплатформенных веб-приложений. В наши дни PWA становятся довольно распространенными. Они относительно просты в развертывании, обслуживании и обратной совместимости не требуется. Но есть и недостатки. Например: PWA не имеют полного доступа к оборудованию на вашем устройстве. Выбор между Progressive Web Apps и Native Android в конечном итоге зависит от требований приложения.
Однако может наступить время, когда вы захотите использовать существующее веб-приложение, чтобы избежать переписывания представлений на нативном языке. Интеграция веб-приложений в native осуществляется через класс Android Webview. Требование может варьироваться от простого рендеринга веб-приложения внутри нативного до двусторонней связи между ними. Для двусторонней связи поддержка Webview для Javascript и JavaScriptInterface становится очевидной, и это основная тема этого блога.
В этом блоге рассказывается больше о разработке собственных приложений для Android и о том, как мы можем подключать веб-компоненты внутри родных приложений для более быстрой итерации разработки. Он предназначен для людей, которые разрабатывают приложения в экосистеме Android
Следующий код описывает, как достичь двусторонней связи. Давайте разберемся с простыми сценариями и разберем каждый из них на примере.
Следующий код описывает, как достичь двусторонней связи. Давайте разберемся с простыми сценариями и разберем каждый из них на примере.
Сценарий 1. Загрузка нового URL-адреса в Webview при нажатии кнопки HTML с помощью прямого вызова JavascriptInterface.
index.html
Давайте сначала создадим образец веб-страницы. Вы можете добавить это в папку с активами в main.
<html> <body> <input type="button" onclick="NativeHandler.callApi()" style="height:100px; white-space: normal;text-align:left;" value="[JavaScript -> Java] Call api and load url" /><br/> <p id="test" style="overflow-wrap:break-word">Demo App</p> </body> </html>
XML-макет
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.sequoia.android.activity. WebviewActivity"> <WebView android:id="@+id/common_web_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
JavascriptInterface
class DemoJavaScriptInterface(val activity: BaseActivity) { companion object { private val TAG = DemoJavaScriptInterface::class.java.simpleName } @JavascriptInterface fun callApi() { Log.v(TAG, "call to native") activity.runOnUiThread({ activity.callApi() }) } }
WebviewActivity
class WebviewActivity : BaseActivity() { companion object { private val TAG = WebviewActivity::class.java.simpleName } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.webview_activity) common_web_view.addJavascriptInterface(DemoJavaScriptInterface(this), "NativeHandler") initWebViewSettingsAndClient("file:///android_asset/index.html") } @SuppressLint("SetJavaScriptEnabled") private fun initWebViewSettingsAndClient(url: String) { if (isDestroyed || isFinishing) return val settings: WebSettings = common_web_view.getSettings() settings.javaScriptEnabled = true settings.javaScriptCanOpenWindowsAutomatically = true common_web_view.setWebChromeClient(object : WebChromeClient() { override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean { Log.d(TAG, consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId()) return super.onConsoleMessage(consoleMessage) } }) common_web_view.loadUrl(url) } override fun callApi() { val url = "https://github.com" loadHtmlInWebView(url) } @SuppressLint("SetJavaScriptEnabled") private fun loadHtmlInWebView(url: String) { common_web_view.loadUrl(url) } }
Здесь при нажатии кнопки в html создается вызов функции callAPi в JavascriptInterface, которую мы установили с помощью addJavascriptInterface для активного веб-просмотра. Нам также необходимо явно установить для javaScriptEnabled значение true в Webview.
Сценарий 2: загрузка нового URL-адреса в Webview при нажатии кнопки HTML с использованием косвенного вызова JavascriptInterface через функцию сценария в HTML.
index.html
<html> <head> <script type="text/javascript"> function testSync() { NativeHandler.callApi(); } </script> </head> <body> <input type="button" onclick="testSync()" style="height:100px; white-space: normal;text-align:left;" value="[Sync JavaScript -> Java] Call api and load url" /><br/> <p id="test" style="overflow-wrap:break-word">Demo App</p> </body> </html>
Здесь единственное, что изменилось, - это обработка кликов в html. Теперь мы вызываем функцию сценария для вызова метода callApi вместо того, чтобы вызывать его напрямую.
Сценарий 3: изменение данных в веб-просмотре - изменение текста при нажатии кнопки HTML.
index.html
К приведенному выше html просто добавьте следующую функцию скрипта
function setUrlInText(url) { if(typeof(url) == 'string') { document.getElementById("test").innerHTML = url; console.log("setUrlInText function entered"); } }
Эта функция будет вызываться Webview для установки нового текста в html.
WebviewActivity
Измените метод loadHtmlInWebView, чтобы вызвать новую функцию скрипта setUrlInText и передать новый URL-адрес в качестве параметра.
@SuppressLint("SetJavaScriptEnabled") private fun loadHtmlInWebView(url: String) { common_web_view.evaluateJavascript("setUrlInText('"+ url+"')", null) }
Это установит новый текст в конкретный elementId. Здесь мы передаем null в обратном вызове. Сценарий 5 демонстрирует, как обрабатывать обратные вызовы javascript.
Сценарий 4: установите данные в собственном формате, передайте объект в Webview и отобразите его свойства в формате html.
UserDetails
Давайте создадим простой класс данных UserDetails со свойством name, email и phoneNumber.
data class UserDetails(val name: String, val email: String, val phoneNumber: String)
index.html
К приведенному выше html просто добавьте следующую функцию скрипта
function getData(userDetails) { if(typeof(userDetails) == 'object') { var name = userDetails.name || ''; var phoneNumber = userDetails.phoneNumber || ''; var email = userDetails.email || ''; document.getElementById("test").innerHTML = "name = " + name + ", " + "email = " + email + ", " + "phoneNumber = " + phoneNumber; console.log("getData function exiting " + name + ", " + email + " , " + phoneNumber); } }
Это установит UserDetails в конкретный elementId. Здесь мы передаем null в обратном вызове.
XML-макет
В макете xml добавьте 3 поля для ввода данных и кнопку для отправки данных в Webview.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.sequoia.android.activity. WebviewActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:id="@+id/etName" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="Enter Name" android:inputType="textPersonName" /> <EditText android:id="@+id/etEmail" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="Enter Email" android:inputType="textEmailAddress" /> <EditText android:id="@+id/etPhone" android:layout_width="match_parent" android:layout_height="wrap_content" android:ems="10" android:hint="Enter Phone Number" android:inputType="phone" /> <Button android:id="@+id/sendData" android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="sendDataToWebview" android:text="Send data to webview" /> </LinearLayout> <WebView android:id="@+id/common_web_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> </RelativeLayout>
WebviewActivity
В действии добавьте следующую функцию для обработки при нажатии собственной кнопки.
fun sendDataToWebview(view: View) { val name = etName.text.toString() val email = etEmail.text.toString() val phone = etPhone.text.toString() val userDetails = UserDetails(name, email, phone) val strUserDetails: String = Gson().toJson(userDetails) common_web_view.evaluateJavascript( "getData($strUserDetails)", null ) }
AssessmentJavascript используется для вызова функции html-скрипта getData и передает строковый объект в его параметры.
Сценарий 5. Измените данные, полученные в формате html, отправьте их обратно в собственный формат и отобразите.
index.html
Добавьте следующий код в функцию скрипта getData. Теперь мы вернем измененный объект UserDetails.
userDetails.name = 'Demo App'; userDetails.email = '[email protected]'; userDetails.phoneNumber = 789; return userDetails;
WebviewActivity
Добавьте следующий код в функцию sendDataToWebview.
common_web_view.evaluateJavascript( "getData($strUserDetails)", object : ValueCallback<String> { override fun onReceiveValue(value: String?) { val userDetails = Gson().fromJson(value, UserDetails::class.java) etName.setText(userDetails.name) etEmail.setText(userDetails.email) etPhone.setText(userDetails.phoneNumber) Log.e(TAG, value) } })
Теперь мы передаем значение обратного вызова методу evalJavascript, и данные из функции скрипта принимаются в onReceiveValue. Мы используем этот объект для установки новых данных в собственном представлении.
В этом блоге мы рассмотрели несколько примеров, которые охватывают большую часть деталей реализации взаимодействия нативных приложений и веб-приложений. Рассмотренные здесь варианты использования были ограничены, но вы можете расширить их, чтобы охватить любые сложные требования, которые могут у вас возникнуть.
Вы можете просмотреть полный код этой статьи здесь.
Не стесняйтесь обращаться ко мне, если у вас есть какие-либо вопросы или отзывы об этом посте - [email protected]
Вот мой профиль в LinkedIn.