1.1 何为ANR

ANR全名Application Not Responding, 也就是"应用无响应". 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.

1.2 为什么会产生ANR

5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
BroadcastReceiver在10s内无法结束.
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等等.

1.3 如何避免ANR

知道了ANR产生的原因, 那么想要避免ANR, 也就很简单了, 就一条规则:

不要在主线程(UI线程)里面做繁重的操作.

2, ANR分析

2.1 获取ANR产生的trace文件

ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过adb命令将其导出到本地:

$adb pull data/anr/traces.txt .

2.2 分析traces.txt

2.2.1 普通阻塞导致的ANR

获取到的tracs.txt文件一般如下:

如下以GithubApp代码为例, 强行sleep thread产生的一个ANR.

----- pid 2976 at 2016-09-08 23:02:47 -----
Cmd line: com.anly.githubapp // 最新的ANR发生的进程(包名)

DALVIK THREADS (41):
“main” prio=5 tid=1 Sleeping
| group=“main” sCount=1 dsCount=0 obj=0x73467fa8 self=0x7fbf66c95000
| sysTid=2976 nice=0 cgrp=default sched=0/0 handle=0x7fbf6a8953e0
| state=S schedstat=( 0 0 0 ) utm=60 stm=37 core=1 HZ=100
| stack=0x7ffff4ffd000-0x7ffff4fff000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep!(Native method)

  • sleeping on <0x35fc9e33> (a java.lang.Object)
    at java.lang.Thread.sleep(Thread.java:1031)
  • locked <0x35fc9e33> (a java.lang.Object)
    at java.lang.Thread.sleep(Thread.java:985) // 主线程中sleep过长时间, 阻塞导致无响应.
    at com.tencent.bugly.crashreport.crash.c.l(BUGLY:258)
  • locked <@addr=0x12dadc70> (a com.tencent.bugly.crashreport.crash.c)
    at com.tencent.bugly.crashreport.CrashReport.testANRCrash(BUGLY:166) // 产生ANR的那个函数调用
  • locked <@addr=0x12d1e840> (a java.lang.Class)
    at com.anly.githubapp.common.wrapper.CrashHelper.testAnr(CrashHelper.java:23)
    at com.anly.githubapp.ui.module.main.MineFragment.onClick(MineFragment.java:80) // ANR的起点
    at com.anly.githubapp.ui.module.main.MineFragment_ViewBinding2.doClick(MineFragmentViewBinding.java:47)atbutterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)atandroid.view.View.performClick(View.java:4780)atandroid.view.View2.doClick(MineFragment_ViewBinding.java:47) at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22) at android.view.View.performClick(View.java:4780) at android.view.View2.doClick(MineFragmentV​iewBinding.java:47)atbutterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)atandroid.view.View.performClick(View.java:4780)atandroid.view.ViewPerformClick.run(View.java:19866)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke!(Native method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
    拿到trace信息, 一切好说.
    如上trace信息中的添加的中文注释已基本说明了trace文件该怎么分析:

文件最上的即为最新产生的ANR的trace信息.
前面两行表明ANR发生的进程pid, 时间, 以及进程名字(包名).
寻找我们的代码点, 然后往前推, 看方法调用栈, 追溯到问题产生的根源.
以上的ANR trace是属于相对简单, 还有可能你并没有在主线程中做过于耗时的操作, 然而还是ANR了. 这就有可能是如下两种情况了:

2.2.2 CPU满负荷

这个时候你看到的trace信息可能会包含这样的信息:

Process:com.anly.githubapp

CPU usage from 3330ms to 814ms ago:
6% 178/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20 major
4.6% 2976/com.anly.githubapp: 0.7% user + 3.7% kernel /faults: 52 minor 19 major
0.9% 252/com.android.systemui: 0.9% user + 0% kernel

100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait
最后一句表明了:

当是CPU占用100%, 满负荷了.
其中绝大数是被iowait即I/O操作占用了.
此时分析方法调用栈, 一般来说会发现是方法中有频繁的文件读写或是数据库读写操作放在主线程来做了.

2.2.3 内存原因

其实内存原因有可能会导致ANR, 例如如果由于内存泄露, App可使用内存所剩无几, 我们点击按钮启动一个大图片作为背景的activity, 就可能会产生ANR, 这时trace信息可能是这样的:

// 以下trace信息来自网络, 用来做个示例
Cmdline: android.process.acore

DALVIK THREADS:
"main"prio=5 tid=3 VMWAIT
|group=“main” sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8
| sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376
atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod)
atandroid.graphics.Bitmap.nativeCreate(Native Method)
atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468)
atandroid.view.View.buildDrawingCache(View.java:6324)
atandroid.view.View.getDrawingCache(View.java:6178)

MEMINFO in pid 1360 [android.process.acore] **
native dalvik other total
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732
可以看到free的内存已所剩无几.

当然这种情况可能更多的是会产生OOM的异常…

2.2 ANR的处理

针对三种不同的情况, 一般的处理情况如下

主线程阻塞的
开辟单独的子线程来处理耗时阻塞事务.

CPU满负荷, I/O阻塞的
I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.

内存不够用的
增大VM内存, 使用largeHeap属性, 排查内存泄露(这个在内存优化那篇细说吧)等.

3, 深入一点

没有人愿意在出问题之后去解决问题.
高手和新手的区别是, 高手知道怎么在一开始就避免问题的发生. 那么针对ANR这个问题, 我们需要做哪些层次的工作来避免其发生呢?

3.1 哪些地方是执行在主线程的

Activity的所有生命周期回调都是执行在主线程的.
Service默认是执行在主线程的.
BroadcastReceiver的onReceive回调是执行在主线程的.
没有使用子线程的looper的Handler的handleMessage, post(Runnable)是执行在主线程的.
AsyncTask的回调中除了doInBackground, 其他都是执行在主线程的.
View的post(Runnable)是执行在主线程的.
3.2 使用子线程的方式有哪些

上面我们几乎一直在说, 避免ANR的方法就是在子线程中执行耗时阻塞操作. 那么在Android中有哪些方式可以让我们实现这一点呢.

3.2.1 启Thread方式

这个其实也是Java实现多线程的方式. 有两种实现方法, 继承Thread 或 实现Runnable接口:

继承Thread

class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}

public void run() {// compute primes larger than minPrime. . .
}

}

PrimeThread p = new PrimeThread(143);
p.start();
实现Runnable接口

class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}

public void run() {// compute primes larger than minPrime. . .
}

}

PrimeRun p = new PrimeRun(143);
new Thread§.start();
3.2.2 使用AsyncTask

这个是Android特有的方式, AsyncTask顾名思义, 就是异步任务的意思.

private class DownloadFilesTask extends AsyncTask {
// Do the long-running work in here
// 执行在子线程
protected Long doInBackground(URL… urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}

// This is called each time you call publishProgress()
// 执行在主线程
protected void onProgressUpdate(Integer... progress) {setProgressPercent(progress[0]);
}// This is called when doInBackground() is finished
// 执行在主线程
protected void onPostExecute(Long result) {showNotification("Downloaded " + result + " bytes");
}

}

// 启动方式
new DownloadFilesTask().execute(url1, url2, url3);
3.2.3 HandlerThread

Android中结合Handler和Thread的一种方式. 前面有云, 默认情况下Handler的handleMessage是执行在主线程的, 但是如果我给这个Handler传入了子线程的looper, handleMessage就会执行在这个子线程中的. HandlerThread正是这样的一个结合体:

// 启动一个名为new_thread的子线程
HandlerThread thread = new HandlerThread(“new_thread”);
thread.start();

// 取new_thread赋值给ServiceHandler
private ServiceHandler mServiceHandler;
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);

private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {// 此时handleMessage是运行在new_thread这个子线程中了.
}

}
3.2.4 IntentService

Service是运行在主线程的, 然而IntentService是运行在子线程的.
实际上IntentService就是实现了一个HandlerThread + ServiceHandler的模式.

以上HandlerThread的使用代码示例也就来自于IntentService源码.

3.2.5 Loader

Android 3.0引入的数据加载器, 可以在Activity/Fragment中使用. 支持异步加载数据, 并可监控数据源在数据发生变化时传递新结果. 常用的有CursorLoader, 用来加载数据库数据.

// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
// 使用LoaderManager来初始化Loader
getLoaderManager().initLoader(0, null, this);

//如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。
//如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器

// 创建一个Loader
public Loader onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don’t care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}

// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,CONTACTS_SUMMARY_PROJECTION, select, null,Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");

}

// 加载完成
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
具体请参看官网Loader介绍.

3.2.6 特别注意

使用Thread和HandlerThread时, 为了使效果更好, 建议设置Thread的优先级偏低一点:

Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
因为如果没有做任何优先级设置的话, 你创建的Thread默认和UI Thread是具有同样的优先级的, 你懂的. 同样的优先级的Thread, CPU调度上还是可能会阻塞掉你的UI Thread, 导致ANR的.

结语

对于ANR问题, 个人认为还是预防为主, 认清代码中的阻塞点, 善用线程. 同时形成良好的编程习惯, 要有MainThread和Worker Thread的概念的…

作者:南岸青栀
来源:CSDN
原文:https://blog.csdn.net/jiankeufo/article/details/53942303
版权声明:本文为博主原创文章,转载请附上博文链接!1.1 何为ANR

ANR全名Application Not Responding, 也就是"应用无响应". 当操作在一段时间内系统无法处理时, 系统层面会弹出上图那样的ANR对话框.

1.2 为什么会产生ANR

5s内无法响应用户输入事件(例如键盘输入, 触摸屏幕等).
BroadcastReceiver在10s内无法结束.
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作, 例如文件读写, 数据库读写, 网络查询等等.

1.3 如何避免ANR

知道了ANR产生的原因, 那么想要避免ANR, 也就很简单了, 就一条规则:

不要在主线程(UI线程)里面做繁重的操作.

2, ANR分析

2.1 获取ANR产生的trace文件

ANR产生时, 系统会生成一个traces.txt的文件放在/data/anr/下. 可以通过adb命令将其导出到本地:

$adb pull data/anr/traces.txt .

2.2 分析traces.txt

2.2.1 普通阻塞导致的ANR

获取到的tracs.txt文件一般如下:

如下以GithubApp代码为例, 强行sleep thread产生的一个ANR.

----- pid 2976 at 2016-09-08 23:02:47 -----
Cmd line: com.anly.githubapp // 最新的ANR发生的进程(包名)

DALVIK THREADS (41):
“main” prio=5 tid=1 Sleeping
| group=“main” sCount=1 dsCount=0 obj=0x73467fa8 self=0x7fbf66c95000
| sysTid=2976 nice=0 cgrp=default sched=0/0 handle=0x7fbf6a8953e0
| state=S schedstat=( 0 0 0 ) utm=60 stm=37 core=1 HZ=100
| stack=0x7ffff4ffd000-0x7ffff4fff000 stackSize=8MB
| held mutexes=
at java.lang.Thread.sleep!(Native method)

  • sleeping on <0x35fc9e33> (a java.lang.Object)
    at java.lang.Thread.sleep(Thread.java:1031)
  • locked <0x35fc9e33> (a java.lang.Object)
    at java.lang.Thread.sleep(Thread.java:985) // 主线程中sleep过长时间, 阻塞导致无响应.
    at com.tencent.bugly.crashreport.crash.c.l(BUGLY:258)
  • locked <@addr=0x12dadc70> (a com.tencent.bugly.crashreport.crash.c)
    at com.tencent.bugly.crashreport.CrashReport.testANRCrash(BUGLY:166) // 产生ANR的那个函数调用
  • locked <@addr=0x12d1e840> (a java.lang.Class)
    at com.anly.githubapp.common.wrapper.CrashHelper.testAnr(CrashHelper.java:23)
    at com.anly.githubapp.ui.module.main.MineFragment.onClick(MineFragment.java:80) // ANR的起点
    at com.anly.githubapp.ui.module.main.MineFragment_ViewBinding2.doClick(MineFragmentViewBinding.java:47)atbutterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)atandroid.view.View.performClick(View.java:4780)atandroid.view.View2.doClick(MineFragment_ViewBinding.java:47) at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22) at android.view.View.performClick(View.java:4780) at android.view.View2.doClick(MineFragmentV​iewBinding.java:47)atbutterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)atandroid.view.View.performClick(View.java:4780)atandroid.view.ViewPerformClick.run(View.java:19866)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke!(Native method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
    拿到trace信息, 一切好说.
    如上trace信息中的添加的中文注释已基本说明了trace文件该怎么分析:

文件最上的即为最新产生的ANR的trace信息.
前面两行表明ANR发生的进程pid, 时间, 以及进程名字(包名).
寻找我们的代码点, 然后往前推, 看方法调用栈, 追溯到问题产生的根源.
以上的ANR trace是属于相对简单, 还有可能你并没有在主线程中做过于耗时的操作, 然而还是ANR了. 这就有可能是如下两种情况了:

2.2.2 CPU满负荷

这个时候你看到的trace信息可能会包含这样的信息:

Process:com.anly.githubapp

CPU usage from 3330ms to 814ms ago:
6% 178/system_server: 3.5% user + 1.4% kernel / faults: 86 minor 20 major
4.6% 2976/com.anly.githubapp: 0.7% user + 3.7% kernel /faults: 52 minor 19 major
0.9% 252/com.android.systemui: 0.9% user + 0% kernel

100%TOTAL: 5.9% user + 4.1% kernel + 89% iowait
最后一句表明了:

当是CPU占用100%, 满负荷了.
其中绝大数是被iowait即I/O操作占用了.
此时分析方法调用栈, 一般来说会发现是方法中有频繁的文件读写或是数据库读写操作放在主线程来做了.

2.2.3 内存原因

其实内存原因有可能会导致ANR, 例如如果由于内存泄露, App可使用内存所剩无几, 我们点击按钮启动一个大图片作为背景的activity, 就可能会产生ANR, 这时trace信息可能是这样的:

// 以下trace信息来自网络, 用来做个示例
Cmdline: android.process.acore

DALVIK THREADS:
"main"prio=5 tid=3 VMWAIT
|group=“main” sCount=1 dsCount=0 s=N obj=0x40026240self=0xbda8
| sysTid=1815 nice=0 sched=0/0 cgrp=unknownhandle=-1344001376
atdalvik.system.VMRuntime.trackExternalAllocation(NativeMethod)
atandroid.graphics.Bitmap.nativeCreate(Native Method)
atandroid.graphics.Bitmap.createBitmap(Bitmap.java:468)
atandroid.view.View.buildDrawingCache(View.java:6324)
atandroid.view.View.getDrawingCache(View.java:6178)

MEMINFO in pid 1360 [android.process.acore] **
native dalvik other total
size: 17036 23111 N/A 40147
allocated: 16484 20675 N/A 37159
free: 296 2436 N/A 2732
可以看到free的内存已所剩无几.

当然这种情况可能更多的是会产生OOM的异常…

2.2 ANR的处理

针对三种不同的情况, 一般的处理情况如下

主线程阻塞的
开辟单独的子线程来处理耗时阻塞事务.

CPU满负荷, I/O阻塞的
I/O阻塞一般来说就是文件读写或数据库操作执行在主线程了, 也可以通过开辟子线程的方式异步执行.

内存不够用的
增大VM内存, 使用largeHeap属性, 排查内存泄露(这个在内存优化那篇细说吧)等.

3, 深入一点

没有人愿意在出问题之后去解决问题.
高手和新手的区别是, 高手知道怎么在一开始就避免问题的发生. 那么针对ANR这个问题, 我们需要做哪些层次的工作来避免其发生呢?

3.1 哪些地方是执行在主线程的

Activity的所有生命周期回调都是执行在主线程的.
Service默认是执行在主线程的.
BroadcastReceiver的onReceive回调是执行在主线程的.
没有使用子线程的looper的Handler的handleMessage, post(Runnable)是执行在主线程的.
AsyncTask的回调中除了doInBackground, 其他都是执行在主线程的.
View的post(Runnable)是执行在主线程的.
3.2 使用子线程的方式有哪些

上面我们几乎一直在说, 避免ANR的方法就是在子线程中执行耗时阻塞操作. 那么在Android中有哪些方式可以让我们实现这一点呢.

3.2.1 启Thread方式

这个其实也是Java实现多线程的方式. 有两种实现方法, 继承Thread 或 实现Runnable接口:

继承Thread

class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}

public void run() {// compute primes larger than minPrime. . .
}

}

PrimeThread p = new PrimeThread(143);
p.start();
实现Runnable接口

class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}

public void run() {// compute primes larger than minPrime. . .
}

}

PrimeRun p = new PrimeRun(143);
new Thread§.start();
3.2.2 使用AsyncTask

这个是Android特有的方式, AsyncTask顾名思义, 就是异步任务的意思.

private class DownloadFilesTask extends AsyncTask {
// Do the long-running work in here
// 执行在子线程
protected Long doInBackground(URL… urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}

// This is called each time you call publishProgress()
// 执行在主线程
protected void onProgressUpdate(Integer... progress) {setProgressPercent(progress[0]);
}// This is called when doInBackground() is finished
// 执行在主线程
protected void onPostExecute(Long result) {showNotification("Downloaded " + result + " bytes");
}

}

// 启动方式
new DownloadFilesTask().execute(url1, url2, url3);
3.2.3 HandlerThread

Android中结合Handler和Thread的一种方式. 前面有云, 默认情况下Handler的handleMessage是执行在主线程的, 但是如果我给这个Handler传入了子线程的looper, handleMessage就会执行在这个子线程中的. HandlerThread正是这样的一个结合体:

// 启动一个名为new_thread的子线程
HandlerThread thread = new HandlerThread(“new_thread”);
thread.start();

// 取new_thread赋值给ServiceHandler
private ServiceHandler mServiceHandler;
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);

private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}

@Override
public void handleMessage(Message msg) {// 此时handleMessage是运行在new_thread这个子线程中了.
}

}
3.2.4 IntentService

Service是运行在主线程的, 然而IntentService是运行在子线程的.
实际上IntentService就是实现了一个HandlerThread + ServiceHandler的模式.

以上HandlerThread的使用代码示例也就来自于IntentService源码.

3.2.5 Loader

Android 3.0引入的数据加载器, 可以在Activity/Fragment中使用. 支持异步加载数据, 并可监控数据源在数据发生变化时传递新结果. 常用的有CursorLoader, 用来加载数据库数据.

// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
// 使用LoaderManager来初始化Loader
getLoaderManager().initLoader(0, null, this);

//如果 ID 指定的加载器已存在,则将重复使用上次创建的加载器。
//如果 ID 指定的加载器不存在,则 initLoader() 将触发 LoaderManager.LoaderCallbacks 方法 //onCreateLoader()。在此方法中,您可以实现代码以实例化并返回新加载器

// 创建一个Loader
public Loader onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don’t care about the ID.
// First, pick the base URI to use depending on whether we are
// currently filtering.
Uri baseUri;
if (mCurFilter != null) {
baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
Uri.encode(mCurFilter));
} else {
baseUri = Contacts.CONTENT_URI;
}

// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("+ Contacts.DISPLAY_NAME + " != '' ))";
return new CursorLoader(getActivity(), baseUri,CONTACTS_SUMMARY_PROJECTION, select, null,Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");

}

// 加载完成
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
具体请参看官网Loader介绍.

3.2.6 特别注意

使用Thread和HandlerThread时, 为了使效果更好, 建议设置Thread的优先级偏低一点:

Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
因为如果没有做任何优先级设置的话, 你创建的Thread默认和UI Thread是具有同样的优先级的, 你懂的. 同样的优先级的Thread, CPU调度上还是可能会阻塞掉你的UI Thread, 导致ANR的.

对于ANR问题, 个人认为还是预防为主, 认清代码中的阻塞点, 善用线程. 同时形成良好的编程习惯, 要有MainThread和Worker Thread的概念的…

Android 内存优化- ANR 详解相关推荐

  1. 【Android 性能优化】—— 详解内存优化的来龙去脉

    本文以独家授权于任玉刚公众号玉刚说,详情请见-https://mp.weixin.qq.com/s/XILFalxJsbGJjx-ZOEDi7g Android 高手核心知识点笔记(不断更新中

  2. Android内存管理机制详解,【安卓知识普及贴】之关于安卓内存管理机制的一些介绍和阐述。...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 安卓系统其实不需要极度精简,只是一味的精简并不会给系统带来什么好处反而只有害处,因为许多系统应用跟设置里的选项有关联,删除掉就会在选择关联选项的时候FC, ...

  3. android布局优化 工具,详解Android布局优化

    怎样才能写出优秀的Android App,是每一个程序员追求的目标.那么怎么才能写出一个优秀的App呢?相信很多初学者也会有这种迷茫.一句话来回答这个问题:细节很重要.今天我们就从最基础的XML布局来 ...

  4. Android App优化之ANR详解

    引言 背景:Android App优化, 要怎么做? Android App优化之性能分析工具 Android App优化之提升你的App启动速度之理论基础 Android App优化之提升你的App ...

  5. Android内存优化(六)LeakCanary使用详解

    相关文章 Android性能优化系列 Java虚拟机系列 1.概述 如果使用MAT来分析内存问题,会有一些难度,并且效率也不是很高,对于一个内存泄漏问题,可能要进行多次排查和对比. 为了能够简单迅速的 ...

  6. php java内存占用_PHP内存溢出优化代码详解

    相信很多人做大批量数据导出和数据导入的时候,经常会遇到PHP内存溢出的问题,在解决了问题之后,总结了一些经验,整理成文章记录下. 优化点 1.优化SQL语句,避免慢查询,合理的建立索引,查询指定的字段 ...

  7. 长虹电视机卡在android,长虹电视视频播放卡怎么办?电视优化教程详解

    原标题:长虹电视视频播放卡怎么办?电视优化教程详解 长虹电视视频播放卡怎么办?引起电视视频播放卡顿的原因是多方面的.单单对于长虹电视来说,有可能是因为内存不足,应用不适配.网速或信号不稳等问题导致的. ...

  8. 长虹电视android优化要多久,长虹电视时间久了卡顿如何解决?最新优化教程详解...

    原标题:长虹电视时间久了卡顿如何解决?最新优化教程详解 长虹电视视频播放卡怎么办?对于喜爱追剧的用户们来说,播放卡顿往往是用户们最常碰到的问题了.一般情况下,播放卡顿往往伴随着黑屏.无响应或死机等多种 ...

  9. android ------- 开发者的 RxJava 详解

    在正文开始之前的最后,放上 GitHub 链接和引入依赖的 gradle 代码: Github:  https://github.com/ReactiveX/RxJava  https://githu ...

最新文章

  1. kali linux ssh不要密码登录密码,kali linux 登录ssh服务常见问题
  2. halcon知识:共生矩阵
  3. python人工智能——机器学习——数据的降维
  4. 你觉得你个性上最大的优点是什么?
  5. Summarize to the Power of Two(CF-1005C)
  6. oc76--NSMutableDictionary
  7. 安装nagios中php安装报错 configure error xml2-config not foud
  8. 14 count(*)
  9. libSVM + VS2013 + C++使用介绍
  10. MAPGIS与ARCVIEW之间的文件转换技巧。(转载自当当吧网络驿站)
  11. 电子电路工程师常用模拟电路整理(20个)!
  12. 【转】聚类——GMM
  13. RSA2048 public key der格式结构
  14. 路由器重温——BGP路由-1
  15. 浅墨博客《游戏编程模式》
  16. 如何在宝贝详情页中制作一张图片多个链接
  17. 程序员一般都需要什么学历?就业前景如何?
  18. 曝李小冉鄢颇分手2个月 男方曾被女方旧爱砍伤
  19. 6款常用且好用的Python编辑器推荐!
  20. 计算机一级msoffice考哪个版本,2021计算机二级office考哪个版本 如何备考

热门文章

  1. 软件系统设计步骤与原理
  2. IntelliJ IDEA设置显示内存指示器的几种方法
  3. 【笔记】cocos2dx xxtea逆向获取lua脚本和资源文件
  4. 支付宝老将樊路远加盟阿里影业 淘票票将成下一个支付宝?
  5. 安卓EROFS (Read-only file system)的解决
  6. Gbase 8a MPP Cluster维护过程中可能遇到的故障管理(五)
  7. flutter中的可选参数
  8. 物联网通信-末端监控点环网组网设计
  9. windows chrome设置为默认浏览器,所有链接,包括本地的html页面都无法打开
  10. 计算机网络实验报告一心得,《计算机网络实验报告》5_12330122_黄嘉敏_实验心得.pdf...