前言
最近在做一个电视看版,拿到需求第一想法就是使用hybrid 开发模式,由于没有android开发人员,所以都得我一个完成(2023.02.01)。
demo编写 -> 测试
开始查找资料,创建android项目,加载百度的地址🤦♀,见证结果,公司几个android 8以上的手机都可以,打包apk在电视安装,也没有问题。切换成自己写的前端vue3项目,手机都可以运行,但是安装在电视上面就是白板,一直loading中😭
踩坑中
百度的网页可以运行,自己还特意搭建了nginx静态资源环境,反正各种折腾,猜测应该是用了比较新的语法,开始疯狂百度,查看官网文档,问下群友,最后决定试试集成x5内核腾讯X5服务。吐槽下官网描述的东西太少,资料感觉没怎么更新。
下载官网的demo,上午运行正常,下午怎么安装都内核都安装不成功,而且首次还加载的是sdk内核,需要杀掉进程,再次重启才能运行x5内核。
继续踩坑中。。。
x5下载到本地,内核安装成功则初始化webview,不然首次加载还是sdk内核。
核心代码
MainActivity.java
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
private final String TAG = "MainActivity";
private String[] perms = {
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_NETWORK_STATE};
private int CODE_REQUEST_PERMISSION = 101;
private WebView mWebView;
private TextView mTextView;
private LinearLayout mLinearLayout;
private Timer timer = new Timer();
private int second = 0;
private X5Util x5Util;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout.activity_main);
mTextView = findViewById(id.load_view);
mLinearLayout = findViewById(id.tv_liner);
if (EasyPermissions.hasPermissions(this, perms)) {
startCheck();
} else {
EasyPermissions.requestPermissions(this, "", CODE_REQUEST_PERMISSION, perms);
}
}
/**
* 初始化weview
*/
private void initWebview() {
mWebView = new WebView(this);
//定义子View中两个元素的布局
ViewGroup.LayoutParams vlp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mWebView.setLayoutParams(vlp);
mLinearLayout.addView(mWebView);
WebSettings webSetting = mWebView.getSettings();
webSetting.setAllowContentAccess(true);
webSetting.setAllowFileAccess(true);
webSetting.setTextZoom(100);
webSetting.setCacheMode(WebSettings.LOAD_DEFAULT);
webSetting.setAllowFileAccessFromFileURLs(true);
webSetting.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(this, "x5");
mWebView.setWebChromeClient(new MyChromeClient());
mWebView.setWebViewClient(new MyWebViewClient());
mWebView.loadUrl("xxx.xx.xx"); // 地址
}
/**
* 开始检测x5内核
*/
private void startCheck() {
int version = QbSdk.getTbsVersion(this);
Log.d(TAG, String.valueOf(version));
// 如果没有安装x5内核则进行安装
if (QbSdk.getTbsVersion(this) <= 0) {
// 初始化x5util
x5Util = new X5Util(this, new LocalInstallListener() {
@Override
public void onSuccess() {
timer.cancel();
runOnUiThread(new Runnable() {
@Override
public void run() {
initWebview();
String message = mWebView.getIsX5Core() ? "X5内核: " + QbSdk.getTbsVersion(MainActivity.this) : "SDK系统内核";
Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onError(String message) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "内核安装失败,请重启!", Toast.LENGTH_SHORT).show();
}
});
timer.cancel();
}
});
customTimer();
// 子线程安装内核
new Thread(new Runnable() {
@Override
public void run() {
x5Util.startInstallX5();
}
}).start();//启动线程
} else {
initWebview();
String message = mWebView.getIsX5Core() ? "X5内核: " + QbSdk.getTbsVersion(this) : "SDK系统内核";
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
/**
* 定时器,定时更新状态
*/
private void customTimer() {
timer.schedule(new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
second++;
Log.d(TAG, String.valueOf(second));
mTextView.setText("内核初始化中" + String.valueOf(second) + "s" + "...");
}
});
}
}, 0, 1000);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
if (CODE_REQUEST_PERMISSION == requestCode) {
startCheck();
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "权限获取失败", Toast.LENGTH_SHORT).show();
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/load_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textSize="16dp"
android:text="正在加载内核..."/>
<LinearLayout
android:id="@+id/tv_liner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"></LinearLayout>
</RelativeLayout>
AndroidManifest.xml 权限配置
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
X5Util.java
public class X5Util {
private final String TAG = "X5Util";
/**
* 内核存放的文件夹
*/
private String DIR =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
/**
* 内核版本号
*/
private final int CORE_VERSION = 46141;
/**
* 内核文件名称
*/
private final String CORE_NAME = "tbs_core_046141.apk";
/**
* 内核文件路径
*/
private String CORE_PATH = DIR + File.separator + CORE_NAME;
/**
* 上下文
*/
private Context mContext;
/**
* 离线内核安装监听
*/
private LocalInstallListener mLocalInstallListener;
public X5Util(Context context, LocalInstallListener listener) {
this.mContext = context;
this.mLocalInstallListener = listener;
}
/**
* 是否初始化x5成功
*
* @param context 上下文环境
* @return
*/
public Boolean isInited(Context context) {
int version = QbSdk.getTbsVersion(context);
return version > 0;
}
/**
* 开始安装x5内核
*/
public void startInstallX5() {
/**
* 校验手机abi型号
*/
String abi = Build.CPU_ABI;
//内核型号匹配则直接进行离线内核安装
if ("arm64-v8a".equals(abi)) {
Boolean isExitCore = copyX5Core();
if (isExitCore) {
// 存在内核文件
startInstallX5();
}
} else {
Log.d(TAG, "内核型号不匹配,无法进行本地离线安装内核");
}
}
/**
* 开始安装本地离线内核
*/
private void startInstallX5() {
try {
QbSdk.installLocalTbsCore(mContext, CORE_VERSION, CORE_PATH);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
int version = QbSdk.getTbsVersion(mContext);
if (version > 0) {
timer.cancel();
Log.d(TAG, "循环检验内核版本" + String.valueOf(version));
QbSdk.initX5Environment(mContext, new QbSdk.PreInitCallback() {
@Override
public void onCoreInitFinished() {
Log.d(TAG, "onCoreInitFinished");
}
@Override
public void onViewInitFinished(boolean b) {
Log.d(TAG, "onViewInitFinished_p0=$p0");
}
});
mLocalInstallListener.onSuccess();
} else {
QbSdk.initX5Environment(mContext, new QbSdk.PreInitCallback() {
@Override
public void onCoreInitFinished() {
Log.d(TAG, "onCoreInitFinished");
}
@Override
public void onViewInitFinished(boolean b) {
Log.d(TAG, "onViewInitFinished_p0=$p0");
}
});
Log.d(TAG, "循环检验内核版本" + String.valueOf(version));
}
}
}, 0, 1000);
} catch (Exception e) {
Log.d(TAG, "本地离线内核安装异常,异常信息>" + e.getMessage());
}
}
/**
* 将内核文件拷贝到指定目录
*/
private Boolean copyX5Core() {
File file = null;
try {
// 目录存在,则将assets中的内核文件复制进去
file = new File(CORE_PATH);
Log.d(TAG, "文件路径:" + CORE_PATH);
if (file.exists()) {
// 如果文件存在,为了确保文件完整性,需要删除后重新从项目中复制进去
Log.d(TAG, "删除文件!");
file.delete();
}
// 开始复制文件到指定目录
InputStream ins = mContext.getResources().getAssets().open(CORE_NAME);
Log.d(TAG, "开始读取内核文件");
FileOutputStream fos = new FileOutputStream(file);
Log.d(TAG, "开始拷贝内核文件");
byte[] buffer = new byte[1024];
int count = 0;
// 循环写出
while ((count = ins.read(buffer)) > 0) {
fos.write(buffer, 0, count);
}
fos.close();
ins.close();
Log.d(TAG, "拷贝内核文件完成");
return true;
} catch (Exception e) {
Log.d(TAG, "拷贝内核文件异常,异常信息>" + e.getMessage());
mLocalInstallListener.onError(e.getMessage().toString());
}
return false;
}
}
感谢
文章链接找不到了,感谢作者提供的demo。