入门文章

Blazor Hybrid / MAUI 简介和实战

https://www.cnblogs.com/densen2014/p/16240966.html

在 Mac 上开发 .NET MAUI

https://www.cnblogs.com/densen2014/p/16057571.html

在 Windows 上开发 .NET MAUI

https://docs.microsoft.com/zh-cn/dotnet/maui/get-started/installation

之前的工程已经能正常使用blazor的webview下获取定位,使用相机等功能,新版释出后反而权限获取不到了,定位页面出现如下错误

由于这个问题主要出现在安卓系统,下面只选了安卓的步骤分享

Android

应用所需的权限和功能在 AndroidManifest.xml 中定义。请参阅 官方文档 了解 Android App Manifest。

某些 Android 设备权限需要在运行时显示提示,以便用户可以授予或拒绝该权限。 Android 有一个推荐的 workflow 用于在运行时请求权限,此工作流必须由应用手动实现。 WebView 的 WebChromeClient 负责对权限请求做出反应,因此该项目提供了一个 PermissionManagingBlazorWebChromeClient 将 Webkit 资源映射到 Android 权限并执行推荐的权限请求工作流。

在向 AndroidManifest.xml 添加其他权限后,请务必更新 PermissionManagingBlazorWebChromeClient.cs 以包含该权限的“基本原理字符串”,解释应用程序需要它的原因。可能还需要在 权限请求类型 和 Android Manifest 权限之间定义其他映射。

1. 应用所需的权限Platforms/Android/AndroidManifest.xml

以下是我所有的测试权限列表,各位看官按需自由组合.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ModifyAudioSettings" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<uses-permission android:name="android.permission.CaptureSecureVideoOutput" />
<uses-permission android:name="android.permission.CaptureVideoOutput" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
</manifest>

2. 添加文件 Platforms/Android/PermissionManagingBlazorWebChromeClient.cs

using Android;
using Android.App;
using Android.Content.PM;
using Android.Graphics;
using Android.OS;
using Android.Views;
using Android.Webkit;
using AndroidX.Activity;
using AndroidX.Activity.Result;
using AndroidX.Activity.Result.Contract;
using AndroidX.Core.Content;
using Java.Interop;
using System;
using System.Collections.Generic;
using View = Android.Views.View;
using WebView = Android.Webkit.WebView; namespace BlazorMaui; internal class PermissionManagingBlazorWebChromeClient : WebChromeClient, IActivityResultCallback
{
// This class implements a permission requesting workflow that matches workflow recommended
// by the official Android developer documentation.
// See: https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions
// The current implementation supports location, camera, and microphone permissions. To add your own,
// update the s_rationalesByPermission dictionary to include your rationale for requiring the permission.
// If necessary, you may need to also update s_requiredPermissionsByWebkitResource to define how a specific
// Webkit resource maps to an Android permission. // In a real app, you would probably use more convincing rationales tailored toward what your app does.
private const string CameraAccessRationale = "This app requires access to your camera. Please grant access to your camera when requested.";
private const string LocationAccessRationale = "This app requires access to your location. Please grant access to your precise location when requested.";
private const string MicrophoneAccessRationale = "This app requires access to your microphone. Please grant access to your microphone when requested."; private static readonly Dictionary<string, string> s_rationalesByPermission = new()
{
[Manifest.Permission.Camera] = CameraAccessRationale,
[Manifest.Permission.AccessFineLocation] = LocationAccessRationale,
[Manifest.Permission.RecordAudio] = MicrophoneAccessRationale,
// Add more rationales as you add more supported permissions.
}; private static readonly Dictionary<string, string[]> s_requiredPermissionsByWebkitResource = new()
{
[PermissionRequest.ResourceVideoCapture] = new[] { Manifest.Permission.Camera },
[PermissionRequest.ResourceAudioCapture] = new[] { Manifest.Permission.ModifyAudioSettings, Manifest.Permission.RecordAudio },
// Add more Webkit resource -> Android permission mappings as needed.
}; private readonly WebChromeClient _blazorWebChromeClient;
private readonly ComponentActivity _activity;
private readonly ActivityResultLauncher _requestPermissionLauncher; private Action<bool>? _pendingPermissionRequestCallback; public PermissionManagingBlazorWebChromeClient(WebChromeClient blazorWebChromeClient, ComponentActivity activity)
{
_blazorWebChromeClient = blazorWebChromeClient;
_activity = activity;
_requestPermissionLauncher = _activity.RegisterForActivityResult(new ActivityResultContracts.RequestPermission(), this);
} public override void OnCloseWindow(Android.Webkit.WebView? window)
{
_blazorWebChromeClient.OnCloseWindow(window);
_requestPermissionLauncher.Unregister();
} public override void OnGeolocationPermissionsShowPrompt(string? origin, GeolocationPermissions.ICallback? callback)
{
ArgumentNullException.ThrowIfNull(callback, nameof(callback)); RequestPermission(Manifest.Permission.AccessFineLocation, isGranted => callback.Invoke(origin, isGranted, false));
} public override void OnPermissionRequest(PermissionRequest? request)
{
ArgumentNullException.ThrowIfNull(request, nameof(request)); if (request.GetResources() is not { } requestedResources)
{
request.Deny();
return;
} RequestAllResources(requestedResources, grantedResources =>
{
if (grantedResources.Count == 0)
{
request.Deny();
}
else
{
request.Grant(grantedResources.ToArray());
}
});
} private void RequestAllResources(Memory<string> requestedResources, Action<List<string>> callback)
{
if (requestedResources.Length == 0)
{
// No resources to request - invoke the callback with an empty list.
callback(new());
return;
} var currentResource = requestedResources.Span[0];
var requiredPermissions = s_requiredPermissionsByWebkitResource.GetValueOrDefault(currentResource, Array.Empty<string>()); RequestAllPermissions(requiredPermissions, isGranted =>
{
// Recurse with the remaining resources. If the first resource was granted, use a modified callback
// that adds the first resource to the granted resources list.
RequestAllResources(requestedResources[1..], !isGranted ? callback : grantedResources =>
{
grantedResources.Add(currentResource);
callback(grantedResources);
});
});
} private void RequestAllPermissions(Memory<string> requiredPermissions, Action<bool> callback)
{
if (requiredPermissions.Length == 0)
{
// No permissions left to request - success!
callback(true);
return;
} RequestPermission(requiredPermissions.Span[0], isGranted =>
{
if (isGranted)
{
// Recurse with the remaining permissions.
RequestAllPermissions(requiredPermissions[1..], callback);
}
else
{
// The first required permission was not granted. Fail now and don't attempt to grant
// the remaining permissions.
callback(false);
}
});
} private void RequestPermission(string permission, Action<bool> callback)
{
// This method implements the workflow described here:
// https://developer.android.com/training/permissions/requesting#workflow_for_requesting_permissions if (ContextCompat.CheckSelfPermission(_activity, permission) == Permission.Granted)
{
callback.Invoke(true);
}
else if (_activity.ShouldShowRequestPermissionRationale(permission) && s_rationalesByPermission.TryGetValue(permission, out var rationale))
{
new AlertDialog.Builder(_activity)
.SetTitle("Enable app permissions")!
.SetMessage(rationale)!
.SetNegativeButton("No thanks", (_, _) => callback(false))!
.SetPositiveButton("Continue", (_, _) => LaunchPermissionRequestActivity(permission, callback))!
.Show();
}
else
{
LaunchPermissionRequestActivity(permission, callback);
}
} private void LaunchPermissionRequestActivity(string permission, Action<bool> callback)
{
if (_pendingPermissionRequestCallback is not null)
{
throw new InvalidOperationException("Cannot perform multiple permission requests simultaneously.");
} _pendingPermissionRequestCallback = callback;
_requestPermissionLauncher.Launch(permission);
} void IActivityResultCallback.OnActivityResult(Java.Lang.Object isGranted)
{
var callback = _pendingPermissionRequestCallback;
_pendingPermissionRequestCallback = null;
callback?.Invoke((bool)isGranted);
} #region Unremarkable overrides
// See: https://github.com/dotnet/maui/issues/6565
public override JniPeerMembers JniPeerMembers => _blazorWebChromeClient.JniPeerMembers;
public override Bitmap? DefaultVideoPoster => _blazorWebChromeClient.DefaultVideoPoster;
public override Android.Views.View? VideoLoadingProgressView => _blazorWebChromeClient.VideoLoadingProgressView;
public override void GetVisitedHistory(IValueCallback? callback)
=> _blazorWebChromeClient.GetVisitedHistory(callback);
public override bool OnConsoleMessage(ConsoleMessage? consoleMessage)
=> _blazorWebChromeClient.OnConsoleMessage(consoleMessage);
public override bool OnCreateWindow(WebView? view, bool isDialog, bool isUserGesture, Message? resultMsg)
=> _blazorWebChromeClient.OnCreateWindow(view, isDialog, isUserGesture, resultMsg);
public override void OnGeolocationPermissionsHidePrompt()
=> _blazorWebChromeClient.OnGeolocationPermissionsHidePrompt();
public override void OnHideCustomView()
=> _blazorWebChromeClient.OnHideCustomView();
public override bool OnJsAlert(WebView? view, string? url, string? message, JsResult? result)
=> _blazorWebChromeClient.OnJsAlert(view, url, message, result);
public override bool OnJsBeforeUnload(WebView? view, string? url, string? message, JsResult? result)
=> _blazorWebChromeClient.OnJsBeforeUnload(view, url, message, result);
public override bool OnJsConfirm(WebView? view, string? url, string? message, JsResult? result)
=> _blazorWebChromeClient.OnJsConfirm(view, url, message, result);
public override bool OnJsPrompt(WebView? view, string? url, string? message, string? defaultValue, JsPromptResult? result)
=> _blazorWebChromeClient.OnJsPrompt(view, url, message, defaultValue, result);
public override void OnPermissionRequestCanceled(PermissionRequest? request)
=> _blazorWebChromeClient.OnPermissionRequestCanceled(request);
public override void OnProgressChanged(WebView? view, int newProgress)
=> _blazorWebChromeClient.OnProgressChanged(view, newProgress);
public override void OnReceivedIcon(WebView? view, Bitmap? icon)
=> _blazorWebChromeClient.OnReceivedIcon(view, icon);
public override void OnReceivedTitle(WebView? view, string? title)
=> _blazorWebChromeClient.OnReceivedTitle(view, title);
public override void OnReceivedTouchIconUrl(WebView? view, string? url, bool precomposed)
=> _blazorWebChromeClient.OnReceivedTouchIconUrl(view, url, precomposed);
public override void OnRequestFocus(WebView? view)
=> _blazorWebChromeClient.OnRequestFocus(view);
public override void OnShowCustomView(View? view, ICustomViewCallback? callback)
=> _blazorWebChromeClient.OnShowCustomView(view, callback);
public override bool OnShowFileChooser(WebView? webView, IValueCallback? filePathCallback, FileChooserParams? fileChooserParams)
=> _blazorWebChromeClient.OnShowFileChooser(webView, filePathCallback, fileChooserParams);
#endregion
}

3. 文件 MainPage.xaml

添加 x:Name="_blazorWebView"

    <BlazorWebView x:Name="_blazorWebView" HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type shared:App}" />
</BlazorWebView.RootComponents>
</BlazorWebView>

4. 文件 MainPage.xaml.cs

添加

_blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; _blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing;

完整代码:

using LibraryShared;
using Microsoft.AspNetCore.Components.WebView;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using System;
using static Microsoft.Maui.ApplicationModel.Permissions;
#if ANDROID
using AndroidX.Activity;
#endif namespace BlazorMaui
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent(); _blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized;
_blazorWebView.BlazorWebViewInitializing += BlazorWebViewInitializing;
} private void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e)
{
#if ANDROID
if (e.WebView.Context?.GetActivity() is not ComponentActivity activity)
{
throw new InvalidOperationException($"The permission-managing WebChromeClient requires that the current activity be a '{nameof(ComponentActivity)}'.");
} e.WebView.Settings.JavaScriptEnabled = true;
e.WebView.Settings.AllowFileAccess = true;
e.WebView.Settings.MediaPlaybackRequiresUserGesture = false;
e.WebView.Settings.SetGeolocationEnabled(true);
e.WebView.Settings.SetGeolocationDatabasePath(e.WebView.Context?.FilesDir?.Path);
e.WebView.SetWebChromeClient(new PermissionManagingBlazorWebChromeClient(e.WebView.WebChromeClient!, activity));
#endif
} private void BlazorWebViewInitializing(object? sender, BlazorWebViewInitializingEventArgs e)
{
#if IOS || MACCATALYST
e.Configuration.AllowsInlineMediaPlayback = true;
e.Configuration.MediaTypesRequiringUserActionForPlayback = WebKit.WKAudiovisualMediaTypes.None;
#endif
}
}
}

4. 其他更改

由于工程是一个共享库给多端用,先定义了一个接口用于注入服务到页面调用演示功能

    public interface ITools
{
Task<string> CheckPermissionsCamera();
Task<string> TakePhoto(); Task<string> CheckPermissionsLocation();
Task<string> GetCachedLocation(); Task<string> GetCurrentLocation(); Task<string> CheckMock(); double DistanceBetweenTwoLocations(); void ShowSettingsUI();
string GetAppInfo();
}

调用MAUI的API功能 BlazorMaui/Services/TestService.cs

#if WINDOWS
using Windows.Storage;
#endif
#if ANDROID
using Android.Webkit;
#endif
using BlazorShared.Services;
using System.Security.Permissions; namespace LibraryShared
{
public class TestService : ITools
{
public string GetAppInfo() {
//读取应用信息
string name = AppInfo.Current.Name;
string package = AppInfo.Current.PackageName;
string version = AppInfo.Current.VersionString;
string build = AppInfo.Current.BuildString;
return $"{name},{version}.{build}";
} public void ShowSettingsUI()
{
//显示应用设置
AppInfo.Current.ShowSettingsUI();
} public async Task<string> CheckPermissionsCamera()
{
//检查权限的当前状态
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.Camera>(); //请求权限
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
} return status.ToString();
}
public async Task<string> CheckPermissionsLocation()
{
//检查权限的当前状态
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.LocationWhenInUse>(); //请求权限
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.LocationWhenInUse>();
} return status.ToString();
}
/// <summary>
/// 拍照
/// CapturePhotoAsync调用该方法以打开相机,让用户拍照。 如果用户拍照,该方法的返回值将是非 null 值。
/// 以下代码示例使用媒体选取器拍摄照片并将其保存到缓存目录:
/// </summary>
public async Task<string> TakePhoto()
{
await CheckPermissionsCamera(); if (MediaPicker.Default.IsCaptureSupported)
{
FileResult photo = await MediaPicker.Default.CapturePhotoAsync(); if (photo != null)
{
// save the file into local storage
string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName); using Stream sourceStream = await photo.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(localFilePath); await sourceStream.CopyToAsync(localFileStream);
return localFilePath;
}
return "photo null"; } return null;
} /// <summary>
/// 获取最后一个已知位置, 设备可能已缓存设备的最新位置。
/// 使用此方法 GetLastKnownLocationAsync 访问缓存的位置(如果可用)。
/// 这通常比执行完整位置查询更快,但可能不太准确。
/// 如果不存在缓存位置,此方法将 null返回 。
/// </summary>
/// <returns></returns>
public async Task<string> GetCachedLocation()
{
await CheckPermissionsLocation();
string result = null;
try
{
Location location = await Geolocation.Default.GetLastKnownLocationAsync(); if (location != null)
{
result = $"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}";
Console.WriteLine(result);
return result;
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
result = $"not supported on device, {fnsEx.Message}";
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
result = $"not enabled on device, {fneEx.Message}";
}
catch (PermissionException pEx)
{
// Handle permission exception
result = $"permission, {pEx.Message}";
}
catch (Exception ex)
{
// Unable to get location
result = $"Unable to get location, {ex.Message}";
} return result ?? "None";
} private CancellationTokenSource _cancelTokenSource;
private bool _isCheckingLocation; /// <summary>
/// 获取当前位置
/// 虽然检查设备 的最后已知位置 可能更快,但它可能不准确。
/// 使用该方法 GetLocationAsync 查询设备的当前位置。
/// 可以配置查询的准确性和超时。
/// 最好是使用 GeolocationRequest 和 CancellationToken 参数的方法重载,
/// 因为可能需要一些时间才能获取设备的位置。
/// </summary>
/// <returns></returns>
public async Task<string> GetCurrentLocation()
{
await CheckPermissionsLocation();
string result = null;
try
{
_isCheckingLocation = true; GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10)); _cancelTokenSource = new CancellationTokenSource(); #if IOS
//从 iOS 14 开始,用户可能会限制应用检测完全准确的位置。
//该 Location.ReducedAccuracy 属性指示位置是否使用降低的准确性。
//若要请求完全准确性,请将 GeolocationRequest.RequestFullAccuracy 属性设置为 true
request.RequestFullAccuracy = true;
#endif Location location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token); if (location != null)
{
result = $"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}";
Console.WriteLine(result);
return result;
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Handle not supported on device exception
result = $"not supported on device, {fnsEx.Message}";
}
catch (FeatureNotEnabledException fneEx)
{
// Handle not enabled on device exception
result = $"not enabled on device, {fneEx.Message}";
}
catch (PermissionException pEx)
{
// Handle permission exception
result = $"permission, {pEx.Message}";
}
catch (Exception ex)
{
// Unable to get location
result = $"Unable to get location, {ex.Message}";
}
finally
{
_isCheckingLocation = false;
}
return result ?? "None";
}
}
}

MauiProgram.cs文件注入

builder.Services.AddSingleton<ITools, TestService>();

razor

        <Button Text="定位权限" OnClick="检查定位权限" />
<span>@定位权限</span><br/><br/>
<Button Text="摄像机权限" OnClick="检查摄像机权限" />
<span>@摄像机权限</span><br /><br />
<Button Text="定位" OnClick="获取定位" />
<span>@Locations</span><br /><br />
<Button Text="TakePhoto" OnClick="TakePhoto" />
<span>@PhotoFilename</span><br /><br />
<Button Text="ShowSettings" OnClick="ShowSettingsUI" />
<span>@version</span><br /><br /> @code{
[Inject] protected ITools Tools { get; set; } private string Locations;
private string PhotoFilename;
private string version;
private string 定位权限;
private string 摄像机权限; async Task 获取定位() => Locations = await Tools.GetCurrentLocation();
async Task TakePhoto() => PhotoFilename = await Tools.TakePhoto();
async Task 检查定位权限() => 定位权限 = await Tools.CheckPermissionsLocation();
async Task 检查摄像机权限() => 摄像机权限 = await Tools.CheckPermissionsCamera();
void ShowSettingsUI() => Tools.ShowSettingsUI();
}

最终效果


项目地址

https://github.com/densen2014/BlazorMaui

https://gitee.com/densen2014/BlazorMaui

参考资料

Permissions

https://docs.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/permissions?tabs=android

Geolocation

https://docs.microsoft.com/en-us/dotnet/maui/platform-integration/device/geolocation?tabs=windows

MauiBlazorPermissionsExample

https://github.com/MackinnonBuck/MauiBlazorPermissionsExample

关联项目

FreeSql QQ群:4336577、8578575、52508226

BA & Blazor QQ群:795206915、675147445

知识共享许可协议

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名AlexChow(包含链接: https://github.com/densen2014 ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

AlexChow

今日头条 | 博客园 | 知乎 | Gitee | GitHub

MAUI Blazor 权限经验分享 (定位,使用相机)的更多相关文章

  1. 【原创经验分享】WCF之消息队列

    最近都在鼓捣这个WCF,因为看到说WCF比WebService功能要强大许多,另外也看了一些公司的招聘信息,貌似一些中.高级的程序员招聘,都有提及到WCF这一块,所以,自己也关心关心一下,虽然目前工作 ...

  2. (转)CMOS Sensor的调试经验分享

    CMOS Sensor的调试经验分享 我这里要介绍的就是CMOS摄像头的一些调试经验. 首先,要认识CMOS摄像头的结构.我们通常拿到的是集成封装好的模组,一般由三个部分组成:镜头.感应器和图像信号处 ...

  3. thinkphp开发技巧经验分享

    thinkphp开发技巧经验分享 www.111cn.net 编辑:flyfox 来源:转载 这里我给大家总结一个朋友学习thinkphp时的一些笔记了,从变量到内置模板引擎及系统变量等等的笔记了,同 ...

  4. 线上Linux服务器运维安全策略经验分享

    线上Linux服务器运维安全策略经验分享 https://mp.weixin.qq.com/s?__biz=MjM5NTU2MTQwNA==&mid=402022683&idx=1&a ...

  5. CMOS Sensor的调试经验分享

    转自:http://bbs.52rd.com/forum.php?mod=viewthread&tid=276351 CMOS Sensor的调试经验分享 我这里要介绍的就是CMOS摄像头的一 ...

  6. 关于Java解压文件的一些坑及经验分享(MALFORMED异常)

    文章也已经同步到我的csdn博客: http://blog.csdn.net/u012881584/article/details/72615481 关于Java解压文件的一些坑及经验分享 就在本周, ...

  7. 沉淀,再出发——在Ubuntu Kylin15.04中配置Hadoop单机/伪分布式系统经验分享

    在Ubuntu Kylin15.04中配置Hadoop单机/伪分布式系统经验分享 一.工作准备 首先,明确工作的重心,在Ubuntu Kylin15.04中配置Hadoop集群,这里我是用的双系统中的 ...

  8. CIO必看:跨国集团采购部报表系统的建设经验分享

    CIO必看:跨国集团采购部报表系统的建设经验分享 引言 福耀集团是国内最具规模.技术水平最高.出口量最大的汽车玻璃生产供应商,产品"FY"商标是中国汽车玻璃行业第一个"中 ...

  9. CMOS Sensor的调试经验分享【转】

    转自:https://blog.csdn.net/yapingmcu/article/details/37817727 转自:http://bbs.52rd.com/forum.php?mod=vie ...

  10. Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理

    Lucene底层原理和优化经验分享(1)-Lucene简介和索引原理 2017年01月04日 08:52:12 阅读数:18366 基于Lucene检索引擎我们开发了自己的全文检索系统,承担起后台PB ...

随机推荐

  1. OpenCASCADE Camera

    OpenCASCADE Camera eryar@163.com Abstract. OpenCASCADE introduce a new class Graphic3d_Camera for th ...

  2. php-fpm中启用慢日志配置(用于检测执行较慢的PHP脚本)

    虽然通过nginx accesslog可以记录用户访问某个接口或者网页所消耗的时间,但是不能清晰地追踪到具体哪个位置或者说函数慢,所以通过php-fpm慢日志,slowlog设置可以让我们很好的看见哪 ...

  3. 编写实现连接oracle数据库并返回Connection对象的Java工具类

    只需要实现一个功能,所以只写一个方法,为了方便调用,设为静态方法 package com.jv; import java.sql.Connection; import java.sql.DriverM ...

  4. [HDOJ5950]Recursive sequence(递推,二项展开,矩阵快速幂)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5950 题意:求解递推式f(n)=f(n-1)+2*f(n-2)+n^4. 写了个小东西,不过我的文章里 ...

  5. ADO.NET EF实体框架

    ADO.NET 实体框架概述 随着.NET Framework 3.5 SP1和Visual Studio 2008 SP1的正式发布.ADO.NET 实体框架正式来到开发人员的面前,它使开发人员可以 ...

  6. AC题目简解-dp

    dp类:A - Bridging signals ZOJ 3627 POJ1631 HDU1950给出一个从1-n的数字排列,求最长上升子序列长度.直接说解法吧.新开一个数组d,d[i]表示的是能构成 ...

  7. JavaSE思维导图(七)

  8. JSP运行过程 JSP脚本 静态动态包含 jsp指令 jsp内置对象jsp四大作用域 jsp动作元素 EL表达式 JSTL 设计模式 JSP开发模式 EL内置对象

    Day38 JSP JSP的运行过程具体如下: (1)客户端发出请求,请求访问JSP文件. (2)JSP容器先将JSP文件转换成一个Java源文件(Java Servlet源程序),在转换过程中,如果 ...

  9. Spring系列之手写一个SpringMVC

    目录 Spring系列之IOC的原理及手动实现 Spring系列之DI的原理及手动实现 Spring系列之AOP的原理及手动实现 Spring系列之手写注解与配置文件的解析 引言 在前面的几个章节中我 ...

  10. Linux 小知识翻译 - 「桌面环境」

    这次聊聊桌面环境. 上次聊了 X Window System 相关的内容,虽然令人意外,但X Window System 和桌面环境不是一回事.请大家稍微考虑一下. X Window System 是 ...