2014-09-05 55 views
1

我對理解AsyncTask有點麻煩,我是否可以實際做我正在嘗試做的事情。使用AsyncTask在後臺創建GLSurfaceView

在我的活動的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) {} 
} 

}

我一直喜歡我是否可以創建一個contstructor並通過東西出來念叨的AsyncTask衝突的事情。

任何方式,它在我已經在上面的代碼中標記的行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,所以我知道我大概弄得一團糟它。所以對此的任何幫助將是偉大的

編輯方法使用註釋仍然產生相同的結果代碼放@Background註釋似乎並沒有運行在一個單獨的線程上oncreate仍在等待它,除非我是這樣做是錯誤

稱爲聯合國的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

如果您使用AsyncTask發瘋,我更願意嘗試AndroidAnnotations。您不必編寫完整的類來處理後臺任務,只需在您的方法上使用@Background註釋。這個概念可以節省很多代碼,它的工作原理!

這是從Android註解維基在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); 
    } 

    // [...] 
} 
+0

你能舉個這個 – Rob85 2014-09-05 09:27:48

+1

的快速例子嗎!我編輯我的答案。 – Bobbelinio 2014-09-05 10:21:34

+0

是否有導入所有上述註釋? – Rob85 2014-09-05 11:13:38

1

我最近幾天一直在與AsyncTask搏鬥。它似乎對錶面產生某種厭惡。

我建議改變方法 - 在onCreate/onResume(取決於您的需要)中創建視圖,並避免使用AsyncTask綁定不存在的曲面。

這種方法適合我,希望它也適合你!

+0

我想避免這是創建表面和設置渲染器需要大約3〜4秒內的onCreate,在原因上面的代碼你可以看到我在頂部創建了一個對話框,就像我的小加載屏幕(只是一個圖像),但是這個對話框不會顯示,直到oncreate完成了SurfaceView的一些操作(幾秒鐘後會得到一個黑屏)如果我刪除了我的表面視圖創建對話框立即顯示,因此爲什麼我想這將移動到後臺任務 – Rob85 2014-09-05 09:11:40

+0

爲什麼不處理創建加載屏幕並在簡歷上啓動AsyncTask?如果這是應用程序的開始,那麼延遲幾秒鐘是可以忍受的 - 只要設備不會抱怨你正在使用UI線程。 您可能已經想到了這一點,但是追蹤堆棧跟蹤呢?我在那裏看到了一些錯誤,這些錯誤指向了Handler,我看不到這可能是真正的罪魁禍首? – Sipty 2014-09-05 10:27:16

1

您的基本問題是,你嘗試的UI從一個非UI線程改變。從documentation

此外,Andoid UI工具包不是線程安全的。所以,你不能從工作者線程操縱你的UI - 你必須從UI線程對你的用戶界面進行所有操作。

Asynctask實際上是一個便利。它會從主線程中完成一些背景工作,然後在ui線程上運行onPostExecute()。這意味着兩兩件事:第一,你可以在onPostExecute()更改UI,一切都在這裏完成的將被執行串行到UI(因此它可以被阻止)

還有就是平時的quickfix用於與

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

這不會幫助你,因爲它會再次阻止用戶界面。我實際上不知道你是否可以做你想做的事情,因爲有些事情可以在後臺完成(例如,在onCreateView()中膨脹一個真正複雜的XML)。您可以嘗試創建自己的活套:

用於爲線程運行消息循環的類。線程默認沒有與它們相關的消息循環;創建一個,在要運行循環的線程中調用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(); 
    } 
} 

和處理您的surfaceview有,但林不知道的作品。

此外resouces:

+0

感謝您的回答,看起來很深奧,如果我從另一個角度來看這件事,那麼我個人不希望在單獨的線程上運行surfaceview,將它留在UI線程上對我很好,所以我可以運行在單獨的線程上的對話框,我這樣做的關鍵是在setrender方法運行時隱藏3-4秒的黑屏。所以,只要我進入oncreate,如果我創建一個後臺線程運行對話框將工作?對話框會立即顯示嗎? – Rob85 2014-09-05 15:20:00