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

做了一个这样的功能:
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

猜你喜欢

转载自blog.csdn.net/qq_24536171/article/details/70788938