В моем приложении есть класс chatService, который хорошо работает в режиме отладки, но совсем не работает, когда я выпускаю apk-релиз приложения.
Мой файл компонента чата выглядит следующим образом
import React from 'react';
import {connect} from 'react-redux';
import {
GiftedChat,
Send,
Bubble,
Time,
Composer,
} from 'react-native-gifted-chat';
import ChatService from '../services/chatService';
const TIMEOUT = 10000;
class Chatbox extends React.Component {
constructor(props) {
super(props);
this.renderChatMessage = this.renderChatMessage.bind(this);
this.renderEarlierMessages = this.renderEarlierMessages.bind(this);
this.receiveChatMessage = this.receiveChatMessage.bind(this);
this.loadEarlier = this.loadEarlier.bind(this);
this.setChannelFailed = this.setChannelFailed.bind(this);
this.chatService = ChatService(
this.props.auth.token,
this.props.auth.roomToken,
this.receiveChatMessage,
this.renderChatMessage,
this.renderEarlierMessages,
);
}
componentWillUnmount() {
this.chatService.close();
}
componentDidMount() {
this.chatService.open();
}
setChannelFailed() {
this.props.setChannelFailed();
}
// renderOnlineUsers(presence) {
// presence.list((id, { metas: [first, ...rest] }) => {
// let count = rest.length + 1;
// });
// }
receiveChatMessage(messages) {
const userUUID = this.props.currentUser.uuid;
let reciveObj = _.map(messages, (value, key) => {
return {
_id: value.id,
text: value.message,
createdAt: value.inserted_at,
user: {
_id: value.user.uuid,
name: value.user.name,
},
};
});
const message = reciveObj[0];
if (message.user._id !== userUUID) {
this.props.appendMessage(message);
}
}
renderChatMessage(messages) {
let newMessages = _.map(messages.messages, (value, key) => {
return {
_id: value.id,
text: value.message,
createdAt: value.inserted_at,
user: {
_id: value.user.uuid,
name: value.user.name,
},
};
});
if (newMessages.length > 0) {
const lastMessage = newMessages[newMessages.length - 1];
if (newMessages.length < 30) {
this.props.setLoadMore(false);
} else {
this.props.setLoadMore(true);
}
this.props.setMessages(newMessages);
this.props.setLastMessage(lastMessage._id);
}
}
loadEarlier() {
const {lastMessageId} = this.props.socket;
this.chatService.loadEarlier(lastMessageId);
}
renderEarlierMessages(messages) {
let newMessages = _.map(messages.messages, (value, key) => {
return {
_id: value.id,
text: value.message,
createdAt: value.inserted_at,
user: {
_id: value.user.uuid,
name: value.user.name,
},
};
});
const lastMessage = newMessages[newMessages.length - 1];
if (newMessages.length < 30) {
this.props.setLoadMore(false);
} else {
this.props.setLoadMore(true);
}
this.props.setEarlierMessages(newMessages);
this.props.setLastMessage(lastMessage._id);
}
onSend(messages) {
let newMessage = messages[0];
this.props.appendMessage(newMessage);
GiftedChat.append(messages);
this.chatService.send(messages);
analytics.track('Messages Sent', {
messages: messages,
});
}
// draw our ui
render() {
const userUUID = this.props.currentUser.uuid;
const userName = this.props.currentUser.name;
const {loadMore} = this.props.socket;
const {token, roomToken} = this.props.auth;
if (token === null || roomToken === null) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Image
style={{width: 300, height: 300, resizeMode: 'contain'}}
source={require('../assets/images/error.png')}
/>
</View>
);
} else {
return (
<KeyboardAvoidingView
style={{flex: 1, paddingTop: 0}}
keyboardVerticalOffset="-210"
behavior="padding"
enabled>
<GiftedChat
scrollToTop
loadEarlier={loadMore}
isTyping={true}
onLoadEarlier={this.loadEarlier}
messages={this.props.messages}
user={{
_id: userUUID,
}}
onSend={messages => {
messages.forEach(element => {
element.user = {
_id: userUUID,
name: userName,
};
});
this.onSend(messages);
}}
maxHeight={300}
renderBubble={props => {
return (
<Bubble
{...props}
textStyle={{
right: {
color: '#fff',
fontFamily: 'Font-Regular',
},
left: {
color: '#fff',
fontFamily: 'Font-Regular',
},
}}
wrapperStyle={{
right: {
backgroundColor: '#5BC388',
borderTopLeftRadius: 5,
borderBottomLeftRadius: 5,
borderBottomRightRadius: 0,
marginBottom: 5,
paddingRight: 10,
paddingBottom:5,
minWidth: 100,
},
left: {
backgroundColor: '#292C35',
borderTopRightRadius: 5,
borderBottomRightRadius: 5,
borderBottomLeftRadius: 0,
marginBottom: 5,
paddingLeft: 10,
paddingBottom:5,
minWidth: 100,
},
}}
/>
);
}}
renderLoading={() => {
return (
<ActivityIndicator
style={{marginTop: 10, paddingTop: 50}}
size="large"
/>
);
}}
// renderComposer={props => {
// return (
// <Composer
// {...props}
// style={{backgroundColor: '#121519'}}
// placeholderTextColor="#fff"
// textInputStyle={{
// backgroundColor: '#121519',
// marginLeft: 0,
// marginTop: 0,
// height: 50,
// color: '#fff',
// }}>
// <View style={{backgroundColor: '#121519'}} />
// </Composer>
// );
// }}
renderSend={props => {
return (
<Send {...props}>
<View
style={{
marginRight: 0,
marginBottom: 0,
height: 44,
width: 60,
//backgroundColor: '#121519',
alignItems: 'center',
justifyContent: 'center',
}}>
<Text
style={{
color: '#5BC388',
fontSize: 14,
fontFamily: 'Font-SemiBold',
}}>
Send
</Text>
</View>
</Send>
);
}}
renderTime={props => {
let time = null;
const {currentMessage} = props;
if (!currentMessage.createdAt.getTime) {
let utcTIme = '';
utcTIme = currentMessage.createdAt + '.000Z';
time = new Date(utcTIme);
}
return (
<Moment
style={{fontSize: 12, color: '#fff', paddingHorizontal: 5}}
utcOffset="+05:30"
format="HH:mm A"
element={Text}
local>
{currentMessage.createdAt}
</Moment>
// <Text
// style={{
// marginLeft: "auto",
// marginLeft: "auto",
// color: "#cecece"
// }}
// >
// {time
// ? time.toLocaleString("en-IN", {
// hour: "numeric",
// minute: "numeric",
// hour12: true
// })
// : currentMessage.createdAt.toLocaleString("en-IN", {
// hour: "numeric",
// minute: "numeric",
// hour12: true
// })}
// </Text>
);
}}
renderUsernameOnMessage={true}
/>
</KeyboardAvoidingView>
);
}
}
}
export default connect(
MapStateToProps,
mapDispatchToProps,
)(Chatbox);
и мой ChatService выглядит следующим образом
import { Socket } from '../components/sockets/phoenix'
import getEnvVars from "../../environment";
const TIMEOUT = 10000
const URL = `ws://${PHOENIX_HOST}/socket`;
export default (token, roomToken, receiveChatMessage, renderChatMessage, renderEarlierMessages) => {
// construct a socket
const LOBBY = `team_room:${roomToken}`;
const topicId = LOBBY.split(":").slice(-1)[0];
const socket = new Socket(URL, {
params: {
token: token
}
})
// configure the event handlers
socket.onOpen(event => console.log('Connected.'))
socket.onError(event => console.log('Cannot connect.'))
socket.onClose(event => console.log('Goodbye.'))
// open a connection to the server
socket.connect()
// configure a channel into a room - https://www.youtube.com/watch?v=vWFX4ylV_ko
const chan = socket.channel(LOBBY, {})
// join the channel and listen for admittance
chan.join()
.receive('ignore', () => console.log('Access denied.'))
.receive("ok", (msg) => renderChatMessage(msg))
.receive('timeout', () => console.log('Must be a MongoDB.'))
// add some channel-level event handlers
chan.onError(event => console.log('Channel blew up.'))
chan.onClose(event => console.log('Channel closed.'))
// when we receive a new chat message, just trigger the appropriate callback
chan.on(`messages:${topicId}:new`, msg => {
receiveChatMessage(msg)
});
// you can can listen to multiple types
chan.on('user:entered', msg => console.log('say hello to ', msg))
// a function to shut it all down
const close = () => socket.disconnect();
const open = () => socket.connect();
// a function to send a message
const send = (messages) => {
let newMessage = messages[0];
chan
.push("message:add", { message: newMessage.text }, TIMEOUT)
.receive("ok", ({ messages }) => {
analytics.track("Messages Sent", {
messages: messages
});
})
.receive("error", reasons => console.log("flop", reasons))
.receive("timeout", () => console.log("slow much?"));
}
const loadEarlier = (lastMessageId) => {
chan
.push("message:load_more", {
last_message_id: lastMessageId
})
.receive("ok", response => {
renderEarlierMessages(response);
});
}
// reveal a couple ways to drive this bus
return { open, close, send, loadEarlier }
}
, когда в режиме выпуска сокет вообще не подключается.
Я не использую proguard, поэтому я не не знаю, что не так
мой уровень приложения build.gradle выглядит следующим образом
apply plugin: "com.android.application"
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
entryFile: "index.js",
enableHermes: false, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
apply from: '../../node_modules/react-native-unimodules/gradle.groovy'
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
def keystorePropertiesFile = rootProject.file("keystore.properties");
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion rootProject.ext.compileSdkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
dexOptions {
javaMaxHeapSize "4g"
}
defaultConfig {
applicationId "com.chatapp"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
packagingOptions {
pickFirst 'lib/x86/libc++_shared.so'
pickFirst 'lib/x86_64/libjsc.so'
pickFirst 'lib/arm64-v8a/libjsc.so'
pickFirst 'lib/arm64-v8a/libc++_shared.so'
pickFirst 'lib/x86_64/libc++_shared.so'
pickFirst 'lib/armeabi-v7a/libc++_shared.so'
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.facebook.react:react-native:+" // From node_modules
implementation project(':react-native-google-signin')
implementation 'com.facebook.fresco:fresco:2.0.0'
implementation 'com.facebook.fresco:animated-gif:2.0.0'
implementation project(':react-native-notifications')
implementation 'com.google.firebase:firebase-core:16.0.0'
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
addUnimodulesDependencies()
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
apply plugin: 'com.google.gms.google-services'
com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true