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
更多相关文章
- Android百度地图(一):百度地图定位sdk 类方法参数、定位原理详细
- Android NFS文件系统挂载遇到的问题解决方法
- [置顶] Android入门系列一(Android学习方法)
- Android控件阴影效果的几种实现方法
- Android Init Language(安卓初始化语言)
- Android VideoView设置静音,Android 设置VideoView静音,Android
- ListView去掉分割线的几种方法
- SDK Platform Tools component is missing! Please use the SDK
- Android Market google play store帐号注册方法流程 及发布应用