我在理解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;
}
这一定是错的
答案 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();
}
}
并在那里处理你的表面视图,但我不确定是否有效。
进一步资源: