1.设计模式的前奏
创始人
2024-05-19 09:16:46
0

哪些维度评判代码质量的好坏?

常用的评价标准

  • 可维护性(maintainability):维护代码的成本
  • 可读性(readability)
  • 可扩展性(extensibility):码应对未来需求变化的能力
  • 灵活性(flexibility)
  • 简洁性(simplicity)
  • 可复用性(reusability)
  • 可测试性(testability)

面向对象、设计原则、设计模式、编程规范、重构

面向对象

  1. 面向对象的四大特性:封装、抽象、继承、多态
  • 封装:信息隐藏或者数据访问保护
  • 抽象:调用者在使用函数的时候,并不需要去研究函数内部的实现逻辑,只需要通过函数的命名、注释或者文档,了解其提供了什么功能,就可以直接使用了。
  • 继承:代码复用(过度继承会导致代码可读性、可维护性变差)
  • 多态:子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现
  1. 面向对象编程与面向过程编程的区别和联系
  • 面向过程:方法和数据结构的定义分开
  • 面向对象:方法和数据结构被绑定一起,定义在类中(能够应对大规模复杂程序的开发,更易复用、易扩展、易维护)
  1. 接口和抽象类的区别以及各自的应用场景
    抽象类(模板设计模式)
// 抽象类
public abstract class Logger {private String name;private boolean enabled;private Level minPermittedLevel;public Logger(String name, boolean enabled, Level minPermittedLevel) {this.name = name;this.enabled = enabled;this.minPermittedLevel = minPermittedLevel;}public void log(Level level, String message) {boolean loggable = enabled && (minPermittedLevel.intValue() <= level.intValueif (!loggable) return;doLog(level, message);}protected abstract void doLog(Level level, String message);
}
// 抽象类的子类:输出日志到文件
public class FileLogger extends Logger {private Writer fileWriter;public FileLogger(String name, boolean enabled,Level minPermittedLevel, String filepath) {super(name, enabled, minPermittedLevel);this.fileWriter = new FileWriter(filepath);}@Overridepublic void doLog(Level level, String mesage) {
// 格式化level和message,输出到日志文件fileWriter.write(...);}
}// 抽象类的子类: 输出日志到消息中间件(比如kafka)
public class MessageQueueLogger extends Logger {private MessageQueueClient msgQueueClient;public MessageQueueLogger(String name, boolean enabled,Level minPermittedLevel, MessageQueueClient msgQueueClient) {super(name, enabled, minPermittedLevel);this.msgQueueClient = msgQueueClient;}@Overrideprotected void doLog(Level level, String mesage) {
// 格式化level和message,输出到消息中间件msgQueueClient.send(...);}
}
  • 抽象类不允许被实例化,只能被继承。
  • 抽象类可以包含属性和方法。
  • 子类继承抽象类,必须实现抽象类中的所有抽象方法。

接口

public interface Filter {void doFilter(RpcRequest req) throws RpcException;
}// 接口实现类:鉴权过滤器
public class AuthencationFilter implements Filter {@Overridepublic void doFilter(RpcRequest req) throws RpcException {
//...鉴权逻辑..}
}// 接口实现类:限流过滤器
public class RateLimitFilter implements Filter {@Overridepublic void doFilter(RpcRequest req) throws RpcException {
//...限流逻辑...}
}// 过滤器使用Demo
public class Application {// filters.add(new AuthencationFilter());
// filters.add(new RateLimitFilter());private List filters = new ArrayList<>();public void handleRpcRequest(RpcRequest req) {try {for (Filter filter : filters) {filter.doFilter(req);}} catch (RpcException e) {
// ...处理过滤结果...}
// ...省略其他处理逻辑...}
}
  • 接口不能包含属性(也就是成员变量)。
  • 接口只能声明方法,方法不能包含代码实现。
  • 类实现接口的时候,必须实现接口中声明的所有方法。
  1. 基于接口而非实现编程的设计思想
  • 越抽象、越顶层、越脱离具体某一实现的设计,越能提高代码的灵活性,越能应对未来的需求变化。
    函数的命名不能暴露任何实现细节。
    封装具体的实现细节。
    为实现类定义抽象的接口。
  1. 多用组合少用继承的设计思想
  • 如果类之间的继承结构稳定(不会轻易改变),继承层次比较浅(比如,最多有两层继承关
    系),继承关系不复杂,我们就可以大胆地使用继承。反之,系统越不稳定,继承层次很深,
    继承关系复杂,我们就尽量使用组合来替代继承
  • 装饰者模式(decorator pattern)、策略模式(strategy pattern)、组合模式(composite pattern)等都使用了组合关系,而模板模式(template pattern)使用了继承关系。
  1. 面向过程的贫血模型(重 Service 轻 BO)和面向对象的充血模型(轻 Service 重 Domain)
    基于贫血模型的传统开发模式
// 接口
public class VirtualWalletController {// 通过构造函数或者IOC框架注入private VirtualWalletService virtualWalletService;public BigDecimal getBalance(Long walletId) { ...} //查询余额public void debit(Long walletId, BigDecimal amount) { ...} //出账public void credit(Long walletId, BigDecimal amount) { ...} //入账public void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) { .
//省略查询transaction的接口}
}public class VirtualWalletBo {//省略getter/setter/constructor方法private Long id;private Long createTime;private BigDecimal balance;
}public Enum TransactionType {DEBIT,CREDIT,TRANSFER;}public class VirtualWalletService {// 通过构造函数或者IOC框架注入private VirtualWalletRepository walletRepo;private VirtualWalletTransactionRepository transactionRepo;public VirtualWalletBo getVirtualWallet(Long walletId) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWalletBo walletBo = convert(walletEntity);return walletBo;}public BigDecimal getBalance(Long walletId) {return walletRepo.getBalance(walletId);}@Transactionalpublic void debit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);BigDecimal balance = walletEntity.getBalance();if (balance.compareTo(amount) < 0) {throw new NoSufficientBalanceException(...);}VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.DEBIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);walletRepo.updateBalance(walletId, balance.subtract(amount));}@Transactionalpublic void credit(Long walletId, BigDecimal amount) {VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.CREDIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);BigDecimal balance = walletEntity.getBalance();walletRepo.updateBalance(walletId, balance.add(amount));}@Transactionalpublic void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.TRANSFER);transactionEntity.setFromWalletId(fromWalletId);transactionEntity.setToWalletId(toWalletId);transactionRepo.saveTransaction(transactionEntity);debit(fromWalletId, amount);credit(toWalletId, amount);}
}

基于充血模型的 DDD 开发模式(钱包系统)

public class VirtualWallet { // Domain领域模型(充血模型)private Long id;private Long createTime = System.currentTimeMillis();private BigDecimal balance = BigDecimal.ZERO;public VirtualWallet(Long preAllocatedId) {this.id = preAllocatedId;}public BigDecimal balance() {return this.balance;}public void debit(BigDecimal amount) {if (this.balance.compareTo(amount) < 0) {throw new InsufficientBalanceException(...);}this.balance = this.balance.subtract(amount);}public void credit(BigDecimal amount) {if (amount.compareTo(BigDecimal.ZERO) < 0) {throw new InvalidAmountException(...);}this.balance = this.balance.add(amount);}
}public class VirtualWalletService {// 通过构造函数或者IOC框架注入private VirtualWalletRepository walletRepo;private VirtualWalletTransactionRepository transactionRepo;public VirtualWallet getVirtualWallet(Long walletId) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);return wallet;}public BigDecimal getBalance(Long walletId) {return walletRepo.getBalance(walletId);}@Transactionalpublic void debit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);wallet.debit(amount);VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.DEBIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);walletRepo.updateBalance(walletId, wallet.balance());}@Transactionalpublic void credit(Long walletId, BigDecimal amount) {VirtualWalletEntity walletEntity = walletRepo.getWalletEntity(walletId);VirtualWallet wallet = convert(walletEntity);wallet.credit(amount);VirtualWalletTransactionEntity transactionEntity = new VirtualWalletTransactitransactionEntity.setAmount(amount);transactionEntity.setCreateTime(System.currentTimeMillis());transactionEntity.setType(TransactionType.CREDIT);transactionEntity.setFromWalletId(walletId);transactionRepo.saveTransaction(transactionEntity);walletRepo.updateBalance(walletId, wallet.balance());}@Transactionalpublic void transfer(Long fromWalletId, Long toWalletId, BigDecimal amount) {
//...跟基于贫血模型的传统开发模式的代码一样...}
}

基于充血模型的 DDD 开发模式跟基于贫血模型的传统开发模式相比,主要区别在 Service
层。在基于充血模型的开发模式下,我们将部分原来在 Service 类中的业务逻辑移动到了一个
充血的 Domain 领域模型中
,让 Service 类的实现依赖这个 Domain 类。

在基于充血模型的 DDD 开发模式下,Service 类并不会完全移除,而是负责一些不适合放在
Domain 类中的功能。比如,负责与 Repository 层打交道、跨领域模型的业务聚合功能、幂
等事务等非功能性的工作。

如何对接口鉴权这样一个功能开发做面向对象分析?

  1. 调用方进行接口请求的时候,将 URL、AppID、密码、时间戳拼接在一起,通过加密算法
    生成 token,并且将 token、AppID、时间戳拼接在 URL 中,一并发送到微服务端。
  2. 微服务端在接收到调用方的接口请求之后,从请求中拆解出 token、AppID、时间戳。
  3. 微服务端首先检查传递过来的时间戳跟当前时间,是否在 token 失效时间窗口内。如果已
    经超过失效时间,那就算接口调用鉴权失败,拒绝接口调用请求。
  4. 如果 token 验证没有过期失效,微服务端再从自己的存储中,取出 AppID 对应的密码,通
    过同样的 token 生成算法,生成另外一个 token,与调用方传递过来的 token 进行匹配。
    如果一致,则鉴权成功,允许接口调用;否则就拒绝接口调用。

进一步变化为:

  1. 把 URL、AppID、密码、时间戳拼接为一个字符串;
  2. 对字符串通过加密算法加密生成 token;
  3. 将 token、AppID、时间戳拼接到 URL 中,形成新的 URL;
  4. 解析 URL,得到 token、AppID、时间戳等信息;
  5. 从存储中取出 AppID 和对应的密码;
  6. 根据时间戳判断 token 是否过期失效;
  7. 验证两个 token 是否匹配;

1、2、6、7 都是跟 token 有关,负责 token 的生成、验证;(AuthToken)
3、4 都是在处理 URL,负责 URL 的拼接、解析;(Url)
5 是操作 AppID 和密码,负责从存储中读取 AppID 和密码。(CredentialStorage)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

public interface ApiAuthenticator {void auth(String url);void auth(ApiRequest apiRequest);
}
public class DefaultApiAuthenticatorImpl implements ApiAuthenticator {private CredentialStorage credentialStorage;public DefaultApiAuthenticatorImpl() {this.credentialStorage = new MysqlCredentialStorage();}public DefaultApiAuthenticatorImpl(CredentialStorage credentialStorage) {this.credentialStorage = credentialStorage;}@Overridepublic void auth(String url) {ApiRequest apiRequest = ApiRequest.buildFromUrl(url);auth(apiRequest);}@Overridepublic void auth(ApiRequest apiRequest) {String appId = apiRequest.getAppId();String token = apiRequest.getToken();long timestamp = apiRequest.getTimestamp();String originalUrl = apiRequest.getOriginalUrl();AuthToken clientAuthToken = new AuthToken(token, timestamp);if (clientAuthToken.isExpired()) {throw new RuntimeException("Token is expired.");}String password = credentialStorage.getPasswordByAppId(appId);AuthToken serverAuthToken = AuthToken.generate(originalUrl, appId, password,if (!serverAuthToken.match(clientAuthToken)) {throw new RuntimeException("Token verfication failed.");}}
}

设计原则

  1. SOLID 原则 -SRP 单一职责原则
  2. SOLID 原则 -OCP 开闭原则(对扩展开放、对修改关闭)
// API 接口监控告警 这种情况进行扩展就需要对方法进行修改
public class Alert {private AlertRule rule;private Notification notification;public Alert(AlertRule rule, Notification notification) {this.rule = rule;this.notification = notification;}public void check(String api, long requestCount, long errorCount, long duration) {long tps = requestCount / durationOfSeconds;if (tps > rule.getMatchedRule(api).getMaxTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {notification.notify(NotificationEmergencyLevel.SEVERE, "...");}}
}

// 当每秒钟接口超时请求个数,超过某个预先设置的最大阈值时,我们也要触发告警发送通知。
代码改动


public class Alert {// ...省略AlertRule/Notification属性和构造函数...
// 改动一:添加参数timeoutCountpublic void check(String api, long requestCount, long errorCount, long timeoutCount) {long tps = requestCount / durationOfSeconds;if (tps > rule.getMatchedRule(api).getMaxTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {notification.notify(NotificationEmergencyLevel.SEVERE, "...");}// 改动二:添加接口超时处理逻辑long timeoutTps = timeoutCount / durationOfSeconds;if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}}
}

重构–》

public class Alert {private List alertHandlers = new ArrayList<>();public void addAlertHandler(AlertHandler alertHandler) {this.alertHandlers.add(alertHandler);}public void check(ApiStatInfo apiStatInfo) {for (AlertHandler handler : alertHandlers) {handler.check(apiStatInfo);}}
}public class ApiStatInfo {//省略constructor/getter/setter方法private String api;private long requestCount;private long errorCount;private long durationOfSeconds;
}public abstract class AlertHandler {protected AlertRule rule;protected Notification notification;public AlertHandler(AlertRule rule, Notification notification) {this.rule = rule;this.notification = notification;}public abstract void check(ApiStatInfo apiStatInfo);
}public class TpsAlertHandler extends AlertHandler {public TpsAlertHandler(AlertRule rule, Notification notification) {super(rule, notification);}@Overridepublic void check(ApiStatInfo apiStatInfo) {long tps = apiStatInfo.getRequestCount() / apiStatInfo.getDurationOfSeconds();if (tps > rule.getMatchedRule(apiStatInfo.getApi()).getMaxTps()) {notification.notify(NotificationEmergencyLevel.URGENCY, "...");}}
}public class ErrorAlertHandler extends AlertHandler {public ErrorAlertHandler(AlertRule rule, Notification notification) {super(rule, notification);}@Overridepublic void check(ApiStatInfo apiStatInfo) {if (apiStatInfo.getErrorCount() > rule.getMatchedRule(apiStatInfo.getApi()).gnotification.notify(NotificationEmergencyLevel.SEVERE, "...");}
}public class ApplicationContext {private AlertRule alertRule;private Notification notification;private Alert alert;public void initializeBeans() {alertRule = new AlertRule(/*.省略参数.*/); //省略一些初始化代码notification = new Notification(/*.省略参数.*/); //省略一些初始化代码alert = new Alert();alert.addAlertHandler(new TpsAlertHandler(alertRule, notification));alert.addAlertHandler(new ErrorAlertHandler(alertRule, notification));}public Alert getAlert() {return alert;}// 饿汉式单例private static final ApplicationContext instance = new ApplicationContext();private ApplicationContext() {initializeBeans();}public static ApplicationContext getInstance() {return instance;}
}public class Demo {public static void main(String[] args) {ApiStatInfo apiStatInfo = new ApiStatInfo();
// ...省略设置apiStatInfo数据值的代码ApplicationContext.getInstance().getAlert().check(apiStatInfo);}
}
  1. SOLID 原则 -LSP 里式替换原则
  • 子类的设计要保证在替换父类的时候,不改变原有程序的逻辑及不破坏原有程序的正确性(继承父类初衷,并进行增强)
  1. SOLID 原则 -ISP 接口隔离原则
  • 要把函数拆分成粒度更细的多个函数,让调用者只依赖它需要的那个细粒度函数
  • 单一职责原则针对的是模块、类、接口的设计。接口隔离原则更针对接口的设计。
  1. SOLID 原则 -DIP 依赖倒置原则
  • 控制反转(IOC):框架提供了一个可扩展的代码骨架,用来组装对象、管理整个执行流程。“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。
  • 依赖注入(DI):不通过 new() 的方式在类内部创建依赖类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类使用。
  • 依赖反转: 高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。
  1. DRY 原则、KISS 原则、YAGNI 原则、LOD 法则
  • KISS 原则(Keep It Simple and Stupid:尽量保持简单)
    • 不要使用同事可能不懂的技术来实现代码。
    • 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出bug 的概率会更高,维护的成本也比较高。
    • 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替 if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。
  • YAGNI(You Ain’t Gonna Need It:你不会需要它):
    • 不要去设计当前用不到的功能;不要去编写当前用不到的代码。
  • DRY原则(Don’t Repeat Yourself:(不写重复的代码))
    • 三种代码重复的情况:实现逻辑重复、功能语义重复、代码执行重复。
      提高复用的办法
    • 减少代码耦合
    • 满足单一职责原则
    • 模块化
    • 业务与非业务逻辑分离
    • 通用代码下沉
    • 继承、多态、抽象、封装
    • 应用模板等设计模式
  • LOD(迪米特法则:Law of Demeter):不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口

设计模式

针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思
路。

积分系统设计案例

  1. 合理地将功能划分到不同模块
    • 第一种划分方式是:积分赚取渠道及兑换规则、消费渠道及兑换规则的管理和维护(增删改查),不划分到积分系统中,而是放到更上层的营销系统中。这样积分系统就会变得非常简单,只需要负责增加积分、减少积分、查询积分、查询积分明细等这几个工作
    • 第二种划分方式是:积分赚取渠道及兑换规则、消费渠道及兑换规则的管理和维护,分散在各个相关业务系统中,比如订单系统、评论系统、签到系统、换购商城、优惠券系统等。还是刚刚那个下订单赚取积分的例子,在这种情况下,用户下订单成功之后,订单系统根据商品对应的积分兑换比例,计算所能兑换的积分数量,然后直接调用积分系统给用户增加积分。
    • 第三种划分方式是:所有的功能都划分到积分系统中,包括积分赚取渠道及兑换规则、消费渠道及兑换规则的管理和维护。还是同样的例子,用户下订单成功之后,订单系统直接告知积分系统订单交易成功,积分系统根据订单信息查询积分兑换规则,给用户增加积分。

综合考虑,我们更倾向于第一种和第二种模块划分方式。但是,不管选择这两种中的哪一种,积分系统所负责的工作是一样的,只包含积分的增、减、查询,以及积分明细的记录和查询。

  1. 设计模块与模块之间的交互关系
    上下层系统之间的调用倾向于通过同步接口,同层之间的调用倾向于异步消息调用。比如,营销系统和积分系统是上下层关系,它们之间就比较推荐使用同步接口调用(上层是调用系统,下层是被调用系统)

  2. 设计模块的接口、数据库、业务模型
    积分流水明细表
    在这里插入图片描述
    表设计这里还可以加一个积分余额表(没有积分余额表容易造成多扣)

积分系统的接口
在这里插入图片描述

编程规范

编程规范主要解决的是代码的可读性问题。编码规范相对于设计原则、设计模式,更加具
体、更加偏重代码细节、更加能落地。持续的小重构依赖的理论基础主要就是编程规范。

重构

重构作为保持代码质量不下降的有效手段,利用的就是面向对象、设计原则、设计模式、编
码规范这些理论。

程序出错该返回啥?NULL、异常、错误码、空对象?

  1. 返回错误码
    • C 语言使用错误码的情况比较多
  2. 返回 NULL 值
    • 如果查询对象为空时,返回null也是正常行为(按照项目统一约定来),比如查询下标返回值会是Int类型,这时候可能用-1来返回
  3. 返回空对象
    • 空对象设计模式(Null Object Design Pattern)
    • 当函数返回的数据是字符串类型或者集合类型的时候,我们可以用空字符串或空集合替代NULL 值,来表示不存在的情况。
  4. 抛出异常对象
    • 对于代码 bug(比如数组越界)以及不可恢复异常(比如数据库连接失败),即便我们捕获了,也做不了太多事情,所以,我们倾向于使用非受检异常。对于可恢复异常、业务异常,比如提现金额大于余额的异常,我们更倾向于使用受检异常,明确告知调用者需要捕获处理。

当我们面对函数抛出异常的时候,应该选择上面的哪种处理方式呢?

  • 如果 func1() 抛出的异常是可以恢复,且 func2() 的调用方并不关心此异常,我们完全可以在 func2() 内将 func1() 抛出的异常吞掉;
  • 如果 func1() 抛出的异常对 func2() 的调用方来说,也是可以理解的、关心的 ,并且在业务概念上有一定的相关性,我们可以选择直接将 func1 抛出的异常 re-throw;
  • 如果 func1() 抛出的异常太底层,对 func2() 的调用方来说,缺乏背景去理解、且业务概念上无关,我们可以将它重新包装成调用方可以理解的新异常,然后 re-throw。

相关内容

热门资讯

安卓子系统windows11,... 你知道吗?最近科技圈可是炸开了锅,因为安卓子系统在Windows 11上的兼容性成了大家热议的话题。...
电脑里怎么下载安卓系统,电脑端... 你有没有想过,你的电脑里也能装上安卓系统呢?没错,就是那个让你手机不离手的安卓!今天,就让我来带你一...
索尼相机魔改安卓系统,魔改系统... 你知道吗?最近在摄影圈里掀起了一股热潮,那就是索尼相机魔改安卓系统。这可不是一般的改装,而是让这些专...
安卓系统哪家的最流畅,安卓系统... 你有没有想过,为什么你的手机有时候像蜗牛一样慢吞吞的,而别人的手机却能像风一样快?这背后,其实就是安...
安卓最新系统4.42,深度解析... 你有没有发现,你的安卓手机最近是不是有点儿不一样了?没错,就是那个一直在默默更新的安卓最新系统4.4...
android和安卓什么系统最... 你有没有想过,你的安卓手机到底是用的是什么系统呢?是不是有时候觉得手机卡顿,运行缓慢,其实跟这个系统...
平板装安卓xp系统好,探索复古... 你有没有想过,把安卓系统装到平板上,再配上XP系统,这会是怎样一番景象呢?想象一边享受着安卓的便捷,...
投影仪装安卓系统,开启智能投影... 你有没有想过,家里的老式投影仪也能焕发第二春呢?没错,就是那个曾经陪你熬夜看电影的“老伙计”,现在它...
安卓系统无线车载carplay... 你有没有想过,开车的时候也能享受到苹果设备的便利呢?没错,就是那个让你在日常生活中离不开的iOS系统...
谷歌安卓8系统包,系统包解析与... 你有没有发现,手机更新换代的速度简直就像坐上了火箭呢?这不,最近谷歌又发布了安卓8系统包,听说这个新...
微软平板下软件安卓系统,开启全... 你有没有想过,在微软平板上也能畅享安卓系统的乐趣呢?没错,这就是今天我要跟你分享的神奇故事。想象你手...
coloros是基于安卓系统吗... 你有没有想过,手机里的那个色彩斑斓的界面,背后其实有着一个有趣的故事呢?没错,我要说的就是Color...
安卓神盾系统应用市场,一站式智... 你有没有发现,手机里的安卓神盾系统应用市场最近可是火得一塌糊涂啊!这不,我就来给你好好扒一扒,看看这...
黑莓平板安卓系统升级,解锁无限... 亲爱的读者们,你是否还记得那个曾经风靡一时的黑莓手机?那个标志性的全键盘,那个独特的黑莓体验,如今它...
安卓文件系统采用华为,探索高效... 你知道吗?最近安卓系统在文件管理上可是有了大动作呢!华为这个科技巨头,竟然悄悄地给安卓文件系统来了个...
深度系统能用安卓app,探索智... 你知道吗?现在科技的发展真是让人惊叹不已!今天,我要给你揭秘一个超级酷炫的话题——深度系统能用安卓a...
安卓系统的分区类型,深度解析存... 你有没有发现,你的安卓手机里藏着不少秘密?没错,就是那些神秘的分区类型。今天,就让我带你一探究竟,揭...
安卓系统铠无法兑换,揭秘无法兑... 最近是不是有很多小伙伴在玩安卓系统的游戏,突然发现了一个让人头疼的问题——铠无法兑换!别急,今天就来...
汽车安卓系统崩溃怎么刷,一键刷... 亲爱的车主朋友们,你是否曾遇到过汽车安卓系统崩溃的尴尬时刻?手机系统崩溃还能重启,但汽车系统崩溃了,...
miui系统可以刷安卓p系统吗... 亲爱的手机控们,你是否对MIUI系统情有独钟,同时又对安卓P系统的新鲜功能垂涎欲滴?今天,就让我带你...