Android调Ajax和动态添加JS中的token(Android 和JS完全交互)

来源:互联网 发布:淘宝卖家服务市场分析 编辑:程序博客网 时间:2024/05/20 11:20

做了一个这样的功能:
Android 调用AJAX进行登录
遇到的问题有:

跨域访问题:
*出现的错误信息是:Origin null is not allowed by Access-Control-Allow-Origin.
原因是:XMLHttpRequest2 进行跨域访问时需要服务器许可,不是任何域都接受跨域请求的,
网上有说是在HTML中加上

<meta http-equiv="Access-Control-Allow-Origin" content="*">

或者是在ajax中把dataType: “json”改为dataType: “jsonp”,

第一种情况没有变化,第二种改为jsonp的话会变为
Console: Uncaught SyntaxError: Unexpected token :错误
如果是在远程服务器里ajax()请求外域服务器里的页面,即使通过服务器环境运行也会报跨域的错误,此时需要通过JSONP的形式!
什么是JSONP?
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
虽然可以解决这个跨越的问题,但是我返回的数据格式就不对了

类似跨越问题是需要服务器端进行配置的,APP端设置是不起作用。
主要的原因是WebKit和Chromium这两个内核
Android 4.4之后出现的Chromium,关于这两个内核的区别和各自的相关信息可参考 理解WebKit和Chromium
本案例整体思路:
Android用户的账号和密码均由EditText输入,点击Button 调用Ajax请求访问,将name和password传入,等Ajax请求完毕后,将信息回调给Android,同时把获得到的token存储到本地。
登录成功后,显示主题界面,主题界面需要token,然后在html加载的时候,把我们存储在APP中的token加载到JS中。
前言:
1.我使用的是腾讯TBS浏览器服务
2.官网腾讯TBS
环境配置步骤:(android studio 2.3)
1.导入jar包和jniLibs下的动态库文件(.so文件)
2.defaultConfig中添加如下信息:
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86_64", 'armeabi-v8a', 'x86', "mips"
}

3.初始化在Application中

public class App extends Application {    private static App myApplaction;    public static  String ID = null;    public static  String TOKEN = null;    @Override    public void onCreate() {        super.onCreate();        myApplaction=this;        preinitX5WebCore();        //预加载x5内核        Intent intent = new Intent(this, AdvanceLoadX5Service.class);        startService(intent);    }    public static App getIntance(){        return myApplaction;    }    private void preinitX5WebCore() {        if (!QbSdk.isTbsCoreInited()) {            QbSdk.preInit(getApplicationContext(), null);// 设置X5初始化完成的回调接口        }    }    // x5 init service    public class AdvanceLoadX5Service extends Service {        @Nullable        @Override        public IBinder onBind(Intent intent) {            return null;        }        @Override        public void onCreate() {            super.onCreate();            initX5();        }        private void initX5() {            //  预加载X5内核            QbSdk.initX5Environment(getApplicationContext(), cb);        }        QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {            @Override            public void onViewInitFinished(boolean arg0) {                // TODO Auto-generated method stub                //初始化完成回调            }            @Override            public void onCoreInitFinished() {                // TODO Auto-generated method stub            }        };    }}

4.封装一个BaseWebAcitity(我封装的不完美,还在改进中)

/** * Created by adminZPH on 2017/4/24. */public class BaseWebViewActivty extends Activity{    private final String TAG="BaseWebViewActivty";    private View headview;    private WebView webView;    private String url;    @Override    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {        super.onCreate(savedInstanceState, persistentState);        getWindow().setFormat(PixelFormat.TRANSLUCENT);        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);    }    public void setHeadview(View headview){        this.headview=headview;//        setstatusbarcolor();    }    public void SetWebView(WebView webView,String url){        this.webView=webView;        this.url=url;        Setting();    }    public void ShowWebView(){        webView.loadUrl(url);        webView.setWebViewClient(new MyWebViewClient());        webView.setWebChromeClient(new MyWebChromeClient());    }    private void Setting(){        WebSettings settings = webView.getSettings();        settings.setSupportZoom(true);        settings.setJavaScriptEnabled(true);        settings.setJavaScriptCanOpenWindowsAutomatically(true);        settings.setBuiltInZoomControls(true);        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);        settings.setBuiltInZoomControls(false);        webView.setHorizontalScrollBarEnabled(false);        webView.setVerticalScrollBarEnabled(false);        /**         * 设置本地缓存策略         * */        webView.getSettings().setDomStorageEnabled(true);        webView.getSettings().setAppCacheMaxSize(1024*1024*8);        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();        webView.getSettings().setAppCachePath(appCachePath);        webView.getSettings().setAllowFileAccess(true);        webView.getSettings().setAppCacheEnabled(true);        //下面方法去掉线条        IX5WebViewExtension ix5 = webView.getX5WebViewExtension();        if (null != ix5) {            ix5.setScrollBarFadingEnabled(false);        }    }    /**     * 自定义的MyWebViewClient     * */    public class MyWebViewClient extends com.tencent.smtt.sdk.WebViewClient{        @Override        public boolean shouldOverrideUrlLoading(WebView view, String url) {            // 返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器            view.loadUrl(url);            return true;        }        @Override        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {            // 重写此方法可以让webview处理https请求。            super.onReceivedSslError(view, handler, error);            handler.proceed();        }        @Override        public void onPageStarted(WebView view, String url, Bitmap favicon) {            // 在页面加载开始时调用。            super.onPageStarted(view, url, favicon);            Log.i(TAG,"onPageStarted");        }       @Override        public void onPageFinished(WebView view, String url) {            // 在页面加载结束时调用。            super.onPageFinished(view, url);            Log.i(TAG,"onPageFinished");            webView.setVisibility(View.VISIBLE);        }        @Override        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {            // 网络不通,加载失败            super.onReceivedError(view, errorCode, description, failingUrl);            view.loadData("网络连接失败,请稍后重试!", "text/html; charset=utf-8", "utf-8");        }        @Override        public void onLoadResource(WebView view, String url) {            // 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。            super.onLoadResource(view, url);        }    }    public class MyWebChromeClient extends com.tencent.smtt.sdk.WebChromeClient {        @Override        public void onProgressChanged(WebView view, int newProgress) {            if (newProgress == 100) {            } else {            }            super.onProgressChanged(view, newProgress);        }        @Override        public void onReceivedTitle(WebView view, String title) {            super.onReceivedTitle(view, title);        }    }    @Override    public void onBackPressed() {//        finish();        if (null!=webView&&webView.canGoBack()){            webView.goBack();//            return true;        }    }    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        if (keyCode==KeyEvent.KEYCODE_BACK){            if (null!=webView&&webView.canGoBack()){                webView.goBack();                return true;            }            setResult(Activity.RESULT_OK);        }        return super.onKeyDown(keyCode, event);    }}

在Src/java目录下建立assets文件夹,写一个html文件如下:

<html><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">    <title>登录</title>    <link rel="stylesheet" type="text/css" href="css/bootstrap.min.css" />    <style type="text/css">        body {            padding-top: 40px;            padding-bottom: 40px;            background: none;        }        .form-signin {            max-width: 300px;            padding: 19px 29px 29px;            margin: 0 auto 20px;            background-color: #fff;            border: 1px solid #e5e5e5;            -webkit-border-radius: 5px;            -moz-border-radius: 5px;            border-radius: 5px;            -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);            -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);            box-shadow: 0 1px 2px rgba(0, 0, 0, .05);        }        .form-signin .form-signin-heading,        .form-signin .checkbox {            margin-bottom: 10px;        }        .form-signin input[type="text"],        .form-signin input[type="password"] {            font-size: 16px;            height: auto;            margin-bottom: 15px;            padding: 7px 9px;        }    </style></head> <body>        <div class="container-fluid">            <div class="form-signin" style="text-align: center;">                <h2 class="form-signin-heading">登录</h2>                <input type="text" id="username" name="username" class="input-block-level" placeholder="账号" style="width: 100%!important;">                <br/>                <input type="password" id="password" name="password" class="input-block-level" placeholder="密码" style="width: 100%!important;">                <p><button class="btn btn-large btn-success" id="login"  type="submit">登录</button></p>            </div>        </div>    </body><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript">    function loginclick(username,password) {        console.log(username);        $.ajax({            url: "http://XXX.XXX.X.XX:8080/XXXX/XXXX.go?method=XXXXXX",            data: { "username": username, "password": password },            type: "post",            dataType: "json",            success: function (result) {                console.log(result.state)                if (result.state == "OK") {                    window.android.ShowMessage(result.msg);                }                if(result.state=="ERROR"){                    window.android.ShowMessage("1");                }            },            error: function () {                window.android.ShowMessage("0");            }        });    }</script></html>

其中有一个function loginclick(),就是我们要调用的ajax方法,
对于Android和JS互调,需要实体类中方法做中间引领人,
这里我封装一个接口如下:

public interface BaseJsImp {    /**     * 显示一些提示信息的方法     * */    @JavascriptInterface    public void ShowMessage(String T);    /**     * Js调用Android的方法     * */    @JavascriptInterface    public void JsToAndroid(Object... T);    /**     * Android调用JS的方法     * */    @JavascriptInterface    public void AndroidToJs(Object... T);    /**     * Js调用调用android方法,带有返回值的     * */    @JavascriptInterface    public Object GetDateFromAndroid();    /**     * Js调用调用android方法,带有返回值的和参数的     * */    @JavascriptInterface    public Object GetDateFromAndroid(Object... T);}

接下来到了我们的登录功能中:
在xml文件中,edittext和button随便写
关键是加上

 <com.tencent.smtt.sdk.WebView        android:layout_width="0dp"        android:id="@+id/login_webview"        android:layout_height="0dp">    </com.tencent.smtt.sdk.WebView>

在LoginActivity中(部分方法可能只有我有)
代码如下

public class LoginActivity extends BaseWebViewActivty implements View.OnClickListener {    private Button login;    private ImageView weixin,qq;    private MaterialEditText name,password;    private LoadingDialog loadingDialog;    private WebView webView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_login);        initHeadView();        //加载资源文件        SetWebView(webView,"file:///android_asset/static/login.html");        ShowWebView();        //初始化回传信息接口        webView.addJavascriptInterface(new LoginClass(), "android");    }    private void initHeadView() {        login= (Button) findViewById(R.id.login_btn);        name= (MaterialEditText) findViewById(R.id.login_username);        password= (MaterialEditText) findViewById(R.id.login_password);        weixin= (ImageView) findViewById(R.id.weixin_login);        qq= (ImageView) findViewById(R.id.qq_login);        login.setOnClickListener(this);        weixin.setOnClickListener(this);        qq.setOnClickListener(this);        webView= (WebView) findViewById(R.id.login_webview);        setstatusbarcolor();    }    @Override    public void onClick(View view) {        switch (view.getId()){            case R.id.login_btn:                if(name.getText().toString().equals("")){                    toast("账号不能为空");                    return;                }                if(password.getText().toString().equals("")){                    toast("密码不能为空");                    return;                }                Login(name.getText().toString(),password.getText().toString());                break;            case R.id.weixin_login:                break;            case R.id.qq_login:                break;        }    }    private void Login(String name,String psd) {        loadingDialog=new LoadingDialog(this,"登录中");        loadingDialog.show();        //把我们的信息传入JS中        webView.loadUrl("javascript:loginclick('"+name+"','"+psd+"')");    }    @Override    protected void onDestroy() {        super.onDestroy();    }    public class LoginClass implements BaseJsImp {        @Override        public void ShowMessage(String message) {            if(loadingDialog!=null){                if(loadingDialog.isShowing()){                    loadingDialog.dismiss();                }            }            if(message.equals("0") ||message.equals("1")) {                toast("登录失败!");                Log.i("TAG","登录失败");            }            else {                Log.i("TAG","登录成功"+message);                toast("登录成功!"+message);                String[] a = message.split(",");                App.ID=a[0];                App.TOKEN=a[1];                Intent intent=new Intent(LoginActivity.this,TestActivity.class);                intent.putExtra("url",message);                startActivity(intent);//                AddDB();            }        }        @Override        public void JsToAndroid(Object... T) {        }        @Override        public void AndroidToJs(Object... T) {        }        @Override        public Object GetDateFromAndroid() {            return null;        }        @Override        public Object GetDateFromAndroid(Object... T) {            return null;        }    }    @Override    public void onBackPressed() {        super.onBackPressed();        this.finish();    }    private void AddDB() {        User u=new User();        u.setLoginState(true);        u.setUsername(name.getText().toString());        u.setPassword(password.getText().toString());        DBUtil.initDataBase(u);        this.finish();    }}

上述实例中,可以看出JS和Android互调用法
Android中方法
webView.addJavascriptInterface(new LoginClass(), “android”);
Js中(JS将信息回传给ANdroid)
window.android.ShowMessage(result.msg);

接下来,如何在JS加载中拿到保存在Android中所需要的token呢?

一个html页面中需要很多js,每个js都可能是一个单独的文件,我们解决在不同JS中调一个Android中的数据时候的思路是:
在主HTML中设置全局变量,然后每一个js都调用这个,对于token的话就需要在初始化数据的时候再ajax请求中添加headers,然后把我们android中的保存token加载过去。
实例如下:
主html文件:
在head中:

<script>        var _access_tocken = window.android.GetDateFromAndroid();</script>

如果我们是在一个新的Activity中加载的话,在新的Activity中需要
webView.addJavascriptInterface(new NewClass(), “android”);

NewClass继承我们的BaseJsImp,只要将重写的GetDateFromAndroid()返回我们所需的token
如下:

 @Override        public Object GetDateFromAndroid() {            if(App.TOKEN!=null)                return App.TOKEN;            else                return null;        }

然后在我们的主html中数据初始化的js中添加headers
如下

//自定义的方法function myDefultAction() {        $.ajax({        url: baseurl+"getAgenda",        headers: {                access_tocken: _access_tocken        },        //data :{"_date":_date,"title":password},        type: "post",        dataType: "json",        // async: false,

通过以上方式我们就可以将动态的添加token到js中

这片海
2017-04-26

0 0