Android开发平台

谷歌在2007年发布Android 是一个开源的基于 Linux 的移动设备操作系统

支持的设备: phone…

语言: 开发语言是java , 后来因为甲骨文准备对android java收费, 又开始向Kotlin转移

IDE: 开发工具以前是eclipse+sdk, 后来谷歌退出了独立的IDE, Android Studio

打包: 使用签名打包生成.apk文件, 可作为app, 安装在android系统.

 

可用的平台框架

现在开发app主要有四种可选方案

首先是原生, 性能最好, 可是针对不同平台业务代码需要重复开发,

并且每次发版都需要经过应用市场审核

所以为了解决这个问题衍生出Cordove+ionic+web的方案,

cordova Apache的开源框架, PhoneGap演化而来

提供的是跨平台运行在各种设备的app

Ionic Web页面提供UI界面和交互.

然而web版本性能方面有些欠缺.

所以Facebook推出ReactNative采用virtual DOMnative UI控件进行绑定,

因此很好待解决了性能问题.

Flutter使用dart 替换掉JSV8, 性能据说有有更好的提升.

 

Android 架构

 > Linux内核层

    主要包含硬件相关的驱动, 电源管理, 以及内核本身的文件管理, 线程调度, 权限管理, 内存管理等.

> 硬件抽象层

    通过抽象封装硬件“驱动”的接口来进一步降低Android系统与硬件的耦合度;

> 运行库:

    运行时核心库, Dalvik 虚拟机(使各java应用拥有独立的进程)

    C++Sqlite, webkit, media, audio, sslopenGL, freetype

> JAVA的应用框架: 

    Activity, Windows, View,  notification, package, Telephony, location, resource.

> APP应用

    短信, 浏览器, 拨号, 计算器, …. 

 

从Js 层到Java层

前面说明了android系统从底层到app的整体架构,

接下来说明一下react-native 实现的android app是怎么交互的.

画这个图的人把上下颠倒了一下.

> Java层:

  该层主要提供了AndroidUI渲染器UIManager(将JavaScript映射成Android Widget

 > Android Widget 控件

  Fresco图片加载okhttp 提供http请求,

  对于ios, 或者windows, 这一层都是native层的类似模块.

> JSEngine

  提供js运行环境

> JSBridage

  负责上下层的通信.

> JavaScript层:

   运行的是react native js代码, 包含时间分发, js组件

Android Studio开发工具

谷歌提供了统一的夸平台的IDE Android Studio来开发安卓应用, 包含

1. 代码编辑 .
2. 布局预览和编辑
3. 性能分析
4. 模拟器
5. 编译 / 调试
6. 查看和操作 android 设备上的文件
 

React Native开发过程中, 这个IDE使用主要是跟模拟器相关的,

 

Environment环境配置

环境上, 需要安装react native命令行工具, python2, JDKandroid studio.

添加环境变量, android studio安装插件和包.

其中包含sdk, 平台工具, intel硬件加速管理, 模拟器,

27版的api, 从右图可以看到27对应的是android8.1, 之所以不使用最新的28, 是因为目前还不够稳定.

x86的系统, 用于模拟器启动android.

安装好react-native命令行工具,

就可以创建出工程了.

工程里包含android的工程, ios的工程,

依赖的模块, app.js, index.js.

进入工程目录, 就可以跑起来打包安装了.

Emulator模拟器

安装或调试之前需要先配一个模拟器

或者将一个android设备配置mtp权限通过usb链接电脑.

ADB(Android Debug Bridge)调试器

path添加:  AppData\Local\Android\Sdk\platform-tools

查看设备: adb devices

远程: adb shell

安装: adb install xxx.apk

卸载: adb uninstall xxx.apk

下载: adb pull

上传: adb push

日志: adb logcat

 Android 的一个通用命令行工具,

帮助PC与模拟器实例或连接的 Android 设备进行通信

首先添加环境变量path添加androidsdk的工具路径

然后就可以通过命令行控制了

Adb会在5037端口运行起来一个负责电脑与设备通信的服务.

AndroidManifest.xml 应用初始化清单

Android 系统启动应用组件之前,  

系统通过读取应用的清单, 得知以下信息:

1. 报名, 版本名, 版本号, 默认安装位置

2. 硬件功能: 如相机, 蓝牙, GPS.

3. 用户权限流量, 联系人, 相册, 读写存储等.

4. 需要链接的API.

5. 启动状态, 比如横屏, 竖屏, 感应, 键盘等.

这里面还有很多节点和属性, 这里就不一一例举了.

link链接

链接的意思是在android ios 工程中引入对某个native库的导入和依赖.

如果手动修改需要找到好几个文件一一添加.

使用命令则可以自动完成这个步骤.

这样做的目的是, 减少不必要库的加载, 从而缩小打包大小.

react-native link xxx

android/app/build.gradle
android/app/src/main/java/com/AppName/MainApplication.java
android/settings.gradle
ios/AppName.xcodeproj/project.pbxproj

Touchable可触摸组件

TouchableHighlight
TouchableNativeFeedback
TouchableOpacity
TouchableWithoutFeedback

onLayout?: (event: LayoutChangeEvent) => void;onLongPress?: (event: GestureResponderEvent) => void;
onPress?: (event: GestureResponderEvent) => void;onPressIn?: (event: GestureResponderEvent) => void;onPressOut?: (event: GestureResponderEvent) => void;
pressRetentionOffset?: Insets;
delayLongPress?: number;
delayPressIn?: number;
delayPressOut?: number;

除了button控件, 大部分控件无法响应触摸行为.

所以需要依赖Touchable系列控件的包裹.

背景会在用户手指按下时变暗
在用户按下时形成墨水涟漪
会在用户手指按下时降低按钮的透明度
不显示任何视觉反馈
 

Scroll滚动

ScrollView
FlatList
SectionList

onContentSizeChange?: (w: number, h: number) => void;
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
onScrollBeginDrag?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
onScrollEndDrag?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
onMomentumScrollBegin?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;

滑动操作主要是针对可滚动的控件

ScrollView是基本的

FlatList针对长列表做了内存优化, 仅针对可现实范围内的部分加载到内存

SectionList也有同样的优化, 不过SectionList有分组的功能, 每个组都有独立的header和多行内容.

 

http 请求

http 请求, 可以使用fetch接口, 或者XMLHttpRequest

左边是fetch, 右边是XMLHttpRequest,

还可以使用第三方库.

fetch("https://mywebsite.com/endpoint/", {
  method: "POST",
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    firstParam: "yourValue",
    secondParam: "yourOtherValue"
  })
}) .then(response => response.json())
    .then(responseJson => {
      return responseJson.movies;
    })
    .catch(error => {
      console.error(error);
    });

var request = new XMLHttpRequest();
request.onreadystatechange = e => {
  if (request.readyState !== 4) {
    return;
  }

  if (request.status === 200) {
    console.log("success", request.responseText);
  } else {
    console.warn("error");
  }
};

request.open("GET", "https://mywebsite.com/endpoint/");
request.send();

File文件访问

\android\app\src\main\AndroidManifest.xml
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

import * as RNFS from "react-native-fs"; 
export let FileOpt = {
  writeDataStr: (dataStr: string | void, pathName: string) => {
    if (dataStr) {
        RNFS.writeFile(
            `${RNFS.DocumentDirectoryPath}/${pathName}`, 
            dataStr,  'utf8'
        ).catch((err)=>{…});
    }
  },
  readDataStr: (pathName: string): Promise<string | void> => {
    return RNFS.readFile(
        `${RNFS.DocumentDirectoryPath}/${pathName}`,  'utf8'
    );
  }
}

非常奇怪的是RN官方未提供对文件读写的接口.

所以不得不使用第三方提供的库.

这是我封装的文件读写操作接口.

这里需要说明的是, 写单例不能使用class, 因为会导致打包的apk闪退.

所以这里使用的是普通object的写法.

另外, android会限制app的权限, 所以需要在前面提到的AndroidManifest.xml中申明读写权限.

Database数据库

但是关系型数据库, 都是第三方提供的.

WatermelonDB

react-native-sqlite-storage

官方提供的数据库比较简陋, 只有key-value

 watermelonDB
 react-native-sqlite-storage

import { AsyncStorage } from "react-native“;
 setCacheStr: async (keyName: string, valueStr: string | void | null) => {
        if (keyName && valueStr) {
                AsyncStorage.setItem(keyName, valueStr)
                .catch((err)=>{
                    NativeSdk.log("[StorageOpt: setCacheStr] failed:", err);
                });
        }
 },
 getCacheStr: (keyName: string): Promise<string | null> => {
        return AsyncStorage.getItem(keyName);
 },

Navigation导航

官方提供的navigator

包含tab页导航, 以及跨页面的跳转.

首先是, 定义名字和导航页面的映射.

然后在根页面层的props中可以获取到navigator对象,

使用navigator对象就可以做跳转操作了.

去到一个页面, 然后返回到前一个页面.

也经过多次跳转, 之后回到最初的页面.

const TabNavigator = createBottomTabNavigator({
  Collection: {
    screen: PhotosPage,
    path: '/TabsCollection',
    swipeEnabled: true,
    navigationOptions: {
      tabBarLabel: '照片',
      tabBarIcon: ({ tintColor, focused }:any) => ( <Image …/>),
      tabBarOptions: tabBarOptions
    }
  }
})
const tabPages = createAppContainer(TabNavigator);
const AppNavigator = createStackNavigator({
  Home: tabPages,
  Photo: BigPhotoPage,
  Setting: SettingsPage,
  About: AboutPage
}, {
  initialRouteName: 'Home',
  headerMode: 'none',
});
let PageRouterMain = createAppContainer(AppNavigator);
 navigation.push("Photo", {
          name: 'BigPhotoPage',
          photoParams: photoParams,
          photoList: photoList
        })

navigation.goBack()

Photos手机照片

获取相片就需要区分平台了,

这里获取相册访问权限, 需要调用PermissionAndroid

获取相机访问权限, 也是类似.

获取到权限之后, 调用CameraRoll.getPhotos才不会发生异常.

在主线程外解码图片

图片解码有可能会需要超过一帧的时间。在 web 上这是页面掉帧的一大因素,

因为解码是在主线程中完成的。然而在 React Native 中,

图片解码则是在另一线程中完成的。不需要改动代码去额外处理。

 if (this.getPlatform() === "android") {
      let granted = null;
        if (access === "read-storage") {
          granted = await PermissionsAndroid.request(
            PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE, 
            {'title': '手机存储', 'message': '访问您的手机存储'}
          );
        } else if (access === "camera") {
          granted = await PermissionsAndroid.request(
            PermissionsAndroid.PERMISSIONS.CAMERA, 
            {'title': '手机摄像头', 'message': '访问您的手机摄像头'}
          );
        }
        if (granted === PermissionsAndroid.RESULTS.GRANTED) {
          NativeSdk.log(`requestPermision ${access} granted`); } else {
          NativeSdk.log(`requestPermision ${access} timeout or denied`);
        }
      } 
}
 CameraRoll.getPhotos({
          first: getNum,
          assetType: 'Photos',
          after: cursor
        }).then((Photos)=>{
          resolve(Photos);
        }).catch(error => {
          reject(error);
        });

需要小心哪些坑?

  1. Promise代码调试时, 大半代码, 无法下断点.
  2. 安装版console.log无输出, 需要额外写文件.
  3. 启动失败, 也许只是需要删除所有打包缓存文件(每个link过的), 重新运行.
  4. 几乎一半的非官方库没有同时兼容ios, android.
  5. CameraRoll获取的照片地址不是实际地址, 没有后缀, 名字还经常会变.
  6. 设备的USB经常需要重复多连几次.
  7. 包含某些库第三方库之后可能导致打包apk失败.
  8. 日志分散在好几个窗口, 发生错误时不得不一个个检查.

 

 

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐