来源如下:
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(我认为这是当天的最后一次编辑)
这是我迄今为止得到的“解决方法”。所有功劳都归功于内特的帮助。
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();
}
}
最佳答案
此答案与问题的更新有关,您正在尝试使用 AsyncTask
。您拥有的代码实际上与 AsyncTask
的预期使用方式相反。 AsyncTask
有多个方法,旨在从不同的线程运行。您实现的方法 doInBackground()
旨在从后台线程调用。因此,您不应该在该方法中(直接)更新 UI。
在AsyncTask
末尾运行的方法是onPostExecute()
。它在 UI 线程上运行,并且可以安全地进行 UI 调用,例如显示 Dialog
。如果您需要在任务运行期间更新 UI,则可以实现第三种方法,onProgressUpdate()
。对于 UI 操作来说也是安全的。如果您的后台处理(在 doInBackground()
中)需要向 onProgressUpdate()
方法传达信息,则可以通过调用 publishProgress() 来实现
方法,传入 onProgressUpdate()
中需要的任何数据。这些调用的参数是通用的,因此您几乎可以将它们设置为任何内容。典型的实现将完成百分比作为整数传递。
See the very start of the API docs for AsyncTask举一个非常简单的例子。
但是,听起来更简单的东西也适合你。如果您更改 Render
类的构造函数以采用 Activity
,而不是 Context
:
private Activity parent;
public Render(Activity activity) {
super(activity);
parent = activity;
然后,你就可以在Activity中使用 super 有用的runOnUiThread()
方法了:
parent.runOnUiThread(new Runnable() {
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(parent);
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();
}
});
上面的代码块可以安全地放在任何地方。您可以将其放入后台线程中的 doInBackground()
方法或 Runnable
的 run()
方法中。尝试一下。
关于java - 如何在背景渲染时让 AlertDialog 工作,而不会使应用程序崩溃?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11382394/