Java 如何使AlertDialog在后台渲染时工作,而不使应用程序崩溃?
资料来源如下:Java 如何使AlertDialog在后台渲染时工作,而不使应用程序崩溃?,java,android,android-alertdialog,looper,Java,Android,Android Alertdialog,Looper,资料来源如下: 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 an
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)
编辑#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();
}
}
此崩溃的原因是您试图关闭main循环器。主线程(也称为UI)始终需要至少一个循环器 所以,永远不要打电话
getMainLooper().quit();
可能,您想调用Looper.myLooper()
,而不是Looper.getMainLooper()
?但是,我不完全确定您的程序试图做什么
也许吧
也可能会让你更容易使用,尽管我对你的应用程序的功能有点不清楚,也许不是
另外,至少看起来您的布尔运行
标志不是线程安全的。它是从多个线程访问的,没有任何保护。这并不是导致崩溃的原因,我只是指出它
Edit:事实上,现在我看到了它,尽管运行的
变量中存在潜在的不安全性,但在创建后台线程之前,它看起来只设置了一次。因此,如果这是您唯一的用法,它并不不安全……但是,该值也永远不会更改。因此,它要么是无用的,要么是不安全的在其他地方调用setLoopFlag()
,这可能不安全(?).此答案与问题的更新有关,您正在尝试使用AsyncTask
。您拥有的代码实际上与AsyncTask
的使用方式相反。AsyncTask
有多个方法,这些方法将从不同的线程运行。您实现的方法,doInBackground()
是从后台线程调用的。因此,您不应该(直接)在该方法中更新UI
在AsyncTask
的端运行的方法是onPostExecute()
。该方法在UI线程上运行,可以安全地进行UI调用,例如显示对话框
。如果在任务运行期间需要更新UI,那么还有第三种方法,onProgressUpdate()
,您可以实现。它对于UI操作也是安全的。如果您的后台处理(在doInBackground()
)中)需要将信息传递给onProgressUpdate()
方法,那么它可以通过调用publishProgress()
方法,在onProgressUpdate()中传递所需的任何数据来实现
。这些调用的参数是泛型的,因此您几乎可以对其进行任何设置。典型的实现将%complete作为整数传递
举一个非常简单的例子
但是,这听起来似乎更简单的方法也适用于您。如果您将呈现
类的构造函数更改为采用活动
,而不是上下文
:
private Activity parent;
public Render(Activity activity) {
super(activity);
parent = activity;
然后,您可以在活动中使用超级有用的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()
方法或run()中
在后台线程中运行的方法。试试看。提供logcat会很有用。在你的情况下,我认为这是一个解决方案。@HaiBison我确实提供了logcat。灰色的注释就是logcat。很抱歉,我搞错了。我认为你应该将对话框生成器移到classBasic
,使用关于线程(prepare(),loop()…),我不确定。Looper.myLooper()与Looper.getMainLooper()相同,因为只有一个活套实例。我要做的是尝试弹出一个对话框,而背景一直在渲染背景色。我专门设计了一个无限循环的程序,所以我可以测试我的程序是否可以渲染背景色(从黑色到白色)同时看到前面出现的对话框。我添加了一些更多信息。请检查。@tom_mai78101,如果myLooper()
返回与getMainLooper()
相同的值,那么您不应该调用myLooper().quit()
。关键是您不应该尝试调用quit()
在主活套上。首先调用quit()
有什么意义?如果我不调用活套,你想用该调用做什么?(我现在检查你的AsyncTask代码…),那么我从一开始就不能使用AlertDialog。根据Logcat,它说要使用AlertDialog,我需要调用Looper.prepare()。我试图用这个调用,在按下对话框的OK按钮后停止获取用户输入。我只是不想
private Activity parent;
public Render(Activity activity) {
super(activity);
parent = activity;
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();
}
});