那些年遇到的坑----------Android Webview

在开发Android Web壳app时,使用WebView加载前端页面遇到了诸多问题,主要由于Android 4.4以下版本的WebView内核与4.4及以上版本不同,导致API适配困难。在Android 4.4中,Webview的定位功能、文件上传等功能存在bug,需要通过特定方法进行修复。此外,Cookie管理也是问题之一,需要手动维护以确保用户登录状态。

最近开发一个Web壳app,我用的是Webview加载前端页面,本来应该是挺简单的事,但是开发过程中Webview的坑还是比较多(IOS一点事都没有,只能说搞Android的命苦),在这总结一下:

主要原因:A:在Android 4.4以下(不包含4.4)系统WebView底层实现是采用WebKit(http://www.webkit.org/)内核,而在Android 4.4及其以上Google 采用了chromium(http://www.chromium.org/)作为系统WebView的底层内核支持。

 B:部分WebView的API在不同的Android版本中不同,所以需要去适配不同的版本,贼坑。

 C:Android对前端的一些标签不支持。


Bug1.在Android4.4版本中Webview定位功能失效,我自己5.0的系统可以定位(Android4.4以下没有去测试)。

原因:Android4.4以下需要我们自己去打开数据库,开启定位。

解决办法:

mWebView.addJavascriptInterface(this, "android");
// 让WebView能够执行javaScript
mWebSettings = mWebView.getSettings();
mWebSettings.setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
//启用数据库
mWebSettings.setDatabaseEnabled(true);
String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
//启用地理定位
mWebSettings.setGeolocationEnabled(true);
//设置定位的数据库路径
mWebSettings.setGeolocationDatabasePath(dir);
并且在WevChromeClient中实现:

onGeolocationPermissionsShowPrompt();

Bug2.在上传本地文件不成功

原因:Android对前端的input标签不支持

解决办法:在WevChromeClient中实现以下方法:

private ValueCallback<Uri[]> UploadMessage;
public ValueCallback<Uri> mUploadMessage;
private static final int KITKAT_RESULTCODE = 99;
public static final int REQUEST_SELECT_FILE = 100;
public final static int FILECHOOSER_RESULTCODE = 1;

// For Android 3.0
private void openFileChooser(ValueCallback<Uri> uploadMsg) {
    mUploadMessage =  uploadMsg;
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("image/*");
    startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}

// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
    mUploadMessage = uploadMsg;
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("*/*");
    startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE);
}

//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
    mUploadMessage = uploadMsg;
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("image/*");
    startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
//For Android 5.0+
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
    UploadMessage = filePathCallback;
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("image/*");
    startActivityForResult(Intent.createChooser(i, "File Chooser"), REQUEST_SELECT_FILE);
    return true;
}

并且在activityResult方法中接收

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode,Intent intent) {
   
    if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage) return;
        Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
        mUploadMessage.onReceiveValue(result);
        mUploadMessage = null;
    } else if (requestCode == REQUEST_SELECT_FILE) {
        if (UploadMessage == null) return;
        UploadMessage.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
        UploadMessage = null;
    } else if (requestCode == KITKAT_RESULTCODE) {
        /**
       	1 上传图片
         2 上传成功后 将服务器返回的URL 返回给 js  : UploadedFileName
         String UploadedFileName = "URL";
         mWebView.loadUrl("javascript:CheckImage('" + UploadedFileName + "')");
         */
        Uri imgUri = intent.getData();
        Toast.makeText(this, "success="+imgUri, Toast.LENGTH_SHORT).show();
        String UploadedFileName = imgUri.toString();
        mWebView.loadUrl("javascript:CheckImage('" + UploadedFileName + "')");
    }
}

Bug3.尽管实现了上述方法上,在Android4.4的机子上任然上传不了图片。

原因:Android4.4取消了webview 上传文件的方法,需要我们自己写一个接口。给js调用

解决办法:(感觉有这一个方法就够了,上面的可以不要,不过我没测试过)

mWebView.addJavascriptInterface(new ModuleJavaScriptInterface(), "$_demo");
mWebView.addJavascriptInterface(new ModuleJavaScriptInterface(), "$_photo");

final class ModuleJavaScriptInterface {

    ModuleJavaScriptInterface() {
    }
    /*android 4.4调用系统相册*/
    @JavascriptInterface
    public void uploadImage() {
        Toast.makeText(MainNewActivity.this, "upload ", Toast.LENGTH_SHORT).show();
        //打开相册
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "File Chooser"), KITKAT_RESULTCODE);

    }

Bug4.没办法保持登陆状态

原因:Cookie会失效

解决办法:把用户账号和密码保存到本地,进入app的时候判断一下,本地数据库中有就设置到cookie中去,关联到webview,后台做个判断,自动登陆。

首先:在setWebviewClient的onpageFinished()添加保存账号和密码的代码

CookieSyncManager.createInstance(getApplicationContext());
CookieManager cookieManager = CookieManager.getInstance();
String cookie = cookieManager.getCookie(url);//获取url中的cookie
Log.d("zyx", "cookie======" + cookie);
String[] cookies = cookie.split(";");
for (int i = 0; i < cookies.length; i++) {
    String[] nvp = cookies[i].split("=");
    if (nvp[0].trim().equals("tel")){//保存账号
        SharedPreferences sharedPreferences = getSharedPreferences("cookise",Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("tel",nvp[1]);
        editor.commit();
        Log.d("zyx", "===tel===" + nvp[1]);
    }
    if (nvp[0].trim().equals("pwd")){//保存密码
        SharedPreferences sharedPreferences = getSharedPreferences("cookise",Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("pwd",nvp[1]);
        editor.commit();
        Log.d("zyx", "==pwd====" + nvp[1]);
    }
    if (nvp[0].trim().equals("logout")){//删除密码和账号
        SharedPreferences sharedPreferences = getSharedPreferences("cookise",Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.clear();
        editor.commit();
        cookieManager.removeSessionCookie();// 移除cooki
        cookieManager.removeAllCookie();
        Log.d("zyx", "==logout====" + nvp[1]);
    }
    Log.d("zyx", "-----nvp:" + nvp[0] + "---->" + nvp[1]);
}
在webview.loadurl(url)之前调用:

 //判断登陆状态
    private void islogin(String url){
        CookieSyncManager.createInstance(this);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setAcceptCookie(true);
        cookieManager.removeAllCookie();
//       cookieManager.setCookie(url, "domain=192.168.1.1");      //域(貌似不设也无所谓)
//       cookieManager.setCookie(url, "path=/");             //路径(貌似不设也无所谓)
        cookieManager.setCookie(url,"tel="+mTel);
        cookieManager.setCookie(url,"pwd="+mPwd);            //账号密码
        mCookie = cookieManager.getCookie(url);//这里可以获取你的cookie看一下是什么样子的,其实就是拼接起来的string
        Log.d("zyx", "-----mCookie:" + mCookie );
        CookieSyncManager.getInstance().sync();
    }

注:在开发过程中也找过很多资料,看过很多大牛的博客,受益匪浅。这里不一一列出,拜谢!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值