Unity3D-Android调用系统分享针对8.0系统的方式

本文介绍了一种在Android 8.0及以上版本中实现原生分享的方法,解决了传统分享方式失效的问题,并提供了兼容低版本Android的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我在原来的帖子上写了一个关于Android原生分享的方法:

http://blog.youkuaiyun.com/pz789as/article/details/77979594

但是,在后面测试的时候发现,这个方式在Android 8.0 上不起作用,点了按钮之后,并没有弹出原生分享框。

网上大部分的原生分享代码都和我上面写的一样,所以只能自己看文档,慢慢再试其他的,最后经过努力,终于给弄出来了,还兼容低版本的Android。

问题的关键就是在图片的加载处理上了,原来的图片只能随意保存在自己的目录中,就可以加载分享,但是8.0之后,好像加强了管理,于是参考文档修改了下加载图片的方式之后,就可以弹出分享框了,代码如下:

private IEnumerator TakeScreenshotAndroid()
	{
		Cuting.SetRaycast(true);
		if (cutObjects != null){
			for(int i=0;i<cutObjects.Length;i++){
				cutObjects[i].GetComponent<CanvasGroup>().alpha = 0;
				CanvasGroup cg = cutObjects[i].GetComponent<CanvasGroup>();
				cg.alpha = 0;
			}
		}
		CookieShop.HideAdsBanner();
        yield return new WaitForEndOfFrame();
		var width = Screen.width;
		var height = Screen.height;
		var tex = new Texture2D(width, height, TextureFormat.RGB24, false);
		tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
		tex.Apply();
		if (height / 4 < 300){
			CompressPNG(tex, 2);
		}else{
			CompressPNG(tex, 4);
		}
		GJCPngEncoder encoder = new GJCPngEncoder(tex, 80);
		encoder.doEncoding();
		while(!encoder.isDone){
			yield return null;
		}
        string destination = Path.Combine(Application.persistentDataPath, "shareAndroid001.png");
		File.WriteAllBytes(destination, encoder.GetBytes());
        yield return new WaitForSeconds(0.3f);
		if (cutObjects != null){
			for(int i=0;i<cutObjects.Length;i++){
				cutObjects[i].GetComponent<CanvasGroup>().alpha = 1;
			}
		}
		CookieGame.ins.ShowAdsBanner();
		
        if (!Application.isEditor)
        {
            // AndroidJavaClass intentClass = new AndroidJavaClass("android.content.Intent");
            // AndroidJavaObject intentObject = new AndroidJavaObject("android.content.Intent");
            // intentObject.Call<AndroidJavaObject>("setAction", intentClass.GetStatic<string>("ACTION_SEND"));
            // AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri");
            // AndroidJavaObject uriObject = uriClass.CallStatic<AndroidJavaObject>("parse", "file://" + destination);
            // intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_STREAM"), uriObject);
            // intentObject.Call<AndroidJavaObject>("putExtra", intentClass.GetStatic<string>("EXTRA_TEXT"), shareDefaultText);
            // intentObject.Call<AndroidJavaObject>("setType", "image/jpeg");
            // AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            // AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
            // AndroidJavaObject chooser = intentClass.CallStatic<AndroidJavaObject>("createChooser", intentObject, "Share");
            // currentActivity.Call("startActivity", chooser);

			WWW www = new WWW("file://" + destination);
			while(!www.isDone){
				yield return null;
			}
			Texture2D picture = www.texture;
			byte[] bytes = new AndroidJavaObject("android.util.Base64").CallStatic<byte[]>("decode",System.Convert.ToBase64String (picture.EncodeToPNG()),0);
			AndroidJavaObject bitmap =  new AndroidJavaObject("android.graphics.BitmapFactory").CallStatic<AndroidJavaObject>("decodeByteArray",bytes,0,bytes.Length);
			AndroidJavaObject compress = new AndroidJavaClass("android.graphics.Bitmap$CompressFormat").GetStatic<AndroidJavaObject>("JPEG");
			bitmap.Call<bool>("compress",compress,100,new AndroidJavaObject("java.io.ByteArrayOutputStream"));
			string path = new AndroidJavaClass("android.provider.MediaStore$Images$Media").CallStatic<string>("insertImage",currentActivity.Call<AndroidJavaObject>("getContentResolver"),bitmap,picture.name,"");
			AndroidJavaObject uri = new AndroidJavaClass("android.net.Uri").CallStatic<AndroidJavaObject>("parse",path);
			AndroidJavaObject sharingIntent  = new AndroidJavaObject("android.content.Intent");
			sharingIntent.Call<AndroidJavaObject>("setAction","android.intent.action.SEND");
			sharingIntent.Call<AndroidJavaObject>("setType","image/*");
			sharingIntent.Call<AndroidJavaObject>("putExtra","android.intent.extra.STREAM",uri);
			sharingIntent.Call<AndroidJavaObject>("putExtra","android.intent.extra.TEXT",shareDefaultText);
			// sharingIntent.Call<AndroidJavaObject>("putExtra","android.intent.extra.SUBJECT","Share to");
			AndroidJavaObject createChooser = sharingIntent.CallStatic<AndroidJavaObject>("createChooser",sharingIntent, "Share to");
			currentActivity.Call("startActivity",createChooser);
            yield return new WaitForSeconds(1f);
        }
        yield return new WaitUntil(() => isFocus);
        Cuting.SetRaycast(false);
		Waiting.SetWaiting(true);
		Destroy(tex);
		OnShareSuccess("Android share");
	}
	private static AndroidJavaObject currentActivity{
		get{ 
			return new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
		}
	}



上面注释的部分是以前的写法,在下面的是新写法。

效果图没有重新截取了,用原来的表示下就好了。。



<think>我们正在讨论的是UnityAndroidAPI的兼容封装。根据用户需求,我们需要提供实现Android平台API兼容封装的最佳设置代码。由于Android平台碎片化严重,不同版本API差异较大,因此兼容封装的核心在于:1.运行时API版本检测2.条件调用不同版本API3.权限请求处理参考引用中提到的几个点:-引用[1]:提到了跨平台通用方案和原生调用Android/iOS),但我们需要专注于Android-引用[2]:展示了UnityAndroid原生代码交互(纹理共享),这种交互方式在兼容封装中会用到。-引用[3]:提到了在Android10及以上获取设备序列号需要特定权限和API,这正是兼容性封装需要处理的情况。因此,我们将重点放在如何封装AndroidAPI以处理不同版本的系统调用,特别是需要运行时检查的情况。以下是一个实现AndroidAPI兼容封装的示例,以获取设备序列号(SerialNumber)为例,因为不同Android版本获取方式不同,且需要权限。###实现步骤####1.创建AndroidJava插件首先,在Unity项目的`Assets/Plugins/Android`目录下创建一个Java类,用于封装AndroidAPI调用。```java//文件名:DeviceInfoHelper.javapackagecom.yourcompany.deviceinfo;importandroid.Manifest;importandroid.annotation.SuppressLint;importandroid.os.Build;importandroid.provider.Settings;importandroid.util.Log;importandroidx.annotation.RequiresApi;publicclassDeviceInfoHelper{//单例模式privatestaticDeviceInfoHelperinstance=null;publicstaticDeviceInfoHelpergetInstance(){if(instance==null){instance=newDeviceInfoHelper();}returninstance;}//获取设备序列号(需要权限处理)@SuppressLint("HardwareIds")publicStringGetDeviceSerial(){try{if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){//Android8.0(API26)及以上returnBuild.getSerial();}else{//Android8.0以下returnBuild.SERIAL;}}catch(Exceptione){Log.e("DeviceInfoHelper","Errorgettingdeviceserial",e);return"unknown";}}}```####2.UnityC#封装层在Unity中,我们使用C#脚本来调用上述Java代码。这里需要处理权限请求和Android版本兼容性。```csharp//DeviceInfo.csusingUnityEngine;usingSystem;publicclassDeviceInfo:MonoBehaviour{//定义Android权限privateconststringREAD_PHONE_STATE_PERMISSION="android.permission.READ_PHONE_STATE";//用于访问Java类的包装器privatestaticAndroidJavaObject_deviceInfoHelper=null;//获取设备序列号(兼容封装)publicvoidGetDeviceSerial(Action<string>callback){#ifUNITY_ANDROID&&!UNITY_EDITOR//检查权限if(!CheckPermission(READ_PHONE_STATE_PERMISSION)){//请求权限RequestPermission(READ_PHONE_STATE_PERMISSION,()=>{//权限被授予后执行callback.Invoke(GetSerialNumberInternal());},()=>{//权限被拒绝callback.Invoke("permission_denied");});return;}//已有权限,直接获取callback.Invoke(GetSerialNumberInternal());#elsecallback.Invoke("non_android_platform");#endif}//内部获取序列号方法privatestringGetSerialNumberInternal(){if(_deviceInfoHelper==null){using(AndroidJavaClassjc=newAndroidJavaClass("com.yourcompany.deviceinfo.DeviceInfoHelper")){_deviceInfoHelper=jc.CallStatic<AndroidJavaObject>("getInstance");}}return_deviceInfoHelper.Call<string>("GetDeviceSerial");}//检查权限privateboolCheckPermission(stringpermission){#ifUNITY_2018_1_OR_NEWERreturnUnityEngine.Android.Permission.HasUserAuthorizedPermission(permission);#else//旧版本Unity使用AndroidJavaAPI检查权限using(AndroidJavaClasscontextClass=newAndroidJavaClass("com.unity3d.player.UnityPlayer"))using(AndroidJavaObjectcontext=contextClass.GetStatic<AndroidJavaObject>("currentActivity"))using(AndroidJavaObjectpackageManager=context.Call<AndroidJavaObject>("getPackageManager"))using(AndroidJavaObjectpackageName=context.Call<AndroidJavaObject>("getPackageName")){intresult=packageManager.Call<int>("checkPermission",permission,packageName);return(result==0);//0表示PERMISSION_GRANTED}#endif}//请求权限privatevoidRequestPermission(stringpermission,ActiononGranted,ActiononDenied){#ifUNITY_2018_1_OR_NEWERUnityEngine.Android.Permission.RequestUserPermission(permission);//由于Unity的权限请求是异步的,我们需要监听回调//注意:Unity没有直接提供每个权限请求的回调,因此通常需要在一个全局权限检查点处理//这里简化处理:假设在请求后立即检查(实际应用中需要更复杂的处理)//建议使用PermissionCallbacks(Unity2020.1+)#ifUNITY_2020_1_OR_NEWERvarcallbacks=newUnityEngine.Android.PermissionCallbacks();callbacks.PermissionGranted+=(permissionName)=>{if(permissionName==permission)onGranted?.Invoke();};callbacks.PermissionDenied+=(permissionName)=>{if(permissionName==permission)onDenied?.Invoke();};UnityEngine.Android.Permission.RequestUserPermission(permission,callbacks);#else//旧版本Unity:轮询检查(不推荐,但作为示例)StartCoroutine(CheckPermissionAfterRequest(permission,onGranted,onDenied));#endif#else//对于旧版Unity,使用Android原生请求using(AndroidJavaClassunityPlayer=newAndroidJavaClass("com.unity3d.player.UnityPlayer"))using(AndroidJavaObjectactivity=unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")){activity.Call("requestPermissions",newstring[]{permission},0);}//同样需要轮询或事件监听(这里省略)#endif}#ifUNITY_2018_1_OR_NEWER&&!UNITY_2020_1_OR_NEWERprivateSystem.Collections.IEnumeratorCheckPermissionAfterRequest(stringpermission,ActiononGranted,ActiononDenied){yieldreturnnewWaitForSeconds(0.5f);//等待一段时间if(CheckPermission(permission))onGranted?.Invoke();elseonDenied?.Invoke();}#endif}```###兼容性封装要点1.**版本条件编译**:-使用`#ifUNITY_ANDROID`确保只在Android平台执行-使用`Build.VERSION.SDK_INT`(Java端)和`UnityEngine.Android`(C#端)处理不同API级别2.**权限处理**:-Android6.0(API23)及以上,运行时权限请求是必须的-Unity提供了`UnityEngine.Android.Permission`类(2018.1+),旧版本需调用Android原生API3.**异常处理**:-Java层捕获异常防止崩溃-C#层处理权限拒绝情况4.**异步回调**:-使用`Action<string>`回调返回结果,因为权限请求是异步操作5.**单例模式**:-Java层使用单例减少JNI调用开销###最佳实践建议1.**权限请求优化**:```csharp//使用Unity2020.1+的PermissionCallbacksvarcallbacks=newPermissionCallbacks();callbacks.PermissionGranted+=(permissionName)=>{...};callbacks.PermissionDenied+=(permissionName)=>{...};callbacks.PermissionDeniedAndDontAskAgain+=(permissionName)=>{...};Permission.RequestUserPermission(permission,callbacks);```2.**API级别抽象**:将API版本判断封装成通用方法:```csharppublicstaticboolIsAndroidVersionAtLeast(intapiLevel){#ifUNITY_ANDROID&&!UNITY_EDITORusing(varversion=newAndroidJavaClass("android.os.Build$VERSION")){intsdkInt=version.GetStatic<int>("SDK_INT");returnsdkInt>=apiLevel;}#elsereturnfalse;#endif}```3.**错误处理增强**:-在Java层返回错误代码而非字符串-在C#层定义错误枚举4.**JNI优化**:-缓存`AndroidJavaClass`和`AndroidJavaObject`引用避免重复创建-减少JNI调用次数###结论UnityAndroidAPI兼容封装的核心在于:1.**分层设计**:Java层处理平台API,C#层处理Unity交互2.**权限管理**:运行时动态请求敏感权限3.**版本适配**:根据`Build.VERSION.SDK_INT`调用不同API4.**异步机制**:使用回调处理权限请求结果此方案可扩展到其他AndroidAPI的封装,如获取AndroidID、存储访问、传感器调用等[^3][^1]。---§§相关问题§§1.如何在Unity中封装Android权限请求的通用模块?2.UnityAndroid原生代码交互的性能优化技巧有哪些?3.如何实现UnityAndroid插件的向后兼容(支持旧版Android系统)?</think>### Unity Android API 兼容封装最佳实践 在Unity中实现Android平台API的兼容封装,核心是处理**不同Android版本**和**不同设备厂商**的API差异。以下是经过优化的完整实现方案: #### 1. 基础架构设计 ```csharp // AndroidCompatibilityManager.cs public class AndroidCompatibilityManager : MonoBehaviour { // 单例模式确保全局访问 private static AndroidCompatibilityManager _instance; public static AndroidCompatibilityManager Instance { get { if (_instance == null) { GameObject go = new GameObject("AndroidCompatibility"); _instance = go.AddComponent<AndroidCompatibilityManager>(); DontDestroyOnLoad(go); } return _instance; } } // API级别检测 public int AndroidSdkVersion { get { #if UNITY_ANDROID && !UNITY_EDITOR using (var version = new AndroidJavaClass("android.os.Build$VERSION")) { return version.GetStatic<int>("SDK_INT"); } #else return 0; // 非Android平台 #endif } } // 设备厂商检测 public string DeviceManufacturer { get { #if UNITY_ANDROID && !UNITY_EDITOR return new AndroidJavaClass("android.os.Build").GetStatic<string>("MANUFACTURER").ToLower(); #else return "unknown"; #endif } } } ``` #### 2. 运行时API版本检测封装 ```csharp // 封装API调用方法 public T CallAndroidApi<T>(string className, string methodName, params object[] args) { #if UNITY_ANDROID && !UNITY_EDITOR try { using (var javaClass = new AndroidJavaClass(className)) { return javaClass.CallStatic<T>(methodName, args); } } catch (Exception e) { Debug.LogError($"Android API调用失败: {e.Message}"); return default(T); } #else return default(T); #endif } // 带版本检查的API调用 public void VersionSafeApiCall(Action action, int minSdkVersion) { if (AndroidSdkVersion >= minSdkVersion) { action?.Invoke(); } else { Debug.LogWarning($"API不支持: 需要Android {minSdkVersion}+, 当前版本 {AndroidSdkVersion}"); } } ``` #### 3. 设备震动功能兼容封装 ```csharp // 震动服务封装 public void Vibrate(long milliseconds) { VersionSafeApiCall(() => { #if UNITY_ANDROID && !UNITY_EDITOR using (var vibrator = new AndroidJavaObject( "android.os.Vibrator", GetActivityContext())) { if (AndroidSdkVersion >= 26) // Android 8.0+ { using (var vibrationEffect = new AndroidJavaClass( "android.os.VibrationEffect") .CallStatic<AndroidJavaObject>("createOneShot", milliseconds, 1)) // 默认振幅 { vibrator.Call("vibrate", vibrationEffect); } } else { vibrator.Call("vibrate", milliseconds); } } #endif }, 1); // 所有Android版本都支持基础震动 } // 获取Android上下文 private AndroidJavaObject GetActivityContext() { #if UNITY_ANDROID && !UNITY_EDITOR using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { return unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); } #else return null; #endif } ``` #### 4. 权限管理封装 ```csharp // 权限请求封装 public void RequestPermission(string permission, Action<bool> callback) { #if UNITY_ANDROID && !UNITY_EDITOR if (AndroidSdkVersion < 23) // 6.0以下无需运行时权限 { callback?.Invoke(true); return; } if (Permission.HasUserAuthorizedPermission(permission)) { callback?.Invoke(true); return; } var callbacks = new PermissionCallbacks(); callbacks.PermissionGranted += _ => callback?.Invoke(true); callbacks.PermissionDenied += _ => callback?.Invoke(false); callbacks.PermissionDeniedAndDontAskAgain += _ => callback?.Invoke(false); Permission.RequestUserPermission(permission, callbacks); #else callback?.Invoke(true); // 非Android平台默认允许 #endif } // 带解释的权限请求 public IEnumerator RequestPermissionWithExplanation(string permission, string title, string message, Action<bool> callback) { if (!Permission.HasUserAuthorizedPermission(permission)) { // 先检查是否应该显示解释 bool shouldShow = Permission.ShouldShowRequestPermissionRationale(permission); if (shouldShow) { // 显示自定义解释UI yield return ShowPermissionDialog(title, message); } RequestPermission(permission, callback); } else { callback?.Invoke(true); } } ``` #### 5. 厂商特定功能兼容层 ```csharp // 厂商特定功能路由 public void ExecuteManufacturerSpecificFeature() { switch (DeviceManufacturer) { case "samsung": ExecuteSamsungFeature(); break; case "xiaomi": ExecuteXiaomiFeature(); break; case "huawei": ExecuteHuaweiFeature(); break; default: ExecuteGenericFeature(); break; } } private void ExecuteSamsungFeature() { // 三星设备专用实现 #if UNITY_ANDROID && !UNITY_EDITOR try { using (var samsungClass = new AndroidJavaClass("com.samsung.android.feature.SemFeature")) { // 三星特定API调用 } } catch { // 回退到通用实现 ExecuteGenericFeature(); } #endif } private void ExecuteGenericFeature() { // 通用实现 } ``` #### 6. 最佳实践建议 1. **API版本检查策略**: ```csharp // 使用常量定义关键API版本 public const int OREO = 26; // Android 8.0 public const int PIE = 28; // Android 9.0 public bool IsFeatureSupported(int minVersion) { return AndroidSdkVersion >= minVersion; } ``` 2. **安全调用模式**: ```csharp public T SafeApiCall<T>(Func<T> apiCall, T defaultValue) { try { return apiCall.Invoke(); } catch (Exception e) { Debug.LogWarning($"API调用失败: {e.Message}"); return defaultValue; } } ``` 3. **异步操作封装**: ```csharp public void RunOnUiThread(Action action) { #if UNITY_ANDROID && !UNITY_EDITOR GetActivityContext().Call("runOnUiThread", new AndroidJavaRunnable(action)); #else action?.Invoke(); #endif } ``` 4. **缓存优化**: ```csharp private AndroidJavaObject _cachedContext; public AndroidJavaObject Context { get { if (_cachedContext == null) { _cachedContext = GetActivityContext(); } return _cachedContext; } } ``` ### 关键兼容性处理点 1. **Android版本差异**: - 使用`Build.VERSION.SDK_INT`精确检测API级别 - 为关键功能提供向后兼容实现 2. **权限系统变化**: - Android 6.0+ 需要运行时权限处理 - 使用Unity的`Permission`类简化流程 3. **厂商定制系统**: - 检测设备制造商(三星、小米等) - 为特定厂商提供优化实现 - 保持通用后备方案 4. **后台限制**: ```csharp // 检查后台限制 public bool IsBackgroundRestricted() { if (AndroidSdkVersion < 28) return false; // Android 9.0以下无限制 using (var activityManager = Context.Call<AndroidJavaObject>( "getSystemService", "activity")) { return activityManager.Call<bool>("isBackgroundRestricted"); } } ``` ### 使用示例 ```csharp // 在游戏代码中的调用示例 void StartVibration() { // 请求震动权限 AndroidCompatibilityManager.Instance.RequestPermission( "android.permission.VIBRATE", (granted) => { if (granted) { // 执行设备兼容的震动 AndroidCompatibilityManager.Instance.Vibrate(200); } else { Debug.Log("震动权限被拒绝"); } }); } ``` ### 结论 实现高质量的Android API兼容封装需要: 1. **分层架构**:基础层处理JNI交互,兼容层处理版本差异 2. **条件执行**:基于`SDK_INT`动态选择API实现 3. **厂商适配**:针对主流设备厂商特殊处理 4. **权限管理**:完善运行时权限请求流程 5. **异常处理**:健壮的错误捕获和回退机制 此方案覆盖了Android 5.0+的设备兼容性要求,可扩展至各种Android原生功能封装[^1][^3]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苏小败在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值