欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于Android HTML5 audio autoplay無(wú)效問(wèn)題的解決方案

 更新時(shí)間:2016年09月09日 10:58:33   作者:周文凱  
這篇文章主要介紹了關(guān)于Android HTML5 audio autoplay無(wú)效問(wèn)題的解決方案,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

前言:在android HTML5 開(kāi)發(fā)中有不少人遇到過(guò) audio 標(biāo)簽 autoplay在某些設(shè)備上無(wú)效的問(wèn)題,網(wǎng)上大多是講怎么在js中操作,即在特定的時(shí)刻調(diào)用audio的play()方法,在android上還是無(wú)效。

一、解決方案

在android 4.2添加了允許用戶手勢(shì)觸發(fā)音視頻播放接口,該接口默認(rèn)為 true ,即默認(rèn)不允許自動(dòng)播放音視頻,只能是用戶交互的方式由用戶自己促發(fā)播放。

WebView webView = this.finishActivity(R.id.main_act_webview);
// ... ...
// 其他配置
// ... ...
// 設(shè)置4.2以后版本支持autoPlay,非用戶手勢(shì)促發(fā)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
}

通過(guò)以上配置就可以加載帶有自動(dòng)播放的音視頻啦!

二、 源碼分析

下面我們沿著該問(wèn)題來(lái)窺探下WebView的系統(tǒng)源碼:

1、 通過(guò)getSettings()獲取到的WebView的配置

/**
* Gets the WebSettings object used to control the settings for this
* WebView.
*
* @return a WebSettings object that can be used to control this WebView's
* settings
*/
public WebSettings getSettings() {
checkThread();
return mProvider.getSettings();
}

這里通過(guò)一個(gè) mProvider來(lái)獲取的配置信息,通過(guò)看WebView的源碼,我們可以看到,WebView的所有操作都是交給 mProvider來(lái)進(jìn)行的。

2、 mPeovider是在哪初始化的?

/**
* @hide
*/
@SuppressWarnings("deprecation") // for super() call into deprecated base class constructor.
protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
super(context, attrs, defStyleAttr, defStyleRes);
if (context == null) {
throw new IllegalArgumentException("Invalid context argument");
}
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
Build.VERSION_CODES.JELLY_BEAN_MR2;
checkThread();
ensureProviderCreated();
mProvider.init(javaScriptInterfaces, privateBrowsing);
// Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
CookieSyncManager.setGetInstanceIsAllowed();
}

可以看到有個(gè)ensureProviderCreated()方法,就是在這里創(chuàng)建的mProvider:

private void ensureProviderCreated() {
checkThread();
if (mProvider == null) {
// As this can get called during the base class constructor chain, pass the minimum
// number of dependencies here; the rest are deferred to init().
mProvider = getFactory().createWebView(this, new PrivateAccess());
}
}

OK,到此知道了mProvider是在WebView的構(gòu)造函數(shù)中創(chuàng)建的,并且WebView的所有操作都是交給mProvider進(jìn)行的。

3、 但是這個(gè)mPeovider到底是誰(shuí)派來(lái)的呢?

看下WebViewFactory#getFactory()做了什么操作:

static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID) {
throw new UnsupportedOperationException(
"For security reasons, WebView is not allowed in privileged processes");
}
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try {
Class<WebViewFactoryProvider> providerClass = getProviderClass();
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
try {
sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
.newInstance(new WebViewDelegate());
if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
return sProviderInstance;
} catch (Exception e) {
Log.e(LOGTAG, "error instantiating provider", e);
throw new AndroidRuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
StrictMode.setThreadPolicy(oldPolicy);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
}
}

可見(jiàn)在23行返回了sProviderInstance, 是由 providerClass 通過(guò)反射創(chuàng)建的,15行中通過(guò)getProviderClass() 得到了providerClass.

private static Class<WebViewFactoryProvider> getProviderClass() {
try {
// First fetch the package info so we can log the webview package version.
sPackageInfo = fetchPackageInfo();
Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
loadNativeLibrary();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {
return getChromiumProviderClass();
} catch (ClassNotFoundException e) {
Log.e(LOGTAG, "error loading provider", e);
throw new AndroidRuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (MissingWebViewPackageException e) {
// If the package doesn't exist, then try loading the null WebView instead.
// If that succeeds, then this is a device without WebView support; if it fails then
// swallow the failure, complain that the real WebView is missing and rethrow the
// original exception.
try {
return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
} catch (ClassNotFoundException e2) {
// Ignore.
}
Log.e(LOGTAG, "Chromium WebView package does not exist", e);
throw new AndroidRuntimeException(e);
}
}

主要的 14行 返回了一個(gè) getChromiumProviderClass(); 是不是有點(diǎn)熟悉,沒(méi)錯(cuò)Android在4.4開(kāi)始使用強(qiáng)大的Chromium替換掉了原來(lái)的WebKit。來(lái)看下這個(gè)getChromiumProviderClass()。

// throws MissingWebViewPackageException
private static Class<WebViewFactoryProvider> getChromiumProviderClass()
throws ClassNotFoundException {
Application initialApplication = AppGlobals.getInitialApplication();
try {
// Construct a package context to load the Java code into the current app.
Context webViewContext = initialApplication.createPackageContext(
sPackageInfo.packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
initialApplication.getAssets().addAssetPath(
webViewContext.getApplicationInfo().sourceDir);
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
try {
return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,
clazzLoader);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (PackageManager.NameNotFoundException e) {
throw new MissingWebViewPackageException(e);
}
}

最后找到了這個(gè) CHROMIUM_WEBVIEW_FACTORY, 可以看到在 WebViewFactory 中的定義:

private static final String CHROMIUM_WEBVIEW_FACTORY =
"com.android.webview.chromium.WebViewChromiumFactoryProvider";

回答2小節(jié)的mProvider的初始化,在WebViewChromiumFactoryProvider 的 createWebView(…) 中進(jìn)行了mProvider的初始化:

@Override
public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) {
WebViewChromium wvc = new WebViewChromium(this, webView, privateAccess);
synchronized (mLock) {
if (mWebViewsToStart != null) {
mWebViewsToStart.add(new WeakReference<WebViewChromium>(wvc));
}
}
ResourceProvider.registerResources(webView.getContext());
return wvc;
}

OK,到這里就真正找到了mProvider 的真正初始化位置,其實(shí)它就是一個(gè)WebViewChromium,不要忘了我們?yōu)槭裁促M(fèi)這么大勁找mProvider,其實(shí)是為了分析 webView.getSettings(),這樣就回到了第一小節(jié),通過(guò)getSettings()獲取到的WebView的配置。

4、 Settings的初始化

通過(guò)第一小節(jié),我們知道Settings是mProvider的一個(gè)變量,要想找到Settings就要到 WebViewChromium 來(lái)看下:

@Override
public WebSettings getSettings() {
return mWebSettings;
}

接下來(lái)就是Settings初始化的地方啦

@Override
// BUG=6790250 |javaScriptInterfaces| was only ever used by the obsolete DumpRenderTree
// so is ignored. TODO: remove it from WebViewProvider.
public void init(final Map<String, Object> javaScriptInterfaces,
final boolean privateBrowsing) {
if (privateBrowsing) {
mFactory.startYourEngines(true);
final String msg = "Private browsing is not supported in WebView.";
if (mAppTargetSdkVersion >= Build.VERSION_CODES.KITKAT) {
throw new IllegalArgumentException(msg);
} else {
Log.w(TAG, msg);
TextView warningLabel = new TextView(mWebView.getContext());
warningLabel.setText(mWebView.getContext().getString(
com.android.internal.R.string.webviewchromium_private_browsing_warning));
mWebView.addView(warningLabel);
}
}
// We will defer real initialization until we know which thread to do it on, unless:
// - we are on the main thread already (common case),
// - the app is targeting >= JB MR2, in which case checkThread enforces that all usage
// comes from a single thread. (Note in JB MR2 this exception was in WebView.java).
if (mAppTargetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mFactory.startYourEngines(false);
checkThread();
} else if (!mFactory.hasStarted()) {
if (Looper.myLooper() == Looper.getMainLooper()) {
mFactory.startYourEngines(true);
}
}
final boolean isAccessFromFileURLsGrantedByDefault =
mAppTargetSdkVersion < Build.VERSION_CODES.JELLY_BEAN;
final boolean areLegacyQuirksEnabled =
mAppTargetSdkVersion < Build.VERSION_CODES.KITKAT;
mContentsClientAdapter = new WebViewContentsClientAdapter(mWebView);
mWebSettings = new ContentSettingsAdapter(new AwSettings(
mWebView.getContext(), isAccessFromFileURLsGrantedByDefault,
areLegacyQuirksEnabled));
mRunQueue.addTask(new Runnable() {
@Override
public void run() {
initForReal();
if (privateBrowsing) {
// Intentionally irreversibly disable the webview instance, so that private
// user data cannot leak through misuse of a non-privateBrowing WebView
// instance. Can't just null out mAwContents as we never null-check it
// before use.
destroy();
}
}
});
}

在第39行進(jìn)行了 mWebSettings 的初始化,原來(lái)是 ContentSettingsAdapter。

5、 setMediaPlaybackRequiresUserGesture() 分析

經(jīng)過(guò)以上我們隊(duì)Google大神的膜拜,我們找到了mWebSettings,下面來(lái)看下 setMediaPlaybackRequiresUserGesture方法:

@Override
public void setMediaPlaybackRequiresUserGesture(boolean require) {
mAwSettings.setMediaPlaybackRequiresUserGesture(require);
}

好吧,又是調(diào)用的 mAwSettings 的 setMediaPlaybackRequiresUserGesture 方法,那 mAwSettings 是什么呢?

public ContentSettingsAdapter(AwSettings awSettings) {
mAwSettings = awSettings;
}

原來(lái)是在構(gòu)造函數(shù)中注入的,回到第4小節(jié)的最后,這里 new 了一個(gè)AwSettings。

mWebSettings = new ContentSettingsAdapter(new AwSettings(
mWebView.getContext(), isAccessFromFileURLsGrantedByDefault,
areLegacyQuirksEnabled));

那么久來(lái) AwSettings 中看下 setMediaPlaybackRequiresUserGesture 吧:

該類位于系統(tǒng)源碼 external/​chromium_org/​android_webview/​java/​src/​org/​chromium/​android_webview/​AwSettings.java

/**
* See {@link android.webkit.WebSettings#setMediaPlaybackRequiresUserGesture}.
*/
public void setMediaPlaybackRequiresUserGesture(boolean require) {
synchronized (mAwSettingsLock) {
if (mMediaPlaybackRequiresUserGesture != require) {
mMediaPlaybackRequiresUserGesture = require;
mEventHandler.updateWebkitPreferencesLocked();
}
}
}

可以看到這里只是給一個(gè)變量 mMediaPlaybackRequiresUserGesture 設(shè)置了值,然后看到下面一個(gè)方法,豁然開(kāi)朗:

@CalledByNative
private boolean getMediaPlaybackRequiresUserGestureLocked() {
return mMediaPlaybackRequiresUserGesture;
}

該方法是由JNI層調(diào)用的,external/​chromium_org/​android_webview/native/aw_settings.cc 中我們看到了:

web_prefs->user_gesture_required_for_media_playback =
Java_AwSettings_getMediaPlaybackRequiresUserGestureLocked(env, obj);

可見(jiàn)在內(nèi)核中去調(diào)用該接口,判斷是否允許音視頻的自動(dòng)播放。

以上所述是小編給大家介紹的關(guān)于Android HTML5 audio autoplay無(wú)效問(wèn)題的解決方案,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

最新評(píng)論