美文网首页
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