美文网首页
google官方URLConnection发起网络请求&&Han

google官方URLConnection发起网络请求&&Han

作者: MengkZhang | 来源:发表于2019-05-10 14:46 被阅读0次

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
Handler原理图

下面开始装逼:上源码

[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

相关文章

网友评论

      本文标题:google官方URLConnection发起网络请求&&Han

      本文链接:https://www.haomeiwen.com/subject/jfraoqtx.html