This post is the 4th one in a series of posts on Electrode Native.
It assumes that one has already completed the steps in Tutorial #3 here - https://gist.github.com/hemanth-manoharan/5460456f019faae2678776427f879bac
In this tutorial, we are going to demonstrate how to communicate between the JS code in the mini-app and the native code in the outer app. We will be using the concept of Electrode Native APIs for the same.
Ref: https://native.electrode.io/introduction/what-is-ern/js-native-communication
- Create the API definition. Ensure that you choose an unique api name since we will be publishing the same to npm. The following command will generate an API with the name
ern-helloworld-api
ern create-api ern-helloworld
-
Change directory into the generated
ern-helloworld-apifolder and runyarn publish. Assuming this publishes version 1.0.0 ofern-helloworld-api -
Create the API implementation project. We are going to implement it in native code specifically for android.
ern create-api-impl ern-helloworld-api -n
-
The generated scaffolding for the implementation project will be in the folder
ern-helloworld-api-impl-native -
Change directory into the generated
ern-helloworld-api-impl-nativefolder and open theandroidsub-folder in Android Studio. -
Open the file
lib/src/main/java/com/ern/api/impl/WalmartItemApiRequestHandlerProvider.java -
Replace the function definition for
registerFindItemsRequestHandleras follows. We are implementing only thefindItemsfunction for now.
@Override
public void registerFindItemsRequestHandler() {
WalmartItemApi.requests().registerFindItemsRequestHandler(new ElectrodeBridgeRequestHandler<Integer, List<Item>>() {
@Override
public void onRequest(Integer payload, ElectrodeBridgeResponseListener<List<Item>> responseListener) {
List<Item> items = new ArrayList<Item>() {{
add(new Item.Builder((long) 1, "iPhone 11").build());
add(new Item.Builder((long) 2, "iPhone 12").build());
}};
responseListener.onSuccess(items);
}
});
}
-
Important Note: Potential fix needed in Electrode Native native implementation generator. In
package.json, need to changepluginConfigconfiguration for therootfolder for android to remove/libat the end. Otherwise,ern run-androidfails with a folder missing error when theern-helloworld-api-impl-nativeis added to temp1-miniapp. -
Run
yarn publishfromern-helloworld-api-impl-nativefolder -
Now, add the API and the implementation dependencies to the temp1-miniapp
cd temp1-miniapp
ern add [email protected]
ern add [email protected]
- Update version in package.json for temp1-miniapp and publish the same to npm.
yarn publish
- Now, add the updated miniapp to the cauldron and create a container. Then, build the updated aar and embed the same into the outer app. This is so that the new native dependency (the api implementation) is available to the app.
ern cauldron update miniapps [email protected] -d ErnOuterApp:android:0.0.1 -v 1.0.3
ern create-container -d ErnOuterApp:android:0.0.1 -p android
- Then, invoke the new API inside temp1-miniapp and render the output inside the same. Some relevant snippets to add to
App.jsis listed below.
import { WalmartItemApi } from 'ern-helloworld-api';
...
_keyExtractor = (item) => item.id;
constructor() {
super();
WalmartItemApi.requests().findItems(100, 500).then((items) => {
if (items) {
this.setState(previousState => {
return {items};
});
}
}).catch(error => {
let items = [{
id: 1,
name: "Mi A2"
}, {
id: 2,
name: "Mi A3"
}];
this.setState(previousState => {
return {items};
});
});
this.state = {
items: []
};
}
...
<FlatList
style={styles.listContainer}
data={this.state.items}
keyExtractor={this._keyExtractor}
renderItem={({item}) =>
<View style={styles.listRow}>
<Text style={styles.itemId}>{item.id}</Text>
<Text style={styles.itemName}>{item.name}</Text>
</View>
}
/>
...
listContainer: {
flex: 1,
marginTop: 20,
padding: 5,
backgroundColor: 'black'
},
listRow: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: 'white',
padding: 12
},
itemId: {
fontSize: 20,
},
itemName: {
paddingTop: 5,
flex: 1,
fontSize: 12
},
...
-
Update version in package.json for temp1-miniapp and publish the same to npm.
-
Publish a new version of temp1-miniapp via an OTA update as explained in the previous blog post.
ern cauldron update miniapps [email protected] -d ErnOuterApp:android:0.0.1 -v 1.0.4
ERN_LOG_LEVEL=debug ern code-push release --miniapps [email protected] -d ErnOuterApp:android:0.0.1 --deploymentName Staging
appcenter codepush release -a hemanth-aero-pmdp/ErnOuterAppAndroid -c TEMP_FOLDER_PATH/bundleOut -t 0.0.1 -d Staging
-
Finally, run the outer android app to see if the results from the API are displayed.
-
Now, let's also see how to invoke the same API from the outer app. This is very much similar to the way we did it in JavaScript. In
MainActivity.javaof the outer app, perform the following changes.
import com.ernhelloworld.ern.api.WalmartItemApi;
...
WalmartItemApi.requests().findItems(100, new ElectrodeBridgeResponseListener<List<Item>>() {
@Override
public void onSuccess(@Nullable List<Item> items) {
System.out.println("findItems API call successful!");
System.out.println(items);
}
@Override
public void onFailure(@NonNull FailureMessage failureMessage) {
System.out.println("findItems API call failed!");
System.out.println(failureMessage);
}
});
-
Now, run the app and check if the API call output is displayed in the console logs.
-
In the next article, we will explore the use of ern-navigation API for back-button script logic.
Ref: https://www.electrode.io/ern-navigation/
The following snippet expects that the path for relPathToApiImplSource starts with lib.
Hence, the corresponding root path in the package.json should exclude the lib part.
if (await coreUtils.isDependencyPathNativeApiImpl(pluginSourcePath)) {
// Special handling for native api implementation as we don't
// want to copy the API and bridge code (part of native api implementations projects)
const relPathToApiImplSource = path.normalize(
'lib/src/main/java/com/ern',
);
const absPathToCopyPluginSourceTo = path.join(
config.outDir,
'lib/src/main/java/com',
);
shell.cp('-R', relPathToApiImplSource, absPathToCopyPluginSourceTo);
}