android-Android相关记录

android-Android相关记录


不错的第三方库

网络


AndroidManifest 配置文件

android:configChanges

相关参考:

mcc:IMSI(国际移动用户识别码)发生改变,检测到SIM卡,或者更新MCC
mnc:IMSI网络发生改变,检测到SIM卡,或者更新MCC
其中mcc和mnc理论上不可能发生变化
locale:语言发生改变,用户选择了一个新的语言,文字应该重新显示
touchscreen:触摸屏发生改变,这通常是不应该发生的
keyboard:键盘类型发生改变,例如,用户使用了外部键盘
keyboardHidden:键盘发生改变,例如,用户使用了硬件键盘(这里说的键盘是硬键盘,自己手机里弹出的是软键盘)
navigation:导航发生改变,(这通常不应该发生) 举例:连接蓝牙键盘,连接后确实导致了navigation的类型发生变化。因为连接蓝牙键盘后,
我可以使用方向键来navigate了
screenLayout:屏幕的布局发生改变,这可能导致激活不同的显示
fontScale:全局字体大小缩放发生改变
orientation:设备旋转,横向显示和竖向显示模式切换。
screenSize: 屏幕大小改变了

smallestScreenSize: 屏幕的物理大小改变了,如:连接到一个外部的屏幕上


安卓提取 sha1

方法一 (推荐)

其实主要就是签名文件 keystore 文件里的信息, 可以直接找到 debug版本(在 %USER%.android 目录下) 和 release版本(自己生成) 的 keystore文件 拿出来查看一下就行.

1
2
3
4
>keytool -list -v -keystore debug.keystore
证书指纹:
MD5: 20:06:20aaaaaaaaaaaaaaaaaaaaa
SHA1: 11:E3:F8:aaaaaaaaaaaaaaaaaaa

方法二

安卓apk 包用 压缩工具打开, 找到 META-INF/ANDROIDD.RSA 文件, 提取出来, 然后使用java工具 keytool.exe (在%JDK%/bin 目录下) 打开,

1
> keytool -printcert -file ANDROIDD.RSA

参考: http://www.voidcn.com/article/p-gpujntey-ps.html


打印堆栈

1
Thread.dumpStack();

导出 logcat 日志

1
$ adb logcat > temp.txt

读取文件

读取 外部 目录需要申请权限

不申请的话会报错: android java.io.FileNotFoundException (Permission denied)

  • 申请权限代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
        private void checkPermission(final Context context, final Define.CodeRunnable task) {
    if (!Tools.isPermOk(context, Manifest.permission.READ_EXTERNAL_STORAGE)
    || !Tools.isPermOk(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
    String[] reqPers = new String[]{
    Manifest.permission.READ_EXTERNAL_STORAGE, // 读
    Manifest.permission.WRITE_EXTERNAL_STORAGE // 写
    };
    ActivityMgr.getIns().setActivity(this);
    ActivityMgr.PerRunnable perRunnable = perReq -> {
    int code = MyCode.ECode.Ok;
    if (!Tools.isPermOk(context, Manifest.permission.READ_EXTERNAL_STORAGE)
    || !Tools.isPermOk(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
    code = MyCode.ECode.LocPermissionError;
    }
    task.run(code, "");
    };
    ActivityMgr.getIns().reqPermissions(reqPers, ActivityMgr.EPerCode.Location, perRunnable);
    } else {
    task.run(MyCode.ECode.Ok, "");
    }
    }



    ---

    ### pc 与 android 互传数据

    - https://www.cnblogs.com/liqw/p/5138774.html



    ---

    ### 包名, 签名, versionCode 对升级的影响

    - Android 应用的 apk 包名和签名一致和不一致时对软件升级有什么样的影响? - https://www.zhihu.com/question/19890258



    包名不一致升级 会安装新的APK,旧的APK不会管。签名不一致,包名一致的话,需要先卸载旧的APK 才能安装新版的。

    在应用程序被升级的时候,Android系统将会验证被升级的应用程序包与升级后的应用程序包是否使用了同样的开发者签名,如果一致,该应用程序可以被升级;如果不一致,那么将被视为非同一开发者开发的应用程序,用户需要先卸载已经安装的应用然后再安装新应用,在卸载的过程中,应用在android系统中所保存的设置信息(SavedPreferences)将被删除,以保护应用本地保存的资料不被盗取。



    #### apk 应用内升级

    必须要求 包名, 签名 相同, 且 `version code` 必须提升 (不提升会导致新包安装失败, 报已存在的错: `Failure [INSTALL_FAILED_ALREADY_EXISTS]`)



    ---

    ### Android System.Load vs System.LoadLibrary

    - http://www.voidcn.com/article/p-gzsrngyk-bom.html

    简而言之就是,System.Load 要带上 so 的绝对路径;而 System.LoadLibrary 不应该带上路径,并且不应该带上 lib 前缀和 .so 后缀。比如,你有一个应用,其 package 为 com.test.demo,并且有一个 libtest.so。

    那么,你可以这样去加载它:

    1. System.Load(getApplicationInfo().nativeLibraryDir + "/libtest.so");

    2. System.LoadLibrary("test");



    ---

    ### Handler

    用于线程间的交互, 与创建的上下文紧密相关

    1. 在 主线程 创建的 *handler*, 就可以在 *handleMessage* 中处理 ui 相关的 api

    ```java
    if (mHandlerTips == null) {
    mHandlerTips = new Handler(Looper.getMainLooper()) { // getMainLooper 表示主循环
    public void handleMessage(Message msg) {
    String showMsg = (String) msg.obj;
    mTipText.setText(showMsg);
    }
    };
    }
  1. 在 其他线程 创建的 handler 则不允许操作 ui 相关的 api, 否则会 闪退

URI 解析异常解决

解决 Android N 7.0 上 报错:android.os.FileUriExposedException android.os.FileUriExposedException: file:///storage/emulated/0/xxx.xxx exposed beyond app through Intent.getData() 崩溃异常

If your targetSdkVersion is 24 or higher, we have to use FileProvider class to give access to the particular file or folder to make them accessible for other apps.


8.0+ 安装 apk


安装APK时解析包错误问题


获取 meta-data 值

可以获取 Manifest.xml 中 activity/application/service/receiver 中定义的 meta-data 值


Android string 中 translatable 作用

意思就是不管系统是什么语言,它都只显示xx, 也就是不参与国际化的意思.


gmail 发邮件 添加附件

在Android中,调用Email有三种类型的Intent:

  • Intent.ACTION_SENDTO 无附件的发送

  • Intent.ACTION_SEND 带附件的发送

  • Intent.ACTION_SEND_MULTIPLE 带有多附件的发送

当然,所谓的调用 Email,只是说 Email 可以接收Intent并做这些事情,可能也有其他的应用程序实现了相关功能,所以在执行的时候,会出现选择框进行选择。

使用 ACTION_SEND 会弹出很多客户端选择, 而 ACTION_SENDTO 又无法带上附件, 所以 正真解决办法数组合这两个 Intent.

1
2
3
4
5
6
7
8
9
10
11
12
13
 Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"jon@example.com"}); // recipients
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
emailIntent.putExtra(Intent.EXTRA_STREAM, Tools.getUri(MainActivity.Instance, tempFileName));

Intent emailSelectorIntent = new Intent(Intent.ACTION_SENDTO);
emailSelectorIntent.setData(Uri.parse("mailto:"));
emailIntent.setSelector(emailSelectorIntent);

startActivity(emailIntent);

参考: https://stackoverflow.com/questions/4883199/using-android-intent-action-send-for-sending-email


service


activity

添加 activity

  1. 创建布局文件

  2. 创建 对应的 activity 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class DynPlatIdActivity extends AppCompatActivity {

    private static final String TAG = "--- DynPlatIdActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dynamic_platid); // 对应的 xml 布局文件
    }
    }
  3. AndroidManifest.xml 声明这个 activity (不声明无法跳转)

    1
    2
    3
    4
    5
    6
    <application
    <activity
    android:name="com.its.demo.testgoogle.activity.DynPlatIdActivity"
    android:label="@string/app_name"
    android:screenOrientation="portrait" />
    </application>
  4. done. 测试跳转

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
        Intent intent = new Intent(this, DynPlatIdActivity.class);
    startActivity(intent);



    ---

    #### 透传参数

    比如从 activityA 带着参数跳转到 activityB, activityB 再带着参数返回到 activityA.

    - activityA

    ```java
    Intent intent = new Intent(this, DynPlatIdActivity.class);
    intent.putExtra("arg01", "hello world");
    ActivityMgr.getIns().startActForResult(intent, 10001, actReq -> {
    LogUtil.TD(TAG, "--- arg02: %s", actReq.data.getStringExtra("arg02"));
    });
  • activityB

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
        public class DynPlatIdActivity extends AppCompatActivity {

    private static final String TAG = "--- DynPlatIdActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dynamic_platid);

    Intent intent = getIntent();
    String arg01 = intent.getStringExtra("arg01"); // 获取 跳转源 的参数
    LogUtil.TD(TAG, "--- arg01: %s", arg01);

    Intent intentBack = new Intent();
    intentBack.putExtra("arg02", "nice to meet you");
    setResult(RESULT_OK, intentBack); // 返回参数给 跳转源
    finish(); // 结束当前 activity
    }
    }



    ---

    ### 默认签名文件 debug.keystore

    - Android 默认签名, 路劲: `C:\Users\%USER_NAME%\.android\debug.keystore`
    - Keystore password: android
    - Key alias: androiddebugkey
    - Key password: android



    ---

    ### adb 常用命令

    - adb shell 常用命令 - https://www.cnblogs.com/JianXu/p/5161179.html



    #### 文件操作命令

    | 子命令 | 参数 | 说明 |
    | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
    | cd | 无 | 进入目录 |
    | cat | [-beflnstuv] [-B bsize] [file...] | 查看文件内容 -n:显示行号 -b:显示行号,但会忽略空行 -s:显示行号,连续空行标记为一行 |
    | df | 无 | 列出分区列表 |
    | du | [-H] [-L] [-P] [-a] [-d depth] [-s] [-cghikmnrx] [file...] | 查询文件或目录的磁盘使用空间 |
    | ls | [-a] [-i] [-l] [-n] [-s] | 列出目录内容 -a:列出所有文件,包括隐藏文件 -i:输出文件的i节点的索引信息 -l列出文件的详细信息 -n:用数字的GUID代替名称 -s:输出该文件的大小 |
    | grep | [-abcDEFGHhliJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num] [-e pattern] [-f file] [--binary-files=value] [--color=when] [--context=num] [--directories=action] [--lable] [--line-buffered] [pattern] [file...] | 指定文件中搜索特定的内容,并将含有这些内容的行标准输出 |
    | mkdir | -p,-parents | 创建目录 -p,--parents:递归创建目录 |
    | touch | touch [-alm] [-t YYYYMMDD [.HHMMSS]] < file > | 创建文件 |
    | rm | rm [-f\|-i][-dPRrvWx]file | 删除文件 -f:强制删除文件,系统不提示 -i:交互式删除,删除前提示 -d:改变硬连接数据删成0,删除该文件 -r:强制删除文件夹包括里面的文件 |
    | mv | mv[-fiv]source target | 移动文件(相当于剪切) -f:强制移动,若文件已经存在目标则直接覆盖 -i:若目标文件已经存在,会询问是否覆盖 |
    | rmdir | rmdir[-p] directory | 删除目录 -p:递归删除目录,只能删除空目录 |
    | dd | dd[operand...] dd if =source of=targe | 复制文件 |



    #### 文件权限命令与其他文件命令

    | 子命令 | 参数 | 说明 |
    | ------ | ------------------------------------------------------------ | ------------------------------------------------------ |
    | chomd | chomd[OPTION]< MODE > < FILE > | 文件权限修改 -R:递归改变文件和目录 -h:不遵循符号连接 |
    | chown | chown[-R[-H\|-L\|-P]] [-fhv] owner : group \| owner \| : group file | 更改某个文件或目录的属主和属组 |
    | md5 | md5 file... | 查询文件的MD5值 |
    | mount | mount [-r] [-w] [-o options] [-t type] device directory | 挂载设备信息 |
    | umount | umount < path > | 卸载分区挂载 |
    | cmp | cmp[-b][-l][-n count] file1 file2 | 要指出两个文件是否存在差异 |
    | ln | ln [-fhinsv] file1 file2 ln [-fhinsv] file...directory | |



    ---

    #### 拷贝文件到电脑 / 拷贝文件到应用

    - Android 用adb pull或push 拷贝手机文件到到电脑上,拷贝手机数据库到电脑上,拷贝电脑数据库到手机上 - https://www.cnblogs.com/liqw/p/5138774.html



    - 拷贝文件到电脑. 拷贝 files 文件夹 `E:\aaa` 目录中

    ```json
    > adb pull /sdcard/Android/data/com.rmgstation.rummymon/files E:\aaa
  • 拷贝文件到应用. 拷贝 E:\aaa 目录 到 files 文件夹中

    1
    > adb push E:\aaa /sdcard/Android/data/com.rmgstation.rummymon/files

命令行导出 logcat 日志

  1. 清楚日志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
        $ adb logcat -c

    2. 导出日志

    ```json
    $ adb logcat -d > C:\Users\wilker\Desktop\logcat.log



    ---

    ### 反编译 apk

    参考:

    - 反编译Android APK详细操作指南 - https://blog.csdn.net/fengyuzhengfan/article/details/80286704
    - 使用工具: APKTool绿色版 v2.3.3 - http://www.xue51.com/soft/5927.html



    使用:

    - 反编译 apk. 执行命令: `apktool d [apk_path]`, 会解出内容到 以 apk 文件名命名的目录中

    ```json
    $ apktool d F:\a_desktop\aaa.2.9.34.apk
    I: Using Apktool 2.3.3 on aaa.2.9.34.apk
    I: Decoding AndroidManifest.xml with resources...
  • 编译 apk. 执行命令: apktool b [apk_dir],

    1
    2
    3
    $ apktool b F:\a_desktop\APKTool_2.3.3\aaa.2.9.34
    I: Using Apktool 2.3.3
    I: Built apk...

aapt

  • 打包好的APK中移除文件

    1
    $ aapt r[emove] [-v] file.{zip,jar,apk} file1 [file2 ...]
  • 添加文件到打包好的APK中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
        $ aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]



    ---

    ### 跳转系统设置 Settings 的各个界面

    - Android跳转系统设置Settings的各个界面 - https://blog.csdn.net/ouzhuangzhuang/article/details/84029295
    - Android中 跳转到系统设置界面方法总结 - https://blog.csdn.net/da_caoyuan/article/details/72829106



    ---

    ### scheme

    - Android 自定义scheme及多端唤起使用方法 - https://segmentfault.com/a/1190000020616748
    - 解锁Activity的跳转新姿势———使用scheme跳转 - https://juejin.im/entry/6844903506822840328



    目的是可以让 浏览页 或 其他 app 直接打开 某个 app 的某个 activity

    比如 app A 打卡 app B 的 activity C

    1. 在 B 中的 *AndroidManifest.xml* 中配置 C 的 scheme

    ```xml
    <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
    android:name="com.its.demo.testgoogle.MainActivity"
    android:label="@string/app_name"
    android:theme="@style/AppTheme.NoActionBar">

    <!-- scheme 跳转配置 -->
    <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data
    android:host="hello"
    android:port="9527"
    android:scheme="testgoogle" />
    </intent-filter>
    </activity>
    </application>
    • 在这个 activity 中打印一下 跳转过来时的参数

      1
      2
      3
      Intent intent = getIntent();
      Uri uri = intent.getData();
      Log.d(TAG, "uri: " + (uri != null ? uri.toString() : "is null"));
  1. 在 A 中跳转

    1
    2
    String url = "testgoogle://hello:9527/world?name=wolegequ&age=123";
    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));

    然后在 C 中就可以打出 log

    1
    --- MainActivity: uri: testgoogle://hello:9527/world?name=wolegequ&age=123

    也可以在网页中跳转

    1
    <a href="testgoogle://hello:9527/world?name=wolegequ&age=123">打开 test app</a>

adb 广播消息

adb shell am broadcast 后面的参数有:

1
2
3
4
5
6
7
8
9
[-a <ACTION>]
[-d <DATA_URI>]
[-t <MIME_TYPE>]
[-c <CATEGORY> [-c <CATEGORY>] ...]
[-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]
[--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]
[-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]
[-n <COMPONENT>]
[-f <FLAGS>] [<URI>]

例如:

1
2
3
4
5
6
$ adb shell am broadcast -a com.android.test --es test_string "this is test string" --ei test_int 100 --ez test_boolean true
// 说明:蓝色为key,红色为alue,分别为String类型,int类型,boolean类型

$ adb shell am broadcast -a com.android.vending.INSTALL_REFERRER --es referrer "utm_source=adwasadasd123123"
Broadcasting: Intent { act=com.android.vending.INSTALL_REFERRER (has extras) }
Broadcast completed: result=0

唯一标识符 - uuid

总体情况可以分为两种:

  1. 安装 id.

    即第一次启动时生成一个唯一id, 使用 java 库即可: UUID.randomUUID().toString(). 以后都是用这个 id. 但是这种每次清除缓存或重装后都会变化.

  2. 设备 id.

    使用硬件相关信息拼在一起, 只要硬件不变, 就不会变换, 无视 app 清除缓存或重装.

这两总方式最好都 md5 哈希一下, 是为了确保 数据等长 和 无法肉眼识别是怎么计算出来的.


获取 ANDROID_ID

  • 这个 id 获取是 Android 支持的. 同一个签名打出来的包, 即使包名不同, 都能获取到相同的值.

    因为签名是 APP 所有权的证明, 同个签名打出来的包都是可以公用相同设备 id

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try {
    // 旧方式, 已废弃
    // String uid = Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID);
    String uid = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
    return EncryptTool.md5Str(uid);
    } catch (Exception e) {
    e.printStackTrace();
    return null;
    }
    • 获取到的值最好也 md5, sha256 处理一下变成固定长度的字符串

.9图报错

报错: top-left corner pixel must be either opaque white or transparent.

参考:


apk 加固 套壳

内嵌 apk


ViewPager2


material 主题使用

  1. gradle 中引入 material 库

    1
    implementation 'com.google.android.material:material:1.1.0'
  2. styles.xml 中 修改 AppTheme 主题为 Theme.MaterialComponents.Light.DarkActionBar

    1
    2
    3
    4
    5
    <resources>
    // <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
    </style>
    </resources>
    • 如果这里没有修改, 会报错: The style on this component requires your app theme to be Theme.MaterialComponents
  3. 然后就可以在 layout 中使用了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
        <com.google.android.material.button.MaterialButton
    android:id="@+id/button2"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:textAllCaps="false"
    android:background="#FFFFFF"
    android:text="clear"
    android:textAlignment="viewEnd" />



    ---

    ### adb 命令行

    - adb logcat 命令详解 log过滤 - https://blog.csdn.net/liao277218962/article/details/50129009



    ---

    ### Android 适配

    #### 11

    - 拖不得了,Android11真的要来了,最全适配实践指南奉上 - https://juejin.cn/post/6860370635664261128
    - Android 11适配方案变更及适配攻略 - https://www.jianshu.com/p/aa21c40e8c2f



    #### 12

    - Android 12 快速适配要点 https://zhuanlan.zhihu.com/p/440582410



    ---

    #### Android 11 访问第三方 app 问题

    解决 Intent.resolveActivity(context.getPackageManager()) == null

    - https://blog.csdn.net/u012452490/article/details/113125945
    - https://blog.csdn.net/weixin_40008286/article/details/122342038



    ---

    ### 安装 apk 显示成功却看不到

    在 AndroidManifest.xml 中配置的启动 activity 缺少 `<intent-filter>` 参数

    ```json
    <activity
    android:name="com.its.demo.testgoogle.page.PageMain"
    android:exported="true"
    android:label="@string/app_name"
    android:screenOrientation="portrait">

    <!-- 缺少这个配置 -->
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>

参考: https://cxyzjd.com/article/a947050390/101848671


公共数据存储

Android系统一共提供了四种数据存储方式。分别是:SharePreference、SQLite、Content Provider和File;此外还有一种网络存储。由于Android系统中,数据基本都是私有的,都是存放于“data/data/程序包名”目录下,所以要实现数据共享,正确方式是使用Content Provider。


一加移除安装权限

  • 用adb指令, 禁用安全应用安装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
        $ adb shell pm disable-user com.oplus.appdetail



    ---

    ### http 请求不安全

    报错: `CLEARTEXT communication to xx.xx.xx.xx not permitted by network security policy`

    原因是 在安卓 P+ 使用了 http 去请求, 就会报这个错

    - 解决办法:

    1. 在 *res*下新增一个 xml 目录,然后创建一个名为:network_security_config.xml

    ```xml
    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
    <base-config cleartextTrafficPermitted="true" />
    </network-security-config>
    1. 后在AndroidManifest.xml文件下的application标签增加以下属性:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
              <application
      ...
      android:networkSecurityConfig="@xml/network_security_config"
      ...
      />



      参考: https://developer.aliyun.com/article/657574



      ---

      ### facebook 登录闪退

      报错: `Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent`

      - 原因 fb sdk 太旧了, 升级一下 sdk 即可, 参考: https://stackoverflow.com/questions/69081726/android-facebook-login-library-11-2-0-crashes-android-12-targeting-s-version



      ---

      ### 安装 aab 找不到 so, 安装 apk 正常

      - 安装 aab 找不到 so, 安装其反编译出来的 apk 正常
      - 换了几部手机测试安装 aab 没有问题
      - 找不到 so 的那台手机, 据说是 adb 安装对该手机支持不友好, 换多几部测试



      ---

      ### 真机打不开 http 地址

      - 报错: `NET::ERR_CLEARTEXT_NOT_PERMITTED`

      - 解决办法: 在 manifest.xml 的 application 标签中添加 `android:usesCleartextTraffic="true"`, 如:

      ```json
      <application
      android:usesCleartextTraffic="true"
      ...>
      </application>

webview 打开 h5 游戏网页黑屏

  • 原因是 activity 未开启硬件加速, 打开即可

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
        <activity
    android:hardwareAccelerated="true"
    >



    ---

    ### 布局报错找不到 id

    - 报错: `Missing required view with ID`
    - 解决办法: rebuild 一下工程即可



    ---

    ### 动态权限判断一直返回失败

    - 授权后, 权限判断 `ActivityCompat.checkSelfPermission(context, permission)` 一直返回失败
    - 原因是没有在 `AndroidManifest.xml` 中配置相应的权限, 配置上即可, 授权后动态判断就会返回成功了



    ---

    ### 方法数量超过 64k

    - 报错

    ```json
    Execution failed for task ':launcher:mergeDexRelease'.
    > A failure occurred while executing com.android.build.gradle.internal.tasks.DexMergingTaskDelegate
    > There was a failure while executing work items
    > A failure occurred while executing com.android.build.gradle.internal.tasks.DexMergingWorkAction
    > com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
    The number of method references in a .dex file cannot exceed 64K.
    Learn how to resolve this issue at https://developer.android.com/tools/building/multidex.html
  • 解决办法, 在 gradle 中设置支持多个 Dex.

    参考: https://stackoverflow.com/questions/36785014/the-number-of-method-references-in-a-dex-file-cannot-exceed-64k-api-17

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    android {
    defaultConfig {
    multiDexEnabled true
    }
    }

    dependencies {
    // implementation 'androidx.multidex:multidex:2.0.1' //with androidx libraries
    implementation 'com.android.support:multidex:1.0.3' //with support libraries
    }

找不到约束属性

  • 报错: Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/recyclerview/widget/RecyclerView$Adapter;

  • 解决办法: gradle 引入库即可

    1
    2
    3
    dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    }

webview 加载 cocos

实测只能在引擎为 2.4.6 版本一下, 3.7.2 会报错

  1. 将网页放入到 assets 目录, 如 myapps 网页目录

  2. 加入联网权限

    1
    <uses-permission android:name="android.permission.INTERNET"/>
  3. 加载本地代码

    1
    2
    3
    4
    5
    6
    // webview 需要设置允许 file:// 协议加载
    WebSettings settings = _webView.getSettings();
    settings.setAllowFileAccessFromFileURLs(true);
    settings.setAllowUniversalAccessFromFileURLs(true);
    // 加载路径
    mWebView.loadUrl("file:///android_asset/myapps/index.html");

open 新工程看不到目录

  • 来回电几下 Home 和 Desktop 按钮, 再粘贴目录就能刷新出来

    20240327_182855


修改代码不生效

直接禁用 Instant Run 功能。
Android Studio升级3.5后,Instant Run 用 HotSwap代替了
打开设置 -> Build,Execution,Deployment -> Debugger -> HotSwap
取消勾选 Enable hot-swap agent for Groovy code ,就可以了。

image-20240327185132905


自动导入依赖包

image-20240328112327066