使用AsyncTask在后台创建GLSurfaceView

时间:2014-09-05 09:00:40

标签: java android android-activity android-asynctask

我在理解AsyncTask时遇到了一些麻烦,无论我是否真的可以做我想做的事。

在我的活动的onCreate方法中,我正在做一些事情,其中​​之一是创建一个GLSurfaceView并设置我的渲染器。我希望这个过程在后台由AsyncTask完成。

这是我的代码

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) {}
}

}

我一直在阅读有关AsyncTask的相互矛盾的事情,比如我是否可以创建一个contstructor并将其传递给它。

任何方式它都会在我在上面的代码中标记的第226行失败。

这是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

这是我第一次尝试使用AsyncTask,所以我意识到我可能会弄乱它。所以对此的任何帮助都会很棒

使用注释的EDIT方法仍会产生相同的结果,因为oncreate仍在等待它,所以@Background注释似乎没有在单独的线程上运行的代码,除非我做错了

调用un onCreate

CreateRender(mGLSurfaceView, this, Score, loader_dialog, mGoogleApiClient, displayMetrics);

方法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;
}         

这一定是错的

3 个答案:

答案 0 :(得分:4)

如果您对使用AsyncTask感到疯狂,我更喜欢尝试使用AndroidAnnotations。您只需在方法上使用@Background注释,而不是编写完整的类来处理后台任务。这个概念可以节省大量代码,而且有效!

这是来自github上的android注释wiki的一个例子:

@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);
    }

    // [...]
}

答案 1 :(得分:1)

我自己过去几天一直在和AsyncTask搏斗。它似乎对表面有某种厌恶。

我建议更改方法 - 在onCreate / onResume中创建视图(根据您的需要)并避免使用AsyncTask绑定不存在的表面。

这种方法对我有用,希望它也适合你!

答案 2 :(得分:1)

您的基本问题是您尝试从非ui线程更改UI。来自documentation

  

此外,Andoid UI工具包不是线程安全的。因此,您不能从工作线程操纵UI - 您必须从UI线程对用户界面进行所有操作。

Asynctask实际上是一种方便。它在主线程上执行一些后台工作,然后在ui-thread上运行onPostExecute()。这意味着两件事:首先你可以在onPostExecute()中更改ui,这里完成的所有操作都将连续执行到ui(因此可能会被阻止)

有通常的quickfix
activity.runOnUiThread(new Runnable() {
  public void run() {
    //this will be executed on the ui thread
  }
});

这对你没有帮助,因为它会再次阻止ui。我实际上不确定你是否可以做你想做的事情,因为有些事情可以在后台完成(例如在onCreateView()中膨胀一个真正复杂的xml)。您可以尝试创建自己的looper,如:

  

用于为线程运行消息循环的类。默认情况下,线程没有与之关联的消息循环;创建一个,在运行循环的线程中调用prepare(),然后循环()让它处理消息,直到循环停止。

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();
    }
}

并在那里处理你的表面视图,但我不确定是否有效。

进一步资源: