google官方API URLConnection发起网络请求
image.png官方URLConnection文档(详见官方文档)
For example, to retrieve the webpage at http://www.android.com/:
URL url = new URL("http://www.android.com/");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
readStream(in);
finally {
urlConnection.disconnect();
}
}
用URLConnection请求豆瓣API
new Thread(new Runnable() {
@Override
public void run() {
// String path = "https://www.baidu.com/";
String path = "https://api.douban.com/v2/movie/in_theaters";
try {
URL url = new URL(path);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
int responseCode = httpURLConnection.getResponseCode();
if (responseCode == 200) {
//获取服务器返回的数据
InputStream inputStream = httpURLConnection.getInputStream();
String response = InputStreamToStringTool.inputStreamToString(inputStream);
System.out.println("=========res start");
System.out.println("----" + response);
System.out.println("=========res end");
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
(inputStream转换成String工具类见上一篇文章)
执行结果
image.png
结合Handle在主线程更新UI
在子线程中做网络请求耗时操作,完毕之后用handler发送消息给主线程 让主线程更新UI操作
public class HttpActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_http);
}
@SuppressLint("HandlerLeak")
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1://成功
String s = (String) msg.obj;
Toast.makeText(HttpActivity.this, "" + s, Toast.LENGTH_SHORT).show();
break;
case 2://失败
Toast.makeText(HttpActivity.this, "失败", Toast.LENGTH_SHORT).show();
break;
case 3://异常
Toast.makeText(HttpActivity.this, "异常", Toast.LENGTH_SHORT).show();
break;
}
}
};
public void reqHttpWeb(View view) {
new Thread(new Runnable() {
@Override
public void run() {
// String path = "https://www.baidu.com/";
String path = "https://api.douban.com/v2/movie/in_theaters";
try {
URL url = new URL(path);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
int responseCode = httpURLConnection.getResponseCode();
//状态吗200表示请求资源成功
if (responseCode == 200) {
//获取服务器返回的数据
InputStream inputStream = httpURLConnection.getInputStream();
String response = InputStreamToStringTool.inputStreamToString(inputStream);
System.out.println("=========res start");
System.out.println("----" + response);
System.out.println("=========res end");
//注意Toast也是一个View 更新UI在主线程操作
Message message = mHandler.obtainMessage();
message.obj = response;
message.what = 1;
mHandler.sendMessage(message);
} else {
Message message = mHandler.obtainMessage();
message.what = 2;
mHandler.sendMessage(message);
}
} catch (MalformedURLException e) {
e.printStackTrace();
Message message = mHandler.obtainMessage();
message.what = 3;
mHandler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
Message message = mHandler.obtainMessage();
message.what = 3;
mHandler.sendMessage(message);
}
}
}).start();
}
}
Handler原理
handler是用来发送和接收消息的
- [1]在主线程定义Handler,覆盖一个handleMessage()方法
- [2] 在子线程的耗时操作完成后,handler在子线程中发送消息
- [3]handler发送的消息存放在主线程的MessageQueen中,主线程的Looper循环的从消息队列中取出消息,取出的消息还是被handler接收,最终在handleMessage()方法中更新UI
下面开始装逼:上源码
[1]ActivityThread.java的main()方法中 调用了一个Looper.loop()方法
ActivityThread的main()调用了Looper.loop() public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
[2]loop()方法中获取了一个Looper对象,该对象的mQueue属性返回一个MessageQueue对象queue,(final Looper me = myLooper();)loop()中有一个死循环for(;;){},该循环中,queue调用next()返回一个Message对象msg,msg.target.dispatchMessage(msg);
核心代码
public static void loop() {
//1 looper对象
final Looper me = myLooper();
//2 MessageQueue对象
final MessageQueue queue = me.mQueue;
//3 死循环获取Message对象
for (;;) {
Message msg = queue.next(); // might block
//4 msg的target属性是一个Handler 调用了它的dispatchMessage
msg.target.dispatchMessage(msg);
}
}
源码:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
msg.target.dispatchMessage(msg)分析
msg的target属性是一个Handler类型
image.png
相当于Handler调用了 dispatchMessage()方法
dispatchMessage()调用了handleMessage()
image.png
源码
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
所以最终处理系统消息是在handleMessage方法中进行的
安卓 APP 启动过程,对于 Activity#onCreate() 等生命周期为什么不会因为 Looper#loop() 里的死循环而无机会执行
Activity 的生命周期方法是通过 Binder 跨进程通信实现的,跨进程通信中系统进程不受 Looper#loop() 的死循环的影响(不在同一个线程),系统进程通知应用进程调用 Activity 的生命周期方法,首先通过 ActivityThread 内部的 Handler#handleMessage() 切换线程到主线程,再通过 Handler 完成 Activity 的生命周期方法的调用
主线程死循环是不是很消耗 CPU 资源呢?
这里涉及到 Linux 的 pipe/epoll 机制,当主线程的消息队列没有消息时,会阻塞在 Queue#next() 中的 nativePollOnce() 方法中
此时主线程会释放 CPU 资源进入休眠状态,直到下一个消息到来
这里主线程休眠,就存在唤醒主线程的操作,该操作是通过往 pipe 管道写端写入数据来唤醒主线程
因此,主线程没消息的时候就睡眠了,不会消耗大量 CPU 资源
源码地址:https://github.com/MengkZhang/base_demo/tree/31c9561a418ed873aae48900f9aa8d770aad7de1
网友评论