-
2019-10-10 10:37:13
之前整理过系统应用的安装方法,大概是这样:android_x86 添加预安装APP
最近要手动升级一下webview,升级的方法是这样的:
-
将com.android.webview_apkmirror.com.apk重命名为webview.apk,然后替换掉/system/app/webview/webview.apk
-
将新的webview apk重命名为webview.zip,然后解压缩,提取libwebviewchromium.so,最后替换掉/system/app/webview/lib/arm/libwebviewchromium.so
-
如果/system/分区是只读的,就通过adb shell输入命令(如下)。将/system挂载为可读写的
mount -o rw,remount /system
- 重启设备
更多相关内容 -
-
Android-ChromiumWebview-Sample
2021-06-09 04:45:01Android-ChromiumWebview-Sample 这是使用 gradle 构建的简单示例项目。 这包含示例项目: 和库项目: , 。 和非常简单的 sompe 模块:app。 如果您有问题,请检查这些项目。 -
webview
2022-01-15 23:32:07public class WebViewActivity extends Activity implements JSCallback { private LinearLayout mWebViewRoot; private WebView mWebView; private RelativeLayout mWVHeadArea; private ImageView mBackBtn; ...public class WebViewActivity extends Activity implements JSCallback {
private LinearLayout mWebViewRoot;
private WebView mWebView;
private RelativeLayout mWVHeadArea;
private ImageView mBackBtn;
private TextView mTitleTV;
private TextView mCloseIV;
private ProgressBar mProgressBar;
private RelativeLayout mLoadError;
private TextView mRetryBtn;private String mTitle;
private boolean isShowTitle = true;
public String mLoadUrl = "";
private int mCurProgressTime = 0;
private Handler mHandler;
private Runnable progressRunnable;
public boolean isFreshedTitle = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.srt_webview_activity);
ARouter.getInstance().inject(this);initView();
initWebSettings();
initWebViewClient();
initWebChromeClient();
addJSMethod();
initData();startLoadUrl();
}private void startLoadUrl() {
if (TextUtils.isEmpty(this.mLoadUrl)) {
Toast(R.string.srt_wv_request_no_data);
finish();} else {
initTitle();
setFirstLoadUrl(this.mLoadUrl);
if (isNetworkAvailable()) {
loadPage(mLoadUrl);
setFirstLoadUrl("");
} else {
showErrorVisibility(true);
setLoadingProgress(15);
}
}
}private void showErrorVisibility(boolean isShow) {
mLoadError.setVisibility(isShow ? View.VISIBLE : View.GONE);
mWebViewRoot.setVisibility(!isShow ? View.VISIBLE : View.GONE);
}public void hideLoadingProgress() {
this.mProgressBar.setVisibility(View.INVISIBLE);
}public void showClose() {
mCloseIV.setVisibility(View.VISIBLE);}
public void showLoadingProgress() {
this.mProgressBar.setVisibility(View.VISIBLE);
}
protected void loadPage(String url) {Log.d("---webview---", "url = " + url);
try {
WebUtils.checkFileAccess(mWebView.getSettings(), url);
this.mWebView.loadUrl(url);} catch (Exception var3) {
Log.e("WebView", var3);
}
}private void initTitle() {
if (this.isShowTitle) {
this.mWVHeadArea.setVisibility(View.VISIBLE);
if (!TextUtils.isEmpty(this.mTitle)) {
mTitleTV.setText(mTitle);
}
} else {
this.mWVHeadArea.setVisibility(View.GONE);
}}
private void setWVTitle(String title) {
if (this.isShowTitle && !TextUtils.isEmpty(title)) {
String[] titles = title.split(":");
String titleTxt = title;
if (titles.length > 0) {
titleTxt = titles[0];
}
mTitleTV.setText(titleTxt);
}}
private void initView() {
mWebViewRoot = findViewById(R.id.wv_container);
mWebView = findViewById(R.id.forum_context);
mWVHeadArea = findViewById(R.id.wv_header_area);mBackBtn = findViewById(R.id.wv_btn_back);
mTitleTV = findViewById(R.id.wv_title);
mCloseIV = findViewById(R.id.wv_btn_close);
mProgressBar = findViewById(R.id.webview_progressbar);mLoadError = findViewById(R.id.wv_layout_load_error);
mRetryBtn = findViewById(R.id.btn_wv_retry);showErrorVisibility(false);
mBackBtn.setOnClickListener(v -> {
if (mWebView.canGoBack()) {
mWebView.goBack();
showClose();
} else {
finish();
}
});
mCloseIV.setOnClickListener(v -> {
finish();
});
mRetryBtn.setOnClickListener(v -> {
refreshWebView();
});
}private void initData() {
MAX_SIGN_COUNT = 30;
mHandler = new ProgressHandler(this);
progressRunnable = new ProgressRunnable();Intent intent = this.getIntent();
if (null == intent) {
return;
}
mTitle = intent.getStringExtra("activityName");
mLoadUrl = intent.getStringExtra("url");
isShowTitle = intent.getBooleanExtra("isShowTitle", true);
}
private void initWebSettings() {
//声明WebSettings子类
WebSettings webSettings = mWebView.getSettings();
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
//缩放操作webSettings.setSupportZoom(false); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(false); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); //关闭webview中缓存
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
webSettings.setUserAgentString(getUserAgent());if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//TODO 安全漏洞,使用File,要禁止私用javaScript的
webSettings.setAllowFileAccess(true); //设置可以访问文件
webSettings.setSavePassword(false);
webSettings.setDomStorageEnabled(true);
}private static String getUserAgent() {
/** user_agent **/
String userAgent = new StringBuffer().append("Mozilla/5.0(Linux; U;XDSRT-APP;SNCLIENT; Android ").append(Build.VERSION.RELEASE).append("; ").append(Locale.getDefault().getLanguage()).append("; ").append(Build.MODEL).append(") AppleWebKit/533.0 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1").toString();
return userAgent;
}
private void initWebViewClient() {
mWebView.setWebViewClient(new WebViewClient() {//重写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//使用WebView加载显示url
WebUtils.checkFileAccess(mWebView.getSettings(), url);
view.loadUrl(url);
//返回true
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.e("sn_webview", "onPageStarted url==" + url);
isFreshedTitle = false;
//设定加载开始的操作
showErrorVisibility(false);
showLoadingProgress();
startVirtualProgress();
}@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//设定加载结束的操作
pageFinished(view, url);
}@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
//TODO 方便验证
// showErrorVisibility(true);
Log.e(TAG, "onReceivedError==");
}@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
Log.e(TAG, "onReceivedSslError==");
if (isVerifySsl()) {
handler.cancel();
} else {
handler.proceed();
}
}
});
}
private void initWebChromeClient() {
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
//获得网页的加载进度并显示
}@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
//当前webview正在加载的页面的title
if (!TextUtils.isEmpty(title)) {
setWVTitle(title);
isFreshedTitle = true;
}
}//Android < 5.0
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
if (Log.logEnabled) {
Log.e("SNWebChromeClient", "openFileChooser 3 acceptType==" + acceptType);
}openFileChooserImpl(uploadMsg);
}
//Android => 5.0
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> uploadMsg,
WebChromeClient.FileChooserParams fileChooserParams) {
onenFileChooseImpleForAndroid(uploadMsg);
return true;
}
});
}@Override
public void onActivityResult(int requestCode, int resultCode,Intent intent) {
Uri result = (intent == null || resultCode != Activity.RESULT_OK) ? null: intent.getData();
switch (requestCode){
case FILE_CHOOSER_RESULT_CODE: //android 5.0以下 选择图片回调
if (null == mUploadMessage)
return;
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
break;
case FILE_CHOOSER_RESULT_CODE_FOR_ANDROID_5: //android 5.0(含) 以上 选择图片回调
if (null == mUploadMessageForAndroid5)
return;
if (result != null) {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
} else {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
}
mUploadMessageForAndroid5 = null;
break;
}
}private boolean hasPermission() {
boolean hasPermission = true;
if (Build.VERSION.SDK_INT >= 23) {
if (ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") == 0
&& ActivityCompat.checkSelfPermission(this, "android.permission.CAMERA") == 0) {
hasPermission = true;
} else {
hasPermission = false;
}
}return hasPermission;
}
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
try {
if (this.hasPermission()) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILE_CHOOSER_RESULT_CODE);} else if (ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") != 0
&& ActivityCompat.checkSelfPermission(this, "android.permission.CAMERA") != 0) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.CAMERA"},
1001);
} else if (ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") != 0) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"},
1002);
} else if (ActivityCompat.checkSelfPermission(this, "android.permission.CAMERA") != 0) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.CAMERA"},
1003);
}
} catch (Exception var3) {
Toaster.showMessage(this, "请打开相机或存储权限");
}}
/**
* android 5.0(含) 以上开启图片选择(原生)
* 可以自己改图片选择框架。
*/
private void onenFileChooseImpleForAndroid(ValueCallback<Uri[]> filePathCallback) {try {
if (this.hasPermission()) {
mUploadMessageForAndroid5 = filePathCallback;
Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.setType("image/*");
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
startActivityForResult(chooserIntent, FILE_CHOOSER_RESULT_CODE_FOR_ANDROID_5);} else if (ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") != 0
&& ActivityCompat.checkSelfPermission(this, "android.permission.CAMERA") != 0) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.CAMERA"},
1001);
} else if (ActivityCompat.checkSelfPermission(this, "android.permission.WRITE_EXTERNAL_STORAGE") != 0) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.WRITE_EXTERNAL_STORAGE"},
1002);
} else if (ActivityCompat.checkSelfPermission(this, "android.permission.CAMERA") != 0) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.CAMERA"},
1003);
}
} catch (Exception var3) {
Toaster.showMessage(this, "请打开相机或存储权限");
}}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == 1001) {
if (grantResults.length == 2 && grantResults[0] == 0 && grantResults[1] == 0) {} else {
this.clearValueCallBack();
Toaster.showMessage(this, R.string.srt_permisson_camer_shoot);
}
} else if (requestCode == 1002) {
if (grantResults.length == 1 && grantResults[0] == 0) {} else {
this.clearValueCallBack();
Toaster.showMessage(this, R.string.srt_permisson_storage_tip);
}
} else if (requestCode == 1003) {
if (grantResults.length == 1 && grantResults[0] == 0) {} else {
this.clearValueCallBack();
Toaster.showMessage(this, R.string.srt_permisson_camer_tip);
}
}
}public void clearValueCallBack() {
if (this.mUploadMessage != null) {
this.mUploadMessage.onReceiveValue(null);
this.mUploadMessage = null;
}if (this.mUploadMessageForAndroid5 != null) {
this.mUploadMessageForAndroid5.onReceiveValue(null);
this.mUploadMessageForAndroid5 = null;
}}
private void addJSMethod() {
if (mWebView != null) {
mWebView.removeJavascriptInterface("searchBoxJavaBridge_");
mWebView.removeJavascriptInterface("accessibility");
mWebView.removeJavascriptInterface("accessibilityTraversal");
}if (null != mWebView) {
mWebView.addJavascriptInterface(new JsBridge(this, this), "srtBridge");
}
}
private void pageFinished(WebView view, String url) {
Log.e("sn_webview", "onPageFinished url==" + url);
if (!isFinishing()) {
hideLoadingProgress();
if (!isFreshedTitle) {
String webTitle = this.mWebView.getTitle();
if (webTitle == null || webTitle.toLowerCase().startsWith("http://") || webTitle.toLowerCase().startsWith("https://")) {
webTitle = "";
}
setWVTitle(webTitle);
}
}
}private void startVirtualProgress() {
this.mCurProgressTime = 0;
this.removeProgressDelay();
this.sendProgressDelay();
}@Override
public void toast(String message) {
if (null == mWebView || isFinishing()) {
return;
}
mWebView.post(() -> {
Toaster.showMessage(WebViewActivity.this, message);
mWebView.loadUrl("javascript:srtCallJs('toast', '1')");
});
}@Override
public void checkSign() {
checkSign(TASK_ID_ONE);
}@Override
public void callPhone(String phoneNum) {
if (!TextUtils.isEmpty(phoneNum)) {
Intent intent = new Intent(Intent.ACTION_DIAL);
Uri data = Uri.parse("tel:" + phoneNum);
intent.setData(data);
startActivity(intent);
}
}@Override
public void closeWeb() {
finish();
}@Override
public void goToReceiveAgain(String purchaseNo) {
Intent intent = new Intent(this, SrtTakeWebActivity.class);
intent.putExtra("purchaseNo",purchaseNo);
startActivity(intent);
}@Override
public void onSignNetResult(int taskId, NetResult result) {if (result == null) {
return;
}if (taskId == TASK_ID_ONE && result.isSuccess()) {
if (!isFinishing() && mWebView != null) {
mWebView.loadUrl("javascript:srtCallJs('checkSign', '0')");
}
}
}private static class ProgressHandler extends Handler {
WeakReference<WebViewActivity> mReference;ProgressHandler(WebViewActivity webViewActivity) {
this.mReference = new WeakReference(webViewActivity);
}public void handleMessage(Message msg) {
WebViewActivity webViewActivity = this.mReference.get();
if (webViewActivity != null) {
webViewActivity.handleMessage(msg);
}}
}public void handleMessage(Message msg) {
if (msg.what == 4353) {
this.mCurProgressTime += 100;
int pro = getVirtualProgress(this.mCurProgressTime);
Log.i("sn_webview", "update virtual progress: " + pro);if (pro > 90) {
removeProgressDelay();
return;
}this.sendProgressDelay();
}
}private void refreshWebView() {
try {
if (isNetworkAvailable()) {
this.showErrorVisibility(false);
if (getFirstLoadUrl() != null && !TextUtils.isEmpty(getFirstLoadUrl().trim())) {
this.mWebView.loadUrl(this.getFirstLoadUrl());
this.setFirstLoadUrl("");
} else {
this.mWebView.reload();
}
} else {
this.showErrorVisibility(true);
this.setLoadingProgress(15);
}
} catch (Exception var2) {
if (Log.logEnabled) {
Log.e(var2.getMessage());
}
}}
private void sendProgressDelay() {
if (this.mHandler != null) {
this.mHandler.postDelayed(progressRunnable, 100L);
}}
class ProgressRunnable implements Runnable {
ProgressRunnable() {
}public void run() {
if (mHandler != null) {
mHandler.sendEmptyMessage(4353);
}}
}private int getVirtualProgress(int time) {
int progress;
if (time >= 0 && time < 3000) {
progress = 20 * time / 1000;
} else if (time >= 3000 && time < 5000) {
progress = 60 + 10 * (time - 3000) / 1000;
} else if (time >= 5000 && time < 9000) {
progress = 80 + 5 * (time - 5000) / 2000;
} else {
progress = 91;
}return progress;
}private void removeProgressDelay() {
if (this.mHandler != null) {
this.mHandler.removeCallbacks(this.progressRunnable);
}}
public void setLoadingProgress(int progress) {
this.mProgressBar.setProgress(progress);
}private boolean isVerifySsl() {
return "prd".equals(Url.ENVIRONMENT);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
showClose();
return true;
}
return super.onKeyDown(keyCode, event);
}@Override
protected void onResume() {
super.onResume();
if (null != mWebView) {
mWebView.onResume();
}}
@Override
protected void onPause() {
super.onPause();
if (null != mWebView) {
mWebView.onPause();
}
}@Override
protected void onDestroy() {
super.onDestroy();if (null != mHandler) {
mHandler.removeCallbacks(null);
mHandler = null;
}
try {
if (null != mWebView) {
if (this.mWebView.getSettings() != null) {
this.mWebView.getSettings().setJavaScriptEnabled(false);
}
this.mWebView.setVisibility(View.GONE);
resetImageSetting(mWebView);
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();ViewGroup view = (ViewGroup) this.mWebView.getParent();
view.removeAllViews();
this.mWebView.removeAllViews();
this.mWebView.destroy();
this.mWebView = null;
}
} catch (Exception e) {
e.printStackTrace();
}}
private void resetImageSetting(WebView webview) {
try {
WebSettings settings = webview.getSettings();
if (!settings.getLoadsImagesAutomatically()) {
settings.setLoadsImagesAutomatically(true);
}if (settings.getBlockNetworkImage()) {
settings.setBlockNetworkImage(false);
}
} catch (Exception var2) {
if (Log.logEnabled) {
Log.e("ConfigManager", "WebView had destroyed,forbid it's interfaces to be called");
}
}}
public String getFirstLoadUrl() {
return this.firstLoadUrl;
}public void setFirstLoadUrl(String firstLoadUrl) {
this.firstLoadUrl = firstLoadUrl;
}}
-
WebView技术原理
2021-10-10 11:15:30我们知道混合应用是在原生应用里嵌套了H5页面,H5是在webview控件里的,那么我们在UI自动化时该如何定位webview里的元素呢?我们可以通过 uiautomatorviewer将webview解析出来android能识别的组件,但是这种方式在...我们知道混合应用是在原生应用里嵌套了H5页面,H5是在webview控件里的,那么我们在UI自动化时该如何定位webview里的元素呢?我们可以通过
uiautomatorviewer
将webview解析出来android能识别的组件,但是这种方式在不同的手机上有可能解析的不一致,导致自动化脚本不稳定,不推荐使用这种方法。另外我们可以手机端的webview页面映射到PC端,在PC端通过将chrome://insepect 查看页面元素,推荐使用,但是注意这里需要外网环境。
如下将分析一段appium的日志来分析webview的工作原理,文章尾部附有自动化脚本及完整日志:解析:
-
获取上下文列表
服务端发送命令adb shell cat /proc/net/unix
获取域套接字列表。那什么是域套接字呢?
域套接字:是unix系统里进程与进程之间通信的一种方式。客户端想要与服务端想要连接,必须有共同的套接字和相应的服务端的端口号。套接字会一直处于监听状态,监听客户端发来的请求。
adb shell cat /proc/net/unix
是获取手机端的套接字,这些套接字是用来监听外界发来的请求
adb shell cat /proc/net/unix |grep webview
显示webview的套接字,其中套接字最后的数字就是进程ID
获取到webview的进程后,通过命令adb shell ps |grep 9986
查询出来该进程对应的应用
-
匹配chromedrvier
这里是将现存的chromediver进程杀掉,并查找可以匹配webview版本的chromedriver,如果没有相对应的版本此处会报错。 -
查看本地和手机端的tcp连接映射关系
adb forward --list
adb forward 命令是查看本地和手机端端tcp连接的映射关系
结果显示本地的57973端口和手机端的webview端口进行通信
adb forward tcp:8888 tcp:9999
建立本地8888端口和手机端9999端口的映射
adb forward --remove tcp:8888
删除8888端口
更多adb forward的使用方法参考:adb |grep forward
注意看:日志上是将这个映射关系给抹除了,目的是防止该进程没有被正常关闭时影响接下来的操作。 -
启动chromedriver
这里启动起来了chromedriver,占用的是8000端口(日志没截全,可以去看文章开头的全量日志)监听的本地的5037端口,为啥占用的是8000?在日志第303行,因为我们的脚本中没有指定端口,所以appium默认选择了一个空闲的8000端口。 -
创建session
这里的session是指的appium server和chromedriver之间通信的session, -
转发请求,将appium server的请求转发给chromedriver
Proxying [GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/source] to [GET http://127.0.0.1:8000/wd/hub/session/ed7fb83742cae3d1847485e4a02ad001/source] with body: {}
这条命令可以看出来,appium server对发到4723端口的操作,转发到了8000端口,并且session_id是appium. server 和chromedriver之间的session,本质上就是appium做了个代理,把请求进行了一次转发。
以上是我对appium 在做webview进行自动化测试时的总结,以上如有疏漏烦请各位指出,以期共同进步。
完整日志:
脚本及appium日志如下:
自动化脚本:from appium import webdriver from appium.webdriver.common.mobileby import MobileBy from appium.webdriver.common.touch_action import TouchAction class TestDemoTwo: def setup(self): caps = { "platformName": "android", "deviceName": "test", "appPackage": "io.appium.android.apis", "appActivity": "io.appium.android.apis.ApiDemos", "noReset": "true" } self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps) self.driver.implicitly_wait(10) def teardown(self): pass def test_webview(self): view = "WebView" self.driver.find_element(MobileBy.XPATH, "//*[@text='Views']").click() # print(self.driver.contexts) self.driver.find_element(MobileBy.ANDROID_UIAUTOMATOR, f'new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text("{view}").instance(0));').click() # 打印上下文 print(self.driver.contexts) # 定位元素及操作 self.driver.find_element(MobileBy.XPATH, '//*[@resource-id="i am a link"]').click() print(self.driver.contexts) # 切换上下文 self.driver.switch_to.context(self.driver.contexts[-1]) # self.driver.find_element(MobileBy.XPATH) # self.driver.find_element(MobileBy.XPATH, "//*[contains(@text,'other')]").get_attribute("text") print(self.driver.page_source)
appium日志:
2021-10-10 08:00:36:303 [Appium] Welcome to Appium v1.15.1 2021-10-10 08:00:36:306 [Appium] Non-default server args: 2021-10-10 08:00:36:307 [Appium] sessionOverride: true 2021-10-10 08:00:36:308 [Appium] logFile: appium1.log 2021-10-10 08:00:36:428 [Appium] Appium REST http interface listener started on 0.0.0.0:4723 2021-10-10 08:00:51:282 [HTTP] --> POST /wd/hub/session 2021-10-10 08:00:51:283 [HTTP] {"capabilities":{"firstMatch":[{"platformName":"android","appium:deviceName":"test","appium:appPackage":"io.appium.android.apis","appium:appActivity":"io.appium.android.apis.ApiDemos","appium:noReset":"true"}]},"desiredCapabilities":{"platformName":"android","deviceName":"test","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":"true"}} 2021-10-10 08:00:51:288 [W3C] Calling AppiumDriver.createSession() with args: [{"platformName":"android","deviceName":"test","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":"true"},null,{"firstMatch":[{"platformName":"android","appium:deviceName":"test","appium:appPackage":"io.appium.android.apis","appium:appActivity":"io.appium.android.apis.ApiDemos","appium:noReset":"true"}]}] 2021-10-10 08:00:51:290 [BaseDriver] Event 'newSessionRequested' logged at 1633852851289 (16:00:51 GMT+0800 (中国标准时间)) 2021-10-10 08:00:51:305 [Appium] 2021-10-10 08:00:51:305 [Appium] ====================================================================== 2021-10-10 08:00:51:306 [Appium] DEPRECATION WARNING: 2021-10-10 08:00:51:306 [Appium] 2021-10-10 08:00:51:307 [Appium] The 'automationName' capability was not provided in the desired 2021-10-10 08:00:51:308 [Appium] capabilities for this Android session 2021-10-10 08:00:51:308 [Appium] 2021-10-10 08:00:51:309 [Appium] Setting 'automationName=UiAutomator2' by default and using the 2021-10-10 08:00:51:310 [Appium] UiAutomator2 Driver 2021-10-10 08:00:51:311 [Appium] 2021-10-10 08:00:51:311 [Appium] The next major version of Appium (2.x) will **require** the 2021-10-10 08:00:51:312 [Appium] 'automationName' capability to be set for all sessions on all 2021-10-10 08:00:51:313 [Appium] platforms 2021-10-10 08:00:51:314 [Appium] 2021-10-10 08:00:51:314 [Appium] In previous versions (Appium <= 1.13.x), the default was 2021-10-10 08:00:51:315 [Appium] 'automationName=UiAutomator1' 2021-10-10 08:00:51:315 [Appium] 2021-10-10 08:00:51:315 [Appium] If you wish to use that automation instead of UiAutomator2, please 2021-10-10 08:00:51:316 [Appium] add 'automationName=UiAutomator1' to your desired capabilities 2021-10-10 08:00:51:317 [Appium] 2021-10-10 08:00:51:317 [Appium] For more information about drivers, please visit 2021-10-10 08:00:51:317 [Appium] http://appium.io/docs/en/about-appium/intro/ and explore the 2021-10-10 08:00:51:318 [Appium] 'Drivers' menu 2021-10-10 08:00:51:318 [Appium] 2021-10-10 08:00:51:318 [Appium] ====================================================================== 2021-10-10 08:00:51:318 [Appium] 2021-10-10 08:00:51:828 [Appium] Appium v1.15.1 creating new AndroidUiautomator2Driver (v1.37.2) session 2021-10-10 08:00:51:833 [BaseDriver] W3C capabilities and MJSONWP desired capabilities were provided 2021-10-10 08:00:51:833 [BaseDriver] Creating session with W3C capabilities: { 2021-10-10 08:00:51:833 [BaseDriver] "alwaysMatch": { 2021-10-10 08:00:51:834 [BaseDriver] "platformName": "android", 2021-10-10 08:00:51:834 [BaseDriver] "appium:deviceName": "test", 2021-10-10 08:00:51:834 [BaseDriver] "appium:appPackage": "io.appium.android.apis", 2021-10-10 08:00:51:834 [BaseDriver] "appium:appActivity": "io.appium.android.apis.ApiDemos", 2021-10-10 08:00:51:835 [BaseDriver] "appium:noReset": "true" 2021-10-10 08:00:51:835 [BaseDriver] }, 2021-10-10 08:00:51:835 [BaseDriver] "firstMatch": [ 2021-10-10 08:00:51:835 [BaseDriver] {} 2021-10-10 08:00:51:836 [BaseDriver] ] 2021-10-10 08:00:51:836 [BaseDriver] } 2021-10-10 08:00:51:848 [BaseDriver] Capability 'noReset' changed from string to boolean. This may cause unexpected behavior 2021-10-10 08:00:51:855 [BaseDriver] Session created with session id: b76bb00a-43bc-4155-bd6a-ccaa3eb865b2 2021-10-10 08:00:51:857 [UiAutomator2] Starting 'io.appium.android.apis' directly on the device 2021-10-10 08:00:51:904 [ADB] Found 3 'build-tools' folders under '/Users/yujin/Library/Android/sdk' (newest first): 2021-10-10 08:00:51:904 [ADB] /Users/yujin/Library/Android/sdk/build-tools/29.0.3 2021-10-10 08:00:51:905 [ADB] /Users/yujin/Library/Android/sdk/build-tools/29.0.2 2021-10-10 08:00:51:905 [ADB] /Users/yujin/Library/Android/sdk/build-tools/28.0.3 2021-10-10 08:00:51:905 [ADB] Using 'adb' from '/Users/yujin/Library/Android/sdk/platform-tools/adb' 2021-10-10 08:00:51:906 [AndroidDriver] Retrieving device list 2021-10-10 08:00:51:906 [ADB] Trying to find a connected android device 2021-10-10 08:00:51:907 [ADB] Getting connected devices... 2021-10-10 08:00:51:932 [ADB] Connected devices: [{"udid":"emulator-5554","state":"device"}] 2021-10-10 08:00:51:933 [AndroidDriver] Using device: emulator-5554 2021-10-10 08:00:51:934 [ADB] Using 'adb' from '/Users/yujin/Library/Android/sdk/platform-tools/adb' 2021-10-10 08:00:51:935 [ADB] Setting device id to emulator-5554 2021-10-10 08:00:51:936 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell getprop ro.build.version.sdk' 2021-10-10 08:00:51:960 [ADB] Current device property 'ro.build.version.sdk': 23 2021-10-10 08:00:51:960 [ADB] Device API level: 23 2021-10-10 08:00:51:961 [AndroidDriver] No app sent in, not parsing package/activity 2021-10-10 08:00:51:964 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 wait-for-device' 2021-10-10 08:00:51:976 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell echo ping' 2021-10-10 08:00:51:995 [AndroidDriver] Pushing settings apk to device... 2021-10-10 08:00:51:996 [ADB] Getting install status for io.appium.settings 2021-10-10 08:00:51:997 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package io.appium.settings' 2021-10-10 08:00:52:047 [ADB] 'io.appium.settings' is installed 2021-10-10 08:00:52:049 [ADB] Getting package info for 'io.appium.settings' 2021-10-10 08:00:52:049 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package io.appium.settings' 2021-10-10 08:00:52:081 [ADB] Using 'apkanalyzer' from '/Users/yujin/Library/Android/sdk/tools/bin/apkanalyzer' 2021-10-10 08:00:52:081 [ADB] Starting '/Users/yujin/Library/Android/sdk/tools/bin/apkanalyzer' with args ["manifest","print","/usr/local/lib/node_modules/appium/node_modules/io.appium.settings/apks/settings_apk-debug.apk"] 2021-10-10 08:00:57:066 [ADB] The version name of the installed 'io.appium.settings' is greater or equal to the application version name ('2.14.2' >= '2.14.2') 2021-10-10 08:00:57:067 [ADB] There is no need to install/upgrade '/usr/local/lib/node_modules/appium/node_modules/io.appium.settings/apks/settings_apk-debug.apk' 2021-10-10 08:00:57:068 [ADB] Getting IDs of all 'io.appium.settings' processes 2021-10-10 08:00:57:068 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell 'pgrep --help; echo $?'' 2021-10-10 08:00:57:101 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell pgrep -f io\\.appium\\.settings' 2021-10-10 08:00:57:130 [AndroidDriver] io.appium.settings is already running. There is no need to reset its permissions. 2021-10-10 08:00:57:130 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell appops set io.appium.settings android\:mock_location allow' 2021-10-10 08:00:58:217 [Logcat] Starting logcat capture 2021-10-10 08:00:58:313 [ADB] Getting install status for io.appium.uiautomator2.server 2021-10-10 08:00:58:315 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package io.appium.uiautomator2.server' 2021-10-10 08:00:58:390 [ADB] 'io.appium.uiautomator2.server' is installed 2021-10-10 08:00:58:393 [ADB] Getting package info for 'io.appium.uiautomator2.server' 2021-10-10 08:00:58:395 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package io.appium.uiautomator2.server' 2021-10-10 08:00:58:446 [ADB] Starting '/Users/yujin/Library/Android/sdk/tools/bin/apkanalyzer' with args ["manifest","print","/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-v4.3.0.apk"] 2021-10-10 08:01:01:855 [ADB] The version name of the installed 'io.appium.uiautomator2.server' is greater or equal to the application version name ('4.3.0' >= '4.3.0') 2021-10-10 08:01:01:856 [UiAutomator2] io.appium.uiautomator2.server installation state: sameVersionInstalled 2021-10-10 08:01:01:857 [ADB] Checking app cert for /usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-v4.3.0.apk 2021-10-10 08:01:01:860 [ADB] Using 'apksigner' from '/Users/yujin/Library/Android/sdk/build-tools/29.0.3/apksigner' 2021-10-10 08:01:01:861 [ADB] Starting '/Users/yujin/Library/Android/sdk/build-tools/29.0.3/apksigner' with args '["verify","--print-certs","/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-v4.3.0.apk"]' 2021-10-10 08:01:03:145 [ADB] apksigner stdout: Signer #1 certificate DN: EMAILADDRESS=android@android.com, CN=Android, OU=Android, O=Android, L=Mountain View, ST=California, C=US 2021-10-10 08:01:03:145 [ADB] Signer #1 certificate SHA-256 digest: a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc 2021-10-10 08:01:03:146 [ADB] Signer #1 certificate SHA-1 digest: 61ed377e85d386a8dfee6b864bd85b0bfaa5af81 2021-10-10 08:01:03:147 [ADB] Signer #1 certificate MD5 digest: e89b158e4bcf988ebd09eb83f5378e87 2021-10-10 08:01:03:147 [ADB] 2021-10-10 08:01:03:147 [ADB] '/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-v4.3.0.apk' is already signed. 2021-10-10 08:01:03:148 [ADB] Getting install status for io.appium.uiautomator2.server.test 2021-10-10 08:01:03:148 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package io.appium.uiautomator2.server.test' 2021-10-10 08:01:03:189 [ADB] 'io.appium.uiautomator2.server.test' is installed 2021-10-10 08:01:03:189 [ADB] Checking app cert for /usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-debug-androidTest.apk 2021-10-10 08:01:03:190 [ADB] Starting '/Users/yujin/Library/Android/sdk/build-tools/29.0.3/apksigner' with args '["verify","--print-certs","/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-debug-androidTest.apk"]' 2021-10-10 08:01:04:340 [ADB] apksigner stdout: Signer #1 certificate DN: EMAILADDRESS=android@android.com, CN=Android, OU=Android, O=Android, L=Mountain View, ST=California, C=US 2021-10-10 08:01:04:340 [ADB] Signer #1 certificate SHA-256 digest: a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc 2021-10-10 08:01:04:341 [ADB] Signer #1 certificate SHA-1 digest: 61ed377e85d386a8dfee6b864bd85b0bfaa5af81 2021-10-10 08:01:04:341 [ADB] Signer #1 certificate MD5 digest: e89b158e4bcf988ebd09eb83f5378e87 2021-10-10 08:01:04:343 [ADB] 2021-10-10 08:01:04:343 [ADB] '/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-debug-androidTest.apk' is already signed. 2021-10-10 08:01:04:343 [UiAutomator2] Server packages are not going to be (re)installed 2021-10-10 08:01:04:350 [UiAutomator2] Waiting up to 30000ms for services to be available 2021-10-10 08:01:04:351 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell pm list instrumentation' 2021-10-10 08:01:04:718 [UiAutomator2] Instrumentation target 'io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner' is available 2021-10-10 08:01:04:718 [UiAutomator2] Forwarding UiAutomator2 Server port 6790 to 8200 2021-10-10 08:01:04:719 [ADB] Forwarding system: 8200 to device: 6790 2021-10-10 08:01:04:721 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 forward tcp\:8200 tcp\:6790' 2021-10-10 08:01:04:738 [UiAutomator2] No app capability. Assuming it is already on the device 2021-10-10 08:01:04:739 [UiAutomator2] Performing shallow cleanup of automation leftovers 2021-10-10 08:01:04:770 [UiAutomator2] No obsolete sessions have been detected (Error: socket hang up) 2021-10-10 08:01:04:770 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell am force-stop io.appium.uiautomator2.server.test' 2021-10-10 08:01:05:311 [UiAutomator2] Starting UIAutomator2 server 4.3.0 2021-10-10 08:01:05:312 [UiAutomator2] Using UIAutomator2 server from '/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-v4.3.0.apk' and test from '/usr/local/lib/node_modules/appium/node_modules/appium-uiautomator2-server/apks/appium-uiautomator2-server-debug-androidTest.apk' 2021-10-10 08:01:05:312 [UiAutomator2] Waiting up to 30000ms for UiAutomator2 to be online... 2021-10-10 08:01:05:313 [ADB] Creating ADB subprocess with args: ["-P",5037,"-s","emulator-5554","shell","am","instrument","-w","io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner"] 2021-10-10 08:01:06:069 [Instrumentation] io.appium.uiautomator2.server.test.AppiumUiAutomator2Server: 2021-10-10 08:01:06:326 [WD Proxy] Matched '/status' to command name 'getStatus' 2021-10-10 08:01:06:328 [WD Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/hub/status] with no body 2021-10-10 08:01:06:335 [WD Proxy] Got an unexpected response with status undefined: {"code":"ECONNRESET"} 2021-10-10 08:01:07:341 [WD Proxy] Matched '/status' to command name 'getStatus' 2021-10-10 08:01:07:343 [WD Proxy] Proxying [GET /status] to [GET http://localhost:8200/wd/hub/status] with no body 2021-10-10 08:01:07:398 [WD Proxy] Got response with status 200: {"sessionId":"None","value":{"ready":true,"message":"UiAutomator2 Server is ready to accept commands"}} 2021-10-10 08:01:07:399 [UiAutomator2] The initialization of the instrumentation process took 2087ms 2021-10-10 08:01:07:401 [WD Proxy] Matched '/session' to command name 'createSession' 2021-10-10 08:01:07:403 [WD Proxy] Proxying [POST /session] to [POST http://localhost:8200/wd/hub/session] with body: {"capabilities":{"firstMatch":[{"platform":"LINUX","webStorageEnabled":false,"takesScreenshot":true,"javascriptEnabled":true,"databaseEnabled":false,"networkConnectionEnabled":true,"locationContextEnabled":false,"warnings":{},"desired":{"platformName":"android","deviceName":"test","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":true},"platformName":"android","deviceName":"emulator-5554","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":true,"deviceUDID":"emulator-5554"}],"alwaysMatch":{}}} 2021-10-10 08:01:07:420 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","capabilities":{"firstMatch":[{"platform":"LINUX","webStorageEnabled":false,"takesScreenshot":true,"javascriptEnabled":true,"databaseEnabled":false,"networkConnectionEnabled":true,"locationContextEnabled":false,"warnings":{},"desired":{"platformName":"android","deviceName":"test","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":true},"platformName":"android","deviceName":"emulator-5554","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":true,"deviceUDID":"emulator-5554"}],"alwaysMatch":{}}}} 2021-10-10 08:01:07:420 [WD Proxy] Determined the downstream protocol as 'W3C' 2021-10-10 08:01:07:437 [WD Proxy] Proxying [GET /appium/device/info] to [GET http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/appium/device/info] with no body 2021-10-10 08:01:07:462 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"androidId":"eeccdb17137abe38","manufacturer":"Netease","model":"MuMu","brand":"Android","apiVersion":"23","platformVersion":"6.0.1","carrierName":"","realDisplaySize":"720x1280","displayDensity":280,"networks":[{"type":1,"typeName":"WIFI","subtype":0,"subtypeName":"","isConnected":true,"detailedState":"CONNECTED","state":"CONNECTED","extraInfo":"\"u4b3zvSCE43\"","isAvailable":true,"isFailover":false,"isRoaming":false,"capabilities":{"transportTypes":"NET_CAPABILITY_SUPL","networkCapabilities":"","linkUpstreamBandwidthKbps":1048576,"linkDownBandwidthKbps":1048576,"signalStrength":-55,"networkSpecifier":null,"SSID":null}}],"locale":"zh_CN","timeZone":"Asia\/Shanghai"}} 2021-10-10 08:01:07:463 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys window' 2021-10-10 08:01:07:500 [AndroidDriver] Screen already unlocked, doing nothing 2021-10-10 08:01:07:502 [UiAutomator2] Starting 'io.appium.android.apis/io.appium.android.apis.ApiDemos and waiting for 'io.appium.android.apis/io.appium.android.apis.ApiDemos' 2021-10-10 08:01:07:503 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell am start -W -n io.appium.android.apis/io.appium.android.apis.ApiDemos -S -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -f 0x10200000' 2021-10-10 08:01:09:768 [WD Proxy] Proxying [GET /appium/device/pixel_ratio] to [GET http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/appium/device/pixel_ratio] with body: {} 2021-10-10 08:01:09:838 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":1.75} 2021-10-10 08:01:09:840 [WD Proxy] Matched '/appium/device/system_bars' to command name 'getSystemBars' 2021-10-10 08:01:09:841 [WD Proxy] Proxying [GET /appium/device/system_bars] to [GET http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/appium/device/system_bars] with body: {} 2021-10-10 08:01:09:861 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"statusBar":42}} 2021-10-10 08:01:09:862 [WD Proxy] Matched '/window/current/size' to command name 'getWindowSize' 2021-10-10 08:01:09:862 [WD Proxy] Proxying [GET /window/current/size] to [GET http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/window/current/size] with body: {} 2021-10-10 08:01:09:872 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"height":1280,"width":720}} 2021-10-10 08:01:09:873 [Appium] New AndroidUiautomator2Driver session created successfully, session b76bb00a-43bc-4155-bd6a-ccaa3eb865b2 added to master session list 2021-10-10 08:01:09:874 [BaseDriver] Event 'newSessionStarted' logged at 1633852869874 (16:01:09 GMT+0800 (中国标准时间)) 2021-10-10 08:01:09:875 [W3C (b76bb00a)] Cached the protocol value 'W3C' for the new session b76bb00a-43bc-4155-bd6a-ccaa3eb865b2 2021-10-10 08:01:09:879 [W3C (b76bb00a)] Responding to client with driver.createSession() result: {"capabilities":{"platform":"LINUX","webStorageEnabled":false,"takesScreenshot":true,"javascriptEnabled":true,"databaseEnabled":false,"networkConnectionEnabled":true,"locationContextEnabled":false,"warnings":{},"desired":{"platformName":"android","deviceName":"test","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":true},"platformName":"android","deviceName":"emulator-5554","appPackage":"io.appium.android.apis","appActivity":"io.appium.android.apis.ApiDemos","noReset":true,"deviceUDID":"emulator-5554","deviceApiLevel":23,"platformVersion":"6.0.1","deviceScreenSize":"720x1280","deviceScreenDensity":280,"deviceModel":"MuMu","deviceManufacturer":"Netease","pixelRatio":1.75,"statBarHeight":42,"viewportRect":{"left":0,"top":42,"width":720,"height":1238}}} 2021-10-10 08:01:09:884 [HTTP] <-- POST /wd/hub/session 200 18599 ms - 867 2021-10-10 08:01:09:884 [HTTP] 2021-10-10 08:01:09:889 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/timeouts 2021-10-10 08:01:09:890 [HTTP] {"implicit":10000} 2021-10-10 08:01:09:893 [W3C (b76bb00a)] Calling AppiumDriver.timeouts() with args: [null,null,null,null,10000,"b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:09:943 [BaseDriver] W3C timeout argument: {"implicit":10000}} 2021-10-10 08:01:09:944 [BaseDriver] Set implicit wait to 10000ms 2021-10-10 08:01:09:945 [W3C (b76bb00a)] Responding to client with driver.timeouts() result: null 2021-10-10 08:01:09:947 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/timeouts 200 57 ms - 14 2021-10-10 08:01:09:948 [HTTP] 2021-10-10 08:01:09:957 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element 2021-10-10 08:01:09:958 [HTTP] {"using":"xpath","value":"//*[@text='Views']"} 2021-10-10 08:01:09:961 [W3C (b76bb00a)] Calling AppiumDriver.findElement() with args: ["xpath","//*[@text='Views']","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:09:963 [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator 2021-10-10 08:01:09:964 [BaseDriver] Waiting up to 10000 ms for condition 2021-10-10 08:01:09:966 [WD Proxy] Matched '/element' to command name 'findElement' 2021-10-10 08:01:09:966 [WD Proxy] Proxying [POST /element] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element] with body: {"strategy":"xpath","selector":"//*[@text='Views']","context":"","multiple":false} 2021-10-10 08:01:10:283 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"ELEMENT":"f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2","element-6066-11e4-a52e-4f735466cecf":"f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2"}} 2021-10-10 08:01:10:284 [W3C (b76bb00a)] Responding to client with driver.findElement() result: {"element-6066-11e4-a52e-4f735466cecf":"f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2","ELEMENT":"f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2"} 2021-10-10 08:01:10:285 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element 200 329 ms - 137 2021-10-10 08:01:10:286 [HTTP] 2021-10-10 08:01:10:291 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element/f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2/click 2021-10-10 08:01:10:291 [HTTP] {"id":"f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2"} 2021-10-10 08:01:10:294 [W3C (b76bb00a)] Calling AppiumDriver.click() with args: ["f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:10:295 [WD Proxy] Matched '/element/f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2/click' to command name 'click' 2021-10-10 08:01:10:296 [WD Proxy] Proxying [POST /element/f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2/click] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element/f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2/click] with body: {"element":"f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2"} 2021-10-10 08:01:11:397 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":null} 2021-10-10 08:01:11:400 [W3C (b76bb00a)] Responding to client with driver.click() result: null 2021-10-10 08:01:11:402 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element/f2eed02a-5ae0-47bb-8a7d-cf9a10f242c2/click 200 1110 ms - 14 2021-10-10 08:01:11:402 [HTTP] 2021-10-10 08:01:11:405 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element 2021-10-10 08:01:11:405 [HTTP] {"using":"-android uiautomator","value":"new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(\"WebView\").instance(0));"} 2021-10-10 08:01:11:406 [W3C (b76bb00a)] Calling AppiumDriver.findElement() with args: ["-android uiautomator","new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(\"WebView\").instance(0));","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:11:407 [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator 2021-10-10 08:01:11:407 [BaseDriver] Waiting up to 10000 ms for condition 2021-10-10 08:01:11:408 [WD Proxy] Matched '/element' to command name 'findElement' 2021-10-10 08:01:11:408 [WD Proxy] Proxying [POST /element] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element] with body: {"strategy":"-android uiautomator","selector":"new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(\"WebView\").instance(0));","context":"","multiple":false} 2021-10-10 08:01:20:730 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"ELEMENT":"4010cba7-b08f-494c-a543-24a1d871b8d1","element-6066-11e4-a52e-4f735466cecf":"4010cba7-b08f-494c-a543-24a1d871b8d1"}} 2021-10-10 08:01:20:731 [W3C (b76bb00a)] Responding to client with driver.findElement() result: {"element-6066-11e4-a52e-4f735466cecf":"4010cba7-b08f-494c-a543-24a1d871b8d1","ELEMENT":"4010cba7-b08f-494c-a543-24a1d871b8d1"} 2021-10-10 08:01:20:734 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element 200 9326 ms - 137 2021-10-10 08:01:20:734 [HTTP] 2021-10-10 08:01:20:737 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element/4010cba7-b08f-494c-a543-24a1d871b8d1/click 2021-10-10 08:01:20:738 [HTTP] {"id":"4010cba7-b08f-494c-a543-24a1d871b8d1"} 2021-10-10 08:01:20:739 [W3C (b76bb00a)] Calling AppiumDriver.click() with args: ["4010cba7-b08f-494c-a543-24a1d871b8d1","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:20:740 [WD Proxy] Matched '/element/4010cba7-b08f-494c-a543-24a1d871b8d1/click' to command name 'click' 2021-10-10 08:01:20:741 [WD Proxy] Proxying [POST /element/4010cba7-b08f-494c-a543-24a1d871b8d1/click] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element/4010cba7-b08f-494c-a543-24a1d871b8d1/click] with body: {"element":"4010cba7-b08f-494c-a543-24a1d871b8d1"} 2021-10-10 08:01:21:503 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":null} 2021-10-10 08:01:21:504 [W3C (b76bb00a)] Responding to client with driver.click() result: null 2021-10-10 08:01:21:506 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element/4010cba7-b08f-494c-a543-24a1d871b8d1/click 200 768 ms - 14 2021-10-10 08:01:21:508 [HTTP] 2021-10-10 08:01:21:511 [HTTP] --> GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/contexts 2021-10-10 08:01:21:512 [HTTP] {} 2021-10-10 08:01:21:518 [W3C (b76bb00a)] Calling AppiumDriver.getContexts() with args: ["b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:21:520 [AndroidDriver] Getting a list of available webviews 2021-10-10 08:01:21:521 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell cat /proc/net/unix' 2021-10-10 08:01:21:608 [AndroidDriver] Not checking whether webviews have active pages; use the 'ensureWebviewsHavePages' cap to turn this check on 2021-10-10 08:01:21:611 [AndroidDriver] Found webviews: [] 2021-10-10 08:01:21:612 [AndroidDriver] Available contexts: ["NATIVE_APP"] 2021-10-10 08:01:21:613 [W3C (b76bb00a)] Responding to client with driver.getContexts() result: ["NATIVE_APP"] 2021-10-10 08:01:21:617 [HTTP] <-- GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/contexts 200 106 ms - 24 2021-10-10 08:01:21:618 [HTTP] 2021-10-10 08:01:21:623 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element 2021-10-10 08:01:21:624 [HTTP] {"using":"xpath","value":"//*[@resource-id=\"i am a link\"]"} 2021-10-10 08:01:21:627 [W3C (b76bb00a)] Calling AppiumDriver.findElement() with args: ["xpath","//*[@resource-id=\"i am a link\"]","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:21:630 [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator 2021-10-10 08:01:21:631 [BaseDriver] Waiting up to 10000 ms for condition 2021-10-10 08:01:21:634 [WD Proxy] Matched '/element' to command name 'findElement' 2021-10-10 08:01:21:636 [WD Proxy] Proxying [POST /element] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element] with body: {"strategy":"xpath","selector":"//*[@resource-id=\"i am a link\"]","context":"","multiple":false} 2021-10-10 08:01:22:194 [WD Proxy] Got an unexpected response with status 404: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"error":"no such element","message":"An element could not be located on the page using the given search parameters","stacktrace":"io.appium.uiautomator2.common.exceptions.ElementNotFoundException: An element could not be located on the page using the given search parameters\n\tat io.appium.uiautomator2.handler.FindElement.findElement(FindElement.java:102)\n\tat io.appium.uiautomator2.handler.FindElement.safeHandle(FindElement.java:72)\n\tat io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:38)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:252)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:242)\n\tat io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:44)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)\n\tat io.netty.channel.AbstractChannelHandlerCon... 2021-10-10 08:01:22:195 [W3C] Matched W3C error code 'no such element' to NoSuchElementError 2021-10-10 08:01:22:196 [BaseDriver] Waited for 565 ms so far 2021-10-10 08:01:22:697 [WD Proxy] Matched '/element' to command name 'findElement' 2021-10-10 08:01:22:698 [WD Proxy] Proxying [POST /element] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element] with body: {"strategy":"xpath","selector":"//*[@resource-id=\"i am a link\"]","context":"","multiple":false} 2021-10-10 08:01:23:948 [WD Proxy] Got an unexpected response with status 404: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"error":"no such element","message":"An element could not be located on the page using the given search parameters","stacktrace":"io.appium.uiautomator2.common.exceptions.ElementNotFoundException: An element could not be located on the page using the given search parameters\n\tat io.appium.uiautomator2.handler.FindElement.findElement(FindElement.java:102)\n\tat io.appium.uiautomator2.handler.FindElement.safeHandle(FindElement.java:72)\n\tat io.appium.uiautomator2.handler.request.SafeRequestHandler.handle(SafeRequestHandler.java:38)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleRequest(AppiumServlet.java:252)\n\tat io.appium.uiautomator2.server.AppiumServlet.handleHttpRequest(AppiumServlet.java:242)\n\tat io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:44)\n\tat io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:366)\n\tat io.netty.channel.AbstractChannelHandlerCon... 2021-10-10 08:01:23:948 [W3C] Matched W3C error code 'no such element' to NoSuchElementError 2021-10-10 08:01:23:949 [BaseDriver] Waited for 2318 ms so far 2021-10-10 08:01:24:455 [WD Proxy] Matched '/element' to command name 'findElement' 2021-10-10 08:01:24:456 [WD Proxy] Proxying [POST /element] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element] with body: {"strategy":"xpath","selector":"//*[@resource-id=\"i am a link\"]","context":"","multiple":false} 2021-10-10 08:01:24:714 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":{"ELEMENT":"eea82ab1-38d9-41c0-be30-edf8637af535","element-6066-11e4-a52e-4f735466cecf":"eea82ab1-38d9-41c0-be30-edf8637af535"}} 2021-10-10 08:01:24:716 [W3C (b76bb00a)] Responding to client with driver.findElement() result: {"element-6066-11e4-a52e-4f735466cecf":"eea82ab1-38d9-41c0-be30-edf8637af535","ELEMENT":"eea82ab1-38d9-41c0-be30-edf8637af535"} 2021-10-10 08:01:24:717 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element 200 3094 ms - 137 2021-10-10 08:01:24:718 [HTTP] 2021-10-10 08:01:24:724 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element/eea82ab1-38d9-41c0-be30-edf8637af535/click 2021-10-10 08:01:24:724 [HTTP] {"id":"eea82ab1-38d9-41c0-be30-edf8637af535"} 2021-10-10 08:01:24:726 [W3C (b76bb00a)] Calling AppiumDriver.click() with args: ["eea82ab1-38d9-41c0-be30-edf8637af535","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:24:728 [WD Proxy] Matched '/element/eea82ab1-38d9-41c0-be30-edf8637af535/click' to command name 'click' 2021-10-10 08:01:24:729 [WD Proxy] Proxying [POST /element/eea82ab1-38d9-41c0-be30-edf8637af535/click] to [POST http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a/element/eea82ab1-38d9-41c0-be30-edf8637af535/click] with body: {"element":"eea82ab1-38d9-41c0-be30-edf8637af535"} 2021-10-10 08:01:24:782 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":null} 2021-10-10 08:01:24:787 [W3C (b76bb00a)] Responding to client with driver.click() result: null 2021-10-10 08:01:24:790 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/element/eea82ab1-38d9-41c0-be30-edf8637af535/click 200 65 ms - 14 2021-10-10 08:01:24:790 [HTTP] 2021-10-10 08:01:24:794 [HTTP] --> GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/contexts 2021-10-10 08:01:24:795 [HTTP] {} 2021-10-10 08:01:24:800 [W3C (b76bb00a)] Calling AppiumDriver.getContexts() with args: ["b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:24:802 [AndroidDriver] Getting a list of available webviews 2021-10-10 08:01:24:802 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell cat /proc/net/unix' 2021-10-10 08:01:24:907 [AndroidDriver] Not checking whether webviews have active pages; use the 'ensureWebviewsHavePages' cap to turn this check on 2021-10-10 08:01:24:910 [AndroidDriver] WEBVIEW_2632 mapped to pid 2632 2021-10-10 08:01:24:911 [AndroidDriver] Getting process name for webview 2021-10-10 08:01:24:912 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell ps' 2021-10-10 08:01:25:026 [AndroidDriver] Parsed pid: '2632' pkg: 'io.appium.android.apis' from 2021-10-10 08:01:25:026 [AndroidDriver] USER PID PPID VSIZE RSS WCHAN PC NAME 2021-10-10 08:01:25:026 [AndroidDriver] u0_a45 2632 291 1136340 120932 0 7f80807c0ea8 S io.appium.android.apis 2021-10-10 08:01:25:027 [AndroidDriver] Returning process name: 'io.appium.android.apis' 2021-10-10 08:01:25:030 [AndroidDriver] Found webviews: ["WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:030 [AndroidDriver] Available contexts: ["NATIVE_APP","WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:031 [W3C (b76bb00a)] Responding to client with driver.getContexts() result: ["NATIVE_APP","WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:036 [HTTP] <-- GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/contexts 200 241 ms - 57 2021-10-10 08:01:25:036 [HTTP] 2021-10-10 08:01:25:042 [HTTP] --> GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/contexts 2021-10-10 08:01:25:043 [HTTP] {} 2021-10-10 08:01:25:044 [W3C (b76bb00a)] Calling AppiumDriver.getContexts() with args: ["b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:25:047 [AndroidDriver] Getting a list of available webviews 2021-10-10 08:01:25:047 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell cat /proc/net/unix' 2021-10-10 08:01:25:109 [AndroidDriver] Not checking whether webviews have active pages; use the 'ensureWebviewsHavePages' cap to turn this check on 2021-10-10 08:01:25:111 [AndroidDriver] WEBVIEW_2632 mapped to pid 2632 2021-10-10 08:01:25:112 [AndroidDriver] Getting process name for webview 2021-10-10 08:01:25:113 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell ps' 2021-10-10 08:01:25:146 [AndroidDriver] Parsed pid: '2632' pkg: 'io.appium.android.apis' from 2021-10-10 08:01:25:147 [AndroidDriver] USER PID PPID VSIZE RSS WCHAN PC NAME 2021-10-10 08:01:25:147 [AndroidDriver] u0_a45 2632 291 1139548 124380 0 7f808082cc1a S io.appium.android.apis 2021-10-10 08:01:25:147 [AndroidDriver] Returning process name: 'io.appium.android.apis' 2021-10-10 08:01:25:148 [AndroidDriver] Found webviews: ["WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:148 [AndroidDriver] Available contexts: ["NATIVE_APP","WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:149 [W3C (b76bb00a)] Responding to client with driver.getContexts() result: ["NATIVE_APP","WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:154 [HTTP] <-- GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/contexts 200 108 ms - 57 2021-10-10 08:01:25:155 [HTTP] 2021-10-10 08:01:25:158 [HTTP] --> POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/context 2021-10-10 08:01:25:159 [HTTP] {"name":"WEBVIEW_io.appium.android.apis"} 2021-10-10 08:01:25:161 [W3C (b76bb00a)] Calling AppiumDriver.setContext() with args: ["WEBVIEW_io.appium.android.apis","b76bb00a-43bc-4155-bd6a-ccaa3eb865b2"] 2021-10-10 08:01:25:164 [AndroidDriver] Getting a list of available webviews 2021-10-10 08:01:25:165 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell cat /proc/net/unix' 2021-10-10 08:01:25:186 [AndroidDriver] Not checking whether webviews have active pages; use the 'ensureWebviewsHavePages' cap to turn this check on 2021-10-10 08:01:25:187 [AndroidDriver] WEBVIEW_2632 mapped to pid 2632 2021-10-10 08:01:25:187 [AndroidDriver] Getting process name for webview 2021-10-10 08:01:25:188 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell ps' 2021-10-10 08:01:25:223 [AndroidDriver] Parsed pid: '2632' pkg: 'io.appium.android.apis' from 2021-10-10 08:01:25:224 [AndroidDriver] USER PID PPID VSIZE RSS WCHAN PC NAME 2021-10-10 08:01:25:224 [AndroidDriver] u0_a45 2632 291 1139548 124380 0 7f808082cc1a S io.appium.android.apis 2021-10-10 08:01:25:224 [AndroidDriver] Returning process name: 'io.appium.android.apis' 2021-10-10 08:01:25:225 [AndroidDriver] Found webviews: ["WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:225 [AndroidDriver] Available contexts: ["NATIVE_APP","WEBVIEW_io.appium.android.apis"] 2021-10-10 08:01:25:226 [AndroidDriver] Connecting to chrome-backed webview context 'WEBVIEW_io.appium.android.apis' 2021-10-10 08:01:25:238 [AndroidDriver] A port was not given, using random free port: 8000 2021-10-10 08:01:25:239 [AndroidDriver] Automated Chromedriver download is disabled. Use 'chromedriver_autodownload' server feature to enable it 2021-10-10 08:01:25:239 [AndroidDriver] Before starting chromedriver, androidPackage is 'io.appium.android.apis' 2021-10-10 08:01:25:240 [Chromedriver] Changed state to 'starting' 2021-10-10 08:01:25:245 [Chromedriver] Found 1 executable in '/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac' 2021-10-10 08:01:25:294 [Chromedriver] The following Chromedriver executables were found: 2021-10-10 08:01:25:294 [Chromedriver] '/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac/chromedriver__mumu_2.42' (version '2.42', minimum Chrome version '68.0.3440') 2021-10-10 08:01:25:295 [ADB] Getting package info for 'com.google.android.webview' 2021-10-10 08:01:25:295 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package com.google.android.webview' 2021-10-10 08:01:25:324 [ADB] Getting package info for 'com.android.webview' 2021-10-10 08:01:25:325 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell dumpsys package com.android.webview' 2021-10-10 08:01:25:354 [Chromedriver] Found Chrome bundle 'com.android.webview' version '68.0.3440' 2021-10-10 08:01:25:357 [Chromedriver] Found 1 Chromedriver executable capable of automating Chrome '68.0.3440'. 2021-10-10 08:01:25:358 [Chromedriver] Choosing the most recent, '/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac/chromedriver__mumu_2.42'. 2021-10-10 08:01:25:358 [Chromedriver] If a specific version is required, specify it with the `chromedriverExecutable`desired capability. 2021-10-10 08:01:25:359 [Chromedriver] Set chromedriver binary as: /usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac/chromedriver__mumu_2.42 2021-10-10 08:01:25:360 [Chromedriver] Killing any old chromedrivers, running: pkill -15 -f "/usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac/chromedriver__mumu_2.42.*--port=8000" 2021-10-10 08:01:25:428 [Chromedriver] No old chromedrivers seem to exist 2021-10-10 08:01:25:428 [Chromedriver] Cleaning any old adb forwarded port socket connections 2021-10-10 08:01:25:429 [ADB] List forwarding ports 2021-10-10 08:01:25:429 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 forward --list' 2021-10-10 08:01:25:441 [ADB] Removing forwarded port socket connection: 55916 2021-10-10 08:01:25:441 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 forward --remove tcp\:55916' 2021-10-10 08:01:25:456 [Chromedriver] Spawning chromedriver with: /usr/local/lib/node_modules/appium/node_modules/appium-chromedriver/chromedriver/mac/chromedriver__mumu_2.42 --url-base=wd/hub --port=8000 --adb-port=5037 --verbose 2021-10-10 08:01:25:477 [Chromedriver] Chromedriver version: '2.42.591059' 2021-10-10 08:01:25:478 [Chromedriver] Chromedriver v. 2.42.591059 does not fully support W3C protocol. Defaulting to MJSONWP 2021-10-10 08:01:25:479 [WD Proxy] Matched '/status' to command name 'getStatus' 2021-10-10 08:01:25:480 [WD Proxy] Proxying [GET /status] to [GET http://127.0.0.1:8000/wd/hub/status] with no body 2021-10-10 08:01:25:486 [WD Proxy] Got an unexpected response with status undefined: {"errno":-61,"code":"ECONNREFUSED","syscall":"connect","address":"127.0.0.1","port":8000} 2021-10-10 08:01:25:690 [WD Proxy] Matched '/status' to command name 'getStatus' 2021-10-10 08:01:25:691 [WD Proxy] Proxying [GET /status] to [GET http://127.0.0.1:8000/wd/hub/status] with no body 2021-10-10 08:01:25:704 [WD Proxy] Got response with status 200: {"sessionId":"","status":0,"value":{"build":{"version":"alpha"},"os":{"arch":"x86_64","name":"Mac OS X","version":"10.16.0"}}} 2021-10-10 08:01:25:705 [Chromedriver] Starting MJSONWP Chromedriver session with capabilities: { 2021-10-10 08:01:25:705 [Chromedriver] "desiredCapabilities": { 2021-10-10 08:01:25:706 [Chromedriver] "chromeOptions": { 2021-10-10 08:01:25:706 [Chromedriver] "androidPackage": "io.appium.android.apis", 2021-10-10 08:01:25:707 [Chromedriver] "androidUseRunningApp": true, 2021-10-10 08:01:25:708 [Chromedriver] "androidDeviceSerial": "emulator-5554" 2021-10-10 08:01:25:708 [Chromedriver] }, 2021-10-10 08:01:25:708 [Chromedriver] "loggingPrefs": { 2021-10-10 08:01:25:709 [Chromedriver] "browser": "ALL" 2021-10-10 08:01:25:709 [Chromedriver] } 2021-10-10 08:01:25:709 [Chromedriver] } 2021-10-10 08:01:25:709 [Chromedriver] } 2021-10-10 08:01:25:710 [WD Proxy] Matched '/session' to command name 'createSession' 2021-10-10 08:01:25:711 [WD Proxy] Proxying [POST /session] to [POST http://127.0.0.1:8000/wd/hub/session] with body: {"desiredCapabilities":{"chromeOptions":{"androidPackage":"io.appium.android.apis","androidUseRunningApp":true,"androidDeviceSerial":"emulator-5554"},"loggingPrefs":{"browser":"ALL"}}} 2021-10-10 08:01:26:408 [Chromedriver] Webview version: 'Chrome/68.0.3440.70' 2021-10-10 08:01:26:542 [WD Proxy] Got response with status 200: {"sessionId":"ed7fb83742cae3d1847485e4a02ad001","status":0,"value":{"acceptInsecureCerts":false,"acceptSslCerts":false,"applicationCacheEnabled":false,"browserConnectionEnabled":false,"browserName":"chrome","chrome":{"chromedriverVersion":"2.42.591059 (a3d9684d10d61aa0c45f6723b327283be1ebaad8)"},"cssSelectorsEnabled":true,"databaseEnabled":false,"goog:chromeOptions":{"debuggerAddress":"localhost:57973"},"handlesAlerts":true,"hasTouchScreen":true,"javascriptEnabled":true,"locationContextEnabled":true,"mobileEmulationEnabled":false,"nativeEvents":true,"pageLoadStrategy":"normal","platform":"ANDROID","rotatable":false,"setWindowRect":false,"takesHeapSnapshot":true,"takesScreenshot":true,"unexpectedAlertBehaviour":"","version":"68.0.3440.70","webStorageEnabled":true}} 2021-10-10 08:01:26:543 [WD Proxy] Determined the downstream protocol as 'MJSONWP' 2021-10-10 08:01:26:543 [Chromedriver] Changed state to 'online' 2021-10-10 08:01:26:545 [W3C (b76bb00a)] Responding to client with driver.setContext() result: null 2021-10-10 08:01:26:547 [HTTP] <-- POST /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/context 200 1389 ms - 14 2021-10-10 08:01:26:548 [HTTP] 2021-10-10 08:01:26:549 [HTTP] --> GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/source 2021-10-10 08:01:26:550 [HTTP] {} 2021-10-10 08:01:26:551 [W3C (b76bb00a)] Driver proxy active, passing request on via HTTP proxy 2021-10-10 08:01:26:553 [WD Proxy] Matched '/wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/source' to command name 'getPageSource' 2021-10-10 08:01:26:553 [WD Proxy] Proxying [GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/source] to [GET http://127.0.0.1:8000/wd/hub/session/ed7fb83742cae3d1847485e4a02ad001/source] with body: {} 2021-10-10 08:01:26:617 [WD Proxy] Got response with status 200: {"sessionId":"ed7fb83742cae3d1847485e4a02ad001","status":0,"value":"\u003C!DOCTYPE html>\u003Chtml xmlns=\"http://www.w3.org/1999/xhtml\">\u003Chead>\n \u003Ctitle>I am a page title\u003C/title>\n\u003C/head>\n\u003Cbody>\n I am some other page content\n\n\n\u003Ciframe name=\"chromedriver dummy frame\" src=\"about:blank\">\u003C/iframe>\u003C/body>\u003C/html>"} 2021-10-10 08:01:26:618 [WD Proxy] Replacing sessionId ed7fb83742cae3d1847485e4a02ad001 with b76bb00a-43bc-4155-bd6a-ccaa3eb865b2 2021-10-10 08:01:26:620 [HTTP] <-- GET /wd/hub/session/b76bb00a-43bc-4155-bd6a-ccaa3eb865b2/source 200 70 ms - 305 2021-10-10 08:01:26:620 [HTTP] 2021-10-10 08:02:26:630 [BaseDriver] Shutting down because we waited 60 seconds for a command 2021-10-10 08:02:26:631 [UiAutomator2] Deleting UiAutomator2 session 2021-10-10 08:02:26:632 [Appium] Closing session, cause was 'New Command Timeout of 60 seconds expired. Try customizing the timeout using the 'newCommandTimeout' desired capability' 2021-10-10 08:02:26:633 [Appium] Removing session b76bb00a-43bc-4155-bd6a-ccaa3eb865b2 from our master session list 2021-10-10 08:02:26:633 [AndroidDriver] Stopping chromedriver for context WEBVIEW_io.appium.android.apis 2021-10-10 08:02:26:635 [Chromedriver] Changed state to 'stopping' 2021-10-10 08:02:26:644 [WD Proxy] Proxying [DELETE /] to [DELETE http://127.0.0.1:8000/wd/hub/session/ed7fb83742cae3d1847485e4a02ad001] with no body 2021-10-10 08:02:26:648 [WD Proxy] Got response with status 200: {"sessionId":"ed7fb83742cae3d1847485e4a02ad001","status":0,"value":null} 2021-10-10 08:02:26:653 [Chromedriver] Changed state to 'stopped' 2021-10-10 08:02:26:654 [UiAutomator2] Deleting UiAutomator2 server session 2021-10-10 08:02:26:654 [WD Proxy] Matched '/' to command name 'deleteSession' 2021-10-10 08:02:26:654 [WD Proxy] Proxying [DELETE /] to [DELETE http://localhost:8200/wd/hub/session/1f7ceff4-e731-4b38-b43f-7fde4ed9268a] with no body 2021-10-10 08:02:26:749 [WD Proxy] Got response with status 200: {"sessionId":"1f7ceff4-e731-4b38-b43f-7fde4ed9268a","value":null} 2021-10-10 08:02:26:749 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 shell am force-stop io.appium.android.apis' 2021-10-10 08:02:27:454 [Instrumentation] . 2021-10-10 08:02:28:500 [Instrumentation] Time: 81.417 2021-10-10 08:02:28:501 [Instrumentation] 2021-10-10 08:02:28:502 [Instrumentation] OK (1 test) 2021-10-10 08:02:28:523 [Logcat] Stopping logcat capture 2021-10-10 08:02:28:525 [ADB] Removing forwarded port socket connection: 8200 2021-10-10 08:02:28:525 [ADB] Running '/Users/yujin/Library/Android/sdk/platform-tools/adb -P 5037 -s emulator-5554 forward --remove tcp\:8200' 2021-10-10 08:02:28:762 [Instrumentation] The process has exited with code 0
-
-
Android WebView性能分析与优化
2021-07-27 10:44:45一提到App内的WebView加载网页,大家的第一印象就是:慢、耗流量、体验比原生差。但WebView加载网页也有其天生的优势:动态,跨平台,开发周期短。 那能如何解决WebView加载网页慢和体验差的问题呢?可以思考下面两...一、简介
一提到App内的WebView加载网页,大家的第一印象就是:慢、耗流量、体验比原生差。但WebView加载网页也有其天生的优势:动态,跨平台,开发周期短。
那能如何解决WebView加载网页慢和体验差的问题呢?可以思考下面两个问题:
- 从打开浏览器到网页完全展示都发生了什么?
- 如何给WebView加载网页提速?
二、整体思维导图
三、衡量标准
快慢是一个相对量,如何衡量WebView的快慢呢?
3.1 用户体验的时间尺度
从用户角度来看,如下图是2018年份百度移动端的统计数据:
根据上面的统计折线图,得出如下表格:
可以发现:
- 小于1s,用户更容易接受,关闭率更低。
- 用户对移动端的容忍比PC端更低,要求更高。
3.2 加载时长标准
加载时长 = 加载结束 - 加载开始
加载开始很好界定,当用户点击feed流里面的item开始,就开始计时。
加载结束呢?WebView有个WebViewClient#onPageFinished回调方法,这个方法是在页面完全加载结束时候回调的,但是页面DOM渲染完页面就已经有内容,对于用户来说算是页面已经展示出来了。统计加载时长以DOM渲染完更好些。
3.3 统计标准
通过收集真正的用户使用数据,才能更好的根据用户的情况进行优化。那如何才能反应用户的真实情况呢?
通常有两种方式:
- 平均数,容易被较长的加载时间给拉高,不容易反应真实情况。
- 中位数,能很好的反应大多数用户的情况,但是中位数的要求较低,可以将其提高到80分位,或者90分位。
在们项目进行数据统计时候可以先采用80分位,检测下优化效果,后续再提高要求,使用更高分位如95分位等。
根据现网数据可知,当95分位的用户页面加载时长为1s以内时,80分位的用户页面加载时长为0.35s以内时,APP内网页的体验最佳。
具体最佳时间可以根据真实的上报数据的统计结果进行调整。
四、问题分析
前端页展示一般分两种:
- 前后端分离,前端加载资源后,通过js请求展示的数据并在前端渲染展示。
- 页面直出,页面数据由服务器填充完成后,直接下发到前端,由前端直接展示。
现在较多的采用前后端分离的方式,下面都以这种方式为例讲解。
4.1 WebView渲染过程
WebView渲染大致需要如下几步:
- 解析 HTML 文件
- 加载 JavaScript 和 CSS 文件
- 解析并执行 JavaScript
- 构建 DOM 结构
- 加载图片等资源
- 页面加载完毕
4.2 WebView耗时统计方法
统计可从两方面入手,一是网页层统计,二是App层统计。
4.2.1 网页层统计:WebView中网页耗时统计方法
WebView加载url到完全展示出各个部分耗时情况,可以根据w3c标准中网页performance参数获取具体耗时统计参数信息,详细的页面加载过程见下图:
根据performance统计情况可以得出如下数据:
- 重定向耗时:redirectEnd - redirectStart
- DNS查询耗时 :domainLookupEnd - domainLookupStart
- TCP链接耗时 :connectEnd - connectStart
- HTTP请求耗时 :responseEnd - responseStart
- 解析dom树耗时 : domComplete - domInteractive
- 白屏时间 :responseStart - navigationStart
- DOMready时间 :domContentLoadedEventEnd - navigationStart
- onload时间:loadEventEnd - navigationStart,也即是onload回调函数执行的时间。
4.2.2 App层统计:App层统计WebView耗时
Android可以通过WebViewClient#onPageFinished回调统计页面整个加载时长,开始时间以WebView创建开始算,严格一点可以从feed流中点击item开始算。这个统计只能算整个加载时长,加载到用户可见的时长以DOM渲染完页面为准,后者比前置时长更短一些。前置供参考,以后者为准。
根据上图可以获取的统计数据:
- WebView创建耗时:navigationStart - createWebView(以初始化开始时间为准,下同)
- 交互开始到页面可见耗时:onClickItem - createWebView
- 页面加载到可见耗时:domContentLoadedEventEnd - createWebView
- 页面完全加载耗时:onPageFinished - createWebView
4.3 资讯统计数据
测试资讯连接1:测试文章1
测试资讯连接2:测试文章2
五、优化方案
从统计数据看,WebView首次加载耗时较多2s左右,二次加载耗时也有0.5s左右。
5.1 离线化
同我们现有的离线包一样,将页面用的公共资源html,css,js等模板化,将模板打成压缩包形成离线包内置或动态下发到App端,在App中访问访问到具体的页面时候优先加载本地的模板资源。
通过WebViewClient#shouldInterceptRequest方法拦截WebView的资源加载,匹配到本地模板中的资源就直接加载本地资源,没有匹配本地模板资源再去加载线上资源。genWebResourceResponse用于实现具体的匹配策略。
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? { return genWebResourceResponse(request, view) }
模板注意事项:
- 精简模板,移除不必要的js、css,进行异步拉取
- 模板内联js、css,减少io
- js尽量放到最后,避免阻碍DOM解析
5.2 数据与模板加载
5.2.1 并行执行:数据请求与模板加并行
虽然进行了本地化网页模板化,但整体的页面加载依然是串行执行的。为了进一步提高页面的加载速度,可以让数据请求由app端代理。使数据加载与模板加载并行执行,待数据加载完成时通过JsBridge回填到网页中。效果如下图:
5.2.2 数据预加载
既然数据请求已经由app代理了,当然也可以通过一定的策略预加载数据,当页面打开时候直接使用缓存数据。这样整个网页加载过程完全离线化不受网络影响。
5.3 WebView预创建
由上面统计数据可知,WebView创建与二次创建耗时相差甚远,如下图总结:
原因是Webview所有的逻辑处理都是通过WebViewProvider来实现的,它需要加载Webview内核,这是一个重量级的操作,内核是以apk的形式存在。而内核加载后在同一页面是共享的,因此后续的初始化时间就很少了。
可以通过预创建WebView来加速这一过程,预创建会消耗一定量的内存,如何平衡预创建和内存消耗问题还需实践把握衡量,具体方式:
WebView池(或统一全局WebView):在app启动时候后台创建WebView池,当app需要展示网页的时候直接拿已创建的WebView,需要在页面销毁时候清除页面数据。池结构如下:
预创建WebView注意事项:
- WebView初始化需要传context,需要注意内存泄漏。
- WebView创建需要较大内存,需要注意内存耗费。
- WebView复用需要清除数据,需要注意状态维护。
5.4 模板预热
经过前面几步处理后,网页加载过程可以实现全部本地化后,但每次打开网页的时候还需要重复加载模板数据。DOM解析耗时,如下图:
为了避免重复加载模板,则需要在WebView池的基础上,让池中的WebView预先加载本地模板。当需要展示网页时候直接拿到已经加载过本地模板的WebView,并通过JsBridge注入数据。池中结构如下:
网页加载的整个过程如下:
5.5 图片加载
WebView在加载大量图片时候表现不佳,重复进入时还会重复加载图片,体验不好且浪费浏览。
5.5.1 App代理图片加载
该方式需要借助图片加载库如Glide,在WebViewClient#shouldInterceptRequest方法拦截WebView的资源加载,判断要加载的资源url是否为图片,是就走Glide加载并生成加载图片的WebResourceResponse,通过Glide来达到缓存图片目的,避免多次打开页面重复加载线上图片资源,genWebResourceResponse用于实现具体的匹配策略。这种方式有点是不需要前端配合,客户端完全自己处理即可。
在api>=21时,可以通过WebResourceRequest获取请求中的accept字段获取返回值类型,用于区分url类型。
override fun shouldInterceptRequest( view: WebView?, request: WebResourceRequest? ): WebResourceResponse? { val url = request.url.toString() if (checkImageRequest(request)) { val imageFile = Glide.with(view.context) .asFile() .load(url) .submit() .get() return WebResourceResponse( "image/png,*/*", "UTF-8", FileInputStream(imageFile) ) } return super.shouldInterceptRequest(view, url) }
在api<21时,只能通过url来判断来判断类型。
override fun shouldInterceptRequest(view: WebView?, url: String?): WebResourceResponse? { // 处理资源匹配 return genWebResourceResponse(url, view) }
注:示例代码仅展示用,细节需要自己处理
5.5.2 hybrid
使用网页和原生控件的混合开发模式,网页中文字部分让WebView渲染,网页中的图片视频等使用原生控件展示。优点即可以避免重复加载图,又能提升图片浏览体验;缺点实现成本高,需要前后端协调处理。今日头条8.0.3版本同样采用了这种方式加载展示图片。
具体思路:
- 图片展示容器与WebView上下叠放,大小一致
- WebView中预留图片占位div
- 获取网页中图片的url、大小以及位置信息
- 通过js或其他方式通知App
- App加载图片并根据WebView中占位div位置设置原生图片位置
- 原生控件与WebView同步滚动
六、总结
Android项目中WebView还存在较大的优化空间,可以进一步提升资讯、活动页等h5页面的浏览体验。本文涉及的优化方式仅是方向性的,为后续Android端的WebView优化提供方向性指引,实际操作会涉及到多端配合,细节较多,需要不断迭代优化。
参考文章:
Does Page Load Time Really Affect Bounce Rate?
-
android webview 面试
2019-03-04 16:35:03()"><div style="width:80px; margin:0px auto; padding:10px; text-align:center; border:2px solid #202020;" >  Click me! </div></a> 面试题 1.WebView的内存泄露。... -
Android WebView无法加载URL
2021-05-28 08:57:07itsrajesh4ug..87您是否在清单文件中添加了...编辑使用以下行.public class WebViewDemo extends Activity {private WebView webView;Activity activity ;private ProgressDialog progDailog;@SuppressLint("... -
微信小程序webview 脚手架使用详解
2020-12-29 00:46:12项目地址项目介绍1.wechat使用taro创建的初始化项目2.react-ssr-h5使用nextjs创建的项目 已经做好完整的兼容处理 使用vw vh为单位简单介绍因小程序对于webview通信做出的限制 从webview发起的postMessage并不会实时... -
Android 中WebView 截图的实现方式
2021-06-03 03:04:02Hybrid App 中网页部分的分享方式越来越趋向于多元化,比较常见的用户操作方式有...这篇文章总结一下 Android 应用中 WebView 截图的实现方式。WebView 作为一种特殊的控件,自然不能像其他系统 View 或者截屏的方式... -
【纵享丝滑】Android WebView H5 秒开方案总结
2022-01-05 16:31:18Hybrid 也叫混合开发,即半原生半 H5 的方式,通过 WebView 来实现需要高度灵活性的业务,在需要和 Native 做交互或者是调用特定平台能力时再通过 JsBridge 来实现两端交互 采取 Hybrid 方案的理由可以有很多个:... -
Android与webview混合开发
2020-11-18 17:39:09formatter: '{a} {b} : {c} ({d}%)' }, visualMap: { show: false, min: 80, max: 600, inRange: { colorLightness: [0, 1] } }, series: [ { name: '访问来源', ... -
Android WebView 预加载方案
2021-05-27 10:04:45由于H5具备 开发周期短、灵活性好 的特点,所以现在 Android App大多嵌入了 Android Webview 组件进行 Hybrid 开发 但我知道你一定在烦恼 Android Webview 的性能问题,特别突出的是:加载速度慢 & 消耗流量 ... -
Android webView无法处理pageFinish
2021-06-04 11:47:34try this codewebview.setWebViewClient(new myWebClient...webview.setWebChromeClient(new WebChromeClient(){public void onProgressChanged(WebView view, int progressInt){if (progressInt < 80 &&... -
安卓IOS客户端调试webview页面的方法
2022-02-10 15:30:19前端在做混合开发的时候总是会遇到各种兼容问题,有时候不得不使用真机来调试,本文章介绍了一个我觉得挺方便的调试webview的方法,希望这篇文章能够对大家有帮助 -
UWP 如何阻止WebView自动打开浏览器?
2021-12-10 15:58:10UWP项目接入Google登录网页版,遇到了一点问题:点击登录打开的网页不在当前的WebView中加载而是打开了浏览器....... 如何解决呢?UWP WebView如何阻止自动打开浏览器一文给出了答案。 不过参考的资料是C#... -
一个webview崩溃的解决办法
2021-03-25 09:59:01一个webview崩溃的解决办法 最近有个机器(魅蓝E2(GIH-PHO-1879)(webview版本:51.0.2704.108))打开浏览器放着就会崩溃,抓堆栈出来。是这样的: Operating system: Android 0.0.0 Linux 3.18.31+ #1 SMP ... -
Android Studio笔记之webview ——实现播放器全屏功能
2021-05-27 09:53:52这篇文章已经解释的非常好了如何实现视频全屏播放我的代码:webview:package cn.edu.caztc.myapp;import androidx.appcompat.app.AppCompatActivity;import android.app.Activity;import android.content.Intent;... -
Android 应用(5)——android10.0升级webview浏览器内核版本
2022-03-10 14:01:19webview资源链接: https://www.apkmirror.com/uploads/?appcategory=android-system-webview 需求是升级webview到97版本。 1、科学上网下载webview apk 选择与我们使用的主芯片相同架构的apk(armv8) 2、替换... -
WebView
2017-09-18 09:30:01目前很多Android app都内置了可以显示web页面的界面,会发现这个界面一般都是由一个叫做WebView的组件渲染出来的,学习该组件可以为你的app开发提升扩展性。 先说下WebView的一些优点: --可以直接显示和渲染web... -
Android之webview详解
2019-03-22 21:01:39一、webview基本介绍 1.什么是webview 2.为什么要使用webview 3.webview基本操作 二、webview高级使用 1.WebView状态 2.资源加载 3.WebView加载优化 4.数据缓存 5.Android 和 JavaScript 交互 6.网页前进与后退 7.... -
android webview中的音乐的暂停与播放
2021-04-22 11:36:14前段时间有这样一个需求,webview显示一个带音乐的网页,在播放音乐的时候进入第三方软件暂停播放,返回时继续播放。后来参考了两篇文章解决了这个问题。AudioManager audioManager = (AudioManager) mContext.... -
Webview Android与js交互(示例代码)
2021-06-03 12:17:47Android中可以通过webview来实现和js的交互,在程序中调用js代码,只需要将webview控件的支持js的属性设置为trueAndroid(Java)与JavaScript(HTML)交互有四种情况:1) Android(Java)调用HTML中js代码2) Android(Java)... -
关于微信小程序webview的使用
2020-12-30 07:40:22目前而言,基本80%的用户会升级微信,所以其实不必担心版本问题,官方截止2017-12-01提供的数据也说明88%的用户支持web-view。 使用 web-view 组件是一个可以用来承载网页的容器,会自动铺满整个小程序页面; 属性... -
(Android)Webview在anroid4.4以下和8.0以上版本兼容性解决方案
2021-06-03 03:09:141 因为项目需要webview需要适配android4.4以下版本。原生的webview在4.4以下版本不显示。可以使用以腾讯x5为内核的webview 完美解决我的问题。腾讯x5为内核的webview网上有很多例子,自己查找 很简单的。2 刚适配完... -
Android MVVM框架搭建(六)腾讯X5WebView + DrawerLayout + NavigationView
2021-12-02 17:03:23Android MVVM框架搭建(六)腾讯X5WebView + DrawerLayout + NavigationView前言正文一、添加依赖二、使用WebView三、获取新闻详情① 新闻详情数据② 新闻详情数据API③ WebRepository④ WebViewModel⑤ 页面数据... -
Android webview设置字体大小,适配屏幕,夜间模式
2021-05-27 04:41:29webView = (WebView) findViewById(R.id.webView1); webView.loadUrl(... -
如何使用webview加载一个本地的Html文件
2021-06-09 08:52:51这个功能对于展示app的使用帮助是很有帮助的核心代码:privatevoidinitView(){mWebView=(WebView)findViewById(R.id.load_webView);mProgressBar=(ProgressBar)findViewById(R.id.load_progressBar);mLoadingLayout=... -
Android用Webview播放优酷视频全屏问题
2021-06-04 05:40:02private String s = " swf" + " + " width=\"80%\" height=\"80%\" align=\"middle\" allowScriptAccess=\"always\"" + " allowFullScreen=\"true\" wmode=\"transparent\" " + "type=\"application...