Skip to content

Commit

Permalink
update kotlin
Browse files Browse the repository at this point in the history
  • Loading branch information
fengyizhao committed Apr 15, 2021
1 parent 3d5c664 commit efe679c
Show file tree
Hide file tree
Showing 11 changed files with 549 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
Binary file added Android/.DS_Store
Binary file not shown.
276 changes: 276 additions & 0 deletions Android/developer/android.developer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
[toc]

# Activity

## 多窗口支持

https://developer.android.com/guide/topics/ui/multi-window

7.0开始支持分屏;

8.0开始支持画中画;

正在交互的activity处于resumed状态,可见但没有交互中的activity处于started而非resumed状态;

配置android:resizableActivity=true|false支持分屏与否;不支持多窗口模式的activity在多窗口模式下启动,将全屏显示;

配置android:supportsPictureInPicture=true|false支持画中画与否;

* API:

isInMultiWindowMode() 是否处于多窗口模式

isInPictureInPictureMode() 是否处于画中画模式

onMultiWindowModeChanged() 进入或退出多窗口模式时系统调用,进入时系统传入true

onPictureInPictureModeChanged() 进入或退出画中画模式时系统调用,进入时系统传入true

Activity.enterPictureInPictureMode() 将activity置于画中画模式(若设备不支持画中画模式则此方法无效)

### Todo

1.

```xml
<activity android:name=".MyActivity">
<layout android:defaultHeight="500dp"
android:defaultWidth="600dp"
android:gravity="top|end"
android:minHeight="450dp"
android:minWidth="300dp" />
</activity>
```

2. 拖放

3. 使用 Intent 标志 `FLAG_ACTIVITY_LAUNCH_ADJACENT`。此标志将告知系统尽量在启动它的 Activity 旁边创建新 Activity,以便两个 Activity 共享屏幕。系统会尽可能这些做,但最终结果无法保证。
4. 如果设备处于自由窗口模式,则在启动新 Activity 时,您可通过调用 `ActivityOptions.setLaunchBounds()` 来指定新 Activity 的尺寸和屏幕位置。如果设备未处于多窗口模式,则调用该方法不会产生任何影响。



## 可折叠设备的适配

在搭载 Android 9.0 及更低版本的设备上运行时,只有获得焦点的应用处于已恢复状态。任何其他可见 Activity 都处于已暂停状态。如果应用在处于暂停状态时关闭资源或停止播放内容,则可能会产生问题。

在 Android 10 中,此行为发生了变化,即当设备处于多窗口模式时,所有 Activity 都会保持已恢复状态。这称为*多项恢复*。请注意,如果顶部有透明 Activity,或者 Activity 不可成为焦点(例如,处于画中画模式),则相应 Activity 可能会处于已暂停状态。还有一种可能是,在特定时间内(例如,当打开抽屉式通知栏时)所有 Activity 都不具有焦点。`OnStop` 会继续照常工作。每当 Activity 从屏幕上移除时,系统都会调用它.



* 新生命周期回调

```java
protected void onTopResumedActivityChanged(boolean topResumed) {
if (topResumed) {
// Top resumed activity
// Can be a signal to re-acquire exclusive resources
} else {
// No longer the top resumed activity
}
}
```

当 Activity 使用共享的单用户资源(例如麦克风或摄像头)时,了解这一点至关重要.

对于使用摄像头的应用,建议使用 `CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged()`方法作为提示,提醒这可能是尝试访问摄像头的好时机。

应用收到 `CameraDevice.StateCallback#onDisconnected()` 回调后,对摄像头设备进行的后续调用将抛出 `CameraAccessException`



# 架构

## App startup

需要启动的模块:

1. 实现Initializer<T>
2. 重写create和dependencies方法

```java
// Initializes ExampleLogger.
class ExampleLoggerInitializer implements Initializer<ExampleLogger> {

@Override
public ExampleLogger create(Context context) {
// WorkManager.getInstance() is non-null only after
// WorkManager is initialized.
return ExampleLogger(WorkManager.getInstance(context));
}

@Override
public List<Class<Initializer<?>>> dependencies() {
// Defines a dependency on WorkManagerInitializer so it can be
// initialized after WorkManager is initialized.
return Arrays.asList(WorkManagerInitializer.class);
}
}
```



启动这个模块有两种方式:在manifest中配置到`androidx.startup.InitializationProvider`中;或者调用AppInitializer启动。

1. 在manifest中配置.

```xml
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<!-- This entry makes ExampleLoggerInitializer discoverable. -->
<meta-data android:name="com.example.ExampleLoggerInitializer"
android:value="androidx.startup" />
</provider>
```

或者

2. 调用AppInitializer

```java
AppInitializer.getInstance(context)
.initializeComponent(ExampleLoggerInitializer.class);
```



## Data Store

SharedPreference缺点:不支持跨进程;sp文件数据会全部存在内存中,不宜存放大数据;任何修改都是全量写入,建议对高频修改的数据放在单独的sp文件中;

如果不需要跨进程,仅存储少量配置项,那么可以选择SharedPreference.

DataStore解决了这些缺点,可以保证原子性,一致性,隔离性,持久性,线程安全,非阻塞。是SP的优秀替代。

但不支持局部更新,也不保证引用完整性,如果需要这些则应选择用Room。

对比:

| DataStore | Room |
| ---------------------- | ------------------------------------------------------------ |
| small, simple datasets | large, complex datasets;<br/>partial updates;<br/>referential integrity(引用完整性) |

引用完整性:要求关系中不允许引用不存在的实体。**whenever a foreign key value is used it must reference a valid, existing primary key in the parent table.**

Preference Datastore: 键值对存储,不要求预定模版,不要求类型安全;

Proto DataStore: 存储类的对象,通过protocol buffers将对象序列化存储,要求类型安全。

### Todo

学了kotlin后实践



## 视图绑定

https://developer.android.com/topic/libraries/view-binding#java

Fragment 的存在时间比其视图长。请务必在 Fragment 的 `onDestroyView()` 方法中清除对绑定类实例的所有引用。

```java
private ResultProfileBinding binding;

@Override
public View onCreateView (LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
binding = ResultProfileBinding.inflate(inflater, container, false);
View view = binding.getRoot();
return view;
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}

```

与使用 `findViewById` 相比,视图绑定具有一些很显著的优点:

- **Null 安全**:由于视图绑定会创建对视图的直接引用,因此不存在因视图 ID 无效而引发 Null 指针异常的风险。此外,如果视图仅出现在布局的某些配置中,则绑定类中包含其引用的字段会使用 `@Nullable` 标记。
- **类型安全**:每个绑定类中的字段均具有与它们在 XML 文件中引用的视图相匹配的类型。这意味着不存在发生类转换异常的风险。

这些差异意味着布局和代码之间的不兼容将会导致构建在编译时(而非运行时)失败。



## 数据绑定

* Null 合并运算符

如果左边运算数不是 `null`,则 Null 合并运算符 (`??`) 选择左边运算数,如果左边运算数为 `null`,则选择右边运算数。

```java
android:text="@{user.displayName ?? user.lastName}"
```

等于

```java
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
```

* 视图引用

绑定类将 ID 转换为驼峰式大小写。

```xml
<EditText
android:id="@+id/example_text"
.../>
<TextView
android:id="@+id/example_output"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{exampleText.text}"/>
```













































69 changes: 69 additions & 0 deletions Android/developer/apilevel.30.tips.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[toc]

# Android 11 (R) Client bindService 失败

## 问题

| targetSDKVersion in build.gradle | device android version | client bindService result |
| -------------------------------- | ---------------------- | ------------------------- |
| 30 | 11(API level 30) | **Fail** |
| 30 | < 11(API level 30) | Success |
| < 30 | 11(API level 30) | Success |

## 原因

从 Android 11 (Api level 30) 开始, 引入了 Package visibility 的行为变更. 简单来说就是查阅其他 App 的信息的行为开始受到约束, 具体可以查看官方文档:[Android 11 中的软件包可见性](https://developer.android.com/training/basics/intents/package-visibility).

## 解决

在 targetSDKVersion 30 的配置下, 需要在 Client的AndroidMainfest.xml 中增加 <queries> 标签, 描述想要查询的远端 App 信息 (例如包名等):

1. 包名:

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.franny.test.aidlclientone">
<queries>
<package android:name="com.franny.test.aidlserver" />
</queries>
<application
...>
...
</application>
</manifest>
```

2. intent filter, 如果不知道具体包名,可以用intent-filter

```xml
<manifest package="com.example.game">
<queries>
<intent>
<action android:name="android.intent.action.SEND" />
<data android:mimeType="image/jpeg" />
</intent>
</queries>
...
</manifest>
```

3. provider, 如果不知道包名,但是知道需要一个content provider

```xml
<manifest package="com.example.suite.enterprise">
<queries>
<provider android:authorities="com.example.settings.files" />
</queries>
...
</manifest>
```

## 扩展

如果你的应用确实需要访问所有的应用,那么可以申请QUERY_ALL_PACKAGES权限





Loading

0 comments on commit efe679c

Please sign in to comment.