hammer崔的程序世界

我的生涯一片无悔,我想起那个午夜在灯泡下的抠代码,那是我逝去的青春!


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索
close

react-native android实战:5 ListView构建信息流

发表于 2016-03-24   |   分类于 react-native android实战   |  

需要掌握的基础语法

  • 1 使用 fetch

fetch是一个更好的网络API,它由标准委员会提出,并已经在Chrome中实现。它在React Native中也默认可以使用。

  • 2 使用promise

ScrollView的使用

ListView使用

android基础:applicationId与packageName的区别

发表于 2016-03-23   |   分类于 android   |  

applicationId:在Android系统中应用的applicationId作为应用的唯一标识,就像人的身份证号一样,在一个Android设备中所有的应用程序的applicationId都是唯一的。

eclipse版本

package name:在用Eclipse构建的Android项目中,使用包名(Package Name)作为应用的唯一标识(applicationId)。可以说在Eclipse下applicationId=package name。所以Eclipse构建的Android项目包名必须唯一,一个包名代表一个应用,不允许两个应用使用同样的包名。包名主要用于系统识别应用,几乎不会被最终用户看到。

androidStudo版本

在用Android studio构建的Android项目中可以为应用配置applicationId·,配置的applicationId可以不和package name一样。由此可以看来用Android studio构建的Android项目package name不一定是应用程序的标识。这一点很重要,开发者一定要记住。比如你在使用第三方SDK开发时,如百度地图,需要你来设置Android SDK安全码,在设置安全码时需要用到应用程序的包名,这个包名指的是applicationId。

注意:识别.R跟Activity文件的路径还是packageName,这个是唯一的

解耦方案

  • application id 对应 apk 中 manifest 定义的应用包名,同时用于设备以及 Google Play 的应用唯一标识。
  • package 用于在源码中引用 R 类以及解析注册相关的 activity/service,对应 Java 的包名概念。

我们在AndroidManifest.xml定义一个包名,这个是唯一的

1
2
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hammercui.app.react">

我们在app.gradle文件使用Gradle DSL 方法来为不同的 flavor 和 build type 定义不同的 applicationId:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
android{
···
productFlavors {
pro {
applicationId = "com.hammercui.app.pro"
}
free {
applicationId = "com.hammercui.app.free"
}
}

buildTypes {
debug {
applicationIdSuffix ".debug"
}
}
....
}

参考文档

Gradle Android Plugin中文手册

applicationID和packageName的区别

react-native基础:热更新

发表于 2016-03-21   |   分类于 react-native basis   |  

已知的热更新方案

  • CodePush ,我在使用的方案,同时支持android跟ios,可以点击查看我的详细教程react-native-android实战:4 CodePush使用

  • React Native for Android 热部署图片自定义方案

  • React-Native Android 热更新,这个方案对反射的方案做了补充

购物清单

发表于 2016-03-20   |   分类于 生活常识   |  

家用电器

洗衣机

名称 类型 价格 个人评价
海尔滚筒洗衣机XQG80-BX12636 滚筒 2899 担心小孩,次要考虑
海尔洗衣机XQS70-Z1626 波轮 2599 这个不错,防缠绕

冰箱

名称 类型 价格 个人评价
海尔冰箱BCD-248WDPM 双开门 2288 风冷无霜
海尔冰箱BCD-251WDBD 双开门 2699 风冷无霜
美菱冰箱BCD-220L3BX芙蓉红 三开门 1699 红色,其他无特点

电视

名称 类型 价格 个人评价
三星(SAMSUNG) UA58J50SWAJXXZ 58英寸 全高清 LED液晶电视 4599 首选项,放弃4k
夏普(SHARP) LCD-55S3A 55英寸 4999 4K超高清 智能网络 液晶电视

react-native-android实战:4 CodePush使用

发表于 2016-03-18   |   分类于 react-native android实战   |  

CodePush简介

CodePush 是微软开发的,可以实时更新 React Native 和 Cordova 应用。

CodePush 是提供给 React Native 和 Cordova 开发者直接部署移动应用更新给用户设备的云服务。CodePush 作为一个中央仓库,开发者可以推送更新到 (JS, HTML, CSS and images),应用可以从客户端 SDKs 里面查询更新。CodePush 可以让应用有更多的可确定性,也可以让你直接接触用户群。在修复一些小问题和添加新特性的时候,不需要经过二进制打包,可以直接推送代码进行实时更新。

CodePush 可以进行实时的推送代码更新:

  • 直接对用户部署代码更新
  • 管理 Alpha,Beta 和生产环境应用
  • 支持 Cordova 和 React Native

CodePush的react-native版本是react-native-code-push

JavaScript API

  • checkForUpdate
  • getCurrentPackage
  • notifyApplicationReady
  • restartApp
  • sync

codePush.checkForUpdate

询问code push 服务器是否app有新的版本

codePush.checkForUpdate(deploymentKey: String = null): Promise<RemotePackage>;

该方法返回Promise,有如下两种值:

  1. null 没有更新
  2. A RemotePackage instance

举例用法:

1
2
3
4
5
6
7
8
codePush.checkForUpdate()
.then((update) => {
if (!update) {
console.log("The app is up to date!");
} else {
console.log("An update is available! Should we download it?");
}
});

codePush.getCurrentPackage

获得当前已安装更新的元数据(比如描述,安装时间,大小)
举例:

1
2
3
4
5
6
7
8
9
codePush.getCurrentPackage()
.then((update) => {
// If the current app "session" represents the first time
// this update has run, and it had a description provided
// with it upon release, let's show it to the end user
if (update.isFirstRun && update.description) {
// Display a "what's new?" modal
}
});

codePush.notifyApplicationReady

通知CodePush进程,一个更新安装好了。当你检查并安装更新,(比如没有使用sync方法去handle的时候),这个方法必须被调用。否则CodePush会认为update失败,并rollback当前版本,在app重启时。

当使用sync方法时,不需要调用本方法,因为sync会自动调用,

codePush.restartApp

立即重启app.

当以下情况时,这个方式是很有用的。:

  1. app 当 调用 sync or LocalPackage.install 方法时,指定的 install mode 是 ON_NEXT_RESTART or ON_NEXT_RESUME时 . 这两种情况都是当app重启或resume时,更新内容才能被看到。
  2. You have an app-specific user event (e.g. the end user navigated back to the app’s home route) that allows you to apply the update in an unobtrusive way, and potentially gets the update in front of the end user sooner then waiting until the next restart or resume.

因为强制重启,能马上显示更新内容嘛!

codePush.sync

1
codePush.sync(options: Object, syncStatusChangeCallback: function(syncStatus: Number), downloadProgressCallback: function(progress: DownloadProgress)): Promise<Number>;

允许检查更新,下载并安装,都在一个方法中。除非你需要自定义UI开表现。我们建议开发者在整合CodePush时,使用这个函数。
checkForUpdate方法只能检查更新,接下来怎么做要自己写。而该方法更新,下载,安装都能处理。该方法提供了两种模式

  • Silent mode(默认的模式),

它会自动下载可用更新,并在下次重启应用时应用它们(比如操作系统或用户杀死进程,或者重新启动设备) 。这样一来,整个更新是“沉默”给最终用户,因为用户看不到任何更新提示。

  • Active mode

当有可用的更新,提示用户是否允许下载,然后立即应用此更新。一旦选择了强制通知用户来手动更新,以后每次更新都会强制通知。个人感觉这种方式最合理

sync方法,有以下设置属性

  • deploymentKey (String): 部署key,用来区分不同的app,在Info.plist(Ios)和MianActivity.java(Android)修改,当然你也可以在JavaScript里填写,这样我们能动态的修改key
  • installMode (codePush.InstallMode): 非强制更新,默认codePush.InstallMode.ON_NEXT_RESTART
  • mandatoryInstallMode (codePush.InstallMode):强制更新,默认codePush.InstallMode.IMMEDIATE
  • minimumBackgroundDuration (Number):
  • updateDialog (UpdateDialogOptions) :可选的,更新的对话框,默认是null,包含以下属性

    • appendReleaseDescription (Boolean) - 是否显示更新description,默认false

    • descriptionPrefix (String) - 更新说明的前缀。 默认是” Description: “

    • mandatoryContinueButtonLabel (String) - 强制更新的按钮文字. 默认 to “Continue”.

    • mandatoryUpdateMessage (String) - 强制更新时,更新通知. Defaults to “An update is available that must be installed.”.

    • optionalIgnoreButtonLabel (String) - 非强制更新时,取消按钮文字. Defaults to “Ignore”.

    • optionalInstallButtonLabel (String) - 非强制更新时,确认文字. Defaults to “Install”.

    • optionalUpdateMessage (String) - 非强制更新时,更新通知. Defaults to “An update is available. Would you like to install it?”.

    • title (String) - 要显示的更新通知的标题. Defaults to “Update available”.

举例:

1
2
3
4
5
6
7
8
9
10
11
12
// Prompt the user when an update is available
// and then display a "downloading" modal
codePush.sync({ updateDialog: true }, (status) => {
switch (status) {
case codePush.SyncStatus.DOWNLOADING_PACKAGE:
// Show "downloading" modal
break;
case codePush.SyncStatus.INSTALLING_UPDATE:
// Hide "downloading" modal
break;
}
});

再贴一个官方demo地址

实战应用

Service部署

安装CodePush CLI

npm install -g code-push-cli

可以输入code-push -v查看版本号

创建CodePush 账号

在发布一个更新前,必须有账号。

  • 终端输入code-push register,执行后弹出https://codepush-management.azurewebsites.net/auth/register?hostname=DESKTOP-3PVIHT6,
  • 选择输入微软账号或者github账号,选择Authorize application。然后注册成功,获得一个token。
  • token输入到控制台,显示
  • 登录 code-push login
  • 注销 code-push loout
  • 列出登录的tokencode-push access-key ls
  • 删除某个access-key code-push access-key rm <accessKey>

注册一个app

常用命令,只有下面几个:

  • access-key View and delete active user sessions
  • app View and manage your CodePush-enabled apps
  • collaborator View and manage collaborators for a given app
  • deployment View and manage the deployments for your apps
  • login Authenticate with the CodePush server in order to begin managing your apps
  • logout Log out of the current session
  • promote Promote the package from one deployment of your app to another
  • register Register a new account with the CodePush server
  • release Release a new version of your app to a specific deployment
  • release-react Release a new version of your React Native app to a specific deployment
  • rollback Performs a rollback on the latest package of a specific deployment

选项:

  • -v, –version 显示版本号 [boolean]

比如

  • 查看当前部署的app: code-push app ls
  • 添加一个app: code-push app add Zlot_react

部署已经注册的app

  • 部署: code-push deployment add app名字 部署名
  • 重命名部署名: code-push deployment rename app名字 旧部署名 新部署名
  • 删除部署名字: code-push deployment rm app名字 部署名字
  • 列表部署名字: code-push deployment ls app名字
  • 查看部署的key: code-push deployment ls app名 -k
  • 查看release 的历史版本
    code-push deployment history appName deploymentName

注意:默认的部署名是staging,所以staging的key值就是我们的deployment key

react-native-code-push插件安装

需要有git环境,并在app工程目录控制台输入

npm install react-native-code-push --save

安装成功在package.json会看到

"react-native-code-push": "^1.8.0-beta",

android配置

  • 编辑android/setting.gradle文件,添加
1
2
include ':app', ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
  • 编辑android/app/builde.gradle文件,添加依赖
    1
    compile project(':react-native-code-push')

android react-native v0.18.0+

此种配置方法仅仅适用react-native 0.18版本及其以上。而且是android studio环境

  • 修改MainActivity.java文件,添加CodePush包
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
// 1. Import the plugin class
import com.microsoft.codepush.react.CodePush;


public class MainActivity extends ReactActivity {
// 2. Define a private field to hold the CodePush runtime instance
private CodePush _codePush;
...

// 3. Override the getJSBundleFile method in order to let
// the CodePush runtime determine where to get the JS
// bundle location from on each app start
@Override
protected String getJSBundleFile() {
return this._codePush.getBundleUrl("index.android.bundle");
}

@Override
protected List<ReactPackage> getPackages() {
// 4. Instantiate an instance of the CodePush runtime, using the right deployment key. If you don't
// already have it, you can run "code-push deployment ls <appName> -k" to retrieve your key.
this._codePush = new CodePush("0dsIDongIcoH0mqAmoR0CYb5FhBZNy1w4Bf-l", this, BuildConfig.DEBUG);

// 5. Add the CodePush package to the list of existing packages
return Arrays.<ReactPackage>asList(
new MainReactPackage(), this._codePush.getReactPackage());
}

...
}
  • 确定在android/app/build.gradle文件有android.defaultConfig.versionName属性
1
2
3
4
5
6
7
8
9
android {
...
defaultConfig {
...
versionName "1.0.0"
...
}
...
}

JavaScript代码

  • 导入CodePush的JavaScript组件

import codePush from "react-native-code-push";

  • 在在每个app启动时,在生命周期的componentDidMount阶段调用sync,来开启一个后台的更新。

codePush.sync();

设计方案

  • isMandatory === true强制更新,采用弹出model框提醒的方式
  • isMandatory === false非强制更新,采用静默安装的方式

如:

1
2
3
4
5
6
7
componentDidMount() {
codePush.sync({
updateDialog: false,
installMode: codePush.InstallMode.ON_NEXT_RESUME,
deploymentKey: CODE_PUSH_PRODUCTION_KEY,
});
},

app如何更新(JavaScript+image)

注意:从react-native v0.19.0开始支持android更新assets资源,并且需要CodePush v1.7.0。所以你需要升级你的react-native跟CodePush。

确保在工程目录,新建release文件夹,如下

package命令

1
2
3
4
5
6
react-native bundle 
--platform ios 选择平台
--entry-file index.ios.js 选择启动项
--bundle-output ./release/main.jsbundle 选择打包js输出文件
--assets-dest ./release \ 选择资源输出目录
--dev false 是否调试
  • Android版本

    1
    react-native bundle --platform android --entry-file index.android.js --bundle-output ./release/main.jsbundle  --assets-dest ./release --dev false
  • ios版本

    1
    react-native bundle --platform ios --entry-file index.ios.js --bundle-output ./release/main.jsbundle  --assets-dest ./release --dev false

release命令

1
2
3
4
code-push release <appName> <updateContents> <targetBinaryVersion>
[--deploymentName <deploymentName>]
[--description <description>]
[--mandatory]

mandatory :是否强制更新。

注意:targetBinaryVersion指的是当前的app版本,比如客户端是1.0.0,如果我们要更新客户端,targetBinaryVersion也要是1.0.0,表示是1.0.0版本的更新,跟native的更新不太一样。这是我们第一次经常犯的错误大家不要搞混了。

  • Android版本
    1
    code-push release Zlot ./release 1.0.0 --description "这是一个1.0.0版本的更新" --mandatory true

常见问题

1 更新不成功,报but it is being ignored due to having been previously rolled back.

问题原因:客户端版本1.0.0,然后release的版本是1.0.1,所以检测的时候发现不了1.0.0版本的更新。

因此我们也建议,js的更新采用静默更新,native的更新,做出提示。

注意:targetBinaryVersion指的是当前的app版本,比如客户端是1.0.0,如果我们要更新客户端,targetBinaryVersion也要是1.0.0,表示是1.0.0版本的更新,跟native的更新不太一样。这是我们第一次经常犯的错误大家不要搞混了。

2 [CodePush] An update is available, but it is being ignored due to having been previously rolled back.

解决方案:一般这种情况是在,我们自定义model,展示更新过程,并监听了安装完成,比如这个demo,我这这里忘了在componentDidMount()方法里添加CodePush.notifyApplicationReady();,就一直报这个错误。添加上就ok了

参考文章

使用CodePush热更新ReactNative JS代码

react-native-code-push官方文档

android最佳实践(中英文)

发表于 2016-03-16   |   分类于 android   |  

转载 android最佳实践

转载中文版

转载英文版github地址

react-native android实战:3 与原生模块混合开发

发表于 2016-03-11   |   分类于 react-native android实战   |  

如何使用

封装好java模块供JavaScript调用,比如我们用js实现不了的控件或模块,可以通过java实现并封装,供JavaScript使用。

java模块的封装

java模块新建LogModule类,继承自ReactContextBaseJavaModule,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LogModule extends ReactContextBaseJavaModule {

private static final String MODULE_NAME= "Log";
//构造函数
public LogModule(ReactApplicationContext reactContext) {
super(reactContext);
}

//字符串是JavaScript调用模块的名字
@Override
public String getName() {
return MODULE_NAME;
}

//暴露给JavaScript端的方法
@ReactMethod
public void d(String tag,String msg)
{
Log.d(tag,msg);
}
}

新建module注册类,实现自ReactPackage,把module注册如package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyAppReactPackage implements ReactPackage {


@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

List<NativeModule> modules = new ArrayList<>();
//add Log模块
modules.add(new LogModule(reactContext));
//add DBlocal模块
modules.add(new DBlocalModule(reactContext));
return modules;
}

@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

注册包,在MainActivity中

1
2
3
4
5
6
7
8
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage()
+ , new VectorIconsPackage()
+ ,new MyAppReactPackage()
);
}

js调用封装的java

我们把需要自定义的java模块,统一管理,放在NativeBrigde.android.js文件

1
2
3
4
5
6
7
8
9
//log模块
var RCTLog = NativeModules.Log;
var Log = {
d: function (tag: string, msg: string): void {
console.log(tag,msg);
RCTLog.d(tag, msg);
},
};
module .exports ={Log,DBlocal};

需要调用的时候

1
2
var {Log,DBlocal} =  require('../AnComponent/NativeBridge');`
Log.d('hehe',"我是打印输出");

js向java传参

下面的参数类型在@ReactMethod注明的方法中,会被直接映射到它们对应的JavaScript类型。

1
2
3
4
5
6
7
8
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array

java执行结果回传给js

1 回调

java层需要把结果传递给js,答案就是回调,最典型的一个场景就是javascript层调用java层的网络请求方法,java层拿到网络数据后需要将结果返回给javascript层

为了测试我们在已建好的模块新增方法,invoke回调的参数是无类型,无个数限制的

1
2
3
4
@ReactMethod
public void getValue(String key,final Callback callback){
callback.invoke("我是结果");
}

JavaScript中的使用

1
2
3
4
//从本地数据库取值
DBlocal.getValue('test',(result)=>{
Log.d('hehe',result);
});

在android studio控制台可以看到结果

2 事件监听调用

还可以用事件调用JavaScript层,假设调用了java层方法后,我们希望发送一个事件给javascript,在javascript层再次进行打印输出。

java端

1
2
3
4
5
6
7
8
9
10
11
12
@ReactMethod
public void d(String tag,String msg){
Log.d(tag, msg);
//发送事件给javascript层
WritableMap params = Arguments.createMap();
params.putString("TAG",tag);
params.putString("MSG",msg);
getReactApplicationContext()
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("logInConsole", params);//对应的javascript层的事件名为logInConsole,注册该事件即可进行回调

}

而对应的log.js并不需要做什么修改,我们只需要在想获得该事件的地方注册事件即可,比如我们想在主界面接收该事件,则在index.android.js中进行注册。

JavaScript端

1
2
3
4
5
6
7
8
9
componentDidMount:function(){
Log.d("tag","tag");//调用java层log方法,之后就会回调对应的事件(前提是注册了事件)
//直接使用DeviceEventEmitter进行事件注册
DeviceEventEmitter.addListener('logInConsole',(e)=>{
console.log(e);
}); //记得在UnDidMount里卸载事件

},
});

参考文献

Android React Native使用原生模块

react-native基础:Image资源适配

发表于 2016-03-10   |   分类于 react-native basis   |  

判断设备的dpi

通关官方文档我们了解到,尽量建立统一的image读取路径,便于管理,我们都知道ios的图片资源通过@2x,@3x自动适配,react-native也使用了这一方法。

官方的PixelRatio告诉我们如何获得dpi

static get()

具体用法

1
var bat = PixelRatio.get();

返回设备的像素密度,例如:

  • PixelRatio.get() === 1
    1. mdpi Android 设备 (160 dpi)
  • PixelRatio.get() === 1.5
    1. hdpi Android 设备 (240 dpi)
  • PixelRatio.get() === 2
    1. iPhone 4, 4S
    2. iPhone 5, 5c, 5s
    3. iPhone 6
    4. xhdpi Android 设备 (320 dpi)
  • PixelRatio.get() === 3
    1. iPhone 6 plus
    2. xxhdpi Android 设备 (480 dpi)
  • PixelRatio.get() === 3.5
    1. Nexus 6

创建不同像素密度的资源

学习官方文档Image可知。
我们在src路径存放nav_back@2x.png nav_back@2x.png,加载时调用

require('../src/nav_back.png')

常见的bug

1 打包apk时报错 Error: Don’t know which android drawable suffix to use for asset:

参考github

参考stackflow

我们知道打开local-cli/bundle/assetPathUtils.js文件可以看到

1
2
3
4
5
6
7
8
9
10
function getAndroidAssetSuffix(scale) {
switch (scale) {
case 0.75: return 'ldpi';
case 1: return 'mdpi';
case 1.5: return 'hdpi';
case 2: return 'xhdpi';
case 3: return 'xxhdpi';
case 4: return 'xxxhdpi';
}
}

而我们创建的资源有@3.5x,应该去掉,去掉之后打包就成功了。

react-native android实战:2 打包apk

发表于 2016-03-10   |   分类于 react-native android实战   |  

为什么要打包

APP 运行的时候不应再从 Debug Server 获取资源。分发应用时 JS 资源应该被打包到 APP 中。
另外对外发布的安装包,资源中的业务代码的混淆也是必须的。

如何打包

1 创建keystore,使用eclipse,android studio都行

2 拷贝keystore到路径android\app\下

3 修改android\app\build.gradle文件
添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 android {

+ signingConfigs {
release {
storeFile file("keystore文件名")
storePassword "密码"
keyAlias ""
keyPassword "密码"
}
}

buildTypes {
release {
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
+ signingConfig signingConfigs.release
}
}
}

4 在工程目录,运行命令行开始打包

cd android && ./gradlew assembleRelease

bug的处理

1打包资源bug

解决方法常见我的blogreact-native之旅:Image资源适配,

参考文献

React Native Android的配置说明

官方文档

react-native基础:StyleSheet样式属性

发表于 2016-03-09   |   分类于 react-native basis   |  

以下是StyleSheet所有可用的属性


如果不知道具体的属性怎么写,可以随便写一个,然后运行,报错,react-native会提示你正确的参数,比如我们选择borderStyle,不知随便写,然后报如下错误。

这样我们就知道了borderStyle的参数可以使

1
'solid', 'dotted','dashed'

具体什么意思可以翻译。
实线 点 虚线

1…4567
hammercui

hammercui

69 日志
13 分类
19 标签
RSS
github
Links
  • logicool的技术专栏
  • xiekw2010的专栏
  • 江清清的技术专栏
© 2017 hammercui
由 Hexo 强力驱动
主题 - NexT.Pisces