Android 7.x 系统路径访问受限

本文记录了解决游戏在Android新系统上遇到的本地文件路径访问问题。原先使用相对路径的方法在7.x以上系统出现问题,通过使用System.Uri(url).AbsoluteUri解决了不同平台的路径兼容性。

前言:辛苦一年的游戏终于测试了,当然也会遇到各自各样的问题。这里记录下android新系统导致的路径访问问题。

  • 以前的做法
    我们访问文件的本地路径,都是使用的www去访问相对路径,比如 “data/data//.unity3d”
    当然这样在我们的内部测试中是完全没有任何问题的,内部测试的手机都是7.x以下的系统,但是当我们封测的时候,有些玩家的手机是7.x以后买的手机,而现在的厂商一般会配置最新的android系统,所以会出现访问不到本地路径。

具体后台日志报错: \data\data***(这里是我们游戏的bundle id)\Project_Scene_Denglu.unity3d is not an absolute path!


  • 现在的做法
    为了适应不同平台的访问路径不同,我统一了写法,这样在访问不同的平台也就能够兼容绝对路径了

url = new System.Uri(url).AbsoluteUri; //这个路径可以统一到任何平台的绝对路径,经过测试后发现这个是ok的~

那你觉得我这样的想法怎么样,可不可行?我要说明的是SearchResultActivity弹出提示框内容几乎完美,我不知道能不能让HomeFragment也改成跟SearchResultActivity一样,或者说让HomeFragment跟SearchResultActivity做到数据互用?只不过这两个不属于同一个Activity,因为HomeFragment和MapFragment他们都是属于MainActivity的,我的MainActivity代码如下:package com.example.bus; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.navigation.NavController; import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; import com.example.bus.databinding.ActivityMainBinding; import com.google.android.material.bottomnavigation.BottomNavigationView; public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private static final String SAVED_NAV_ID = "saved_nav_id"; private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001; // 删除 shouldNavigateToMap 标志位(它是罪魁祸首) // 改为传入 Runnable 控制后续行为 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); BottomNavigationView navView = findViewById(R.id.nav_view); AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( R.id.navigation_home, R.id.navigation_map, R.id.navigation_settings) .build(); NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main); NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); NavigationUI.setupWithNavController(navView, navController); // 设置 BottomNavigationView 的监听器 navView.setOnItemSelectedListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.navigation_map) { // 只有点击地图 tab 才走权限 + 跳转逻辑 ensureFineLocationPermission(() -> { if (navController.getCurrentDestination().getId() != R.id.navigation_map) { navController.navigate(R.id.navigation_map); } }); } else { navController.navigate(itemId); } return true; }); if (savedInstanceState != null) { int savedId = savedInstanceState.getInt(SAVED_NAV_ID, R.id.navigation_home); navView.setSelectedItemId(savedId); } } /** * 统一权限请求方法,权限通过后执行 onGranted */ public void ensureFineLocationPermission(Runnable onGranted) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { onGranted.run(); } else { requestLocationPermission(onGranted); } } private void requestLocationPermission(Runnable onGranted) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { new AlertDialog.Builder(this) .setTitle("需要精确定位权限") .setMessage("为了提供更好的服务,我们需要获取您的精确位置。\n否则将无法使用地图相关功能。\n\n请务必选择【允许】或【仅限这一次】。") .setPositiveButton("去允许", (d, w) -> startRequestPermission(onGranted)) .setNegativeButton("取消", null) .show(); } else { startRequestPermission(onGranted); } } private void startRequestPermission(Runnable onGranted) { // 使用成员变量保存回调 this.pendingRunnable = onGranted; ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); } private Runnable pendingRunnable; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; if (granted) { Toast.makeText(this, "已获得精确定位权限", Toast.LENGTH_SHORT).show(); // 执行传进来的任务(可能是跳转 SearchResultActivity 或进入 MapFragment) if (pendingRunnable != null) { pendingRunnable.run(); pendingRunnable = null; } } else { boolean hasCoarse = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED; if (hasCoarse) { new AlertDialog.Builder(this) .setTitle("需要精确位置") .setMessage("检测到您使用的是【大致位置】,这会导致地图功能无法正常使用。\n\n" + "请在设置中将定位权限修改为【精确位置】。") .setPositiveButton("去设置", (d, w) -> openAppSettings()) .setNegativeButton("取消", null) .show(); } else { Toast.makeText(this, "定位权限未授予,部分功能受限", Toast.LENGTH_LONG).show(); } pendingRunnable = null; // 清理 } } } private void openAppSettings() { Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivity(intent); } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); BottomNavigationView navView = findViewById(R.id.nav_view); outState.putInt(SAVED_NAV_ID, navView.getSelectedItemId()); } } 其实在MainActivity这里,已经做到点击底部地图进入MapFragment会请求用户定位权限,而HomeFragment这里是点击了HomeFragment的搜索按钮才会请求,我想能不能在这也进行一个修改,就是如果让用户点击搜索框就开始请求这个权限,只要用户不同意权限,点击HomeFragment的搜索框就一直请求用户权限这样。其实请求用户权限这跟逻辑可以不用更改,就是按照现在这样,如果用户拒绝的多了他会提示用户去设置手动开启的。而且我的SettingsFragment代码如下:package com.example.bus.ui.settings; import android.os.Bundle; import android.provider.Settings; import android.net.Uri; import android.content.Intent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import com.example.bus.AboutActivity; import com.example.bus.SurveyActivity; import com.example.bus.databinding.FragmentSettingsBinding; import android.app.AlertDialog; public class SettingsFragment extends Fragment { private FragmentSettingsBinding binding; @NonNull @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { SettingsViewModel settingsViewModel = new ViewModelProvider(this).get(SettingsViewModel.class); // 1. 创建 View Binding binding = FragmentSettingsBinding.inflate(inflater, container, false); View root = binding.getRoot(); // final TextView textView = binding.textSettings; // settingsViewModel.getText().observe(getViewLifecycleOwner(), textView::setText); // 2. 获取 ViewModel 并绑定数据 settingsViewModel.getText().observe(getViewLifecycleOwner(), text -> { binding.textSettings.setText(text); }); //新增:为四个按钮设置点击事件 setupButtonListeners(); return root; } /** * 为四个按钮设置点击行为 */ private void setupButtonListeners() { // 按钮1:跳转到应用权限设置页(定位权限请求) binding.btnGps.setOnClickListener(v -> { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", requireContext().getPackageName(), null); intent.setData(uri); startActivity(intent); new AlertDialog.Builder(requireContext()) .setTitle("请手动开启定位权限") .setMessage("进入后请点击【权限】→【位置信息】→ 选择【允许使用期间访问】。\n\n不同手机路径略有差异,请留意。") .setPositiveButton("我知道了", null) .show(); }); // 按钮2:跳转到 "关于APP" binding.btnApps.setOnClickListener(v -> { Intent intent = new Intent(requireContext(), AboutActivity.class); startActivity(intent); }); // 按钮3:跳转到 "用户调研" binding.btnUser.setOnClickListener(v -> { Intent intent = new Intent(requireContext(), SurveyActivity.class); startActivity(intent); }); // 按钮4:退出程序 → 将应用退到后台 binding.btnExit.setOnClickListener(v -> { requireActivity().moveTaskToBack(true); // 安全退出至后台 }); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; // 防止内存泄漏 } }这里面就有一个按钮是跳转到设置的,所以这些也不必担心。现在就是你也来分析分析我刚刚提到的那些内容可不可行,想法如何,包括现在提到的这些
最新发布
12-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值