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

  1. Added optimization to the Service declaration when the kernel is first loaded in androidmanifest.xml.
  2. 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.