这是来源:
package ff.ff;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Looper;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Surface.OutOfResourcesException;
public class Basic extends Activity {
private Render view;
public class Render extends SurfaceView implements Runnable {
//TODO: Test if AlertDialog can be able to work while another
//thread is running continuously.
//
// Failed miserably.
//ERROR Received:
/*
* 07-08 17:34:51.035: E/AndroidRuntime(7356): FATAL EXCEPTION: Thread-12
* 07-08 17:34:51.035: E/AndroidRuntime(7356): java.lang.RuntimeException: Main thread not allowed to quit
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:191)
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at android.os.Looper.quit(Looper.java:231)
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at ff.ff.Basic$Render$1$1.run(Basic.java:45)
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at java.lang.Thread.run(Thread.java:1027)
*
*/
private int r, g, b;
private boolean running;
private SurfaceHolder holder;
private AlertDialog.Builder builder;
private AlertDialog dialog;
public Render(Context context) {
super(context);
holder = this.getHolder();
r = g = b = 0;
builder = new AlertDialog.Builder(context);
builder.setTitle("Enter");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Render Dialog", "Working...");
Log.d("Render Dialog", "Exiting the Looper loop...");
new Thread(new Runnable(){
public void run(){
Looper.getMainLooper().quit();
}
}).start();
}
});
dialog = builder.create();
}
public void setLoopFlag(boolean value) {
running = value;
}
public void run() {
boolean flag = false;
while(running) {
if (holder.getSurface().isValid()) {
Canvas c = null;
try {
c = holder.getSurface().lockCanvas(null);
}
catch(IllegalArgumentException e) {
e.printStackTrace();
}
catch(OutOfResourcesException e) {
e.printStackTrace();
}
c.drawARGB(255, r, g, b);
r++;
g++;
b++;
if (r > 250 || g > 250 || b > 250) {
r = 0;
g = 0;
b = 0;
}
if (!flag){
flag = true;
Looper.prepare();
dialog.show();
Looper.loop();
}
holder.getSurface().unlockCanvasAndPost(c);
}
}
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new Render(this);
view.setLoopFlag(true);
setContentView(view);
Thread thread = new Thread(view);
thread.setName("Render Thread");
thread.start();
}
}
你知道当一个游戏结束时,游戏会要求玩家输入一个名字,这样记分板上就会有一个名字和一个分数吗?通常,就是这样。我有一个将所有 3 个对象都渲染到屏幕上的游戏。当满足某个条件时,游戏会出现一个对话框,询问玩家的名字并祝贺玩家完成它。
正是这个为玩家名字弹出对话框的简单任务引起了很多头痛。上面给出了提供的源代码。
当线程处于紧密循环中时(例如游戏循环),当程序想要向用户显示对话框时,通常推荐的执行方式是什么?为什么 Looper.prepare() 在这种情况下有用?
我无法理解这一点。:(
编辑(更多信息):
我尝试使用 AsyncTask,它真的让我更加困惑。并不是说我不想使用 AsyncTask,而是一个简单的“在背景更改颜色时显示对话框”工作如何变得越来越难以修复?
日志猫:
07-08 20:20:02.445: E/AndroidRuntime(11085): FATAL EXCEPTION: AsyncTask #1
07-08 20:20:02.445: E/AndroidRuntime(11085): java.lang.RuntimeException: An error occured while executing doInBackground()
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.os.AsyncTask$3.done(AsyncTask.java:200)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:274)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.FutureTask.setException(FutureTask.java:125)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:308)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.FutureTask.run(FutureTask.java:138)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1088)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:581)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.lang.Thread.run(Thread.java:1027)
07-08 20:20:02.445: E/AndroidRuntime(11085): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.os.Handler.<init>(Handler.java:121)
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.app.Dialog.<init>(Dialog.java:122)
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.app.AlertDialog.<init>(AlertDialog.java:63)
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.app.AlertDialog.<init>(AlertDialog.java:59)
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.app.AlertDialog$Builder.create(AlertDialog.java:786)
07-08 20:20:02.445: E/AndroidRuntime(11085): at ff.ff.Basic$DialogTask.doInBackground(Basic.java:112)
07-08 20:20:02.445: E/AndroidRuntime(11085): at ff.ff.Basic$DialogTask.doInBackground(Basic.java:1)
07-08 20:20:02.445: E/AndroidRuntime(11085): at android.os.AsyncTask$2.call(AsyncTask.java:185)
07-08 20:20:02.445: E/AndroidRuntime(11085): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:306)
07-08 20:20:02.445: E/AndroidRuntime(11085): ... 4 more
07-08 20:20:03.276: E/msm8660.gralloc(11085): [unregister] handle 0x341330 still locked (state=c0000001)
来源:
package ff.ff;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Basic extends Activity {
private Render view;
public class Render extends SurfaceView implements Runnable {
//TODO: Test if AlertDialog can be able to work while another
//thread is running continuously.
//
// Failed miserably.
//ERROR Received:
/*
* 07-08 17:34:51.035: E/AndroidRuntime(7356): FATAL EXCEPTION: Thread-12
* 07-08 17:34:51.035: E/AndroidRuntime(7356): java.lang.RuntimeException: Main thread not allowed to quit
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at android.os.MessageQueue.enqueueMessage(MessageQueue.java:191)
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at android.os.Looper.quit(Looper.java:231)
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at ff.ff.Basic$Render$1$1.run(Basic.java:45)
* 07-08 17:34:51.035: E/AndroidRuntime(7356): at java.lang.Thread.run(Thread.java:1027)
*
*/
private int r, g, b;
private boolean running;
private SurfaceHolder holder;
private DialogTask task;
public Render(Context context) {
super(context);
holder = this.getHolder();
task = new DialogTask(context);
r = g = b = 0;
}
public void setLoopFlag(boolean value) {
running = value;
}
public void run() {
boolean flag = false;
while(running) {
if (holder.getSurface().isValid()) {
Canvas c = null;
try {
c = holder.getSurface().lockCanvas(null);
}
catch(IllegalArgumentException e) {
e.printStackTrace();
}
catch(OutOfResourcesException e) {
e.printStackTrace();
}
c.drawARGB(255, r, g, b);
r++;
g++;
b++;
if (r > 250 || g > 250 || b > 250) {
r = 0;
g = 0;
b = 0;
}
if (!flag){
flag = true;
Void[] v = new Void[1];
v[0] = null;
task.execute(v);
}
holder.getSurface().unlockCanvasAndPost(c);
}
}
}
}
public class DialogTask extends AsyncTask<Void, Void, Void>{
private Context context;
private boolean exit;
public DialogTask(Context c){
context = c;
exit = false;
}
@Override
protected Void doInBackground(Void... params) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
exit = true;
}
});
builder.setTitle("Enter...");
AlertDialog dialog = builder.create();
dialog.show();
return null;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new Render(this);
view.setLoopFlag(true);
setContentView(view);
Thread thread = new Thread(view);
thread.setName("Render Thread");
thread.start();
}
}
编辑#2(runOnUIThread() 和 onTouchEvent(MotionEvent e) 锁定,源代码如下:
public class Basic extends Activity {
Render view;
public class Render extends SurfaceView implements Runnable {
private Activity activity;
private SurfaceHolder holder;
private boolean running;
public Render(Activity a){
super(a);
activity = a;
holder = this.getHolder();
running = true;
}
public void run(){
int r = 0;
while (running){
if (holder.getSurface().isValid()){
Canvas canvas = holder.lockCanvas();
canvas.drawARGB(255, r, 255, 255);
r++;
if (r > 255)
r = 0;
holder.unlockCanvasAndPost(canvas);
}
}
}
public void start(){
new Thread(this).start();
}
public boolean onTouchEvent(MotionEvent event){
activity.runOnUiThread(new Runnable(){
public void run(){
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Activity", "It worked also......");
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
});
return false;
}
public void stop(){
running = false;
boolean r = true;
while(r){
try {
Thread.currentThread().join();
r = false;
}
catch(InterruptedException e) {
r = true;
}
}
}
}
public void onCreate(Bundle b){
super.onCreate(b);
view = new Render(this);
this.setContentView(view);
}
public void onPause(){
super.onPause();
view.stop();
}
public void onResume(){
super.onResume();
view.start();
}
}
编辑#3(我认为这是当天的最后一次编辑)
这是迄今为止我得到的“解决方法”。所有功劳都归功于 Nate 的帮助。
package ff.ff;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class Basic extends Activity {
private AlertDialog dialog;
private AlertDialog.Builder builder;
private BackgroundColors view;
public class BackgroundColors extends SurfaceView implements Runnable {
private Thread thread;
private boolean running;
private SurfaceHolder holder;
public BackgroundColors(Context context) {
super(context);
}
public void run() {
int r = 0;
while (running){
if (holder.getSurface().isValid()){
Canvas canvas = holder.lockCanvas();
if (r > 250)
r = 0;
r += 10;
canvas.drawARGB(255, r, 255, 255);
holder.unlockCanvasAndPost(canvas);
}
}
}
public void start() {
running = true;
thread = new Thread(this);
holder = this.getHolder();
thread.start();
}
public void stop() {
running = false;
boolean retry = true;
while (retry){
try {
thread.join();
retry = false;
}
catch(InterruptedException e) {
retry = true;
}
}
}
public boolean onTouchEvent(MotionEvent e){
dialog.show();
return false;
}
}
public void onCreate(Bundle b) {
super.onCreate(b);
view = new BackgroundColors(this);
this.setContentView(view);
builder = new AlertDialog.Builder(this);
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d("Basic", "It worked");
}
});
dialog = builder.create();
}
public void onPause(){
super.onPause();
view.stop();
}
public void onResume(){
super.onResume();
view.start();
}
}