Giter Site home page Giter Site logo

androidq's Introduction

文档说明

  1. 本文档基于谷歌AndroidQ官方文档华为Q版本应用兼容性整改指导(华为的有点过时)

  2. 所用测试机:Google初代Pixel,AndroidQ-beta6-190730.005

  3. 版本号对应关系

    Android-Q = Android-10 = Api29

    Android-P = Android-9.0 = Api28

设备硬件标识符访问限制

限制应用访问不可重设的设备识别码,如 IMEI、序列号等,系统应用不受影响。

原来的做法

// 返回:866976045261713; 
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.getDeviceId();
//返回:66J0218B19000977; 
Build.getSerial();
  1. 在低于AndroidQ的系统上没问题

  2. 在AndroidQ及以上的系统上运行时:

    • targetSdkVersion<Q,返回null或“unknown”;

    • targetSdkVersion>=Q,抛异常:

      SecurityException: getDeviceId: The user 10196 does not meet the requirements to access device identifiers.
      

替代方案

  1. 方案一:

    使用AndroidId代替,缺点是应用签署密钥或用户(如系统恢复出产设置)不同返回的Id不同。与实际测试结果相符。

    经实际测试:相同签名密钥的不同应用androidId相同,不同签名的应用androidId不同。恢复出产设置或升级系统没测。

    // 返回:496836e3a48d2d9d
    String androidId = Settings.System.getString(context.getContentResolver(),
            Settings.Secure.ANDROID_ID);
  2. 方案二:

    通过硬件信息拼接,缺点是还是不能保证唯一。 经测试:似乎与方案一比更稳定,不受密钥影响,但非官方建议,没安全感。

    private static String makeDeviceId(Context context) {
        String  deviceInfo = new StringBuilder()
                .append(Build.BOARD).append("#")
                .append(Build.BRAND).append("#")
            	   //CPU_ABI,这个值和appp使用的so库是arm64-v8a还是armeabi-v7a有关,舍弃
                //.append(Build.CPU_ABI).append("#")
                .append(Build.DEVICE).append("#")
                .append(Build.DISPLAY).append("#")
                .append(Build.HOST).append("#")
                .append(Build.ID).append("#")
                .append(Build.MANUFACTURER).append("#")
                .append(Build.MODEL).append("#")
                .append(Build.PRODUCT).append("#")
                .append(Build.TAGS).append("#")
                .append(Build.TYPE).append("#")
                .append(Build.USER).append("#")
                .toString();
        try {
           //22a49a46-b39e-36d1-b75f-a0d0b9c72d6c
           return UUID.nameUUIDFromBytes(deviceInfo.getBytes("utf8")).toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String androidId = Settings.System.getString(context.getContentResolver(),
                 Settings.Secure.ANDROID_ID);
        return androidId;
    }

禁止后台启动Activity

官方文档

情况描述

  1. AndroidQ上,后台启动Activity会被系统忽略,不管targetSdkVersion多少;
  2. AndroidQ上,即返回应用有前台服务也不行;
  3. AndroidQ以下版本没影响。

解决方法

发送全屏通知:

//AndroidManifest 声明新权限,不用动态申请
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>

Intent intent = new Intent(this, ScopedStorageActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
        REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, Constants.CHANNEL_ID)
        .setSmallIcon(R.drawable.ic_launcher_foreground)
        .setContentTitle("Incoming call")
        .setContentText("(919) 555-1234")
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setCategory(NotificationCompat.CATEGORY_ALARM)
        //设置全屏通知后,发送通知直接启动Activity
        .setFullScreenIntent(pendingIntent, true)
        .build();
NotificationManager manager = getSystemService(NotificationManager.class);
manager.notify(445456, notification);

但是:在华为mate20(Api-28)上需要到设置中打开横幅通知;原生AndroidQ(beta6)上有效。

后台应用增加定位限制

官方文档

情况描述

  1. 后台应用要获取位置信息需要动态申请权限,

    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

  2. 在AndroidQ上运行:

    • targetSdkVersion<Q,没影响,申请权限时系统默认会加上后台位置权限
    • targetSdkVersion>=Q,需申请;
    • 应用变为后台应用90s后开始定位失败(Pixel AndroidQ-beta6)
  3. ACCESS_BACKGROUND_LOCATION不能单独申请,需要和ACCESS_COARSE_LOCATION/ACCESS_FINE_LOCATION一起申请

解决方法

  1. 动态申请即可;

  2. 启动前台服务

    <!-需要设置foregroundServiceType为“location” ->
    <service 
     android:name=".permission.LocationService"
        android:foregroundServiceType="location"/>

分区存储

官方文档

应用只能访问自己过滤视图下的文件或XXXX,无权访问其他文件目录。

情况描述

  1. 在AndroidQ上运行: * targetSdkVersion<Q,没影响; * targetSdkVersion>=Q,默认启用过滤视图,应用以外的文件需要通过存储访问框架(SAF,StorageAccessFramework)读写。

解决方法

方法一、停用过滤视图,使用旧版存储模式
<manifest ... >
  <!-- This attribute is "false" by default on apps targeting Android Q. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>
方法二、将文件存储到过滤视图中
// /Android/data/com.example.androidq/files/Documents
File dir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);

**优点:**不用申请读写权限;

**缺点:**随应用卸载而删除;

方法三、使用存储访问框架(SAF),由用户指定要读写的文件。

​ 这个功能Android 4.4(API: 19)就有,官方文档在此

方法四、获取用户指定的某个目录的读写权限

​ 这种方式从Android5.0(Api 21)开始就有了。官方文档

  1. 申请目录的访问权限

    会打开系统的文件目录,由用户自己选择允许访问的目录,不用申请WRITE/READ_EXTERNAL_STORAGE权限。

    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | 
            Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);				
    startActivityForResult(intent, REQ_CODE);

    执行上述代码后会出现类似如下图界面,点击‘允许访问“DuoKan”’按钮,

    允许了之后通过onActivityResult()intent.getData()得到该目录的Uri,通过Uri可获取子目录和文件。这种方式的缺点是应用重装后权限失效,即使可以保存了这个Uri也没用。

    Uri dirUri = intent.getData();
    // 持久化;应用重装后权限失效,即使知道这个uri也没用
    SPUtil.setValue(this, SP_DOC_KEY, dirUri.toString());
    //重要:少这行代码手机重启后会失去权限
    getContentResolver().takePersistableUriPermission(dirUri, 
            Intent.FLAG_GRANT_READ_URI_PERMISSION);
  2. 通过Uri读写文件

    • 创建文件

      // 在mUri目录(‘DuoKan’目录)下创建'test.txt'文件
      private void createFile() {
          DocumentFile documentFile = DocumentFile.fromTreeUri(this, mUri);
          DocumentFile file = documentFile.createFile("text/plain", "test.txt");
          if (file != null && file.exists()) {
              LogUtil.log(file.getName() + " created");
          }
      }

      主要用到DocumentFile类,和File类的方法类似,有isFile、isDirectory、exists、listFiles等方法

    • 删除文件

      //删除"test.txt"
      private void deleteFile() {
          DocumentFile documentFile = DocumentFile.fromTreeUri(this, mUri);
          // listFiles(),列出所有的子文件和文件夹
          for (DocumentFile file : documentFile.listFiles()) {
              if (file.isFile() && "test.txt".equals(file.getName())) {
                  boolean delete = file.delete();
                  LogUtil.log("deleteFile: " + delete);
                  break;
              }
          }
      }
    • 写入数据

      private void writeFile(Uri uri) {
          try {
              ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "w");
              //这种方法会覆盖原来文件内容
              OutputStreamWriter output = 
                      new OutputStreamWriter(new FileOutputStream(pfd.getFileDescriptor()));
              // 不能传uri.toString(),否则FileNotFoundException
              // OutputStreamWriter output = new OutputStreamWriter(new FileOutputStream(uri.toString(), true));
              output.write("这是一段文件写入测试\n");
              output.close();
              LogUtil.log("写入成功。");
          } catch (IOException e) {
              LogUtil.log(e);
          }
      }

androidq's People

Stargazers

Jimmy avatar 杨培星 avatar Ren  Yu avatar

Watchers

Ren  Yu avatar  avatar

androidq's Issues

访问剪贴板数据

Android Q
开发者网站上说
访问剪贴板数据
除非您的应用是默认输入法 (IME) 或是目前处于焦点的应用,否则应用无法访问剪贴板数据。

现在需要在Acitivity onCreate()时访问剪贴板数据, 这时候返回为空. 但是在某个view的setOnclickListener{}时是可以获取到的m 目前好像没有好的解决办法
临时解决办法:
1.在onCreate()时执行某个view的performClick()方法, 需要postDelay({view.performClick()}, 1)
在onClickListener{}获取剪贴板数据
2.或者是, 直接在onCreate()延迟执行获取剪贴板数据的方法, 比如延迟500毫秒, 这时候也可以获取到

请问作者有其他好的解决办法吗, 目前发现淘宝是正常的

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.