欢迎喜欢或者从事CocosCreator开发的小伙伴请加入我的大家庭CocosCreator游戏开发Q群:26855530
1.首先增加你需要申请的权限,
修改:AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxxx.xxx"
android:installLocation="auto">
<uses-feature android:glEsVersion="0x00020000" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />-->
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
<!-- Android 13 特殊权限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<application
android:allowBackup="true"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:icon="@mipmap/ic_launcher"
android:supportsRtl="true">
<!-- Tell Cocos2dxActivity the name of our .so -->
<meta-data android:name="android.app.lib_name"
android:value="cocos2djs" />
<activity
android:name="org.cocos2dx.javascript.AppActivity"
android:screenOrientation="sensorLandscape"
android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|uiMode"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2. 确保你使用的是AndroidX
确保项目的 gradle.properties
文件中包含如下配置:
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# Android SDK version that will be used as the compile project
PROP_COMPILE_SDK_VERSION=33
# Android SDK version that will be used as the earliest version of android this application can run on
PROP_MIN_SDK_VERSION=26
# Android SDK version that will be used as the latest version of android this application has been tested on
PROP_TARGET_SDK_VERSION=33
# Android Build Tools version that will be used as the compile project
PROP_BUILD_TOOLS_VERSION=28.0.3
# List of CPU Archtexture to build that application with
# Available architextures (armeabi-v7a | arm64-v8a | x86)
# To build for multiple architexture, use the `:` between them
# Example - PROP_APP_ABI=armeabi-v7a:arm64-v8a:x86_64
PROP_APP_ABI=armeabi-v7a:arm64-v8a:x86_64
# fill in sign information for release mode
RELEASE_STORE_FILE=/Applications/Cocos/Creator/2.4.10/CocosCreator.app/Contents/Resources/static/build-templates/native/debug.keystore
RELEASE_STORE_PASSWORD=123456
RELEASE_KEY_ALIAS=debug_keystore
RELEASE_KEY_PASSWORD=123456
android.injected.testOnly=false
# android.enableJetifier=true
android.useAndroidX=true
android.enableJetifier=true
3. 更新 build.gradle
文件
在你的项目的 build.gradle
文件中,确保包含了AndroidX库的依赖项:
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion PROP_COMPILE_SDK_VERSION.toInteger()
buildToolsVersion PROP_BUILD_TOOLS_VERSION
defaultConfig {
applicationId "com.zhcj.xzjh"
minSdkVersion PROP_MIN_SDK_VERSION
targetSdkVersion PROP_TARGET_SDK_VERSION
versionCode 1
versionName "1.0"
externalNativeBuild {
ndkBuild {
if (!project.hasProperty("PROP_NDK_MODE") || PROP_NDK_MODE.compareTo('none') != 0) {
// skip the NDK Build step if PROP_NDK_MODE is none
targets 'cocos2djs'
arguments 'NDK_TOOLCHAIN_VERSION=clang'
def module_paths = [project.file("../../../cocos2d-x"),
project.file("../../../cocos2d-x/cocos"),
project.file("../../../cocos2d-x/external")]
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
arguments 'NDK_MODULE_PATH=' + module_paths.join(";")
}
else {
arguments 'NDK_MODULE_PATH=' + module_paths.join(':')
}
arguments '-j' + Runtime.runtime.availableProcessors()
}
}
ndk {
abiFilters PROP_APP_ABI.split(':')
}
}
}
sourceSets.main {
java.srcDirs "../src", "src"
res.srcDirs "../res", 'res'
jniLibs.srcDirs "../libs", 'libs'
manifest.srcFile "AndroidManifest.xml"
}
externalNativeBuild {
ndkBuild {
if (!project.hasProperty("PROP_NDK_MODE") || PROP_NDK_MODE.compareTo('none') != 0) {
// skip the NDK Build step if PROP_NDK_MODE is none
path "jni/Android.mk"
}
}
}
signingConfigs {
release {
if (project.hasProperty("RELEASE_STORE_FILE")) {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
debuggable false
jniDebuggable false
renderscriptDebuggable false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.release
}
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=0'
}
}
}
debug {
debuggable true
jniDebuggable true
renderscriptDebuggable true
externalNativeBuild {
ndkBuild {
arguments 'NDK_DEBUG=1'
}
}
}
}
}
android.applicationVariants.all { variant ->
// delete previous files first
delete "${buildDir}/intermediates/merged_assets/${variant.dirName}"
variant.mergeAssets.doLast {
def sourceDir = "${buildDir}/../../../../.."
copy {
from "${sourceDir}"
include "assets/**"
include "src/**"
include "jsb-adapter/**"
into outputDir
}
copy {
from "${sourceDir}/main.js"
from "${sourceDir}/project.json"
into outputDir
}
}
}
dependencies {
implementation fileTree(dir: '../libs', include: ['*.jar','*.aar'])
implementation fileTree(dir: 'libs', include: ['*.jar','*.aar'])
implementation fileTree(dir: "../../../cocos2d-x/cocos/platform/android/java/libs", include: ['*.jar'])
implementation project(':libcocos2dx')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
}
确保在项目级别的 build.gradle
文件中包含Google的Maven仓库:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.8.10' // 确保使用最新的 Kotlin 版本
repositories {
google()
jcenter()
}
dependencies {
// classpath 'com.android.tools.build:gradle:7.1.2'
classpath 'com.android.tools.build:gradle:7.4.2' // Android Gradle 插件版本
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
flatDir {
dirs 'libs'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
两外附上一个监听网络工具类:
package org.cocos2dx.javascript.utils;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkInfo;
import android.os.Build;
public class NetworkUtil {
private Context context;
public NetworkUtil(Context context) {
this.context = context;
}
public boolean isWiFiAvailable() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnected() && activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
}
public void useWiFiThenMobile(final NetworkCallback callback) {
final ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
cm.registerNetworkCallback(builder.build(), new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
// WiFi is available
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.bindProcessToNetwork(network);
} else {
ConnectivityManager.setProcessDefaultNetwork(network);
}
callback.onNetworkSelected(true);
}
@Override
public void onUnavailable() {
// WiFi is not available, use mobile data
cm.unregisterNetworkCallback(this);
useMobileData(callback);
}
});
}
private void useMobileData(NetworkCallback callback) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
cm.requestNetwork(builder.build(), new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
// Mobile data is available
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cm.bindProcessToNetwork(network);
} else {
ConnectivityManager.setProcessDefaultNetwork(network);
}
callback.onNetworkSelected(false);
}
});
}
public interface NetworkCallback {
void onNetworkSelected(boolean isWiFi);
}
}
主角登场AppActivity.java:
package org.cocos2dx.javascript;
import org.cocos2dx.javascript.utils.NetworkUtil;
import org.cocos2dx.lib.Cocos2dxActivity;
import org.cocos2dx.lib.Cocos2dxGLSurfaceView;
import android.os.Bundle;
import android.content.Intent;
import android.content.res.Configuration;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class AppActivity extends Cocos2dxActivity {
private static final int PERMISSION_REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// DO OTHER INITIALIZATION BELOW
SDKWrapper.getInstance().init(this);
// 检查并请求权限
checkAndRequestPermissions();
}
private void checkAndRequestPermissions() {
boolean internetPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED;
boolean networkStatePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED;
boolean wifiStatePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED;
// boolean readStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
// boolean writeStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13
boolean notificationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
if (!notificationPermission || !internetPermission || !networkStatePermission || !wifiStatePermission
//|| !readStoragePermission || !writeStoragePermission
) {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.ACCESS_WIFI_STATE,
//Manifest.permission.READ_EXTERNAL_STORAGE,
//Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.POST_NOTIFICATIONS
}, PERMISSION_REQUEST_CODE);
}
} else {
if (!internetPermission || !networkStatePermission || !wifiStatePermission
//|| !readStoragePermission || !writeStoragePermission
) {
ActivityCompat.requestPermissions(this, new String[]{
Manifest.permission.INTERNET,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.ACCESS_WIFI_STATE,
// Manifest.permission.READ_EXTERNAL_STORAGE,
// Manifest.permission.WRITE_EXTERNAL_STORAGE
}, PERMISSION_REQUEST_CODE);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean allPermissionsGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false;
break;
}
}
if (allPermissionsGranted) {
// Toast.makeText(this, "所有权限已授予", Toast.LENGTH_SHORT).show();
initializeNetworkUtil();
} else {
Toast.makeText(this, "权限被拒绝,程序即将关闭", Toast.LENGTH_SHORT).show();
finish(); // 关闭程序
//另一种关闭程序方式
// finishAffinity();
// System.exit(0);
}
}
}
private void initializeNetworkUtil() {
NetworkUtil networkUtil = new NetworkUtil(this);
networkUtil.useWiFiThenMobile(isWiFi -> {
// Do something based on network selection
if (isWiFi) {
Log.d("NetworkUtil", "Connected to WiFi. Starting download service...");
// startDownloadService();
} else {
// 用户已连接到移动数据网络
// 可以提示用户注意流量消耗
Log.d("NetworkUtil", "Connected to mobile data. Be cautious of data usage.");
showMobileDataWarning();
}
});
}
private void showMobileDataWarning() {
// 显示移动数据警告的代码
}
@Override
public Cocos2dxGLSurfaceView onCreateView() {
Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
// TestCpp should create stencil buffer
glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);
SDKWrapper.getInstance().setGLSurfaceView(glSurfaceView, this);
return glSurfaceView;
}
@Override
protected void onResume() {
super.onResume();
SDKWrapper.getInstance().onResume();
// 每次恢复活动时,检查权限是否被授予
boolean internetPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED;
boolean networkStatePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED;
boolean wifiStatePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED;
// boolean readStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
// boolean writeStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
// || !readStoragePermission || !writeStoragePermission
// 重新请求权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13
boolean notificationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
}
if (!internetPermission || !networkStatePermission || !wifiStatePermission
// || !readStoragePermission || !writeStoragePermission || !notificationPermission
) {
checkAndRequestPermissions(); // 重新请求权限
}
}
@Override
protected void onPause() {
super.onPause();
SDKWrapper.getInstance().onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
// Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
if (!isTaskRoot()) {
return;
}
SDKWrapper.getInstance().onDestroy();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
SDKWrapper.getInstance().onActivityResult(requestCode, resultCode, data);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
SDKWrapper.getInstance().onNewIntent(intent);
}
@Override
protected void onRestart() {
super.onRestart();
SDKWrapper.getInstance().onRestart();
}
@Override
protected void onStop() {
super.onStop();
SDKWrapper.getInstance().onStop();
}
@Override
public void onBackPressed() {
SDKWrapper.getInstance().onBackPressed();
super.onBackPressed();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
SDKWrapper.getInstance().onConfigurationChanged(newConfig);
super.onConfigurationChanged(newConfig);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
SDKWrapper.getInstance().onRestoreInstanceState(savedInstanceState);
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
SDKWrapper.getInstance().onSaveInstanceState(outState);
super.onSaveInstanceState(outState);
}
@Override
protected void onStart() {
SDKWrapper.getInstance().onStart();
super.onStart();
}
}
然后就ok,打包拉~
补上我项目的ndk版本:26.3.11579264