Low Rhino performance on android real device - android

I'm having low Rhino performance issues while executing Rhino code on Android real device.
The Rhino context is initiated with a string which represents big JSON object, the total string size is around 120K, to test code performance we wrote a few instrumental tests to check the code performance, however, we are getting not clear result the same code, with the same parameters shows absolutely different results between tests and sample app.
The test performance 10 times faster than the same code is executed as an instrumental test then on it's running as part of the sample app on the same device (G5). BTW the android emulator also shows good performance result.
the code is pretty simple
private void init(String jFfunctionsDeclaration) throws ScriptInitException {
StringBuilder ruleEngineContextBuffer = new StringBuilder();
//create a JSON object in the string representation, later Rhino context will be initialized with this string
for (Map.Entry<String, String> e : scriptObjects.entrySet()) {
String key = e.getKey();
String value = e.getValue();
ruleEngineContextBuffer.append("\nvar ");
ruleEngineContextBuffer.append(key);
ruleEngineContextBuffer.append(" = "); // append(" = JSON.parse(");
ruleEngineContextBuffer.append(value);
}
// create and enter safe execution context to prevent endless loop or deadlock in JS
// because Rhino input it provided from outside
SafeContextFactory safeContextFactory = new SafeContextFactory();
rhino = safeContextFactory.makeContext().enter();
try {
// the fisrt init step, init Rhino cotext with JS utils methods
// functions input is the list of JS functions
sharedScope = rhino.initStandardObjects();
rhino.evaluateString(sharedScope, functions, "<init1>", 1, null);
String str = ruleEngineContextBuffer.toString();
long startContextInit = System.currentTimeMillis();
rhino.evaluateString(sharedScope, str, "<init2>", 1, null);
long totalContextInit = System.currentTimeMillis() - startContextInit;
Log.d(TAG, "Rhino context init duration = " + totalContextInit);
} catch (Throwable e) {
throw new ScriptInitException("Javascript shared scope initialization error: " + e.getMessage());
}
}
could someone explain me this mystery, thanks.

Related

UnityWebRequest does nothing on IL2CPP Build

I've been trying to simply call an api on an android build supporting 64 bit (IL2CPP build) and the UnityWebRequest class didnt seem to work. It's being called via a simple ui button click. It hits the webRequest.SendWebRequest(); and nothing happens. Ive tried the following samples. One, directly from the Unity docs for UnityWebRequest and others using standard HttpClient.
UnityWebRequest:
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
webRequest.SetRequestHeader("Authorization", "Bearer " + API_KEY);
yield return webRequest.SendWebRequest();
if (webRequest.isNetworkError)
{
debugText.text = ": Error: " + webRequest.error;
coroutineAllowed = false;
}
else
{
debugText.text = ":\nReceived: " + webRequest.downloadHandler.text;
dynamic jsonObj = JsonConvert.DeserializeObject(webRequest.downloadHandler.text);
foreach (var obj in jsonObj["businesses"])
{
businessResults.Add(new Business()
{
name = (string)obj["name"],
image_url = (string)obj["image_url"],
review_count = (string)obj["review_count"],
rating = (string)obj["rating"],
Coordinates = new Coordinates()
{
Latitude = (float)obj["coordinates"]["latitude"],
Longitude = (float)obj["coordinates"]["longitude"]
},
price = (string)obj["price"]
});
}
debugText.text = businessResults.Count.ToString();
//coroutineAllowed = true;
}
debugText.text = "getRequest 4";
}
}
This unfortunately did nothing at the yield return webRequest.SendWebRequest();
The next sample I tried was using HttpClient():
IEnumerator HttpClientCall(string uri) //possibly wrap in IEnumerator
{
debugText.text += "http coroutine started" +Environment.NewLine;
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", API_KEY);
var response = httpClient.GetAsync(uri);
if (response.Result.StatusCode != HttpStatusCode.OK)
{
debugText.text += "FAILED HTTP GET";
}
yield return response.Result.Content.ReadAsStringAsync();
dynamic jsonObj = JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result);
foreach (var obj in jsonObj["businesses"])
{
businessResults.Add(new Business()
{
name = (string)obj["name"],
image_url = (string)obj["image_url"],
review_count = (string)obj["review_count"],
rating = (string)obj["rating"],
Coordinates = new Coordinates()
{
Latitude = (float)obj["coordinates"]["latitude"],
Longitude = (float)obj["coordinates"]["longitude"]
},
price = (string)obj["price"]
});
debugText.text += Environment.NewLine + ((string)obj["name"]);
}
}
}
Once again, nothing when it hits yield return response.Result.Content.ReadAsStringAsync();
These all work on PC, and they both return results that i'm expecting.
The next thing i heard was about setting the android manifest application tag with android:usesCleartextTraffic="true"
This unfortunately, also did nothing for me lol. I know it has to be the 64 support, because this works on a standard build. The moment i go to build with 64 support, it doesnt work.
Any help on why it's not returning appropriately would be very helpful.
side note, i know the code is pretty ugly, but after i can figure out why the build doesnt work on the device a heavy refactoring is going to be in play. Thanks in advance!
So after a lot of trouble shooting ive found out why this was not working. The main issue seems to be stemming from my use of the standard Newtonsoft Json package when Unity, apparently, has their own internal JsonUtility class. After changing this:
dynamic jsonObj = JsonConvert.DeserializeObject(response.Result.Content.ReadAsStringAsync().Result);
To This:
var js = JsonUtility.FromJson<T>(response.Result.Content.ReadAsStringAsync().Result);
my results are finally showing in the the apk build correctly.
Also, to note that to map correctly, the JsonUtility.FromJson must be typed to a class that exactly mirrors the incoming json object explicitly.
The page article that finally helped me with this issue is here.
P.S.
Thank you to #RetiredNinja for trying to help instead of just downvoting and saying nothing of value. You're amazing!

Android send log lines as an email periodically

I want to send the log lines to email in every 10 minutes.
To do that, I have used a Timer and inside of timer I send the logs via email.
However I loose some log lines between 2 emails.
For example my first email contains no lines which is normal according to my algorithm.
My second email contains log lines between 15.37 and 15.38 seconds.
My third email contains logs in between 15.44 and 15.48 time intervals.
My fourth email contains logs in between 15.55 and 15.58 time intervals.
As you can see I loose some of my logs but I could not find a way to avoid that.
Following is my code in my service class:
#Override
public void onCreate() {
super.onCreate();
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
sendLogFile();
}
}, 0, 1000 * 60 * 10 );
}
Inside of sendSupport method the second parameter is sent as a content of the log lines where logs is a static string variable.
private void sendLogFile() {
mInteractor.sendSupport("LOG FILE", "MSG"+logs, "SUBJECT"+ System.currentTimeMillis(), "",
result -> {
Timber.log(Log.DEBUG, "sendSupport Thread.currentThread().getName() " + Thread.currentThread().getName());
if (result.isSuccess) {
Timber.d("is sent");
writeLogFile();
} else {
Timber.d("is NOT sent");
}
}
);
}
private void writeLogFile()
{
try {
StringBuilder logBuilder = new StringBuilder();
process = Runtime.getRuntime().exec( "logcat -d");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
logBuilder.append(line + "\n");
}
logs = logBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
}
}
As a result I could not figure out how am I going to be able to get all logs in periodically in my email.
Thanks.
While the #Knossos answer is pointing the reason why some logs are missing, it doesn't suggest how to use that knowledge to reliably get the logs from users' phones, when you don't have an access to their devices to run some adb commands.
Here is what I suggest to do instead:
As you do already use Timber, just add some more power to it. Namely, add an additional LoggingTree that will save the logs into a file, instead of just posting them to Logcat. You can have many Timber Trees working simultaneously, so you can have both Logcat and File logs if needed.
Use the same timer to send an email message when and where needed. But, instead of using access to logcat -d, simply use the file where Timber have already written the logs. Don't forget to flush the stream before sending the email. In order not to send the same logs again and again, configure the FileTree in a way that it creates a new file every time when a previous one is sent (seta a new file name via a method call, for example).
Profit :)
To log into two different systems (Trees), you need to simply add one more to Timber:
Timber.plant(new Timber.DebugTree());
Timber.plant(new FileLoggingTree());
And here is an example of FileLoggingTree(source):
public class FileLoggingTree extends Timber.DebugTree {
private static Logger mLogger = LoggerFactory.getLogger(FileLoggingTree.class);
private static final String LOG_PREFIX = "my-log";
public FileLoggingTree(Context context) {
final String logDirectory = context.getFilesDir() + "/logs";
configureLogger(logDirectory);
}
private void configureLogger(String logDirectory) {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.reset();
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<>();
rollingFileAppender.setContext(loggerContext);
rollingFileAppender.setAppend(true);
rollingFileAppender.setFile(logDirectory + "/" + LOG_PREFIX + "-latest.html");
SizeAndTimeBasedFNATP<ILoggingEvent> fileNamingPolicy = new SizeAndTimeBasedFNATP<>();
fileNamingPolicy.setContext(loggerContext);
fileNamingPolicy.setMaxFileSize("1MB");
TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setContext(loggerContext);
rollingPolicy.setFileNamePattern(logDirectory + "/" + LOG_PREFIX + ".%d{yyyy-MM-dd}.%i.html");
rollingPolicy.setMaxHistory(5);
rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(fileNamingPolicy);
rollingPolicy.setParent(rollingFileAppender); // parent and context required!
rollingPolicy.start();
HTMLLayout htmlLayout = new HTMLLayout();
htmlLayout.setContext(loggerContext);
htmlLayout.setPattern("%d{HH:mm:ss.SSS}%level%thread%msg");
htmlLayout.start();
LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setContext(loggerContext);
encoder.setLayout(htmlLayout);
encoder.start();
// Alternative text encoder - very clean pattern, takes up less space
// PatternLayoutEncoder encoder = new PatternLayoutEncoder();
// encoder.setContext(loggerContext);
// encoder.setCharset(Charset.forName("UTF-8"));
// encoder.setPattern("%date %level [%thread] %msg%n");
// encoder.start();
rollingFileAppender.setRollingPolicy(rollingPolicy);
rollingFileAppender.setEncoder(encoder);
rollingFileAppender.start();
// add the newly created appenders to the root logger;
// qualify Logger to disambiguate from org.slf4j.Logger
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.DEBUG);
root.addAppender(rollingFileAppender);
// print any status messages (warnings, etc) encountered in logback config
StatusPrinter.print(loggerContext);
}
#Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE) {
return;
}
String logMessage = tag + ": " + message;
switch (priority) {
case Log.DEBUG:
mLogger.debug(logMessage);
break;
case Log.INFO:
mLogger.info(logMessage);
break;
case Log.WARN:
mLogger.warn(logMessage);
break;
case Log.ERROR:
mLogger.error(logMessage);
break;
}
}
}
The problem is that logcat -d is only delivering you the latest X bytes of data from the stream. You aren't guaranteed to get everything between 10 minute intervals.
In the best case, you get what you want. In the worst cases, you miss log data or log sections overlap (you get some from the previous dump too).
You can see this here: adb logcat -d | dd
...
03-27 11:36:27.474 791 22420 E ResolverController: No valid NAT64 prefix (147, <unspecified>/0)
03-27 11:36:27.612 3466 3521 I PlayCommon: [657] alsu.c(187): Successfully uploaded logs.
453+111 records in
499+1 records out
255863 bytes (256 kB, 250 KiB) copied, 0,136016 s, 1,9 MB/s
As you can see, it is clearly a 256 kB chunk that is pulled through logcat -d.
On the plus side, you can change that! If you look at adb logcat --help you can see options.
For example, if you use adb logcat -d -t '100000' | dd (all logs in the last 100000 seconds. I now have the following:
...
03-27 11:45:41.687 791 1106 I netd : bandwidthSetGlobalAlert(2097152) <0.90ms>
03-27 11:45:42.098 21897 23376 V FA : Inactivity, disconnecting from the service
2237+1558 records in
2879+1 records out
1474408 bytes (1,5 MB, 1,4 MiB) copied, 1,20785 s, 1,2 MB/s
1.5 MB of logs. You should be able to get all logs with this.
You log the timestamp of each logcat pull, then use that each time to determine the seconds since the last pull.
I hope that helps!
You cannot send an email from the device without user interaction or implementing the email function yourself that allows to do this without user interaction.
Most apps have some api endpoint to send the logs to.

Format exception message to contain the whole stacktrace in Google Analytics for Android?

I would like to have the whole stacktrace in Google Analytic's report's for my mobile application.
I wrote class that print's the stacktrace and put's it into string, but it doesn't work.
My custom ExceptionParser:
#Override
public String getDescription(String threadName, Throwable throwable) {
return threadName + " " + getStackTrace(throwable);
}
private String getStackTrace(Throwable throwable) {
final Writer result = new StringWriter();
final PrintWriter printWriter = new PrintWriter(result);
throwable.printStackTrace(printWriter);
return result.toString();
}
And I set it like this:
EasyTracker.getTracker().setExceptionParser(new StacktraceExceptionParser());
The method below combines the entire stack trace into a single comma separated line, which may help in case Analytics returns just the first line. But there still may be a length limit so it may be prudent to do filtering to eliminate items you do not need (see comment)
public String getCombinedStackTrace(Throwable aThrowable) {
final StringBuilder result = new StringBuilder();
result.append(aThrowable.toString());
result.append(',');
String oneElement;
for (StackTraceElement element : aThrowable.getStackTrace() ) {
// you can do some filtering here, selecting only the elements you need
oneElement = element.toString();
result.append( oneElement );
result.append( ",");
}
return result.toString();
}
I second Nikolay's comment about using an error reporting library. I found it to be tremendously helpful.
I know this thread is old but I am trying to figure out how to get this working, but just for completeness there is a useful method on Log that does what you want
String stackTraceStr = Log.getStackTraceString(exception);
EDIT: In response to the 100 char limit comment
I could never get EasyTracker.getTracker().setExceptionParser(...) working, infact I do not think it works, so I followed the blog post here http://dandar3.blogspot.co.uk/2013/03/google-analytics-easytracker-detailed.html
The important point in the blog post is to make sure you set your ExceptionParser on the GA exception handler:
// Make sure you set the context on EasyTracker first
EasyTracker.getInstance().setContext(this);
// As in in the blog post, we have to get the ExceptionReporter
// in order to set the ExceptionParser
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
if (uncaughtExceptionHandler instanceof ExceptionReporter) {
ExceptionReporter exceptionReporter = (ExceptionReporter) uncaughtExceptionHandler;
exceptionReporter.setExceptionParser(new AnalyticsExceptionParser());
}
This worked for me and logs more than 100 chars.
Analytics may be limiting the size of messages you can send. I suggest using a real error reporting library like ACRA or BugSense instead. You will get extra features such as device info, configuration details and better error reporting (combining multiple exceptions if they have the same trace, etc.).

Android app uses static method to transfer and plot data

I've been asking questions regarding my Android project that continually plots Bluetooth data in real-time.
Basically what I've already done is create a first version of my app by cobbling together some open source code Blueterm and OrientationSensorExample
It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.
Here's a description of the Blueterm open source code I started with (see link above). Blueterm is basically a terminal emulator program that communicates over Bluetooth. It consists of several activities with Blueterm being the most important. It discovers, pairs, and connects with a remote Bluetooth device that supports SPP/RfComm. When connected I can use Blueterm to configure the remote device by sending it commands to turn on sampling, change the number of channels to sample (to one channel), change to format of the incoming data (I like comma separated data), etc
Here's a description of the OrientationSensorExample open source code I started with (see link above). It's basically an example application of the AnroidPlot library. The OrientationSensor activity implements SensorEventListener. This includes overriding onSenorChanged() which is called whenever new orientation sensor data is taken, and it redraws the graph.
Having cobbled together these two open source projects (Blueterm and OrientationSensorExample) into one application (Blueterm) here's a description of how the overall application (Blueterm) works. When I start Blueterm the whole screen emulates a nice blue terminal. From the Options Menu I discover, pair with, connect to, and configure a remote bluetooth device as described above. Once I have configured the remote device, I go again to the Options Menu and select "Plot data" which launches the Plot activity. The terminal emulator goes away, and a nice scrolling real-time plot from the Plot activity shows up.
As far as I can tell there is a background thread that calls an update() method as follows:
/**
* Look for new input from the ptty, send it to the terminal emulator.
*/
private void update() {
int bytesAvailable = mByteQueue.getBytesAvailable();
int bytesToRead = Math.min(bytesAvailable, mReceiveBuffer.length);
try {
int bytesRead = mByteQueue.read(mReceiveBuffer, 0, bytesToRead);
append(mReceiveBuffer, 0, bytesRead);
//VTR use existing handler that calls update() to get data into plotting activity
Plot.plotData(mReceiveBuffer, 0, bytesRead);
} catch (InterruptedException e) {
//VTR OMG their swallowing this exception
}
}
In the update() method I found it convenient to call my Plot.plotData() method and pass it the same date that is passed to the append() method to plot the data. NOTE: This only works if plotData() is a static method. No one has been able to explain why.
Anyway plotData() is a static method and here's how it and it's helper methods look now:
private static StringBuffer strData = new StringBuffer("");
public static void plotData(byte[] buffer, int base, int length) {
Log.i("Entering: ", "plotData()");
/*
byte[] buffer = (byte[]) msg.obj;
int base = msg.arg1;
int length = msg.arg2;
*/
for (int i = 0; i < length; i++) {
byte b = buffer[base + i];
try {
if (true) {
char printableB = (char) b;
if (b < 32 || b > 126) {
printableB = ' ';
}
Log.w("Log_plotData", "'" + Character.toString(printableB)
+ "' (" + Integer.toString(b) + ")");
strData.append(Character.toString(printableB));
if (b == 10)
{
Log.i("End of line: ", "processBlueData()");
Log.i("strData", strData.toString());
splitData(strData);
strData = new StringBuffer("");
}
}
} catch (Exception e) {
Log.e("Log_plotData_exception", "Exception while processing character "
+ Integer.toString(i) + " code "
+ Integer.toString(b), e);
}
}
Log.i("Leaving: ", "plotData()");
}
private static void splitData(StringBuffer strBuf) {
String strDash = strBuf.toString().trim();
String[] strDashSplit = strDash.split("-");
for (int ndx = 0; ndx < strDashSplit.length; ndx++)
{
if (strDashSplit[ndx].length() > 0)
Log.i("strDashSplit", ndx + ":" + strDashSplit[ndx]);
String strComma = strDashSplit[ndx].trim();
String[] strCommaSplit = strComma.split(",");
for (int mdx = 0; mdx < strCommaSplit.length; mdx++)
{
if (strCommaSplit[mdx].length() > 0)
Log.i("strCommaSplit", mdx + ":" + strCommaSplit[mdx]);
if (mdx == 1)
{
int raw = Integer.parseInt(strCommaSplit[1],16);
Log.i("raw", Integer.toString(raw));
float rawFloat = raw;
Log.i("rawFloat", Float.toString(rawFloat));
float ratio = (float) (rawFloat/65535.0);
Log.i("ratio", Float.toString(ratio));
float voltage = (float) (5.0*ratio);
Log.i("voltage", Float.toString(voltage));
nowPlotData(voltage);
}
}
}
}
public static void nowPlotData(float data) {
// get rid the oldest sample in history:
if (plotHistory.size() > HISTORY_SIZE) {
plotHistory.removeFirst();
}
// add the latest history sample:
plotHistory.addLast(data);
// update the plot with the updated history Lists:
plotHistorySeries.setModel(plotHistory, SimpleXYSeries.ArrayFormat.Y_VALS_ONLY);
//VTR null pointer exception?
if (plotHistoryPlot == null)
Log.i("aprHistoryPlot", "null pointer exception");
// redraw the Plots:
plotHistoryPlot.redraw();
}
If it is strongly recommended that plotData() not be a static method and that I should do something else please explain here and how. Thanks!
This might be a question much better suited for Code Review, rather than here. Perhaps you can reformulate to post it there, or trim it a lot to repost it here.
Furthermore, to answer: "It's been suggested that I add a thread, a handler, a Service, or use Async Task, or AIDL, etc. But I don't know how to use any of these and would appreciate an explanation.", the best advise would be to link you to a book about android, such as: http://commonsware.com/Android/ . Chapters 35 and 36 deal with services, while chapter 20 is about threads. You will never get an answer as complete as those chapters here.

Using java.util.regex in Android apps - are there issues with this?

In an Android app I have a utility class that I use to parse strings for 2 regEx's. I compile the 2 patterns in a static initializer so they only get compiled once, then activities can use the parsing methods statically.
This works fine except that the first time the class is accessed and loaded, and the static initializer compiles the pattern, the UI hangs for close to a MINUTE while it compiles the pattern! After the first time, it flies on all subsequent calls to parseString().
My regEx that I am using is rather large - 847 characters, but in a normal java webapp this is lightning fast. I am testing this so far only in the emulator with a 1.5 AVD.
Could this just be an emulator issue or is there some other reason that this pattern is taking so long to compile?
private static final String exp1 = "(insertratherlong---847character--regexhere)";
private static Pattern regex1 = null;
private static final String newLineAndTagsExp = "[<>\\s]";
private static Pattern regexNewLineAndTags = null;
static {
regex1 = Pattern.compile(exp1, Pattern.CASE_INSENSITIVE);
regexNewLineAndTags = Pattern.compile(newLineAndTagsExp);
}
public static String parseString(CharSequence inputStr) {
String replacementStr = "replaceMentText";
String resultString = "none";
try {
Matcher regexMatcher = regex1.matcher(inputStr);
try {
resultString = regexMatcher.replaceAll(replacementStr);
} catch (IllegalArgumentException ex) {
} catch (IndexOutOfBoundsException ex) {
}
} catch (PatternSyntaxException ex) {
}
return resultString;
}
please file a reproduceable test case at http://code.google.com/p/android/issues/entry and i'll have a look. note that i will need a regular expression that reproduces the problem. (our regular expressions are implemented by ICU4C, so the compilation actually happens in native code and this may end up being an ICU bug, but if you file an Android bug i'll worry about upstream.)
If you launched with debugging you can expect it to be about twice as slow as a regular launch. However a minute does seem extraordinary. Some things to suggest, i. look at the console output to see if warnings are being spat out, ii. when it is doing the compile, in the debugger press 'pause' and just see what it is doing. There are ways to get the source, but even so just looking at the call stack may reveal something.

Categories

Resources