近期开发小记

181 阅读7分钟

1、framework调试入门及相关资源

  • 内容
    • 安卓源码调试技巧:链接
    • 定制开机动画:链接
    • 刘望舒AOSP下载:链接
    • 调试Android源码的博客:链接
    • Linux学习资源:链接

2、Android原生深色模式理解

  • 内容
    • 单独处理某个应用深色模式的方法:
      1. 在主题中添加<item name="android:forceDarkAllowed">true</item>
      2. 设置整个应用进入深色模式:AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
      3. 设置某个Activity进入深色模式:getDelegate().setLocalNightMode(AppCompatDelegate.MODE_NIGHT_YES);

3、屏蔽WebView的文本控件长按事件

  • 解决方案:通过设置WebView的长按监听并返回true来屏蔽长按事件。
    webView.setLongClickable(true);
    webView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            webView.setHapticFeedbackEnabled(false); // 屏蔽长按振感
            return true; // 屏蔽长按操作
        }
    });
    

4、封装自定义View的兼容性

  • 内容:封装自定义View时,除了开放指定接口外,也应兼容原生接口,如Dialog的isShowingdismisssetOnDismissListener等。
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class CustomDialog extends Dialog {

    private Button closeButton;

    public CustomDialog(Context context) {
        super(context);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.dialog_custom); // 设置布局文件

        closeButton = findViewById(R.id.close_button);
        closeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();  // 使用原生的dismiss方法关闭对话框
            }
        });
    }

    // 兼容原生的isShowing方法
    @Override
    public boolean isShowing() {
        return super.isShowing();
    }

    // 兼容原生的dismiss方法
    @Override
    public void dismiss() {
        super.dismiss();
    }

    // 兼容原生的setOnDismissListener方法
    @Override
    public void setOnDismissListener(OnDismissListener listener) {
        super.setOnDismissListener(listener);
    }

    // 可以根据需要开放额外的接口
    public void setTitleText(String title) {
        // 假设你有一个标题TextView
        TextView titleView = findViewById(R.id.title_text_view);
        if (titleView != null) {
            titleView.setText(title);
        }
    }
}

5、判断页面是WebView还是原生APP

  • 方法
    1. 使用Android Studio的Layout Inspector。
    2. 打开手机开发者模式,开启布局边界显示,观察边界框数量。
    3. 通过抓包、查看日志或缓存来判断。

6、配置资源更新

  • 内容:在Android开发中,需要注意及时更新configuration,特别是在需要保持应用字体不随系统变化时。
    @Override
    public Resources getResources() {
        Resources resources = super.getResources();
        if (resources.getConfiguration().fontScale != 1) {
            Configuration configuration = new Configuration();
            configuration.setToDefaults();
            resources.updateConfiguration(configuration, resources.getDisplayMetrics());
        }
        return resources;
    }
    

7、adb安装命令

  • 内容
    • adb install -r:强制覆盖安装。
    • adb install -t:允许测试包安装。
    • adb install -d:允许低版本系统安装。

8、获取带声调的拼音字符串

  • 内容
```java
import java.util.Arrays;
import java.util.Locale;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import android.text.TextUtils;
import android.util.Log;

public class PinyinHelper {

    private static final String TAG = "PinyinHelper";

    /**
     * 获取汉字拼音,这里获取到的是带有声调的拼音字符串,比如强袭:qiang1 xi1
     *
     * @param chinese  汉字字符串
     * @param caseType 大小写类型
     * @return 拼音字符串
     */
    public static String toPinyin(String chinese, int caseType) {
        if (TextUtils.isEmpty(chinese)) {
            return "";
        }
        char[] strChar = chinese.toCharArray();
        HanyuPinyinOutputFormat hanYuPinOutputFormat = new HanyuPinyinOutputFormat();
        // 输出设置,大小写,音标方式等
        if (1 == caseType) {
            hanYuPinOutputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
        } else {
            hanYuPinOutputFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        }
        hanYuPinOutputFormat.setToneType(HanyuPinyinToneType.WITH_TONE_NUMBER);
        hanYuPinOutputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
        StringBuffer pyStringBuffer = new StringBuffer();
        String[] pinyinArray;
        try {
            for (int i = 0; i < strChar.length; i++) {
                if (Character.toString(strChar[i]).matches("[\\u4E00-\\u9FA5]+")) {
                    // 如果是汉字字符,将汉字的几种全拼都存到pinyinArray数组中
                    pinyinArray = PinyinHelper.toHanyuPinyinStringArray(strChar[i], hanYuPinOutputFormat);
                    // 取出汉字全拼的第一种读音并连接到字符串pyStringBuffer后
                    if (pinyinArray != null && pinyinArray.length > 0) {
                        // 特殊处理“长”字
                        if (strChar[i] == '长') {
                            pyStringBuffer.append("chang ").append(" ");
                        } else {
                            pyStringBuffer.append(pinyinArray[0]).append(" ");
                        }
                    } else {
                        pyStringBuffer.append(strChar[i]);
                    }
                } else {
                    // 如果不是汉字字符,直接取出字符并连接到字符串pyStringBuffer
                    pyStringBuffer.append(strChar[i]);
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            Log.e(TAG, "toPinyin: BadHanyuPinyinOutputFormatCombination e = " + e);
        }
        return pyStringBuffer.toString().trim(); // 去除末尾多余的空格
    }
}

关键点解释:

  1. 特殊处理“长”字:在处理每个字符时,检查是否为“长”字。如果是“长”字,则直接追加 "chang " 到 pyStringBuffer 中。
  2. 其他字符处理:对于其他字符,继续使用 PinyinHelper 库获取其拼音,并追加到 pyStringBuffer 中。
  3. 去除末尾多余空格:在返回结果之前,使用 trim() 方法去除末尾的多余空格。

使用示例:

String chinese = "长河落日圆";
String pinyin = PinyinHelper.toPinyin(chinese, 1); // 1 表示小写
System.out.println(pinyin); // 输出: chang he2 luo4 ri4 yuan2

9、哈希字符串得到唯一值

Android中,如果你需要将一串字符串转换为一个独一无二的数字,可以考虑使用哈希函数。虽然像MD5SHA-1SHA-256等哈希算法通常返回的是字符串(通常是十六进制表示),但你可以将这些字符串进一步转换为数字。以下是一些常见的方法和算法,可以帮助你生成一个独一无二的数字:

### 1. 使用哈希算法生成数字
你可以使用哈希算法生成一个哈希值,然后将这个哈希值转换为一个数字。以下是一些常见的哈希算法:

- **MD5**
- **SHA-1**
- **SHA-256**
- **SHA-512**

### 示例代码
以下是一个使用MD5哈希算法生成一个独一无二的数字的示例:

```java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class UniqueNumberGenerator {

    // 计算哈希值
    public static long generateUniqueNumber(String input, String algorithm) {
        try {
            // 获取指定算法的MessageDigest实例
            MessageDigest digest = MessageDigest.getInstance(algorithm);

            // 计算哈希值
            byte[] hashBytes = digest.digest(input.getBytes());

            // 将字节数组转换为长整型数字
            long uniqueNumber = bytesToLong(hashBytes);
            return uniqueNumber;
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Algorithm not found: " + algorithm, e);
        }
    }

    // 将字节数组转换为长整型数字
    public static long bytesToLong(byte[] bytes) {
        long result = 0;
        for (int i = 0; i < ˜; i++) {
            result = (result << 8) | (bytes[i] & 0xFF);
        }
        return result;
    }

    public static void main(String[] args) {
        String input = "Hello, World!";
        String hashAlgorithm = "MD5";

        // 生成独一无二的数字
        long uniqueNumber = generateUniqueNumber(input, hashAlgorithm);
        System.out.println("Input: " + input);
        System.out.println("Unique Number (MD5): " + uniqueNumber);
    }
}

代码解释

  1. generateUniqueNumber 方法:

    • 接受一个输入字符串和一个哈希算法名称(如 "MD5")。
    • 使用 MessageDigest 类来计算哈希值。
    • 调用 bytesToLong 方法将字节数组转换为长整型数字并返回。
  2. bytesToLong 方法:

    • 接受一个字节数组。
    • 将字节数组转换为长整型数字。这里我们只取前8个字节(64位)来组成一个长整型数字。如果你需要更大的数字范围,可以考虑使用 BigInteger
  3. main 方法:

    • 定义输入字符串和哈希算法(这里是 "MD5")。
    • 生成并打印独一无二的数字。

输出示例

当你运行这个程序时,输出将类似于:

Input: Hello, World!
Unique Number (MD5): 18684752,

注意事项

  • 哈希碰撞:虽然哈希算法(如MD5)生成的哈希值通常是唯一的,但理论上存在哈希碰撞的可能性。也就是说,不同的输入可能会生成相同的哈希值。
  • 数字范围:长整型(long)在Java中是64位,范围是-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。如果你需要更大的数字范围,可以考虑使用 BigInteger
  • 性能:哈希算法的计算通常很快,但对于大量数据或高并发场景,仍需考虑性能问题。

其他方法

除了哈希算法,还有一些其他方法可以生成独一无二的数字,例如:

  • UUID:UUID(Universally Unique Identifier)可以生成一个128位的唯一标识符,可以将其转换为数字。
  • 自定义算法:根据具体需求,可以设计自定义算法来生成独一无二的数字,例如结合时间戳、随机数等。

总之,使用哈希算法是一种常见且有效的方法,可以根据你的具体需求选择合适的算法和转换方式。

10、设置应用字体不跟随系统变化

    @Override
    public Resources getResources() {
        //设置应用字体不跟随系统变化
        Resources resources = super.getResources();
        if (resources.getConfiguration().fontScale != 1) {
            Configuration configuration = new Configuration();
            configuration.setToDefaults();
            resources.updateConfiguration(configuration, resources.getDisplayMetrics());
        }
        return resources;
    }

11、定时管理器

```java
import android.os.Handler;
import android.os.Looper;

public class TimerManager {
    private static volatile TimerManager INSTANCE;
    private final Handler mHandler;
    private Runnable mTimeoutRunnable;
    private OnTimeoutListener mListener;
    private static final int DEFAULT_TIMEOUT = 10000;
    private boolean isCancelled = false;

    private TimerManager() {
        mHandler = new Handler(Looper.getMainLooper());
    }

    public static TimerManager getInstance() {
        if (INSTANCE == null) {
            synchronized (TimerManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new TimerManager();
                }
            }
        }
        return INSTANCE;
    }

    public void start(OnTimeoutListener listener) {
        this.start(DEFAULT_TIMEOUT, listener);
    }

    public void start(int delayMillis, OnTimeoutListener listener) {
        this.mListener = listener;
        mTimeoutRunnable = new Runnable() {
            @Override
            public void run() {
                if (mListener != null) {
                    mListener.onTimeout();
                }
            }
        };
        mHandler.postDelayed(mTimeoutRunnable, delayMillis);
        isCancelled = false; // 重置取消标志
    }

    public void cancel() {
        if (!isCancelled && mHandler != null && mTimeoutRunnable != null) {
            mHandler.removeCallbacks(mTimeoutRunnable);
            isCancelled = true; // 设置取消标志
        }
    }

    public interface OnTimeoutListener {
        void onTimeout();
    }
}

关键点解释:

  1. 取消标志 isCancelled:引入了一个布尔变量 isCancelled,用于标记 cancel 方法是否已经被调用过。
  2. 重置取消标志:在 start 方法中,每次重新启动计时器时,重置 isCancelled 标志为 false
  3. 检查取消标志:在 cancel 方法中,首先检查 isCancelled 标志。如果已经取消过,则不再执行 removeCallbacks 方法,否则执行并设置 isCancelledtrue

使用示例:

TimerManager timerManager = TimerManager.getInstance();

timerManager.start(new TimerManager.OnTimeoutListener() {
    @Override
    public void onTimeout() {
        // 处理超时事件
        System.out.println("Timeout occurred!");
    }
});

// 模拟外部多次调用 cancel 方法
timerManager.cancel();
timerManager.cancel();
timerManager.cancel();

这样,即使外部多次调用 cancel 方法,removeCallbacks 方法也只会被执行一次,从而避免了不必要的操作。