当前位置: 首页 > news >正文

哈尔滨自助模板建站平面设计主要学哪些软件

哈尔滨自助模板建站,平面设计主要学哪些软件,最简单的电子商务网站建设代码,公众号开发者id在哪找前言 相信不少读者都阅读过相类似的文章了#xff0c;但是我还是想完整的把这之间的关系梳理清楚#xff0c;细节聊好#xff0c;希望你也能从中学到一些。进入正题#xff0c;大家应该都听过这样一句话——“UI更新要在主线程#xff0c;子线程更新UI会崩溃”。久而久之就… 前言 相信不少读者都阅读过相类似的文章了但是我还是想完整的把这之间的关系梳理清楚细节聊好希望你也能从中学到一些。进入正题大家应该都听过这样一句话——“UI更新要在主线程子线程更新UI会崩溃”。久而久之就感觉这是个真理甚至被认为是“官方结论”。但是如果问你官方什么时候在哪里说过这句话你会不会有点懵。而且就算是官方说的也就不一定对的是吧众所周知Google官方文档一直都有点说的不清不楚需要我们进行大量实践得出实际的结论。就好比之前的Android11更新文档我也是看了好久通过一个个实践才写出了适配指南然后就发现其中一个比较明显的BUGGoogle官方有说过这样一句下面是首先需要关注的行为变更 (无论您应用的 targetSdkVersion 是多少):  外部存储访问权限  - 应用无法再访问外部存储空间中其他应用的文件。其实经过实践会发现外部存储访问权限还是会和targetSdkVersion有关具体可以看这篇Android11适配指南。废话有点多了今天还是通过实践案例看看这个关于线程和UI更新的 “官方结论” 正确吗案例一子线程更新button文字 1)onCreate方法中更新了按钮显示文字修改Button的宽度为固定或者wrap_content都不崩溃。            android:idid/btn_ui        android:layout_width100dp        android:layout_height70dp        android:layout_centerInParenttrue        android:text我是一个按钮        /        //或者            android:idid/btn_ui        android:layout_widthwrap_content        android:layout_height70dp        android:layout_centerInParenttrue        android:text我是一个按钮        /            override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_ui)        thread {            btn_ui.text年轻人要讲武德        }    }2)onCreate方法中更新了按钮显示文字加了延时。Button的宽度为固定不崩溃。Button的宽度为wrap_content崩溃报错——Only the original thread that created a view hierarchy can touch its views。            android:idid/btn_ui        android:layout_width100dp        android:layout_height70dp        android:layout_centerInParenttrue        android:text我是一个按钮        /        //或者            android:idid/btn_ui        android:layout_widthwrap_content        android:layout_height70dp        android:layout_centerInParenttrue        android:text我是一个按钮        /               override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_ui)        thread {         Thread.sleep(3000)            btn_ui.text年轻人要讲武德        }    }案例一分析 有点懵的感觉不慌来看看崩溃信息。崩溃是在按钮宽度为wrap_content也就是根据内容设定宽度然后3秒之后去更新按钮文字发生了崩溃。相比之下有两个崩溃影响点需要注意下宽度wrap_content。如果设置为固定值是不会崩溃的见案例2所以是不是跟布局改变的逻辑有关呢延时3秒。如果不延时的话即使是wrap_content也不会崩溃见案例1所以是不是跟某些类的加载进度有关呢带着这些疑问去源码中找找答案。先看看崩溃日志android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:9219)        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1600)        at android.view.View.requestLayout(View.java:24884)可以看到是ViewRootImpl的requestLayout中检查线程的时候报错了那我们就看看这个方法    Override    public void requestLayout() {        if (!mHandlingLayoutInLayoutRequest) {            checkThread();            mLayoutRequested  true;            scheduleTraversals();        }    }    void checkThread() {        if (mThread ! Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    Only the original thread that created a view hierarchy can touch its views.);        }    }    在解开谜底之前我们先了解下ViewRootImpl。ViewRootImplActivity从创建到我们看到界面其实是经历了两个过程加载布局和绘制。加载布局加载布局其实就是我们常用的setContentView(int layoutResID)方法这个方法主要做的就是新建了一个DecorView然后根据activity设置的主题(theme)或者特征(Feature)加载不同的根布局文件最后再加载layoutResID资源文件。为了方便大家理解画了一张图加载布局流程这里的最后一步是调用了LayoutInflater的inflate()方法这个方法只做了一件事就是解析xml文件然后根据节点生成了view对象。最后形成了一个完整的DOM结构返回最顶层的根布局View。(DOM是一种文档对象模型他的层次结构是除了顶级元素所有元素都被包括到另外的元素节点中有点像家谱树结构很典型的就是html代码解析)到这里一个有完整view结构的DecorView就创建出来了但是它还没有被绘制也没有被显示到手机界面上。绘制绘制的流程发生在handleResumeActivity中熟悉app启动流程的朋友应该知道handleResumeActivity方法是用来触发onResume方法的这里也完成了DecorView的绘制。再来一张图绘制流程总结由此我们可以得出一些结论1)setContentView用来新建DecorView并加载布局的资源文件。2)onResume方法之后会新建一个ViewRootImpl作为DecorView的parent对DecorView进行测量布局和绘制等操作。3)PhoneWindow作为Window的唯一子类存储了DecorView变量并对其进行管理属于Activity和View交互的中间层。分析崩溃好了。再回来看看崩溃的原因    void checkThread() {        if (mThread ! Thread.currentThread()) {            throw new CalledFromWrongThreadException(                    Only the original thread that created a view hierarchy can touch its views.);        }    }  可以看到是因为当前线程currentThread不是mThread的时候就会崩溃报的错误是 “只有创建视图层次结构的原始线程才能触摸它的视图” 看到这里是不是猜到一些了这个mThread难道就是“创建视图的原始线程”通过查找其实这个mThread是在ViewRootImpl被创建的时候赋值的public ViewRootImpl(Context context, Display display) {    mThread  Thread.currentThread();}而通过上方分析Activity加载布局过程得知ViewRootImpl实例化发生在onResume之后用来绘制DecorView到window上。所以我们就可以得知崩溃的真正原因就是当前线程不是ViewRootImpl创建时候的线程就会崩溃。翻译的还是比较准确的只有创建视图的原始线程才能修改这个视图听起来也蛮有道理的我创造了你才有权利改变你有那味了。然后再看看前面的案例案例一在onCreate中修改Button这时候只是在修改DecorView都没创建ViewRootImpl也就没走到所以checkThread方法当然不会崩溃了。ViewRootImpl的创建是在onResume之后。案例二延时3秒之后界面也绘制完成了创建ViewRootImpl显然是在主线程完成的所以mThread为主线程而改变Button的线程为子线程所以setText方法会触发requestLayout方法重新绘制最终导致崩溃。但是Button的宽度设置为固定值咋又不崩溃了难道就不会执行checkThread方法了奇怪。找找setText的源码可以发现有一个方法是负责检查是否需要新的布局——checkForRelayout()private void checkForRelayout() {        // If we have a fixed width, we can just swap in a new text layout        // if the text height stays the same or if the view height is fixed.        if ((mLayoutParams.width ! LayoutParams.WRAP_CONTENT                || (mMaxWidthMode  mMinWidthMode  mMaxWidth  mMinWidth))                 (mHint  null || mHintLayout ! null)                 (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight()  0)) {                        if (mEllipsize ! TextUtils.TruncateAt.MARQUEE) {                // In a fixed-height view, so use our new text layout.                if (mLayoutParams.height ! LayoutParams.WRAP_CONTENT                         mLayoutParams.height ! LayoutParams.MATCH_PARENT) {                    autoSizeText();                    invalidate();                    return;                }               //...            }            // We lose: the height has changed and we have a dynamic height.            // Request a new view layout using our new text layout.            requestLayout();            invalidate();        } else {            // Dynamic width, so we have no choice but to request a new            // view layout with a new text layout.            nullLayouts();            requestLayout();            invalidate();        }    }可以看到如果布局大小没有改变的话我们是不会去执行requestLayout方法重新进行布局绘制的只会调用autoSizeText方法计算文字大小invalidate绘制文字本身所以当我们宽高设置为固定值setText()方法就不会执行到requestLayout()方法了自然也就执行不到checkThread()方法了。反思解决了问题还需要反思下为什么需要checkThread检查线程呢检查线程其实就是检查更新UI操作的当前线程是不是当初创建UI的那个线程这样就保证了线程安全因为UI控件本身不是线程安全的但是加锁又显得太重会降低View加载效率毕竟是跟交互相关的。所以就直接通过判断线程这一逻辑来形成一个单线程模型保证View操作的线程安全。案例二子线程和主线程分别showToast 1)onCreate方法中弹出toast崩溃——Cant toast on a thread that has not called Looper.prepare()    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_ui)        thread {            showToast(我去年买了个表)        }    }2)onCreate方法中弹出toast增加Looper.prepare()Looper.loop()方法。不崩溃。加上延时3秒不崩溃。    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_ui)        thread {            //Thread.sleep(3000)            Looper.prepare()            showToast(我去年买了个表)            Looper.loop()        }    }3)使用同一个Toast实例在子线程中的Toast没消失之前点击按钮在主线程中修改Toast文字并显示则程序崩溃——Only the original thread that created a view hierarchy can touch its views.。重新运行在子线程中显示并消失后点击按钮不崩溃。换个手机——三星s9重新运行在子线程中的Toast没消失之前点击按钮不崩溃。    lateinit var mToast: Toast    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_ui)        thread {            Looper.prepare()            mToastToast.makeText(thisUIMainActivity,我去年买了个表,Toast.LENGTH_LONG)            mToast.show()            Looper.loop()        }        btn_ui.setOnClickListener {            mToast.setText(我今年买了个表)            mToast.show()        }    }案例二分析 在解开谜底之前我们先了解下Toast。Toast原理Toast.makeText(this,msg,Toast.LENGTH_SHORT).show()简单又常用的一句代码还是通过流程图的方式看看它是怎么创建并展示的。Toast流程图和DecorView加载绘制流程如出一辙首先加载了布局文件创建了View。然后通过addView方法再次新建一个ViewRootImpl实例作为parent进行测量布局和绘制。崩溃分析1)首先说下第一次崩溃——Cant toast on a thread that has not called Looper.prepare()也就是在创建Toast的线程必须要有Looper在运行。根据源码我们也得知Toast的显示和隐藏都是通过Handler传递消息的所以必须要有Handler使用环境也就是绑定Looper对象并且通过loop方法开始循环处理消息。2)第二次崩溃——Only the original thread that created a view hierarchy can touch its views。这里的崩溃和之前更新Button一样的报错所以我们有理由怀疑也是一样的原因在不同的线程调用了ViewRootImpl的requestLayout方法。我们看到点击按钮的时候调用了mToast.setText()方法咦这不就跟案例一一模一样了吗。setText方法中调用了TextView的setText()方法然后由于Toast中的TextView宽高都是wrap_content的所以会触发requestLayout方法最后会调用到最上层View也就是ViewRootImpl的requestLayout方法。所以崩溃的原因就是因为Toast在第一次在子线程中show的时候新建了一个ViewRootImpl实例绑定了当前线程也就是子线程到mThread变量。然后同一个Toast在主线程调用setText方法最终会调用到ViewRootImpl的requestLayout方法引起线程检查当前线程也就是主线程并不是当初那个创建ViewRootImpl实例的线程所以导致崩溃。3)那为什么等Toast消失之后点击按钮又不崩溃了呢原因就在Toast的hide方法中最终会调用到View的assignParent方法将Toast的mParent设置为null也就是ViewRootImpl设置为null了。所以调用setText方法的时候也就执行不到requestLayout方法了也就不会到checkThread方法检查线程了。贴下代码public void handleHide() {    if (mView ! null) {        if (mView.getParent() ! null) {            mWM.removeViewImmediate(mView);        }        mView  null;    }}removeViewImmediate---removeViewLockedprivate void removeViewLocked(int index, boolean immediate) {    ViewRootImpl root  mRoots.get(index);    View view  root.getView();  //...    if (view ! null) {        view.assignParent(null);        if (deferred) {            mDyingViews.add(view);        }    }}void assignParent(ViewParent parent) {        if (mParent  null) {            mParent  parent;        } else if (parent  null) {            mParent  null;        } else {            throw new RuntimeException(view   this   being added, but  it already has a parent);        }}4)但是但是为啥换个手机又不崩溃了呢这是我偶然发现的在我的三星S9手机上运行时不会崩溃的而且界面给我的反馈并不是修改当前页面上Toast上的文字而是像新建了一个Toast展示即时代码中写的是setText方法。所以我猜测在部分手机上应该是改变了Toast的设置当调用setText方法的时候就会马上结束当前的Toast展示调用hide方法。然后再进行Toast文字修改并展示也就是刚才第三点的做法。当然这只是我的猜测有研究过手机源码的大神也可以补充下。总结 任何线程都可以更新UI也都有更新UI导致崩溃的可能。其中的关键就是view被绘制到界面时候的线程(也就是最顶层ViewRootImpl被创建时候的线程)和进行UI更新时候的线程是不是同一个线程如果不是就会报错。参考 https://www.jianshu.com/p/1cdd5d1b9f3dhttps://www.cnblogs.com/fangg/p/12917235.html拜拜 有一起学习的小伙伴可以关注下我的公众号——码上积木❤️❤️ 每日三问知识点/面试题积少成多。
http://www.zqtcl.cn/news/207307/

相关文章:

  • 做网站找不到客户兰州 网站制作
  • 广州中小学智慧阅读门户网站中山网站建设方案推广
  • 长沙网站建设专家排行榜
  • 清河企业做网站wordpress淘宝客插件开发
  • 网站上传连接失败的原因微信app网站建设
  • 服装网站源码php建设厅网站合同备案在哪里
  • o2o网站建设多少钱公司设计网站定制
  • asp.net 企业网站后台管理系统源码中国vs菲律宾
  • 成都家装排名前十名wordpress优化方法
  • 南阳做网站公司网站开发服务费分录
  • 网络课程网站建设龙岩个人小程序开发
  • 上海网络推广报价seo技术培训机构
  • 在线看免费网站哪个设计培训机构好
  • 网站建设制作确认单网站建设策划书格式及范文
  • framework7做网站如何在社交网站做销售
  • dedecms_v5.6室内装饰设计公司企业网站模板.rarwordpress添加3d地图吗
  • 开发网站的意义百度推广计划
  • 网站设计师网站网站建设从入门到精通pdf
  • 游戏网站建设方案百度文库wordpress调用搜索框
  • 京东物流网站建设特点网站开发与维护岗位说明书
  • 制作一个网站的基本步骤星巴克网站建设ppt
  • 搭建企业网站宽带多大php微信公众号开发教程
  • 国家建设公债拍卖网站新手如何自己建网站
  • 网站建设颊算网站注册界面代码
  • 微信h5网站模板下载百姓网征婚
  • 模板网站和插件有哪些河南第一火电建设公司网站
  • 怎么测网站流量吗网络运维工程师教程
  • 有谁帮做网站网站建设seo合同书
  • 自己做视频网站只能用地址连接专业网站建设效果
  • 重庆网站建设价格费用酒店协会网站集静态模板