Reputation: 1729
I am have a little trouble understanding the AsyncTask and whether or not i can actually do what i am trying to do.
Within my onCreate method of my activity I am doing a few things, one of which is to create a GLSurfaceView and set my renderer. I want this process to be done by an AsyncTask in the background.
Here is my code
public class ActivityTest extends BaseGameActivity
{
/** Hold a reference to our GLSurfaceView */
private MYGLSurfaceView mGLSurfaceView;
public MYGamePlay GamePlay;
public GoogleApiClient mGoogleApiClient = null;
private AdView adView;
private static final String AD_UNIT_ID = "*******************************";
private Button RotateButton;
private CreateRenderer mCreateRenderer;
private TextView Score;
private RelativeLayout layout;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Dialog loader_dialog = new Dialog(this,android.R.style.Theme_Black_NoTitleBar_Fullscreen);
loader_dialog.setContentView(R.layout.loading_screen);
loader_dialog.show();
// Create an ad.
adView = new AdView(this);
adView.setAdSize(AdSize.SMART_BANNER);
adView.setAdUnitId(AD_UNIT_ID);
// create the layout that holds the combined game layout
layout = new RelativeLayout(this);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// Create an ad request. Check logcat output for the hashed device ID to
// get test ads on a physical device.
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice("***********************").build();
// Start loading the ad in the background.
adView.loadAd(adRequest);
Score = new TextView(this);
Score.setText(" 0");
RelativeLayout.LayoutParams scoreParams = new
RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
Score.setLayoutParams(scoreParams);
Typeface tf = Typeface.createFromAsset(getAssets(),"Fonts/D3Euronism_b.ttf");
Score.setTextSize(getResources().getDimension(R.dimen.textsize));
Score.setTypeface(tf);
Score.setTextColor(Color.parseColor("#FDAA03"));
LayoutInflater inflater = (LayoutInflater) this.getSystemService(this.LAYOUT_INFLATER_SERVICE);
View userInterface = inflater.inflate(R.layout.user_interface, null);
RotateButton = (Button) userInterface.findViewById(R.id.rotbutton);
RotateButton.setOnTouchListener(new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
//irrelivant
}
});
RelativeLayout.LayoutParams adParams =
new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
adParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
adParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
mCreateRenderer = new CreateRenderer(this, Score, mGLSurfaceView);
mCreateRenderer.execute();
layout.addView(userInterface);
layout.addView(Score);
layout.addView(adView, adParams);
setContentView(layout);
}
@Override
protected void onResume()
{
// The activity must call the GL surface view's onResume() on activity onResume().
super.onResume();
//mGLSurfaceView.onResume();
}
@Override
protected void onPause()
{
// The activity must call the GL surface view's onPause() on activity onPause().
super.onPause();
//mGLSurfaceView.onPause();
}
private class CreateRenderer extends AsyncTask<Void, Void, GLSurfaceView>
{
Context baseClassContext;
RBGLSurfaceView myGLSurfaceView;
TextView GameScore;
public CreateRenderer(Context mContext, TextView mScore, RBGLSurfaceView rGLSurfaceView )
{
baseClassContext = mContext;
GameScore = mScore;
myGLSurfaceView = rGLSurfaceView;
}
@Override
protected GLSurfaceView doInBackground(Void... params)
{
final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000;
if (supportsEs2)
{
// Request an OpenGL ES 2.0 compatible context.
/*line 226*/ myGLSurfaceView = new MYGLSurfaceView(baseClassContext); new GLSurfaceView(baseClassContext);
myGLSurfaceView.setEGLContextClientVersion(2);
final DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
// Set the renderer to our demo renderer, defined below.
mGoogleApiClient = getApiClient();
layout.addView(myGLSurfaceView);
myGLSurfaceView.setRenderer(new MYRenderer(baseClassContext, GameScore, mGoogleApiClient),displayMetrics);
}
else
{
// This is where you could create an OpenGL ES 1.x compatible
// renderer if you wanted to support both ES 1 and ES 2.
}
return myGLSurfaceView;
}
@Override
protected void onPostExecute(GLSurfaceView result)
{
}
@Override
protected void onPreExecute() {}
@Override
protected void onProgressUpdate(Void... values) {}
}
}
I have been reading conflicting things about AsyncTask like whether i can create a contstructor and pass things to it.
Any way it fails on line 226 which i have marked in the code above.
Here is the logCat
09-05 09:29:29.554: E/AndroidRuntime(7585): FATAL EXCEPTION: AsyncTask #2
09-05 09:29:29.554: E/AndroidRuntime(7585): java.lang.RuntimeException: An error occured while executing doInBackground()
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.os.AsyncTask$3.done(AsyncTask.java:299)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.util.concurrent.FutureTask.run(FutureTask.java:239)
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.lang.Thread.run(Thread.java:856)
09-05 09:29:29.554: E/AndroidRuntime(7585): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.os.Handler.<init>(Handler.java:197)
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.os.Handler.<init>(Handler.java:111)
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.view.SurfaceView$1.<init>(SurfaceView.java:122)
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.view.SurfaceView.<init>(SurfaceView.java:122)
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.opengl.GLSurfaceView.<init>(GLSurfaceView.java:213)
09-05 09:29:29.554: E/AndroidRuntime(7585): at com.game.cuberush.MYGLSurfaceView.<init>(MYGLSurfaceView.java:34)
09-05 09:29:29.554: E/AndroidRuntime(7585): at com.game.test.ActivityTest$CreateRenderer.doInBackground(ActivityTest.java:226)
09-05 09:29:29.554: E/AndroidRuntime(7585): at com.game.test.ActivityTest$CreateRenderer.doInBackground(ActivityTest.java:1)
09-05 09:29:29.554: E/AndroidRuntime(7585): at android.os.AsyncTask$2.call(AsyncTask.java:287)
09-05 09:29:29.554: E/AndroidRuntime(7585): at java.util.concurrent.FutureTask.run(FutureTask.java:234)
09-05 09:29:29.554: E/AndroidRuntime(7585): ... 4 more
this is the first time i have tried to use AsyncTask so i realise i am probably making a mess with it. So any help with this would be great
EDIT method using annotations still produces the same result the code a put the @Background annotation doesnt seem to be running on a seperate thread as the oncreate is still waiting for it, unless i am doing it wrong
called un onCreate
CreateRender(mGLSurfaceView, this, Score, loader_dialog, mGoogleApiClient, displayMetrics);
method CreateRender
@Background
protected void CreateRender(MYGLSurfaceView myGLSurfaceView, Context mContext, TextView mScore, Dialog mDialog, GoogleApiClient gAPI, DisplayMetrics mDispMet )
{
myGLSurfaceView.setRenderer(new MYRenderer(mContext, mScore, mDialog, gAPI),mDispMet);
mGLSurfaceView = returnResult(myGLSurfaceView);
}
@UiThread
MYGLSurfaceView returnResult(MYGLSurfaceView res)
{
return res;
}
this must be wrong
Upvotes: 1
Views: 630
Reputation: 35224
Your basic problem is that you try to change the UI from a non-ui thread. From the documentation:
Additionally, the Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread.
Asynctask is actually a convience for that. It does some background work off the main thread and then runs the onPostExecute()
on the ui-thread. That means 2 things: first you can change the ui in onPostExecute()
and everything done here will be executed serial to the ui (so it could be blocked)
There is the usual quickfix for that with
activity.runOnUiThread(new Runnable() {
public void run() {
//this will be executed on the ui thread
}
});
which will not help you since it will then block the ui again. Im actually not sure if you can do what you want, since some things just can be done in the background (e.g. inflating a real complex xml in onCreateView()). You could try to create your own looper like:
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
and handle your surfaceview there, but Im not sure that works.
Further resouces:
Upvotes: 1
Reputation: 124
If you get crazy using AsyncTask i prefer to try AndroidAnnotations. Instead of writing a complete class to handle your background tasks, you just use a @Background annotation over your method. This concept saves a lot of code and it works!
This is an example from the android annotations wiki at github:
@EActivity(R.layout.translate) // Sets content view to R.layout.translate
public class TranslateActivity extends Activity {
@ViewById // Injects R.id.textInput
EditText textInput;
@ViewById(R.id.myTextView) // Injects R.id.myTextView
TextView result;
@AnimationRes // Injects android.R.anim.fade_in
Animation fadeIn;
@Click // When R.id.doTranslate button is clicked
void doTranslate() {
translateInBackground(textInput.getText().toString());
}
@Background // Executed in a background thread
void translateInBackground(String textToTranslate) {
String translatedText = callGoogleTranslate(textToTranslate);
showResult(translatedText);
}
@UiThread // Executed in the ui thread
void showResult(String translatedText) {
result.setText(translatedText);
result.startAnimation(fadeIn);
}
// [...]
}
Upvotes: 4
Reputation: 1154
Ive been wrestling with AsyncTask the last couple of days myself. It seems to have some kind of an aversion to surfaces.
I would suggest a change of approach -- create the view in onCreate/onResume ( depending on your needs ) and avoid tying in nonexistent surfaces with AsyncTask.
This approach worked for me, hope it does for you as well!
Upvotes: 1