8种机械键盘轴体对比
本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?
https://developer.android.com/reference/android/os/NetworkOnMainThreadException.html
android.os.NetworkOnMainThreadException众所周知是在主线程上做网络操作抛出的异常,这里通过源代码对其产生的原因做一点分析和扩展
在4.4.2手机上运行一段异常代码,日志如下E/AndroidRuntime: FATAL EXCEPTION: main
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:163)
at libcore.io.IoBridge.recvfrom(IoBridge.java:506)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240)
通过提示可以看到是在PlainSocketImpl的内部类PlainSocketInputStream读取字节流时抛出的,这里的access$000方法是java编译器为内部类访问外部方法时自动生成的1220 private static class PlainSocketInputStream extends InputStream {
221 private final PlainSocketImpl socketImpl;
222
223 public PlainSocketInputStream(PlainSocketImpl socketImpl) {
224 this.socketImpl = socketImpl;
225 }
226
227 @Override public int available() throws IOException {
228 return socketImpl.available();
229 }
230
231 @Override public void close() throws IOException {
232 socketImpl.close();
233 }
234
235 @Override public int read() throws IOException {
236 return Streams.readSingleByte(this);
237 }
238
239 @Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
240 return socketImpl.read(buffer, byteOffset, byteCount);
241 }
242 }
实际上调用了PlainSocketImpl的read方法1480 private int read(byte[] buffer, int offset, int byteCount) throws IOException {
481 if (byteCount == 0) {
482 return 0;
483 }
484 Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
485 if (shutdownInput) {
486 return -1;
487 }
488 int readCount = IoBridge.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false);
489 // Return of zero bytes for a blocking socket means a timeout occurred
490 if (readCount == 0) {
491 throw new SocketTimeoutException();
492 }
493 // Return of -1 indicates the peer was closed
494 if (readCount == -1) {
495 shutdownInput = true;
496 }
497 return readCount;
498 }
从这段代码找到IoBridge.recvfrom这个方法可以看到,IoBridge作为桥接,调用Libcore1502 public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
503 int result;
504 try {
505 InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
506 result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
507 result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
508 } catch (ErrnoException errnoException) {
509 result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
510 }
511 return result;
512 }
再看LibCore119public final class Libcore {
20 private Libcore() { }
21
22 public static Os os = new BlockGuardOs(new Posix());
23}
可以看到出问题的在BlockGuardOs中的1162 @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
163 BlockGuard.getThreadPolicy().onNetwork();
164 return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
165 }
这里的os的实现类Posix最终通过native方法完成了网络操作,而在此之前的1BlockGuard.getThreadPolicy().onNetwork();
这行代码,抛出了NetworkOnMainThreadException异常,BlockGuard顾名思义是对可能产生阻塞的地方做预处理,BlockGuardOs其中有很多IO方法,猜测android的IO操作都是通过了这个预处理来防止主线程阻塞。
再看BlockGuard1126 private static ThreadLocal threadPolicy = new ThreadLocal() {
127 @Override protected Policy initialValue() {
128 return LAX_POLICY;
129 }
130 };
刚才用来做异常抛出的ThreadPolicy原来是一个ThreadLocal,让每个线程持有自己的Policy来做阻塞判断,可以预见这里主线程持有的Policy和其他子线程不同,对网络请求有特殊处理。
BlockGuard中的这个Policy是在StrictMode的setBlockGuardPolicy方法中设置1781 // Sets the policy in Dalvik/libcore (BlockGuard)
782 private static void setBlockGuardPolicy(final int policyMask) {
783 if (policyMask == 0) {
784 BlockGuard.setThreadPolicy(BlockGuard.LAX_POLICY);
785 return;
786 }
787 final BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
788 final AndroidBlockGuardPolicy androidPolicy;
789 if (policy instanceof AndroidBlockGuardPolicy) {
790 androidPolicy = (AndroidBlockGuardPolicy) policy;
791 } else {
792 androidPolicy = threadAndroidPolicy.get();
793 BlockGuard.setThreadPolicy(androidPolicy);
794 }
795 androidPolicy.setPolicyMask(policyMask);
796 }
通常我们对StrictMode严格模式的使用是为了避免在主线程上做IO或者网络等耗时操作,优化用户体验,避免ANR
AndroidBlockGuardPolicy实现了Policy接口,其中11140 public void onNetwork() {
1141 if ((mPolicyMask & DETECT_NETWORK) == 0) {
1142 return;
1143 }
1144 if ((mPolicyMask & PENALTY_DEATH_ON_NETWORK) != 0) {
1145 throw new NetworkOnMainThreadException();//这里抛出的异常
1146 }
1147 if (tooManyViolationsThisLoop()) {
1148 return;
1149 }
1150 BlockGuard.BlockGuardPolicyException e = new StrictModeNetworkViolation(mPolicyMask);
1151 e.fillInStackTrace();
1152 startHandlingViolationException(e);
1153 }
使用位操作来判断阻塞,PENALTY_DEATH_ON_NETWORK在enableDeathOnNetwork中被打开1987 /**
988 * Used by the framework to make network usage on the main
989 * thread a fatal error.
990 *
991 * @hide
992 */
993 public static void enableDeathOnNetwork() {
994 int oldPolicy = getThreadPolicyMask();
995 int newPolicy = oldPolicy | DETECT_NETWORK | PENALTY_DEATH_ON_NETWORK;
996 setThreadPolicyMask(newPolicy);
997 }
而在ActivityThread的handleBindApplication中有这么一行代码14196 if (data.appInfo.targetSdkVersion > 9) {
4197 StrictMode.enableDeathOnNetwork();
4198 }
也就是说在app启动时,4.4.2版本默认对sdk9以上的主线程开启了不得在主线程上做网络操作
到这里可以总结一下,网络操作通过BlockGuardOs来执行,而其中的IO操作被BlockGuard预处理,BlockGuard则是通过当前线程上的policy策略来做处理,而主线程上的policy在app启动时就被设置为不得在主线程上做网络操作,所以如果我们在主线程上做将这个开关重置,即可在主线程上做网络操作而不会抛出NetworkOnMainThreadException1StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitNetwork().build();
StrictMode.setThreadPolicy(threadPolicy);
当然这里只是为了理解原理,不建议在生产环境关闭这个开关