Java 使用AsyncTask在后台创建GLSurfaceView

Java 使用AsyncTask在后台创建GLSurfaceView,java,android,android-activity,android-asynctask,Java,Android,Android Activity,Android Asynctask,我在理解异步任务以及我是否能真正做到我想做的事情上有点困难 在我的活动的onCreate方法中,我做了一些事情,其中之一就是创建一个GLSurfaceView并设置渲染器。我希望这个过程由一个异步任务在后台完成 这是我的密码 public class ActivityTest extends BaseGameActivity { /** Hold a reference to our GLSurfaceView */ private MYGLSurfaceView mGLSurfaceView;

我在理解异步任务以及我是否能真正做到我想做的事情上有点困难

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

这是我的密码

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

这肯定是错的,这几天我自己也在努力解决这个问题。它似乎对表面有某种厌恶

我建议改变方法——根据需要在onCreate/onResume中创建视图,避免将不存在的表面与AsyncTask绑定


这种方法对我有效,希望对你也有效

过去几天我自己也在努力解决这个问题。它似乎对表面有某种厌恶

我建议改变方法——根据需要在onCreate/onResume中创建视图,避免将不存在的表面与AsyncTask绑定


这种方法对我有效,希望对你也有效

如果你疯狂使用AsyncTask,我更愿意尝试AndroidAnnotations。您只需在方法上使用@background注释,而不是编写一个完整的类来处理后台任务。这个概念节省了大量的代码,而且很有效

这是github上android annotations 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);
    }

    // [...]
}

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

这是github上android annotations 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);
    }

    // [...]
}

您的基本问题是试图从非UI线程更改UI。从:

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

Asynctask实际上是一种方便。它在主线程之外执行一些后台工作,然后在ui线程上运行onPostExecute。这意味着两件事:首先,您可以在onPostExecute中更改ui,这里所做的一切都将以串行方式执行到ui,以便阻止它

对于这一点,有通常的快速修复方法

activity.runOnUiThread(new Runnable() {
  public void run() {
    //this will be executed on the ui thread
  }
});
这对您没有帮助,因为它会再次阻止ui。事实上,我不确定你是否可以做你想做的事情,因为有些事情可以在后台完成,例如在onCreateView中膨胀一个真正复杂的xml。您可以尝试创建自己的活套,如:

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

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

其他资源:


您的基本问题是试图从非UI线程更改UI。从:

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

Asynctask实际上是一种方便。它在主线程之外执行一些后台工作,然后在ui线程上运行onPostExecute。这意味着两件事:首先,您可以在onPostExecute中更改ui,这里所做的一切都将以串行方式执行到ui,以便阻止它

对于这一点,有通常的快速修复方法

activity.runOnUiThread(new Runnable() {
  public void run() {
    //this will be executed on the ui thread
  }
});
这对您没有帮助,因为它会再次阻止ui。事实上,我不确定你是否可以做你想做的事情,因为有些事情可以在后台完成,例如在onCreateView中膨胀一个真正复杂的xml。您可以尝试创建自己的活套,如:

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

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

其他资源:


我想要避免这种情况的原因是,在onCreate中创建曲面和设置渲染器大约需要3到4秒,在上面的代码中,你可以看到我在顶部创建了一个对话框,就像我的小加载屏幕一样,只是一个图像,但是这个对话框直到创建完成对SurfaceView的操作后才会显示。如果我删除了我的曲面视图创建,则该对话框会立即显示,因此,为什么我希望将其移动到后台任务?为什么不在创建时处理加载屏幕,并在恢复时启动异步任务?如果这是应用程序的开始,则允许几秒钟的延迟
le-只要设备没有抱怨你占用了UI线程。你可能已经想到了这一点,但是追踪stacktrace呢?我在那里看到了几个错误,它们指向一个处理程序,我看不到-也许这才是真正的罪魁祸首?我想避免这种情况的原因是创建曲面和设置渲染器在onCreate中需要大约3到4秒,在上面的代码中,你可以看到我在顶部创建了一个对话框,就像我的小加载屏幕一样,只是一个图像,但是这个对话框直到创建完成对SurfaceView的操作后才会显示。如果我删除了我的曲面视图创建,则该对话框会立即显示,因此,为什么我希望将其移动到后台任务?为什么不在创建时处理加载屏幕,并在恢复时启动异步任务?如果这是应用程序的开始,那么几秒钟的延迟是可以忍受的——只要设备不抱怨你占用了UI线程。你可能已经想到了这一点,但是追踪stacktrace呢?我在那里看到了几个错误,它们指向一个处理程序,我看不到-也许这才是真正的罪魁祸首?你能举一个简单的例子吗?上面所有的注释都有导入吗?好的,我已经设法添加了androidannotations,现在将尝试这个方法,听起来好像它会帮我解决这个问题,一旦我测试了Cheers,我会回复你的。我已经试着按照你说的去实现了,但是我一定是做错了什么,就像以前一样,如果它不是在一个单独的线程上运行的,我已经用我做的代码更新了。你能看看你包括库和项目,对吗?你有例外吗?如果是这样的话,请给我们看一下这个,同时也给我们看一下你的方法。你能给我们举个简单的例子吗?上面所有的注释都有意义吗?好的,我已经设法添加了androidannotations,现在将尝试这个方法,听起来好像它会帮助我,一旦我测试了Cheers,我会回复你的。我已经试着按照你说的去实现了,但是我一定是做错了什么,就像以前一样,如果它不是在一个单独的线程上运行的,我已经用我做的代码更新了。你能看看你包括库和项目,对吗?你有例外吗?如果是这样的话,请给我们看一下这个,同时也给我们看一下你的方法。谢谢你的回答,看起来很深入,如果我从不同的角度来看这个问题,我个人不想在单独的线程上运行surfaceview,让它在UI线程上运行对我来说非常好,那么我可以在单独的线程上运行对话框吗,我这样做的全部目的是在setrender方法运行时隐藏3-4秒的黑屏。那么,一旦我输入oncreate,如果我创建了一个运行对话框的后台线程,这会起作用吗?对话框会立即显示吗?谢谢你的回答,看起来很深入,如果我从另一个角度来看,我个人不想在单独的线程上运行surfaceview,让它在UI线程上运行对我来说很好,那么我可以在单独的线程上运行对话框吗,我这样做的全部目的是在setrender方法运行时隐藏3-4秒的黑屏。那么,一旦我输入oncreate,如果我创建了一个运行对话框的后台线程,这会起作用吗?对话框会立即显示吗?
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();
    }
}