Writing Android Native View Based on TBS to provide a summary of React Navtive usage
Integration process of TBS
1. Download SDK and place jar packages in app/libs / directory (Project presentation)
2. Copy liblbs.so in Demo Project to main/jniLibs/armeabi/directory
3. Add NDK support in build.gradle
ndk { abiFilters "armeabi-v7a", "x86", "armeabi" }
4. Adding the required permissions
<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" />
Writing of TBS Android native plug-in
- Provide React Native usage in native module mode
How to write RN native plug-ins is not demonstrated here, you can refer to the official website.
In fact, it's the RN end that jumps to the native Activity loading TBS view.
In the project, TbsReaderView is used to load PDF, Word and other files, WebView is used to load videos and SDK is used to play videos.
1. Initialization of Tbs
//Initialization in onCreate() method of MainApplication private void initTBS() { //Initialize the X5 kernel QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() { @Override public void onCoreInitFinished() { //The x5 kernel initialization completes the callback interface, which calls back and indicates that x5 has been loaded. It may fail to load the x5 kernel and switch to the system kernel under special circumstances. Log.e("QbSdk","onCoreInitFinished:"); } @Override public void onViewInitFinished(boolean result) { //The callback of the x5 kernel initialization indicates that the x5 kernel was loaded successfully for true, otherwise it means that the x5 kernel failed to load and will automatically switch to the system kernel. Log.e("QbSdk","Successful loading of the kernel:"+ result); isLoadX5 = result; } }); // Monitor downloads of the X5 kernel QbSdk.setTbsListener(new TbsListener() { @Override public void onDownloadFinish(int i) { Log.e("QbSdk","onDownloadFinish:"+ i); } @Override public void onInstallFinish(int i) { Log.e("QbSdk","onInstallFinish:"+ i); } @Override public void onDownloadProgress(int i) { Log.e("QbSdk","onDownloadProgress:"+ i); } }); }
2. Using TbsReaderView to load PDF, World and other files in activity
public class TbsActivity extends AppCompatActivity { private TbsReaderView mTbsReaderView; private RelativeLayout mRelativeLayout; private TextView mTextView; private Button backButton; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tbs); mTbsReaderView = new TbsReaderView(this, new TbsReaderView.ReaderCallback() { @Override public void onCallBackAction(Integer integer, Object o, Object o1) { Log.i("onCallBackAction", "o = " + o + " o1 = " + o1); } }); backButton = findViewById(R.id.tbs_back_button); backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); mRelativeLayout = findViewById(R.id.tbsRelativeView); mRelativeLayout.addView(mTbsReaderView,new RelativeLayout.LayoutParams(-1,-1)); mTextView = findViewById(R.id.tbs_title); String localPath = getIntent().getStringExtra("localPath"); displayFile(localPath); mTextView.setText(FileUtils.fileName(localPath)); } private void displayFile(String localPath) { if (!MainApplication.getInstance().getLoadX5()) { Toast.makeText(TbsActivity.this, "Load X5 Kernel failure", Toast.LENGTH_SHORT).show(); return; } if (localPath.length() == 0 || localPath == null) { Toast.makeText(TbsActivity.this, "Failed to obtain filename", Toast.LENGTH_SHORT).show(); return; } Bundle bundle = new Bundle(); String tempPath = Environment.getExternalStorageDirectory() .getPath(); String filePath = localPath; int index = localPath.indexOf("file:///"); if (index != -1) { filePath = localPath.substring(localPath.indexOf("/") + 2); } Log.e("displayFile - filePath", filePath); Boolean isExists = FileUtils.fileIsExists(filePath); if (!isExists) { Toast.makeText(TbsActivity.this, "Local file does not exist", Toast.LENGTH_SHORT).show(); return; } bundle.putString("filePath", filePath); bundle.putString("tempPath",tempPath); boolean result = mTbsReaderView.preOpen(parseFormat(localPath), false); if (result) { mTbsReaderView.openFile(bundle); } else { Toast.makeText(TbsActivity.this, "Preview file failed", Toast.LENGTH_SHORT).show(); } } private String parseFormat(String fileName) { return fileName.substring(fileName.lastIndexOf(".") + 1); } @Override protected void onDestroy() { super.onDestroy(); mTbsReaderView.onStop(); } }
3. Playing video with WebView, but this way will not play automatically
public class TbsVideoActivity extends AppCompatActivity { private X5WebView webView; private Button backButton; private TextView mTextView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tbs_video); webView = findViewById(R.id.video_webView); mTextView = findViewById(R.id.tbs_title); backButton = findViewById(R.id.tbs_back_button); backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); initDisplayVideoView(); } private void initDisplayVideoView() { if (!MainApplication.getInstance().getLoadX5()) { Toast.makeText(TbsVideoActivity.this, "Load X5 Kernel failure", Toast.LENGTH_SHORT).show(); return; } String localPath = getIntent().getStringExtra("localPath"); mTextView.setText(FileUtils.fileName(localPath)); Log.i("TbsVideoActivity", localPath); if (localPath.length() == 0 || localPath == null) { Toast.makeText(TbsVideoActivity.this, "Failed to obtain filename", Toast.LENGTH_SHORT).show(); return; } Boolean isExists = FileUtils.fileIsExists(localPath); if (!isExists) { Toast.makeText(TbsVideoActivity.this, "Local file does not exist", Toast.LENGTH_SHORT).show(); return; } if (localPath.indexOf("file://") == -1) { localPath = "file://" + localPath; } webView.loadUrl(localPath); getWindow().setFormat(PixelFormat.TRANSLUCENT); webView.getView().setOverScrollMode(View.OVER_SCROLL_ALWAYS); webView.setWebChromeClient(new com.tencent.smtt.sdk.WebChromeClient()); } @Override protected void onDestroy() { super.onDestroy(); if (webView != null) { webView.onPause(); } } }
4. Automatically Playing Video with Activity in SDK
activyty To configure <activity android:name="com.tencent.smtt.sdk.VideoActivity" android:alwaysRetainTaskState="true" android:configChanges="orientation|screenSize|keyboardHidden" android:exported="false" android:launchMode="singleTask"> <intent-filter> <action android:name="com.tencent.smtt.tbs.video.PLAY" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> // Play video public void autoPreviewVideoByTBSForRN(String localPath) { Log.i("autoPlayVideo", localPath); if (localPath.length() == 0) { Toast.makeText(mReactContext, "Failed to obtain filename", Toast.LENGTH_SHORT).show(); return; } Boolean isExists = FileUtils.fileIsExists(localPath); if (!isExists) { Toast.makeText(mReactContext, "Local file does not exist", Toast.LENGTH_SHORT).show(); return; } if (localPath.indexOf("file://") == -1) { localPath = "file://" + localPath; } if (TbsVideo.canUseTbsPlayer(mReactContext)){ //Play video TbsVideo.openVideo(mReactContext, localPath); } else { Toast.makeText(mReactContext, "Broadcast failure", Toast.LENGTH_SHORT).show(); } }
- Preview PDF, Word and other files by providing native View view
Problem encountered: Native View cannot be refreshed in RN and onLayout overridden in ReactRootView is empty implementation
public class TbsPreviewView extends RelativeLayout { private TbsReaderView mTbsReaderView; private RelativeLayout mRelativeLayout; private Context mContext; public String localPath; public TbsPreviewView(Context context) { super(context); LayoutInflater.from(context).inflate(R.layout.tbs_preview, this); mContext = context; mRelativeLayout = findViewById(R.id.tbs_preview_relativeView); mTbsReaderView = new TbsReaderView(MainApplication.getInstance().getMainActivity(), new TbsReaderView.ReaderCallback() { @Override public void onCallBackAction(Integer integer, Object o, Object o1) { Log.i("onCallBackAction", "o = " + o + " o1 = " + o1); } }); mRelativeLayout.addView(mTbsReaderView,new RelativeLayout.LayoutParams(-1,-1)); } public void setLocalPath(String localPath) { this.localPath = localPath; if (localPath.length() != 0) { Log.i("TbsReaderView", "displayFile setLocalPath = " + localPath); displayFile(localPath); } } private void displayFile(String localPath) { if (!MainApplication.getInstance().getLoadX5()) { Toast.makeText(mContext, "Load X5 Kernel failure", Toast.LENGTH_SHORT).show(); return; } if (localPath.length() == 0) { Toast.makeText(mContext, "Failed to obtain filename", Toast.LENGTH_SHORT).show(); return; } Bundle bundle = new Bundle(); String tempPath = Environment.getExternalStorageDirectory() .getPath(); String filePath = localPath; int index = localPath.indexOf("file:///"); if (index != -1) { filePath = localPath.substring(localPath.indexOf("/") + 2); } Log.e("displayFile - filePath", filePath); Boolean isExists = FileUtils.fileIsExists(filePath); if (!isExists) { Toast.makeText(mContext, "Local file does not exist", Toast.LENGTH_SHORT).show(); return; } bundle.putString("filePath", filePath); bundle.putString("tempPath",tempPath); boolean result = mTbsReaderView.preOpen(parseFormat(localPath), false); if (result) { mTbsReaderView.openFile(bundle); } else { Toast.makeText(mContext, "Preview file failed", Toast.LENGTH_SHORT).show(); } } private String parseFormat(String fileName) { return fileName.substring(fileName.lastIndexOf(".") + 1); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); Log.i("TbsPreviewView", "onDetachedFromWindow"); if (mTbsReaderView != null) { mTbsReaderView.onStop(); } } /** * * Empty implementation in ReactRootView * protected void onLayout(boolean changed, int left, int top, int right, int bottom) {} * */ @Override public void requestLayout() { super.requestLayout(); if (getWidth() > 0 && getHeight() > 0) { int w = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY); int h = MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY); measure(w, h); layout(getPaddingLeft() + getLeft(), getPaddingTop() + getTop(), getWidth() + getPaddingLeft() + getLeft(), getHeight() + getPaddingTop() + getTop()); } } } // RN end use render() { return ( <View style={styles.container}> <TbsPreviewView style={styles.previewViewStyle} localPath={this.state.localPath}/> </View> ) }
Problems in this way: Preview related documents will have the problem of failure exception, instability, it is better to use RN to jump native Activity.