You can't prevent reverse engineering but you can make it harder, involving more work and extra steps that not everyone is capable of.
Putting constants in a native library prevents them from appearing in the decompiled java code. Connecting which string in the compiled library matches which value in the final app isn't trivial. A common method involves just exporting all the strings from the native library and matching them by context - for example, looking for things that look like API keys. If you encrypt these constants and decrypt them in the app itself that adds another step to reverse engineering, although since the encryption keys are in the app an attacker can certainly do that themselves eventually.
To quickly and easily encrypt values in the NDK consider a basic salsa20 or chacha20 implementation - for obfuscation purposes no authentication is needed and the keys can be hard-coded.
As an example, using this simple Salsa20 C implementation:
// hard-coded 128-bit key, can be anything
uint8_t k[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
// hard-coded nonce, can be anything
uint8_t n[8] = { 101, 102, 103, 104, 105, 106, 107, 108 };
// The s20_crypt() function encrypts/decrypts in place.
// During development, do this:
std::string myString = ...;
std::vector<uint8_t> vec(myString.begin(), myString.end());
s20_crypt(k, S20_KEYLEN_128, n, 0, vec.data(), vec.size());
// export the vector bytes somehow - you can run this on your computer and write then to a file or to stdout for example
// After you have the encrypted bytes:
Java_com_galaxy_mars_moon_MainActivity_getDefaultNegative(JNIEnv* env, jobject)
{
uint8_t[] defaultNegative = [...]; // the encrypted values
s20_crypt(k, S20_KEYLEN_128, n, 0, defaultNegative, sizeof(defaultNegative));
std::string decrypted(defaultNegative, defaultNegative+sizeof(defaultNegative));
return env->NewStringUTF(decrypted.c_str());
}