The preface
Tencent browsing services powerful, stable, integration is relatively simple, stronger than the native WebView. The most important is to browse PDF, Word documents, many convenient. This article is not mainly about integration, although integration is much longer, but the most important purpose of my writing is the problems ENCOUNTERED in the actual use of the process, and the solutions. If you’ve already integrated successfully, look directly at other issues and you may have what you want.
TBS document access address
Basically, there is no problem to access according to this document, but when opening the local file, there is still a small problem, because there is no description in the document.
Basic configuration
Android Studio is now used for Android development, so you only need to add dependencies to build. Gradle app. This article is dated 2020/9/30, and the latest id version is as follows
api 'com.tencent.tbs.tbssdk:sdk:43939'
Copy the code
Access configuration
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Copy the code
Confuse configuration
-dontwarn dalvik.**
-dontwarn com.tencent.smtt.**
-keep class com.tencent.smtt. * *{*; } -keepclass com.tencent.tbs. * *{*; }Copy the code
Abnormal Reporting Configuration
To improve the webview stability of the partner, discover and resolve X5-related problems in a timely manner, bring x5 kernel information to the client when an exception such as a crash occurs and reports to the server. X5 kernel anomaly information acquisition interface for: com. Tencent. SMTT. SDK. WebView. GetCrashExtraMessage (context). Take bugly log reporting as an example:
UserStrategy strategy = newUserStrategy (appContext); strategy.setCrashHandleCallback(newCrashReport.CrashHandleCallback() {public Map<String, String> onCrashHandleStart(
int crashType,
String errorType,
String errorMessage,
String errorStack) {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
String x5CrashInfo = com.tencent.smtt.sdk.WebView.getCrashExtraMessage(appContext);
map.put("x5crashInfo", x5CrashInfo);
returnThe map; }@Override
public byte[] onCrashHandleStart2GetExtraDatas(
int crashType,
String errorType,
String errorMessage,
String errorStack) {
try {
return "Extra data.".getBytes("UTF-8");
} catch (Exception e) {
return null; }}}); CrashReport.initCrashReport(appContext, APPID,true, strategy);
Copy the code
Initial cold start optimization
When the TBS kernel is used and loaded for the first time, the ART virtual machine will convert Dex file into Oat. This process is triggered by the bottom layer of the system and takes a long time, which is easy to cause ANR problem. The solution is to use the “Dex2OAT optimization scheme” of TBS.
(1). Set and enable the optimization scheme
Do the following configuration before calling TBS to initialize and create a WebView
HashMap map = new HashMap();
map.put(TbsCoreSettings.TBS_SETTINGS_USE_SPEEDY_CLASSLOADER, true);
map.put(TbsCoreSettings.TBS_SETTINGS_USE_DEXLOADER_SERVICE, true);
QbSdk.initTbsSettings(map);
Copy the code
(2). Add a Service declaration
- Added optimization to the Service declaration when the kernel is first loaded in androidmanifest.xml.
- The Service triggers and performs the Dex2OAT task only when the TBS kernel loads Dex for the first time, and the task ends automatically after completion.
<service
android:name="com.tencent.smtt.export.external.DexClassLoaderProviderService"
android:label="dexopt"
android:process=":dexopt" >
</service>
Copy the code
Initialize the X5 kernel
QbSdk.setDownloadWithoutWifi(true);
QbSdk.setTbsListener(
new TbsListener() {
@Override
public void onDownloadFinish(int i) {
Log.d("QbSdk"."OnDownloadFinish --> Download X5 kernel completed:" + i);
}
@Override
public void onInstallFinish(int i) {
Log.d("QbSdk"."OnInstallFinish --> X5 installation progress:" + i);
}
@Override
public void onDownloadProgress(int i) {
Log.d("QbSdk"."OnDownloadProgress --> Download X5 kernel progress:"+ i); }}); QbSdk.PreInitCallback cb =new QbSdk.PreInitCallback() {
@Override
public void onViewInitFinished(boolean arg0) {
Table x5 is successfully loaded. If table x5 fails to load, the system will automatically switch to the system kernel.
Log.d("QbSdk"."Kernel loading" + arg0);
}
@Override
public void onCoreInitFinished(a) {}};// X5 kernel initializes the interface
QbSdk.initX5Environment(getApplicationContext(), cb);
Copy the code
The WebView access
In fact, this part is to replace all the things of the native WebView with the package name of com.tencent. SMTT, which is introduced in the TBS document, but not explained here.
File access
That should be the point.
Open the file inside the APP
TbsReaderView inside TBS is used to open the APP internally, which is shown in my Demo. You can watch my TBSDemo
One problem you might encounter is when you open a file with TbsReaderView on one page, but when you jump to another page, you still open the file with TbsReaderView, and it shows that the file is still loading. The only way I can think of so far is to reinitialize TbsReaderView at onResume,
mFlRoot.removeAllViews();
mTbsReaderView = new TbsReaderView( mActivity, readerCallback );
mTbsReaderView.setBackgroundColor(
ContextCompat.getColor( mActivity, R.color.color_white ) );
mFlRoot.addView(
mTbsReaderView,
new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT ) );
Copy the code
Calls the code that opens the file
private void openFile(a) {
String extensionName = FileUtils.getFileType( mReaderFile.getPath() );
Bundle bundle = new Bundle();
bundle.putString( TbsReaderView.KEY_FILE_PATH, mReaderFile.getPath() );
bundle.putString( TbsReaderView.KEY_TEMP_PATH, FileUtils.createCachePath( mActivity ) );
boolean result = mTbsReaderView.preOpen( extensionName, false );
MLog.i( "addTbsReaderView: " + result );
if( result ) { mTbsReaderView.openFile( bundle ); }}Copy the code
Call it during onPause.
if( mTbsReaderView ! =null ) {
mTbsReaderView.onStop();
}
Copy the code
Open the file using TBS openFileReader
Supported formats
TBS has provided 9 mainstream file formats to open locally, if you need to use more advanced capabilities please use QQ browser to open files
- Access TBS can support open file formats:
Doc, docx, PPT, PPTX, XLS, XLSX, PDF, TXT, epub
- Call QQ browser to open:
Rar (including encryption format), ZIP (including encryption format), tar, BZ2, gz, 7Z (including encryption format), doc, DOCx, PPT, PPTX, XLS, XLSX, TXT, PDF, epub, CHM, HTML/HTM, XML, MHT, URL, INI, log, and B At, PHP, JS, LRC, JPG, JPEG, PNG, GIF, BMP, TIFF, webP, MP3, M4A, AAC, AMR, WAV, OGG, MID, RA, WMA, MPGA, APE, FLAC
The interface is introduced
public static int openFileReader( Context context, String filePath, HashMap
extraParams, ValueCallback
callback )
,>
Copy the code
(1) This method is under the Qbsdk class
(2) After the call, the priority of the QQ browser to open the file. If not installed QQ browser, X5 kernel down version QB open text. If the system kernel is used, then the file reader pop-up box is raised.
(3) Only local files can be opened temporarily
Context: Invokes the miniQB Activity context. This parameter can only be an Activity context, not an Application context. FilePath: indicates the filePath. Format for android local storage path format, for example: / sdcard/Download/XXX. Doc. file:/// is not supported. Online files are not supported. ExtraParams: An extension to miniQB. This parameter is optional. If no special configuration is required, null can be passed by default. ValueCallback: Provides callback notifications to callers when miniQB is turned on or off for the application layer to handle accordingly. You can terminate your process when the following callback occurs, saving memory. The main callback values are as follows:
filepath error
TbsReaderDialogClosed
default browser
fileReaderClosed
Copy the code
The return value:
1: Open it with QQ browser2: Open with MiniQB3: Turn up the reader popbox -1: Failed to open filePath for emptyCopy the code
public static void closeFileReader(Context context)
Copy the code
Close files to open UI and clean up memory usage.
Access to the sample
HashMap<String, String> params = new HashMap<String, String>();
params.put("style"."1");
params.put("local"."true");
params.put("memuData", jsondata); QbSdk. OpenFileReader (CTX, "/ sdcard/XXX. Doc," params, callback);Copy the code
You think you’re gonna be able to open it up and browse it? Still too young, Tencent documentation will not give up until you dig holes, let you develop the habit of solving their own bugs. When you happily call qbsdK. openFileReade, log will give you an error.
QbSdk: Must declare com.tencent.smtt.utils.FileProvider in AndroidManifest above Android 7.0,please view document in x5.tencent.com
Copy the code
This means that you can add a provider to androidmanifest.xml because Android version 7.0 has changed the way files are accessed. That’s when you add it.
Start by adding the following code to androidmanifest.xml
<provider
android:name="com.tencent.smtt.utils.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/x5webview_file_paths" />
</provider>
Copy the code
Then create an XML folder under the RES folder, skip if you have one, and create a x5webview_file_paths.xml file with the following contents
<? xml version="1.0" encoding="utf-8"? > <paths> <external-path name="sdcard" path="."/>
</paths>
Copy the code
Run again, there should be no problem, here need to ensure that the path of the file is to be right, because you can not browse online, can only download the file to the local call. I looked at the document for a long time but failed to find this mistake. Later, I searched online and found the following code.
<provider
android:name="com.tencent.smtt.utils.FileProvider"
android:authorities="The package name. Fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/ XML file name" />
</provider>
Copy the code
Android :authorities=” package name.fileProvider “; / / QbSdk; / / QbSdk; / / Android :authorities=” package name.provider “; this error has been debuggable for a long time.
Other problems
Night mode problem with viewing files using TbsReaderView
When I was in the use of millet 10 ultimate, no adapter deep black pattern, in my APP. Use the Theme is the Theme of the AppCompat. Light. NoActionBar, but millet, 10 or mandatory my APP USES the dark mode. And I found that not just my APP, but every APP on my phone, whether adapted or not, is in dark mode, which is night mode. This led to a problem. When using TbsReaderView to browse the file, the background was black and the font was a little black, which made the document hard to read. Later when I read the source code of TbsReaderView, I found that I could set it to force not to use night mode. Add the following code.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Do not use dark mode
mTbsReaderView.setForceDarkAllowed(false);
}
Copy the code
TargetAPI to 29
If the Android targetSdkVersion is 29, need under application configuration Android: requestLegacyExternalStorage = “true”
Failed to download kernel when targetAPI is Android P?
Kernel download, installation, and availability query need to send requests to the background. Currently, some requests are in HTTP format. When targetAPI is 28, non-HTTPS requests will be blocked, which will cause some kernel functions to be abnormal. You can manually lower targetAPi to 27 and below or add it in the Application tag in your AndroidManifst.xml
android:networkSecurityConfig="@xml/network_security_config"
Copy the code
Add the network_security_config. XML file to the res/ XML directory of app
<? xml version="1.0" encoding="utf-8"? > <network-security-config> <base-config cleartextTrafficPermitted="true" />
</network-security-config>
Copy the code
Kernel initialization takes time. During this period, you need to wait for a while to open the file and browse. Otherwise, the file will fail to be loaded. If loading fails, uninstall and reinstall.
Unable to download or failed to load
QbSdk.setTbsListener(
new TbsListener() {
@Override
public void onDownloadFinish(int i) {
If the download is successful, the errorCode is 100. If the download is successful, the errorCode is 100. If the download is successful, the errorCode is 100
Log.d("QbSdk"."OnDownloadFinish --> Download X5 kernel completed:" + i);
}
@Override
public void onInstallFinish(int i) {
If the installation is successful, the errorCode is 200. If the installation is successful, the errorCode is 200. If the installation is successful, the errorCode is 200
Log.d("QbSdk"."OnInstallFinish --> X5 installation progress:" + i);
}
@Override
public void onDownloadProgress(int i) {
// Download process notification, providing the current download progress [0-100]
Log.d("QbSdk"."OnDownloadProgress --> Download X5 kernel progress:"+ i); }});Copy the code
Above is the download listening, but in practice I often find that onDownloadFinish returns 110, 120, etc. The website says only 100 are successful. As long as it is not 100, the X5 kernel load will definitely fail. But the website doesn’t say how. I have no choice but to find my own way. In very difficult to read the most confused with TBS jar package, even guess take Inner Mongolia I found TbsDownloader startDownload (this); This method. A re-download is possible, but simply re-downloading does not guarantee a successful x5 load. Qbsdk. reset(this); This method. You can reset the configuration of X5. After the APP is killed, it will be re-downloaded and initialized. On the actual line usage is very complex, some people haven’t put APPkill off the download, download didn’t finish, or download is finished loading didn’t finish, so just use TbsDownloader. StartDownload (this); A callback from qbsdK. setTbsListener and a callback from qbsdK. PreInitCallback should be used to determine the download.
QbSdk.PreInitCallback cb =
new QbSdk.PreInitCallback() {
@Override
public void onViewInitFinished(boolean arg0) {
Table x5 is successfully loaded. If table x5 fails to load, the system will automatically switch to the system kernel.
Log.d("QbSdk"."Kernel loading" + arg0);
}
@Override
public void onCoreInitFinished(a) {
// The kernel is initialized
Log.d("QbSdk"."Kernel initialization completed"); }};Copy the code
QbSdk.reset(this); It’s the best way. It’s simple. Check out my demo, which has what I think is a better solution. Of course, still have to use according to their own needs.
Confuse unusable
If obfuscation is used, add the following obfuscation rules
#-optimizationpasses 7#-optimizations ! code/simplification/arithmetic,! field/*,!class/merging/*
-dontoptimize
-dontusemixedcaseclassnames
-verbose
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontwarn dalvik.**
-dontwarn com.tencent.smtt.**
#-overloadaggressively
# ------------------ Keep LineNumbers and properties ---------------- #
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# --------------------------------------------------------------------------
# Addidional for x5.sdk classes for apps
-keep class com.tencent.smtt.export.external.**{
*;
}
-keep class com.tencent.tbs.video.interfaces.IUserStateChangedListener {
*;
}
-keep class com.tencent.smtt.sdk.CacheManager {
public *;
}
-keep class com.tencent.smtt.sdk.CookieManager {
public *;
}
-keep class com.tencent.smtt.sdk.WebHistoryItem {
public *;
}
-keep class com.tencent.smtt.sdk.WebViewDatabase {
public *;
}
-keep class com.tencent.smtt.sdk.WebBackForwardList {
public *;
}
-keep public class com.tencent.smtt.sdk.WebView {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebView$HitTestResult {
public static final <fields>;
public java.lang.String getExtra();
public int getType();
}
-keep public class com.tencent.smtt.sdk.WebView$WebViewTransport {
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebView$PictureListener {
public <fields>;
public <methods>;
}
-keepattributes InnerClasses
-keep public enum com.tencent.smtt.sdk.WebSettings$** {
*;
}
-keep public enum com.tencent.smtt.sdk.QbSdk$** {
*;
}
-keep public class com.tencent.smtt.sdk.WebSettings {
public *;
}
-keepattributes Signature
-keep public class com.tencent.smtt.sdk.ValueCallback {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebViewClient {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.DownloadListener {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebChromeClient {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebChromeClient$FileChooserParams {
public <fields>;
public <methods>;
}
-keep class com.tencent.smtt.sdk.SystemWebChromeClient{
public *;
}
# 1. extension interfaces should be apparent
-keep public class com.tencent.smtt.export.external.extension.interfaces.* {
public protected *;
}
# 2. interfaces should be apparent
-keep public class com.tencent.smtt.export.external.interfaces.* {
public protected *;
}
-keep public class com.tencent.smtt.sdk.WebViewCallbackClient {
public protected *;
}
-keep public class com.tencent.smtt.sdk.WebStorage$QuotaUpdater {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebIconDatabase {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.WebStorage {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.DownloadListener {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.QbSdk {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.QbSdk$PreInitCallback {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.Tbs* {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.LogFileUtils {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.TbsLog {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.TbsLogClient {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {
public <fields>;
public <methods>;
}
# Added for game demos
-keep public class com.tencent.smtt.sdk.TBSGamePlayer {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerClient* {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerClientExtension {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerService* {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.utils.Apn {
public <fields>;
public <methods>;
}
-keep class com.tencent.smtt.** {
*;
}
# end
-keep public class com.tencent.smtt.export.external.extension.proxy.ProxyWebViewClientExtension {
public <fields>;
public <methods>;
}
-keep class MTT.ThirdAppInfoNew {
*;
}
-keep class com.tencent.mtt.MttTraceEvent {
*;
}
# Game related
-keep public class com.tencent.smtt.gamesdk.* {
public protected *;
}
-keep public class com.tencent.smtt.sdk.TBSGameBooter {
public <fields>;
public <methods>;
}
-keep public class com.tencent.smtt.sdk.TBSGameBaseActivity {
public protected *;
}
-keep public class com.tencent.smtt.sdk.TBSGameBaseActivityProxy {
public protected *;
}
-keep public class com.tencent.smtt.gamesdk.internal.TBSGameServiceClient {
public *;
}
#---------------------------------------------------------------------------
#------------------ 下方是android平台自带的排除项,这里不要动 ----------------
-keep public class * extends android.app.Activity{
public <fields>;
public <methods>;
}
-keep public class * extends android.app.Application
{
public <fields>;
public <methods>;
}
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepattributes *Annotation*
-keepclasseswithmembernames class *{
native <methods>;
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#------------------ 下方是共性的排除项目 ----------------
# 方法名中含有“JNI”字符的,认定是Java Native Interface方法,自动排除
# 方法名中含有“JRI”字符的,认定是Java Reflection Interface方法,自动排除
-keepclasseswithmembers class * {
... *JNI*(...);
}
-keepclasseswithmembernames class * {
... *JRI*(...);
}
-keep class **JNI* {*;}
Copy the code
Integrated Demo Address
Welcome to subscribe to my blog, persistence will always see the light.