翻看谷歌源码 那些让人感兴趣的东西--WebView如何申请授权
2016-06-02 11:48
435 查看
转载请注明出处:王亟亟的大牛之路
强行安利下自己的收纳库(日更):https://github.com/ddwhan0123/Useful-Open-Source-Android昨天写了个CardView的貌似,不太受欢迎,今天上午没啥大事就继续翻源码,这一篇讲的是WebView的授权实现(WebView这个坑大家怨声载道,现在看看官方是如何操作的吧)
先看下运行效果
OK,来看看包结构
比平时的那些sample东西稍微多点 其实主要业务就在 PermissionRequestFragment和ConfirmationDialogFragment里
我们一个个看(主要看授权行为)
MainActivity初始化一些UI 然后把 Log部分和WebView部分以及bar逻辑的一些相关内容做了处理,不涉及授权操作(主要做呈现)
他继承于SampleActivityBase,也是做一些初始化Log的行为,这里不做解释
接下来是授权行为操作的PermissionRequestFragment(重要步骤已经注释)
/** * This fragment shows a {@link WebView} and loads a web app from the {@link SimpleWebServer}. */ public class PermissionRequestFragment extends Fragment implements ConfirmationDialogFragment.Listener { private static final String TAG = PermissionRequestFragment.class.getSimpleName(); private static final String FRAGMENT_DIALOG = "dialog"; /** * We use this web server to serve HTML files in the assets folder. This is because we cannot * use the JavaScript method "getUserMedia" from "file:///android_assets/..." URLs. * 异步对WebView的操作 */ private SimpleWebServer mWebServer; /** * A reference to the {@link WebView}. * 嵌入的ViewView */ private WebView mWebView; /** * This field stores the {@link PermissionRequest} from the web application until it is allowed * or denied by user. * 授权行为 */ private PermissionRequest mPermissionRequest; /** * For testing. * 控制台行为 */ private ConsoleMonitor mConsoleMonitor; @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //布局处理 return inflater.inflate(R.layout.fragment_permission_request, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { //初始化WebView相关操作 mWebView = (WebView) view.findViewById(R.id.web_view); // Here, we use #mWebChromeClient with implementation for handling PermissionRequests. mWebView.setWebChromeClient(mWebChromeClient); configureWebSettings(mWebView.getSettings()); } @Override public void onResume() { super.onResume(); //模拟访问某URLWebView final int port = 8080; mWebServer = new SimpleWebServer(port, getResources().getAssets()); mWebServer.start(); mWebView.loadUrl("http://localhost:" + port + "/sample.html"); } @Override public void onPause() { //关闭 mWebServer.stop(); super.onPause(); } //允许JS操作行为 @SuppressLint("SetJavaScriptEnabled") private static void configureWebSettings(WebSettings settings) { settings.setJavaScriptEnabled(true); } /** * This {@link WebChromeClient} has implementation for handling {@link PermissionRequest}. * 主要的时间传递行为 */ private WebChromeClient mWebChromeClient = new WebChromeClient() { // This method is called when the web content is requesting permission to access some // resources. //当网页内容被请求允许访问某些资源,调用此方法。 @Override public void onPermissionRequest(PermissionRequest request) { Log.i(TAG, "onPermissionRequest"); mPermissionRequest = request; //把事件传递给了ConfirmationDialogFragment ConfirmationDialogFragment.newInstance(request.getResources()) .show(getChildFragmentManager(), FRAGMENT_DIALOG); } // This method is called when the permission request is canceled by the web content. //这个方法在授权请求被取消时被调用 @Override public void onPermissionRequestCanceled(PermissionRequest request) { Log.i(TAG, "onPermissionRequestCanceled"); // 驳回提示UI作为请求不再有效。 mPermissionRequest = null; DialogFragment fragment = (DialogFragment) getChildFragmentManager() .findFragmentByTag(FRAGMENT_DIALOG); if (null != fragment) { fragment.dismiss(); } } @Override public boolean onConsoleMessage(@NonNull ConsoleMessage message) { switch (message.messageLevel()) { case TIP: Log.v(TAG, message.message()); break; case LOG: Log.i(TAG, message.message()); break; case WARNING: Log.w(TAG, message.message()); break; case ERROR: Log.e(TAG, message.message()); break; case DEBUG: Log.d(TAG, message.message()); break; } if (null != mConsoleMonitor) { mConsoleMonitor.onConsoleMessage(message); } return true; } }; @Override public void onConfirmation(boolean allowed) { if (allowed) { mPermissionRequest.grant(mPermissionRequest.getResources()); Log.d(TAG, "Permission granted."); } else { mPermissionRequest.deny(); Log.d(TAG, "Permission request denied."); } mPermissionRequest = null; } public void setConsoleMonitor(ConsoleMonitor monitor) { mConsoleMonitor = monitor; } /** * For testing. */ public interface ConsoleMonitor { public void onConsoleMessage(ConsoleMessage message); } }
PermissionRequestFragment这个类做了一系列初始化的行为(主要是对于 WebView的),然后接收JS的回调,把处理的权限给予ConfirmationDialogFragment,然后根据ConfirmationDialogFragment的回调来做相关处理
再来看下ConfirmationDialogFragment
/** * Prompts the user to confirm permission request. */ public class ConfirmationDialogFragment extends DialogFragment { private static final String ARG_RESOURCES = "resources"; /** * Creates a new instance of ConfirmationDialogFragment. * * @param resources The list of resources requested by PermissionRequeste. * @return A new instance. */ public static ConfirmationDialogFragment newInstance(String[] resources) { ConfirmationDialogFragment fragment = new ConfirmationDialogFragment(); Bundle args = new Bundle(); args.putStringArray(ARG_RESOURCES, resources); fragment.setArguments(args); return fragment; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String[] resources = getArguments().getStringArray(ARG_RESOURCES); return new AlertDialog.Builder(getActivity()) .setMessage(getString(R.string.confirmation, TextUtils.join("\n", resources))) .setNegativeButton(R.string.deny, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ((Listener) getParentFragment()).onConfirmation(false); } }) .setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ((Listener) getParentFragment()).onConfirmation(true); } }) .create(); } /** * Callback for the user's response. */ public interface Listener { /** * Called when the PermissoinRequest is allowed or denied by the user. * * @param allowed True if the user allowed the request. */ public void onConfirmation(boolean allowed); } }
这边做的事情比较简单,就不在注释里讲了,这边笼统的介绍下。
首先当被调用的时候接受到了具体申请授权的内容,然后显示dialog,根据用户不同的操作传递给PermissionRequestFragment ,他本身不做什么具体授权行为,主要是dialog的显示功能。
@Override public void onConfirmation(boolean allowed) { if (allowed) { mPermissionRequest.grant(mPermissionRequest.getResources()); Log.d(TAG, "Permission granted."); } else { mPermissionRequest.deny(); Log.d(TAG, "Permission request denied."); } mPermissionRequest = null; }
因为在授权之后又再次为null,所以我们也就能反复对他进行操作了。
那么问题来了,从头到尾我们是如何跟JS交互的?
再来看下JS的代码
(function () { "use strict"; navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia; window.URL = window.URL || window.webkitURL; window.onload = function () { var video = document.querySelector('#video'), toggle = document.querySelector('#toggle'), stream = null; if (!navigator.getUserMedia) { console.error('getUserMedia not supported'); } toggle.addEventListener('click', function () { if (null === stream) { // This call to "getUserMedia" initiates a PermissionRequest in the WebView. navigator.getUserMedia({ video: true }, function (s) { stream = s; video.src = window.URL.createObjectURL(stream); toggle.innerText = 'Stop'; console.log('Started'); }, function (error) { console.error('Error starting camera. Denied.'); }); } else { stream.stop(); stream = null; toggle.innerText = 'Start'; console.log('Stopped'); } }); console.log('Page loaded'); }; })();
整个html把事情都交付给了JS做逻辑判断 而navigator.getUserMedia 就是js向手机申请需要做某件事,手机知晓后向用户申请授权的代码了
里面还有一些读不到媒体干嘛干嘛的判断,大家爱看不看,不是太重要。
这样一来流程完全走了一遍
总结几个关键步骤:
1 WebView设置的时候要能接受JS请求2 在需要native支持的时候通过双方的接口进行通信
3 手机接收到请求授权后问用户是否给予,如果用户不同意就 mPermissionRequest.deny()
代码那么多其实并不难理解,只是一大堆回调地狱而已!
源码地址:https://github.com/ddwhan0123/BlogSample/tree/master/PermissionRequest
源码下载地址:https://github.com/ddwhan0123/BlogSample/blob/master/PermissionRequest/PermissionRequest.zip?raw=true
相关文章推荐
- 学习iOS swift问题记录
- androidstudio中生成百度地图两种AK的方式(debug版和正式版)
- HAL开发全流程(一)
- android -view基础
- 修正nagios主机配置文件
- android6.0 framework修改使用两个声卡
- android studio最操蛋的错误(1)
- Android学习路线指南
- 国内优秀的Android资源你都知道吗?
- 详细讲解Android修改键盘文字的方法
- Android学习之TextView显示html图片的方法
- Android线程中设置控件的值提示报错的解决方法
- android实现截屏操作
- 统设置中“自动转屏“设置跟app中Activity设置横竖屏之间的影响
- Unity渲染路径 Rendering Paths_2_Forward Rendering 正向渲染
- 提高iOS开发效率的第三方框
- Android开发之基本控件和四种布局方式详解
- iOS开发关于上传图片后,图片发生旋转的处理
- iOS开发之CoreLocation框架(地图/定位)
- iOS极光推送集成步骤