Jump to content
  • Advertisement
Sign in to follow this  
Nanoha

Android - Passing errors to a user when opengl context fails to be created

This topic is 921 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have an app which I've created using opengl es 2 and ndk which I currently have up as an open alpha on the play store. I only have access to ARM devices (2 tablets, 2 phones) plus emulators. I had a friend of my brother's try my app since he was the only one I could find with a 10" device and I needed a screenshot on a 10" device for the 'designed for tablets' thing on the Play store.

 

When he tried it is just crashed on him, I found out the device was x86 rather than ARM so I then built an x86 version which also crashed, I found out that the ARM should have run anyway under emulation. I don't have access to the device myself so I tried using an emulator. Emulators had actually been broken with my app for quite some time, I found out what was causing the problem (it was the method I was using to solve my issue in this thread http://www.gamedev.net/topic/673909-using-gles23-depending-on-hardware-android/) so I disabled that.

 

Now it works for me on all my devices as well as the ARM emulators as well as the x86 emulators I tried BUT I am told he just gets a black screen now (which is better than crashing I suppose). I suspect it is because the opengl context is not being created, it realises this and quits but quitting doesn't seem to be as simple as just exiting main so the main function ends but whatever is actually running the app is still doing it's thing. 

 

I send my brother a debug version with logging and attempted to get him to attach with logcat and see what's going on but they were unable to install the usb drivers required to actually do it as the work computers are locked down. I've pretty much hassled them a great deal as it is and I don't want to ask more of them as they were doing me a favour trying it.

 

My question is, how do I give feedback to a user when something fatal like this occurs? I built a toast like system to pass info to a user but that requires opengl to use and if that isn't available I can't use it. I looked in to using the actual android toast system but it won't work as you need to access it from the thread running the ui (which I have no access to). With that in mind how am I supposed to notify a user about what's gone on? I am currently at a loss to fix this as I can't reproduce it myself. I am able to access some android api stuff through use of JNI if that helps. Now that it no longer actually crashes I don't get any crash reports either to work on.

 

Any suggestions are most welcome.

Share this post


Link to post
Share on other sites
Advertisement

I looked in to using the actual android toast system but it won't work as you need to access it from the thread running the ui (which I have no access to)

 

You should have access to it. 

I'm guessing you have saved a reference to your activity somewhere in your NDK code?

You can call a method you define on your Activity, through JNI in your rendering thread, and in the Activity you do:

 

runOnUiThread(new Runnable() {
@Override
public void run() {
//Your code to run in UI thread here
}
});

 

to make sure the code is ran on the UI thread.

 

Other ideas for an alpha, could be to launch the mail composer with a log attached and ask your friends to press "send" to send it to you.

Edited by Olof Hedman

Share this post


Link to post
Share on other sites

 


I looked in to using the actual android toast system but it won't work as you need to access it from the thread running the ui (which I have no access to)

 

You should have access to it. 

I'm guessing you have saved a reference to your activity somewhere in your NDK code?

You can call a method you define on your Activity, through JNI in your rendering thread, and in the Activity you do:

 

runOnUiThread(new Runnable() {
@Override
public void run() {
//Your code to run in UI thread here
}
});

 

to make sure the code is ran on the UI thread.

 

Other ideas for an alpha, could be to launch the mail composer with a log attached and ask your friends to press "send" to send it to you.

 

 

Yeah I've seen runOnThread but I have no idea how to make a runnable from the c++ side :/. I did some searching but I'm not sure it's possible to inherit/override form a java class but I don't really know. My search was far from fruitful.

 

I'm currently looking at that mail thing, I have a share screen capture function already so I definitely know I can do that much. It's not a good general solution but it'll certainly help me finding this particular issue. 

Edited by Nanoha

Share this post


Link to post
Share on other sites

Yeah I've seen runOnThread but I have no idea how to make a runnable from the c++ side :/. I did some searching but I'm not sure it's possible to inherit/override form a java class but I don't really know. My search was far from fruitful.

 

Ah, no, the idea is that you do the thread switch on the java side.

I mean a java method like this in your activity:

 

public void showToast(final String text) {  

runOnUiThread(new Runnable() {
@Override
public void run() {

int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(this, text, duration);

toast.show();
}
});
}
 
which you call using JNI with a jstring you create on the c++ side
Edited by Olof Hedman

Share this post


Link to post
Share on other sites

Ah that makes more sense. I don't actually have any java side at all. I'm using the Visual Studio native application project. I'm not sure how I would go about making any java bits at all. This is unexplored territory for me. I would like to pursue this idea thoguh, there are a few things that would be so much easier if I could make on the java side rather than making so many jni calls.

Share this post


Link to post
Share on other sites

imma doing application logging and savit the crap to a file:

#ifndef stringlistH
#define stringlistH

#include "FileHandling.h"
#include <vector>
#include <android/log.h>
#include <fstream>
#include <sstream>
#include "DxcMath.h"
#include "string"
#include "stdlib.h"
#include <iostream>

#define APP_LOG2 "WNB_TXTLOG"
typedef std::string AnsiString;

extern std::vector<AnsiString> w_nawiasie[10];

enum tFloatConversionRule { tFCDot, tFCcomma };

extern tFloatConversionRule FLOAT_CONVERSION;
extern void  init_string_float_conversion_rule();

inline AnsiString IntToStr(int i)
{
	std::stringstream s;

	s << i;

	AnsiString converted(s.str());
	return converted;
}

inline AnsiString FloatToStr(float i)
{
	std::stringstream s;

	s << i;

	AnsiString converted(s.str());
	return converted;
}



inline AnsiString POINT_TO_TEXT(t3dpoint<float> p)
{
	return "X "+FloatToStr(p.x) + " Y "+FloatToStr(p.y)+" Z "+FloatToStr(p.z);
}

inline AnsiString iPOINT_TO_TEXT(t3dpoint<int> p)
{
	return "X "+IntToStr(p.x) + " Y "+IntToStr(p.y)+" Z "+IntToStr(p.z);
}

inline int Pos(AnsiString sub, AnsiString str)
{
	 std::size_t found = str.find(sub,0);
	  if (found!=std::string::npos)
		  return int(found)+1;
	  else
		  return 0;
}


inline AnsiString booltostring(bool hue)
{
if (hue) return "true"; else return "false";
}

inline int pstrtoint(AnsiString num)
{
return ::atoi(num.c_str());
}


inline AnsiString StringReplace(AnsiString str, AnsiString substr, AnsiString with)
{
	AnsiString s = str;
while (Pos(substr, s) > 0)
{
	int pos = Pos(substr, s)-1;
	 s.erase(pos,substr.length());
	 s.insert(pos, with);
}
return s;
}

inline float pstrtofloat(AnsiString num)
{
	AnsiString temp = num;
	if (FLOAT_CONVERSION == tFCDot)     //English rule (floats are written as 10.25)
	temp = StringReplace(num,",",".");
									 else      //Polish rule (floats are written as 10,25)
	temp = StringReplace(num,".",",");


return ::atof(temp.c_str());
}


inline int StrToInt(AnsiString num)
{
return ::atoi(num.c_str());
}

inline AnsiString stddelete(AnsiString str, int pos, int len) //this is for std::string only because i will call only Pos()-1 from it
{
	AnsiString s = str;
	 s.erase(pos, len);
	 return s;
}






extern AnsiString LowerCase(AnsiString str);
extern AnsiString UpperCase(AnsiString str);


inline int booltoint(bool k)
{
	if (k) return 1; else return 0;
}

inline bool stringtobool(AnsiString hue)
{
	if (LowerCase(hue) == "false") return false;
	if (LowerCase(hue) == "true") return true;
	if (LowerCase(hue) == "0") return false;
	if (LowerCase(hue) == "1") return true;
		return false;
}

inline AnsiString get_text_between2(AnsiString b1, AnsiString b2, AnsiString original_str)
{

if (Pos(b1,original_str) == 0) return original_str;
if (Pos(b2,original_str) == 0) return original_str;

//float, 2, 3); ahue ahue
AnsiString temp1 = stddelete(original_str, 0, Pos(b1,original_str) + b1.length() - 1 );
int k = Pos(b2,temp1) - 1;
AnsiString temp2 = stddelete(temp1, k, temp1.length() - k);
return temp2;
}



inline AnsiString get_before_char(AnsiString text, AnsiString sign, bool casesensitive)
{
AnsiString s = text;
AnsiString tmp;
if (casesensitive == false)
	tmp = stddelete(s,Pos(LowerCase(sign),LowerCase(s))-1,s.length()-Pos(LowerCase(sign),LowerCase(s))+1);
else
	tmp = stddelete(s,Pos(sign,s)-1,s.length()-Pos(sign,s)+1);


return tmp;
}


inline AnsiString get_after_char(AnsiString text, AnsiString sign, bool casesensitive)
{
AnsiString s = text;
AnsiString tmp;
if (casesensitive == false)
	tmp = stddelete(s,0,Pos(LowerCase(sign),LowerCase(s)));
	else
		tmp = stddelete(s,0,Pos(sign,s));


return tmp;
}



inline AnsiString get_after_char2(AnsiString text, AnsiString sign, bool casesensitive)
{
AnsiString s = text;
AnsiString tmp;
if (casesensitive == false)
	tmp = stddelete(s,0,Pos(LowerCase(sign),LowerCase(s))+sign.length()-1);
	else
		tmp = stddelete(s,0,Pos(sign,s)+sign.length()-1);


return tmp;
}




inline void get_all_in_nawias(AnsiString ainput, AnsiString aznak, int index)
{
    std::string input = ainput;
    std::string delimiter = aznak;


    w_nawiasie[index].clear();

    AnsiString pikok;

    std::size_t  start = 0U;
    std::size_t end = input.find(delimiter);

    while (end != std::string::npos) {

    	pikok =  input.substr(start, end - start);
      w_nawiasie[index].push_back(pikok);

        start = end + delimiter.length();
        end = input.find(delimiter, start);
    }
  	pikok =  input.substr(start, end);
    w_nawiasie[index].push_back(pikok);

}


inline AnsiString ExtractFileName(AnsiString pikok)
{
	get_all_in_nawias(pikok,"/",0);
	return w_nawiasie[0][ w_nawiasie[0].size()-1 ];
}


inline AnsiString ExtractFilePath(AnsiString pikok)
{
	get_all_in_nawias(pikok,"/",0);
	if (w_nawiasie[0].size() <= 0) return "";
	AnsiString ahue = "";
	for (int i=0; i < w_nawiasie[0].size()-1; i++)
	ahue = ahue + w_nawiasie[0][i]+"/";

	return ahue;
}


inline AnsiString get_filename_ext(AnsiString pikok)
{
	AnsiString filename = ExtractFileName(pikok);
	return get_after_char(filename, ".", false);
}


inline AnsiString change_filename_ext(AnsiString pikok, AnsiString next) //push .extension important with dot ex. ".tga"
{
AnsiString path 	= ExtractFilePath(pikok);
AnsiString filename = ExtractFileName(pikok);
AnsiString fname	= get_before_char(filename,".", false);

return path+fname+next;

}



struct TStringList
{

	int Count;
std::vector<AnsiString> Strings;


void Add(AnsiString text)
{
AnsiString p = text;
Strings.push_back(text);
	Count = Count + 1;
}

	AnsiString GetText()
	{
	AnsiString res = "";
	int i;
	for (i=0; i < Count; i++)
		res = res + Strings[i] + "\n";
	return res;
	}

	void AddLineAtPos(int atline, int atpos)
	{
		std::vector<AnsiString> tmp;
		tmp.clear();
		for (int i=0; i < atline; i++)
			tmp.push_back(Strings[i]);

		AnsiString prefix;
		AnsiString suffix;

if (Strings[atline].length() > 0)
{
		prefix = stddelete(Strings[atline], atpos, 100000);
		suffix = stddelete(Strings[atline], 0, atpos);

tmp.push_back(prefix);
tmp.push_back(suffix);
} else
{
tmp.push_back("");
tmp.push_back("");
}


		for (int i=atline+1; i < Count; i++)
			tmp.push_back(Strings[i]);

	Strings.clear();

	Count = Count + 1;
	for (int i=0; i < Count; i++)
	Strings.push_back(tmp[i]);

	tmp.clear();
	}

	void Clear()
	{
		Count = 0;
		Strings.clear();
	}

	TStringList()
	{
		Clear();
	}

	~TStringList()
	{
		Clear();
	}


void LoadFromFile(AnsiString fname)
{

	std::ifstream file(fname.c_str());
	if (file.good() == false) {
file.close();
return;
	}

	AnsiString str;

	Count = 0;
	if (Strings.size() > 0) Strings.clear();

	while (std::getline(file, str))
	{
		Strings.push_back(str);
		Count = Count + 1;
	}

	file.close();

	//now remove next line characters from strings \n \r  \r\n
for (int i=0; i < Count; i++)
	{
		AnsiString str = Strings[i];

		 char lastChar = str.at( str.length() - 1 );
	if ( (lastChar == '\r') || (lastChar == '\n') )
	{
//		AnsiString p = "deleted newline: "+str;
//		__android_log_print(ANDROID_LOG_VERBOSE, APP_LOG2, p.c_str(), 1+1);

	str.erase(str.length()-1);
	Strings[i] = str;
	}
	}
}

//std::string p;
AnsiString pc;

void SaveToFile(AnsiString fname)
{
	pc = GetText();
	std::ofstream outfile (fname.c_str(),std::ofstream::binary);
	int len = pc.length();
	char * buff = new char[ len ];
	memcpy(buff, pc.c_str(), sizeof(char) * len);
	outfile.write (buff, len);
	outfile.close();
}




};

#endif

cpp

#include "stringlist.h"
#include <algorithm>    // std::transform


tFloatConversionRule FLOAT_CONVERSION;

std::vector<AnsiString> w_nawiasie[10];

AnsiString LowerCase(AnsiString str)
{
	AnsiString s = str;
	std::transform(s.begin(), s.end(), s.begin(), ::tolower);
return s;
}

AnsiString UpperCase(AnsiString str)
{
	AnsiString s = str;
	std::transform(s.begin(), s.end(), s.begin(), ::toupper);
return s;
}

void  init_string_float_conversion_rule()
{
AnsiString f;
float p = 10.250f;
f = FloatToStr(p);
if (Pos(".",f) > 0)
FLOAT_CONVERSION = tFCDot; else FLOAT_CONVERSION = tFCcomma;

}

usage:

cpp

//#pragma hdrstop
#include "logme.h"
#include <android/log.h>
#define APP_LOG "WNB_LOG"
//#pragma package(smart_init)
TStringList * LOG_FILE;

bool CAN_LOG;
bool FORCE_STOP_LOG;
void ShowGLERROR()
{
GLenum res = glGetError();
if ( res == GL_INVALID_ENUM) 		ALOG("GL_INVALID_ENUM");
if ( res == GL_INVALID_VALUE) 		ALOG("GL_INVALID_VALUE");
if ( res == GL_INVALID_OPERATION) 	ALOG("GL_INVALID_OPERATION");
if ( res == GL_OUT_OF_MEMORY) 		ALOG("GL_OUT_OF_MEMORY");
}


void InitializeLog()
{
	LOG_FILE = new TStringList();
	CAN_LOG = true;
	FORCE_STOP_LOG = false;
}



void ALOG(AnsiString txt)
{
if (FORCE_STOP_LOG) return;
	if (!CAN_LOG)
	{
		AnsiString c = "Called log but its disabled:    "+txt;
		__android_log_print(ANDROID_LOG_VERBOSE, APP_LOG, c.c_str(), 1+1);
		return;
	}

	__android_log_print(ANDROID_LOG_VERBOSE, APP_LOG, txt.c_str(), 1+1);


	LOG_FILE->Add(txt);
	LOG_FILE->SaveToFile("/mnt/sdcard/WNB_LOG.txt");
}





h

#ifndef logmeH
#define logmeH
#include "txtfile\stringlist.h"
#include "glwrapper.h"

extern void ShowGLERROR();
extern bool CAN_LOG;
extern bool FORCE_STOP_LOG;
extern TStringList * LOG_FILE;

extern void InitializeLog();
extern void ALOG(AnsiString txt);

#endif

ALOG("CRASH");

 

 

then he has to send you the log. 

Share this post


Link to post
Share on other sites

I abstract my logging a bit like you have so for now I just add each call to a vector of strings. If my 'window' fails to initialise I then use the share plain text intent which popups up all the send via email, hangouts etc thing.

 

I'm still stuck in how I can get a clean message to a user though. Ideally I would like a dialog to popup saying it failed to initialise and asking if they'd like to email the last run's logs or something but I believe I end up in a similar situation where I try to use toasts (not being on ui thread).

Share this post


Link to post
Share on other sites

unfortunatetly its seems no can do, either way his phone doesnt support es 2.0, or your initialization code is wrong.

Edited by WiredCat

Share this post


Link to post
Share on other sites

I can see why you would be concerned about giving the user some feedback as to what happen, but I frankly don't think they care. To them, your app just crashed and thats its. They don't care why it crash, just that it did for the most part. The reason it crashed is important to you as you will require the crash log, so if thats what you are after, then it should be easy to just popup an Android dialog box with some information and then maybe tie that in with some means to submit a crash log. You can create a second activity in your manifest for this or just handle it in the current activity. Either way ( and I'm assuming you are using NativeActivity ) you would need to do some JNI hookup.

Share this post


Link to post
Share on other sites

unfortunatetly its seems no can do, either way his phone doesnt support es 2.0, or your initialization code is wrong.

I will soon find out. I do have opengl es 2 as a requirement for my app so I would think the play store would prevent it being used if the device was incompatible. 

 

The device in question is a Samsung Galaxy Tab 3 10.1.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!