2025年UE UPL的使用

UE UPL的使用UPL 调研 基本思路 使用 UPL 中的 xml 语言 使用他来介入安卓的打包过程 将我们需要的功能添加到 UE 工程中 UPL 定义 UPL 全称 Unreal Plugin Language 主要的作用是参加 ue 的打包过程 理解 UPL 对应的就是安卓工程中的 activty main xml

大家好,我是讯享网,很高兴认识大家。

UPL调研

基本思路:
使用UPL中的xml语言,使用他来介入安卓的打包过程,将我们需要的功能添加到UE工程中
UPL
定义:UPL 全称 Unreal Plugin Language,主要的作用是参加ue的打包过程
理解:UPL对应的就是安卓工程中的activty_main.xml(找个文件的补充),它规定了安卓包构建的方式和顺序

所以我们在开发的过程中,可以通过UPL在Gameactivty增加我们写的java代码来保证使用C++来在安卓的环境下来使用java的代码

<!-- 导入对象到 GameActivity.java --> <gameActivityImportAdditions> <insert> import android.net.Uri; import android.net.ConnectivityManager; </insert> </gameActivityImportAdditions> <!-- 导入 java方法到 GameActivity.java --> <gameActivityClassAdditions> </gameActivityClassAdditions> <!-- 增加 代码到 OnCreateAdditions函数 --> <gameActivityOnCreateAdditions></gameActivityOnCreateAdditions> 

讯享网

通过这些方法就可以将代码增加到Gameactivity.java,这些具体的定义可以参考
UnrealBuildTool/System/UnrealPluginLanguage.csUE的源码中的注释
Java函数签名
JNI:JNI全称Java Native Interface,及java原生接口,主要用于jave调用其他语言代码,其他语言调用java代码。
我们通过C++ 去java的方法,通常是使用函数签名来获取Java中的函数,函数签名定义了函数的名称,返回值和参数类型

讯享网public int AndroidThunkJava_GetCurCpu() { int result = 0; FileReader fr = null; BufferedReader br = null; } 
  • String:Ljava/lang/String;
  • Object:Ljava/lang/Object;
    以上两条为补充内容,他不在基本类型中,所以为特殊表示
    public string AndroidThunkJava_GetCurCpu(int val)
    //他的函数签名为 (I)Ljava/lang/String;

JNI:Java to C++

UE 给我们的游戏生成的 GameActivity 中也声明了很多的 native 函数,这些函数是在 C++ 实现的,在 Java 中执行到这些函数会自动调用到引擎的 C++ 代码中:

public native int nativeGetCPUFamily(); public native boolean nativeSupportsNEON(); public native void nativeSetAffinityInfo(boolean bEnableAffinity, int bigCoreMask, int littleCoreMask); public native void nativeSetConfigRulesVariables(String[] KeyValuePairs); public native boolean nativeIsShippingBuild(); public native void nativeSetAndroidStartupState(boolean bDebuggerAttached); public native void nativeSetGlobalActivity(boolean bUseExternalFilesDir, boolean bPublicLogFiles, String internalFilePath, String externalFilePath, boolean bOBBInAPK, String APKPath); public native void nativeSetObbFilePaths(String OBBMainFilePath, String OBBPatchFilePath); public native void nativeSetWindowInfo(boolean bIsPortrait, int DepthBufferPreference); public native void nativeSetObbInfo(String ProjectName, String PackageName, int Version, int PatchVersion, String AppType); public native void nativeSetAndroidVersionInformation( String AndroidVersion, String PhoneMake, String PhoneModel, String PhoneBuildNumber, String OSLanguage ); public native void nativeSetSurfaceViewInfo(int width, int height); public native void nativeSetSafezoneInfo(boolean bIsPortrait, float left, float top, float right, float bottom); public native void nativeConsoleCommand(String commandString); public native void nativeVirtualKeyboardChanged(String contents); public native void nativeVirtualKeyboardResult(boolean update, String contents); public native void nativeVirtualKeyboardSendKey(int keyCode); public native void nativeVirtualKeyboardSendTextSelection(String contents, int selStart, int selEnd); public native void nativeVirtualKeyboardSendSelection(int selStart, int selEnd); public native void nativeInitHMDs(); public native void nativeResumeMainInit(); public native void nativeOnActivityResult(GameActivity activity, int requestCode, int resultCode, Intent data); public native void nativeGoogleClientConnectCompleted(boolean bSuccess, String accessToken); public native void nativeVirtualKeyboardShown(int left, int top, int right, int bottom); public native void nativeVirtualKeyboardVisible(boolean bShown); public native void nativeOnConfigurationChanged(boolean bPortrait); public native void nativeOnInitialDownloadStarted(); public native void nativeOnInitialDownloadCompleted(); public native void nativeHandleSensorEvents(float[] tilt, float[] rotation_rate, float[] gravity, float[] acceleration); 
讯享网<gameActivityClassAdditions> <insert> public native void nativeDoTester(String Msg); </insert> </gameActivityClassAdditions> 

在C++代码的任何位置去实现这个代码就好

#if PLATFORM_ANDROID JNI_METHOD void Java_com_epicgames_unreal_GameActivity_nativeDoTester (JNIEnv* jenv, jobject thiz, jstring msg) { if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) { FString FinalResult = FJavaHelper::FStringFromLocalRef(Env, msg); MyFunc::Name = FinalResult; } } 

com.epicgames_unreal 是 UE 生成的 GameActivity.java 的包名 (package com.epicgames_unreal)。
可以看到,在 C++ 中实现 JNIMETHOD 的函数名是以下规则:.
RType Java_PACKAGENAME_CLASSNAME_FUNCNAME(JNIEnv*,jobject thiz,Oher…)

JNI:C++ to Java
通过上面的转化,我们知道了怎么通过函数签名来找到java代码,我们在UE中就可以通过函数名和函数签名来在C++中调用java
其中通常用到的为下面三个头文件

讯享网// Runtime/Launch/Public/Android #include "Android/AndroidJNI.h" // Runtime/Core/Public/Android #include "Android/AndroidJavaEnv.h" // Runtime/Core/Public/Android #include "Android/AndroidJava.h" 

想要在 UE 中调用到它,首先要获取它的 jmethodID,需要通过函数所属的类、函数名字,签名三种信息来获取:

if (JNIEnv* Env = FAndroidApplication::GetJavaEnv()) { //通过FindMethod来获取他的jmethodID ,其中需要填类名,函数名字,签名 static jmethodID Method = FJavaWrapper::FindMethod(Env, FJavaWrapper::GameActivityClassID, "AndroidThunkJava_GetCurCpu", "()I", false); //找到jmethodID 后,通过CallIntMethod就可以调用到函数 jint Result = FJavaWrapper::CallIntMethod(Env, FJavaWrapper::GameActivityThis, Method); return Result; } 
遇到的问题
  1. 找不到头文件 #include “Android/AndroidJNI.h”
    解决:需要在bulid.cs中添加
讯享网if (Target.Platform == UnrealTargetPlatform.Android) { PrivateDependencyModuleNames.AddRange(new string[] { "Launch", "AndroidPermission" }); string PluginPath = Utils.MakePathRelativeTo(ModuleDirectory, Target.RelativeEnginePath); AdditionalPropertiesForReceipt.Add("AndroidPlugin", Path.Combine(PluginPath, "Android_clik_APL.xml")); } 
  1. 直接在VS平台编译JNI代码无法通过编译
    解决:需要在编译时增加预处理宏,保证在安卓平台时才可以运行
    #if PLATFORM_ANDROID
    #endif
  2. 导入对象出现错误
    解决:
    如果有如下错误:提示下面两个包找不到
    import android.support.v4.app.ActivityCompat;
    import android.support.v7.app.AppCompatActivity;
    可以改为:
    import androidx.core.app.ActivityCompat;
    import androidx.appcompat.app.AppCompatActivity;

这里库找不到可以替换为

import android.support.annotation. 替换为 import androidx.annotation; 4. 示例代码的修改,以下代码的删除,这里会导致软件闪退 <gameActivityOnPauseAdditions> <insert> android.util.Log.d("UE4", "onPause"); nativeUELog("onPause"); </insert> </gameActivityOnPauseAdditions> <gameActivityOnResumeAdditions> <insert> android.util.Log.d("UE4", "onResume"); nativeUELog("onResume"); </insert> </gameActivityOnResumeAdditions> 

功能实现效果

通过UMG在屏幕生成按钮,点击按钮后触发事件,调用JAVA函数,获取到CPU数值,并显示在屏幕右上角

java中native方法

定义:他的作用就是一个java调用非java接口的方法,他只是在java中声明,而具体的实现是在C语言中实现,其实很类似于C++中的extern c,是编译器的一种方法
public class IHaveNatives
{
native public void Native1( int x ) ;
native static public long Native2() ;
native synchronized private float Native3( Object o ) ;
native void Native4( int[] ary ) throws Exception ;
}

1.1 安卓项目文件结构

activity定义:用户看得见摸的着的是手机屏幕。我们要在手机屏幕上显示文字图像等信息,并对用户的点击滑动等等操作作出不同的反应。 App中与用户交互的大任由Activity来承担。当用户手指点击手机屏幕时,Android系统检测到屏幕发生的事情,将这一事件分发对应的App处理。这里要注意,activity接收到的是系统给的信息。系统会判断这些交互消息该给哪个app来处理。

入口函数:public class GameActivity extends NativeActivity
在gameActivity他的入口类为GameActivity ,是继承自NativeActivity,也就是他的页面
生命周期:

  1. Oncreate 布局的初始化
  2. Onstart 启动的时候的状态
  3. Onresume 渲染的完成
  4. Onpause 暂停后去停止
  5. Onstop 回到桌面
  6. Onresart 重新开始

同时在UE的UPL中也提供相应的接口供我们使用,在其中添加代码,下面是一些举例

讯享网* <!-- optional additions to GameActivity onCreate in GameActivity.java --> * <gameActivityOnCreateAdditions> </gameActivityOnCreateAdditions> * * <!-- optional additions to GameActivity onDestroy in GameActivity.java --> * <gameActivityOnDestroyAdditions> </gameActivityOnDestroyAdditions> * * <!-- optional additions to GameActivity onConfigurationChanged in GameActivity.java --> * <gameActivityonConfigurationChangedAdditions> </gameActivityonConfigurationChangedAdditions> * * <!-- optional additions to GameActivity onStart in GameActivity.java --> * <gameActivityOnStartAdditions> </gameActivityOnStartAdditions> * * <!-- optional additions to GameActivity onStop in GameActivity.java --> * <gameActivityOnStopAdditions> </gameActivityOnStopAdditions> * * <!-- optional additions to GameActivity onPause in GameActivity.java --> * <gameActivityOnPauseAdditions> </gameActivityOnPauseAdditions> * * <!-- optional additions to GameActivity onResume in GameActivity.java --> * <gameActivityOnResumeAdditions> </gameActivityOnResumeAdditions> * 
2.1 Activity 启动,携带参数启动

通常activity离不开intent这个类,通常把信息包含在intent对象中,然后执行启动,常用的方法是 startActivity (Intent intent) ,我们在activity中通信的过程中的信息就可以听过intent这个类去传递
//实现一个简单的跳转
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Intent intent = new Intent(getApplicationContext(),myActivity2.class); intent.putExtra("13",100); startActivities(new Intent[]{intent}); 

}

2.2 任务,返回栈

任务指的是在执行特定作业的时候与用户交互的一系列Activity,这些Activity按照各组打开顺序排列在堆栈中.

2.3 Avtivity的四种模式
2.4 Service综述

service是一种在后台执行,长时间运行操作而不提供界面的应用组件,服务可由其他组件启动,就是用户在切换到其他应用的时候,服务仍在后台运行

2.4.1 前台,后台服务与绑定

前台:前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必 须显示通知。 即使用户停止与应用的交互,前台服务仍会继续运行
后台:后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
绑定服务:当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。 绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行,这些操作仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

2.4.2 启动服务

startService(Intent(applicationContext, ServiceStartDemo::class.java))


讯享网

2.4.3 停止前台服务
3.1 Fragment基础概念
  1. Fragment加载灵活,替换方便。定制你的UI,在不同尺寸的屏幕上创建合适的UI,提高用户体
    验。
  2. 可复用,页面布局可以使用多个Fragment,不同的控件和内容可以分布在不同的Fragment上。
  3. 使用Fragment,可以少用一些Activity。一个Activity可以管辖多个Fragment。
    3.2 fragment的生命周期

使用DialogFragment来完成一个弹窗的功能

  1. 在onCreate方法中接收传入的数据。传递数据使用了Bundle
  2. 在onCreateView方法中,使用上文建立的layout
  • 在onViewCreated方法中进行ui操作
讯享网import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; 
public class SimpleDialog extends DialogFragment { public static final String K_TITLE = "k_title"; // 传输数据时用到的key public static final String K_CONTENT = "k_content"; private String title; private String content; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle in = getArguments(); if (in != null) { title = in.getString(K_TITLE); content = in.getString(K_CONTENT); } } //作用是将片段布局插入到父级viewGrroup中 @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.dialog_simple, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); TextView titleTv = view.findViewById(R.id.title_tv); TextView contentTv = view.findViewById(R.id.content_tv); titleTv.setText(title); contentTv.setText(content); } } 

定义的layout文件如下

讯享网<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="12dp"> <TextView android:id="@+id/title_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#" android:textSize="16sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/content_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:textColor="#" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/title_tv" /> </androidx.constraintlayout.widget.ConstraintLayout> 

最后在activity中调用

public class MainActivity extends AppCompatActivity {

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); popSimpleDialog1("123", "test"); } private void popSimpleDialog1(String title, String content) { SimpleDialog dialog = new SimpleDialog(); Bundle bundle = new Bundle(); bundle.putString(SimpleDialog.K_TITLE, title); bundle.putString(SimpleDialog.K_CONTENT, content); dialog.setArguments(bundle); //getSupportFragmentManager为获取FragmentTransaction实例 dialog.show(getSupportFragmentManager(), "one-tag"); } 

}

最终实现的效果如下图所示
在这里插入图片描述

java中native方法

定义:他的作用就是一个java调用非java接口的方法,他只是在java中声明,而具体的实现是在C语言中实现,其实很类似于C++中的extern c,是编译器的一种方法

讯享网 public class IHaveNatives { native public void Native1( int x ) ; native static public long Native2() ; native synchronized private float Native3( Object o ) ; native void Native4( int[] ary ) throws Exception ; } 
扩展:GameActivity.java文件的学习
1.1 安卓项目文件结构

在这里插入图片描述
activity定义:用户看得见摸的着的是手机屏幕。我们要在手机屏幕上显示文字图像等信息,并对用户的点击滑动等等操作作出不同的反应。 App中与用户交互的大任由Activity来承担。当用户手指点击手机屏幕时,Android系统检测到屏幕发生的事情,将这一事件分发对应的App处理。这里要注意,activity接收到的是系统给的信息。系统会判断这些交互消息该给哪个app来处理。

生命周期:
  1. Oncreate 布局的初始化
  2. Onstart 启动的时候的状态
  3. Onresume 渲染的完成
  4. Onpause 暂停后去停止
  5. Onstop 回到桌面
  6. Onresart 重新开始

在这里插入图片描述
同时在UE的UPL中也提供相应的接口供我们使用,在其中添加代码,下面是一些举例

* <!-- optional additions to GameActivity onCreate in GameActivity.java --> * <gameActivityOnCreateAdditions> </gameActivityOnCreateAdditions> * * <!-- optional additions to GameActivity onDestroy in GameActivity.java --> * <gameActivityOnDestroyAdditions> </gameActivityOnDestroyAdditions> * * <!-- optional additions to GameActivity onConfigurationChanged in GameActivity.java --> * <gameActivityonConfigurationChangedAdditions> </gameActivityonConfigurationChangedAdditions> * * <!-- optional additions to GameActivity onStart in GameActivity.java --> * <gameActivityOnStartAdditions> </gameActivityOnStartAdditions> * * <!-- optional additions to GameActivity onStop in GameActivity.java --> * <gameActivityOnStopAdditions> </gameActivityOnStopAdditions> * * <!-- optional additions to GameActivity onPause in GameActivity.java --> * <gameActivityOnPauseAdditions> </gameActivityOnPauseAdditions> * * <!-- optional additions to GameActivity onResume in GameActivity.java --> * <gameActivityOnResumeAdditions> </gameActivityOnResumeAdditions> * 

2.1 Activity 启动,携带参数启动

通常activity离不开intent这个类,通常把信息包含在intent对象中,然后执行启动,常用的方法是 startActivity (Intent intent) ,我们在activity中通信的过程中的信息就可以听过intent这个类去传递

讯享网//实现一个简单的跳转 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(getApplicationContext(),myActivity2.class); intent.putExtra("wdf",100); startActivities(new Intent[]{intent}); } 

2.2 任务,返回栈

任务指的是在执行特定作业的时候与用户交互的一系列Activity,这些Activity按照各组打开顺序排列在堆栈中.

2.3 Avtivity的四种模式

在这里插入图片描述

2.4 Service综述

service是一种在后台执行,长时间运行操作而不提供界面的应用组件,服务可由其他组件启动,就是用户在切换到其他应用的时候,服务仍在后台运行

2.4.1 前台,后台服务与绑定

前台:前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必 须显示通知。 即使用户停止与应用的交互,前台服务仍会继续运行
后台:后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
绑定服务:当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。 绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行,这些操作仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

2.4.2 启动服务
 startService(Intent(applicationContext, ServiceStartDemo::class.java)) 
2.4.3 停止前台服务
3.1 Fragment基础概念
  1. Fragment加载灵活,替换方便。定制你的UI,在不同尺寸的屏幕上创建合适的UI,提高用户体
    验。
  2. 可复用,页面布局可以使用多个Fragment,不同的控件和内容可以分布在不同的Fragment上。
  3. 使用Fragment,可以少用一些Activity。一个Activity可以管辖多个Fragment。
    3.2 fragment的生命周期
    在这里插入图片描述
使用DialogFragment来完成一个弹窗的功能
  1. 在onCreate方法中接收传入的数据。传递数据使用了Bundle
  2. 在onCreateView方法中,使用上文建立的layout
  • 在onViewCreated方法中进行ui操作
讯享网import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; public class SimpleDialog extends DialogFragment { public static final String K_TITLE = "k_title"; // 传输数据时用到的key public static final String K_CONTENT = "k_content"; private String title; private String content; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle in = getArguments(); if (in != null) { title = in.getString(K_TITLE); content = in.getString(K_CONTENT); } } //作用是将片段布局插入到父级viewGrroup中 @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.dialog_simple, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); TextView titleTv = view.findViewById(R.id.title_tv); TextView contentTv = view.findViewById(R.id.content_tv); titleTv.setText(title); contentTv.setText(content); } } 

定义的layout文件如下

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="12dp"> <TextView android:id="@+id/title_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textColor="#" android:textSize="16sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/content_tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:textColor="#" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/title_tv" /> </androidx.constraintlayout.widget.ConstraintLayout> 
讯享网public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); popSimpleDialog1("王东方", "test"); } private void popSimpleDialog1(String title, String content) { SimpleDialog dialog = new SimpleDialog(); Bundle bundle = new Bundle(); bundle.putString(SimpleDialog.K_TITLE, title); bundle.putString(SimpleDialog.K_CONTENT, content); dialog.setArguments(bundle); //getSupportFragmentManager为获取FragmentTransaction实例 dialog.show(getSupportFragmentManager(), "one-tag"); } } 

最终实现的效果如下图所示
在这里插入图片描述

res应用资源

资源分类的视图如下把资源放进对应的目录后,可使用在项目 R 类中生成的资源ID来访问这些资源。形如 R.drawable.icon , R.layout.main_activity 。 R 类是自动生成的。代表resources。

在这里插入图片描述

小讯
上一篇 2025-03-24 11:17
下一篇 2025-03-07 23:16

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/40086.html