1

我前段时间写的一个游戏存在 ANR 问题,调试表明它们归结为 HTTP 请求需要很长时间(从而导致 ANR)。

我认为通过将 HTTP 代码分配到从处理程序中调用的 Runnable 中,我可以避免 ANR - 但似乎情况并非如此?

堆栈转储表明可运行/处理程序代码仍在“主”线程中运行,因此仍会导致 ANR?

它正在执行的任务是异步的(上传高分和成就),因此可以启动并完全留给它自己的设备 - 实现这一点的最佳方法是什么,以免 ANR 成为问题?

一个主题建议应该在 Application 类中而不是在 Game 的 Activity 中创建 Handler - 但我找不到有关这些案例之间差异的任何细节?

所有的想法都非常赞赏。

ps 将此扩展到询问 - 我假设与 HTTP 相关的 ANR 归结为电话服务中断/网络/WiFi,因为我为这些请求设置了 SHORT 超时(它们不是必需的,可以稍后重试!?)

4

2 回答 2

3

A将在当前线程中Handler默认执行代码/处理消息(任何没有Loopereg的构造函数)。new Handler()在几乎所有情况下,这都是主线程。如果您希望它在不同的线程中执行,您必须告诉它Looper应该使用哪个线程。

Android 有一个名为的实用程序类HandlerThread,它创建一个Thread带有Looper.

简短的例子:

public class MyActivity extends Activity {
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        HandlerThread handlerThread = new HandlerThread("background-handler");
        handlerThread.start();
        Looper looper = handlerThread.getLooper();
        mHandler = new Handler(looper);

        mHandler.post(new Runnable() {
            public void run() {
                // code executed in handlerThread
            }
        });
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // stops the HandlerThread
        mHandler.getLooper().quit();
    }
}

如果您的任务只需要一些信息并且不需要报告,我会选择IntentService. 如果您的活动生命周期重新创建活动,这些不会发疯。

Service你会在它自己的文件中创建一个小文件

public class SaveService extends IntentService {
    public SaveService() {
        super("SaveService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        if ("com.example.action.SAVE".equals(intent.getAction())) {
            String player = intent.getStringExtra("com.example.player");
            int score = intent.getIntExtra("com.example.score", -1);
            magicHttpSave(player, score); // assuming there is an implementation here
        }
    }
}

将其添加到AndroidManifest.xml

<application ....

    <service android:name=".SaveService" />
</application>

并在您的代码中以

Intent intent = new Intent(this /* context */, SaveService.class);
intent.setAction("com.example.action.SAVE");
intent.putExtra("com.example.player", "John123");
intent.putExtra("com.example.score", 5123);
startService(intent);

IntentService#onHandleIntent()已经在后台线程上运行,因此您不必为此烦恼。

于 2012-11-13T21:35:22.670 回答
1

您的处理程序在主线程上运行。这就是导致 ANR 的原因。即使你在 Application 中创建它,默认情况下(没有给 Handler 的参数)也会在主线程上创建。您必须创建一个带有自己线程的 Looper。见这里

使用您自己的 Looper 初始化的处理程序,这是解决 ANR 的可行选项...

如果您将异步网络操作放入AsyncTask中,则可以使用更简单的替代解决方案。一种简单的方法是将 AsyncTask 放入您的 Activity 中。更复杂的可能是创建一个服务(非 UI 相关功能的持有者),它进行通信,并在通信结束后从内存中清除自身......

我会使用 AsyncTask 并将其放入活动中/从活动中启动它......

最后,在这里你可以找到一个很好的关于 android 线程的教程。

于 2012-11-13T21:19:55.567 回答