首页 文章 精选 留言 我的

精选列表

搜索[解耦化],共10020篇文章
优秀的个人博客,低调大师

Transform动画初解 in Swift

创建一个界面,就像这样的: 顶部是一个UISegmentControl,用来制定transform的类型。分别是:CGAffineTransformMakeTranslation、CGAffineTransformTranslate、CGAffineTransformIdentity. 然后是一个UILabel,这个Label实时的显示当前的动画类型是什么。 桔色的是动画的View。 最下面是一个按钮,按这个按钮桔色的View开始执行动画。 其他的,蓝色的线就是这几个View的Constraints。指定这几个view的定位是如何的,比如,相对于顶部的距离,相对于左边的距离,右边的距离等。 配置好页面上的View之后,给这些View在Controller中指定对应的对象。并在nib文件中指定各个View的事件。 View在controller中对应的对象: @IBOutlet weak var animationView: UIView! @IBOutlet weak var animationLabel: UILabel! UISegmentControl的事件: @IBAction func segmentAction(sender: AnyObject) { var segmentControl = sender as UISegmentedControl animationType = segmentControl.selectedSegmentIndex } 运行动画的按钮的事件: @IBAction func runAction(sender: AnyObject) { var distance: CGFloat = 30 switch animationType { case 0: self.animationLabel.text = "CGAffineTransformMakeTranslation" UIView.animateWithDuration(1.0, animations: { self.animationView.transform = CGAffineTransformMakeTranslation(distance, 0) }) case 1: self.animationLabel.text = "CGAffineTransformTranslate" UIView.animateWithDuration(1.0, animations: { self.animationView.transform = CGAffineTransformTranslate(self.animationView.transform, distance, 0) }) case 2: self.animationLabel.text = "CGAffineTransformIdentity" UIView.animateWithDuration(1.0, animations: { self.animationView.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, distance, 0) }) default: println("") } } 在ViewDidLoad设置选择的动画的类型,这里我们不直接取UISegmentControl的selectedIndex的值。所以,在ViewDidLoad方法内设定默认值是1(即选定到是第一个)。 全部代码: // // ViewController.swift // TransformDemo // // Created by Bruce Lee on 30/11/14. // Copyright (c) 2014 Dynamic Cell. All rights reserved. // // QQ:1828099940, 群:58099570 欢迎加入讨论 // import UIKit class ViewController: UIViewController { @IBOutlet weak var animationView: UIView! @IBOutlet weak var animationLabel: UILabel! var animationType: Int! override func viewDidLoad() { super.viewDidLoad() animationType = 0 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func segmentAction(sender: AnyObject) { var segmentControl = sender as UISegmentedControl animationType = segmentControl.selectedSegmentIndex } @IBAction func runAction(sender: AnyObject) { var distance: CGFloat = 30 switch animationType { case 0: self.animationLabel.text = "CGAffineTransformMakeTranslation" UIView.animateWithDuration(1.0, animations: { self.animationView.transform = CGAffineTransformMakeTranslation(distance, 0) }) case 1: self.animationLabel.text = "CGAffineTransformTranslate" UIView.animateWithDuration(1.0, animations: { self.animationView.transform = CGAffineTransformTranslate(self.animationView.transform, distance, 0) }) case 2: self.animationLabel.text = "CGAffineTransformIdentity" UIView.animateWithDuration(1.0, animations: { self.animationView.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, distance, 0) }) default: println("") } } } 欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处! 本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/4133266.html ,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android DataBinding 实战全解

2015年的Google IO大会上,Android 团队发布了一个数据绑定框架(Data Binding Library),官方原生支持 MVVM 模型。数据绑定的概念并不陌生,Web开发中已经很是普遍,因此DataBinding或多或少地都借鉴了Web端开成熟的经验,其语法与使用方式都和JSP中的EL表达式非常类似。经过不断地强化,到了2016年,DataBinding已经可以支持数据双向绑定。 1. DataBinding简介 DataBinding解决了Android UI 编程的一个痛点,其主要优势在于: 对于MVVM的支持:官方原生支持 MVVM 模型可以让我们在不改变既有代码框架的前提下,非常容易地使用这些新特性。 提高开发效率: 去掉Acitivity和Fragments中更新UI数据的代码,让业务逻辑和UI代码分离; XML成为UI数据的唯一真实来源; 减少定义view id 和使用findViewById(); 性能高、功能强: 充分考虑了性能因素,高效的绑定和更新数据; 更安全,在编译时会发现由于错误的ID而引起的Errors; 保证代码在主线程经常; 在DataBinding推出之前,市面上已经有类似的第三方库在被使用,比如: ButterKnife; Android Annotations; RoboBinding; DataBinding让这些依赖注入框架会慢慢地失去市场,因为在 Java代码中直接使用View变量的情况会越来越少。 但是DataBinding毕竟还在发展阶段,有些地方还值得完善: IDE的支持不足善:虽然DataBinding是Google亲生的,但是Android Studio中的支持还不是不尽如人意,尤其是在XML文件中代码提示有待加强; 报错信息不直接:这个是老生常谈的问题,虽然DataBinding在编译时能发现数据绑定的错误,但是报错信息往往不直观,需要用户在很多错误信息中自己去找错误点。 没有重构的支持:这可以说是所有数据绑定框架都会面临的问题,因为数据的绑定都是在XML中,一旦用户需要继承原来的代码,就压面临这将所有数据绑定代码重新编写。 2. DataBinding的基本使用 准备工作 确保Android stuido的版本一定要大于1.3。 Android的Gradle插件版本不低于 1.5.0-alpha1: classpath 'com.android.tools.build:gradle:1.5.0' 然后修改对应模块(Module)的 build.grade: android { .... dataBinding { enabled = true } } 工程创建完成后,我们通过一个最简单的例子来说明 Data Binding 的基本用法。 布局文件 使用DataBinding之后,xml的布局文件就不再用于单纯地展示UI元素,还需要定义UI元素用到的变量。所以,它的根节点不再是一个 ViewGroup,而是变成了layout,并且新增了一个节点data,用来定义绑定的数据。 <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <!--添加要绑定的数据--> </data> <!--下面的布局文件就是原先的根节点(Root Element)--> <LinearLayout> .... </LinearLayout> </layout> data节点的作用就像一个桥梁,Activity中将data需要数据传入,XML定义data数据绑定的位置,搭建了 View 和 Model 之间的通路,也就是实现MVVM的ViewModel。 下面说明下如何在XML定义data节点:我们先在 xml 布局文件的data节点中声明variable节点,这个节点将会定义变量的名称、类型,为UI元素提供数据(例如将String绑定到TextView的android:text属性上)。 <data> <!--java.lang.*包是自动导入的不需要你再次的导入--> <!--当出现了import相同的类名的时候需要为其指定外号alias加以区分--> <import type="com.example.administrator.databindingdemo.Student"/> <import type="com.example.administrator.databindingdemo.MainActivity.Presenter"/> <variable name="student" type="Student"/> <variable name="presenter" type="Presenter"/> <variable name="visibility" type="Boolean"/> </data> 其中Presenter类时用来绑定方法的类,我们稍后再说;Student类是我们定义的一个类,用来呈现数据,其定义如下: public class Student extends BaseObservable{ private String firstName; private String lastName; public void setFirstName(String firstName) { this.firstName = firstName; //提示该属性刷新了 notifyPropertyChanged(BR.firstName); //提示所有的属性都刷新 //notifyAll(); } //注解来提示Binding生成这个字段 @Bindable public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; notifyPropertyChanged(BR.lastName); } @Bindable public String getFirstName() { return firstName; } public Student(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } @Override public String toString() { return "Student{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '}'; } } Studnet类继承了BaseObservable,这个是数据更新时所需要的,我们稍后在再来讨论。 当我们设置DataBinding生效后,如果一个xml文件的被如上这般设置了Data节点,编译器就会自动生成一个继承自 ViewDataBinding 的类(build 目录下)。其命名规则也是固定的:如果 xml 的文件名叫 activity_main.xml,那么生成的类就是 ActivityMainBinding。 绑定数据变量 下面需要修改MainActivity的onCreate方法,用 DatabindingUtil.setContentView()来替换掉setContentView(),然后创建一个Student对象,通过mBinding.setStudent(mStudent)与 variable 进行绑定。 Student mStudent = new Student("guo","chengqian"); private ActivityMainBinding mBinding; Boolean visiblity = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this,R.layout.activity_main); //绑定变量 mBinding.setStudent(mStudent); mBinding.setPresenter(new Presenter()); mBinding.setVisibility(visiblity); } ActivityMainBinding类中所有的set方法是根据variable名称生成的。我们在XML文件中定义了三个变量,所以会生成三个set方法。 在XML中使用变量 当数据传入ViewDataBinding与Variable绑定之后,xml的UI元素就可以直接使用了。 <TextView android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{student.firstName}" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="@{visibility?View.VISIBLE:View.GONE}" android:text="@{student.lastName}" /> 直接在对应属性上@{variable}就可以使用该变量的值以及对应的属性。 现在运行程序,就会看到我们传入Student的姓名已经显示出来了 1500001896236.png 3. 事件方法绑定 仅仅是在UI上绑定数据还远远不够,UI上的响应事件也需要被处理,因此需要要把响应事件的方法绑定到View上。 事件方法的绑定有两种方法:第一种是方法引用,这种方法要求函数的参数个数,类型以及顺序都会原生的事件方法相同;第二种方法是使用lambda表达式,这种方法更为灵活,可以自定义参数。 我们来看下例子: 这是一个方法类的实现,之前在xml的data节点中已经声明了变量,在Activity中也进行了绑定。 //方法类 public class Presenter{ //方法引用 参数要求符合接口定义 public void onTextChanged(CharSequence s, int start, int before, int count){ mStudent.setFirstName(s.toString()); //如果已经为该类继承了BaseObservable 就不需要再赋值了; //mBinding.setStudent(mStudent); } //方法引用,参数要求符合接口定义; public void onClick(View view){ Toast.makeText(MainActivity.this,"onClick!",Toast.LENGTH_SHORT).show(); } //自定义方法 public void onClickListenerBinding(Student student){ Toast.makeText(MainActivity.this, student.getLastName(),Toast.LENGTH_SHORT).show(); } } 首先在EidtText控件中使用,将onTextChanged方法绑定。 <EditText android:id="@+id/first_name_ed_txt" android:onTextChanged="@{presenter.onTextChanged}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="First Name"/> 这样一旦输入的内容改变,变量student中对应的firstName属性也会跟着改变。 同样,我们将onClick方法绑定到一个TextView上 <TextView android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{student.firstName}" android:onClick="@{presenter.onClick}" /> 但是这种方法不够灵活,绑定的方法必须和View所要求的接口参数一致。为了更为灵活的绑定方法,还可以使用Lambda表达式。 <TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="@{visibility?View.VISIBLE:View.GONE}" android:text="@{student.lastName}" android:onClick="@{()-> presenter.onClickListenerBinding(student)}"/> lambda表达式实际上也是创建了匿名函数,然后再这个匿名函数中调用了Presenter中的方法,只不过省去了匿名函数的申明,只保留最为核心的逻辑部分。 此外,lambd表达式支持传入Context参数,比如Presenter中定义这样的函数 public void onStudentLongClick(Student student, Context context){ Toast.makeText(LambdaActivity.this, "LongClick: "+student.toString(), Toast.LENGTH_SHORT).show(); } Button中可以这样调用,Context参数会被自动生成: <Button ...... android:longClickable="true" android:onClick="@{() -> presenter.onStudentLongClick(student,context)}" ...... /> 4. DataBinding所支持的表达式 为了扩展更为强大的功能,DataBinding在使用变量时支持一下表达式: 运算符:Mathematical + - / * % 字符串连接:String concatenation + 逻辑运算:Logical && || 位操作: Binary & | ^ Unary + - ! ~ 移位操作:Shift >> >>> << 比较操作:Comparison == > < >= <= 类型判断:instanceof Grouping () Literals - character, String, numeric, null 类型转化:Cast Method calls Field access Array access [] Ternary operator ?: 但不支持的表达式: this super new Explicit generic invocation Null Coalescing 运算符 DataBinding对于引用变量是否为空,也有特殊的支持,使用符号??。 android:text="@{student.firstName ??student.lastName}" 就等价于 android:text="@{student.firstName != null ? student.firstName : student.lastName}" 5. 在include文件里使用BataBinding 为了让Xml布局文件更为简洁易懂,对于重复使用的布局可以单独建立布局文件,然后使用include导入。DataBinding也考虑到了这种使用情况。可以使用bind:variable_name导入数据变量。 <!--当你使用include的时候,你可以使用命名空间和属性中的变量名将数据传送到另一个布局中去--> <include layout="@layout/include_show_name" bind:presenter="@{presenter}" bind:student="@{student}" bind:visibility="@{visibility}" /> 需要注意的是: 一定要使用app命名空间xmlns:app="http://schemas.android.com/apk/res-auto" 必须在ViewGroup的根布局中导入布局文件,如果在非根节点的ViewGroup中使用include会导致错误; 6. 在SubView中的使用 在SubView的使用方式和在Include中使用是类似的。 <ViewStub bind:visibility="@{visibility}" android:id="@+id/view_stub" android:layout_width="368dp" android:layout_height="wrap_content" android:layout="@layout/viewstub" /> 然后在Activity中通过DataBindingUtil获得SubView对象,然后填充View; //viewstub inflate; View inflate = mBinding.viewStub.getViewStub().inflate(); 7. 双向绑定 上面提的内容都是单向绑定,也就是把后台的数据绑定到前台显示出来。DataBinding如果只有如此功能,也不足为奇,最重要的功能点还是还在于双向绑定,前台显示的数据改变也会自动影响后台的数据。 首先我们新建一个数据对象类 public class FormModel extends BaseObservable { private String name; private String password; @Bindable //该注解表示数据绑定 public String getName() { return name; } public void setName(String name) { this.name = name; //通知name属性发生改变 notifyPropertyChanged(BR.name); } //注解标志该属性是绑定的 @Bindable public String getPassword() { return password; } public void setPassword(String password) { this.password = password; //通知有属性发生改变 notifyPropertyChanged(BR.password); } public FormModel(String name, String password) { this.name = name; this.password = password; } } 该类继承了BaseObservable,在getter方法之前使用@Bindable,标记该属性就是需要绑定的,会在BR类中生成对应的ID;在setter方法中notifyPropertyChanged(BR.ID),通知对应ID的属性发生了改变。 然后在XML中使用“@={variable}”表示数据绑定,如下。 <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="model" type="com.example.administrator.databindingdemo.FormModel"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.databindingdemo.TowWayBindingActivity"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Name" android:text="@={model.name}"/> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="pwd" android:text="@={model.password}"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{model.name}"/> </LinearLayout> </layout> 最后在Activity中传入变量。 public class TowWayBindingActivity extends AppCompatActivity { ActivityTowWayBindingBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil. setContentView(this,R.layout.activity_tow_way_binding); FormModel model = new FormModel("user", "12345"); mBinding.setModel(model); //属性改变监听器 model.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { @Override public void onPropertyChanged(Observable sender, int propertyId) { if(BR.name == propertyId) Toast.makeText(TowWayBindingActivity.this, "姓名改变", Toast.LENGTH_SHORT).show(); } }); } } 这是运行程序就会发现,在前端输入姓名时,其他绑定name属性的地方也会改变,说明后台数据也响应改变了。 8. 表达式链与隐式更新 当多个View的属性相互关联式时,可以通过表达式链实现隐式更新,比如: <CheckBox android:id="@+id/check_box"/> <ImageView android:visibility="check_box.checked?View.VISIBILE:View.GONE"/> 9. 添加动画 数据绑定必然涉及到UI的变化,默认情况下往往都是很生硬的UI变化,没有任何过度。DataBinding也提供办法来插入动画。 实现方法也很简单,就是设置一个重新绑定时的回调函数addOnRebindCallback,在函数中设置当前View的UI变化时开启一定延时。 public class AnimationActivity extends AppCompatActivity { ActivityAnimationBinding mBinding; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this,R.layout.activity_animation); mBinding.setPresenter(new AnimatorPresenter()); //设置重绑定回调函数 mBinding.addOnRebindCallback(new OnRebindCallback() { @Override public boolean onPreBind(ViewDataBinding binding) { ViewGroup viewGroup = (ViewGroup) binding.getRoot(); //开启延时位移动画 TransitionManager.beginDelayedTransition(viewGroup); return true; } }); } public class AnimatorPresenter{ public void onCheckedChanged(boolean isChecked){ mBinding.setShowImage(isChecked); } } } 在XML中正常使用DataBinding <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="android.view.View"/> <variable name="presenter" type="com.example.administrator.databindingdemo.AnimationActivity.AnimatorPresenter"/> <variable name="showImage" type="Boolean"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.databindingdemo.AnimationActivity"> <ImageView android:contentDescription="Animation Show" android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/default_avatar" android:visibility="@{showImage?View.VISIBLE:View.GONE}"/> <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="showImage" android:onCheckedChanged = "@{(v,isChecked)->presenter.onCheckedChanged(isChecked)}"/> </LinearLayout> </layout> 这样图片在显示或者出现的过程中都会添加一个CheckBox位移匀速渐变的动画效果。 需要说明的是,虽然布局文件中CheckBox原本并没有onCheckedChanged属性,但是CheckBox类有对应的属性,并且设有setOnCheckedChanged方法来设置该属性,所以也是可以进行绑定的。 按照惯例,给出Demo源码:https://github.com/sunrongxin7666 欢迎大家指正批评 需要说明的是,由于篇幅的限制,还有动态绑定和在RecyclerView中使用没有介绍,如果感兴趣请读者参考一下两篇文章,基于以上的内容,相信读者不会很难理解。Android DataBinding完全解析精通 Android Data Binding

优秀的个人博客,低调大师

Android 资源溢出崩溃轻松解

一、资源溢出是什么? 毫无疑问,应用的运行需要占用系统的资源。其中最为人所熟知的资源是内存,内存溢出便是耳熟能详的OOM。 常见的简单OOM一般可以通过堆栈来解决,如Java OOM,一部分可以直接从堆栈中看到哪里使用了多大内存导致了内存溢出,复杂一些的Java OOM,则可以使用其他分析工具来进行处理。但如果堆栈里看不出来呢?或者它不是Java崩溃呢? java.lang.OutOfMemoryError:Failedtoallocatea3237132byteallocationwith1612328freebytesand1574KBuntilOOM 比如下面这样的Native崩溃,堆栈全是系统堆栈,不花时间去研究就很难确定此崩溃的原因(事实上这个崩溃也是一个OOM)。尤其是,我们并不能说这是系统代码的问题。 接下来本文将会介绍,对于这类崩溃如何进行识别、以及解决。 二、内存溢出(俗称OOM) 如下case: Signal6(SIGABRT),Code-1(SI_QUEUE) #00pc000604de/apex/com.android.runtime/lib/bionic/libc.so(abort+165) #01pc0003606d/system/lib/libc++.so(abort_message+88) #02pc000361f1/system/lib/libc++.so(_ZL28demangling_terminate_handlerv+160) #03pc00045e4b/system/lib/libc++.so(_ZSt11__terminatePFvvE+2) #04pc00045653/system/lib/libc++.so(_ZN10__cxxabiv1L12failed_throwEPNS_15__cxa_exceptionE+12) #05pc000455b5/system/lib/libc++.so(__cxa_throw+72) #06pc00047c6d/system/lib/libc++.so(_Znwj+52) #07pc0000132b/system/lib/libbinderthreadstate.so(_ZNSt3__15dequeIN7android18IPCThreadStateBase9CallStateENS_9allocatorIS3_EEE19__add_back_capacityEv+186) #08pc0000120b/system/lib/libbinderthreadstate.so(_ZNSt3__15dequeIN7android18IPCThreadStateBase9CallStateENS_9allocatorIS3_EEE12emplace_backIJRS3_EEES8_DpOT_+52) #09pc0000115f/system/lib/libbinderthreadstate.so(_ZN7android18IPCThreadStateBase16pushCurrentStateENS0_9CallStateE+18) #10pc0003b901/system/lib/libbinder.so(_ZN7android14IPCThreadState14executeCommandEi+608) #11pc0003b5db/system/lib/libbinder.so(_ZN7android14IPCThreadState20getAndExecuteCommandEv+98) #12pc0003bb7b/system/lib/libbinder.so(_ZN7android14IPCThreadState14joinThreadPoolEb+38) #13pc00054de5/system/lib/libbinder.so(_ZN7android10PoolThread10threadLoopEv+12) #14pc0000d96f/system/lib/libutils.so(_ZN7android6Thread11_threadLoopEPv+210) #15pc00080eb5/system/lib/libandroid_runtime.so(_ZN7android14AndroidRuntime15javaThreadShellEPv+88) #16pc000ab3dd/apex/com.android.runtime/lib/bionic/libc.so(_ZL15__pthread_startPv+20) #17pc00061989/apex/com.android.runtime/lib/bionic/libc.so(__start_thread+30) 特征很明显,堆栈全是系统代码(/system/lib/xxx)。 这时候 无法一眼看出代码问题,那么就 可以怀疑下内存原因。 2.1 崩溃原因 众所周知,32位CPU寻址范围最大可以到2的32次方 = 4GB,其实就是 32位操作系统最大支持 4G内存。 如果你试图装过系统就会明白,32位操作系统下,内存不可能达到4G以上,一般会是3G左右。 为什么是3G?因为还有 1G被系统吃掉了(不一定真的是1G,可多可少但不会差的远),它们用于操作系统内核相关的运作,如下图。 这里直接 总结重点: 32位的App在 32位的手机操作系统上使用 超过3G的内存,极大概率会发生 Native崩溃; 32位的App在 64位的手机操作系统上使用 超过4G的内存,极大概率会发生 Native崩溃; 其中前者容易理解,1G被系统吃了,就剩下了3G;后者是因为64位手机上,系统是64位的,所以不需要跟App抢那4G空间。 至于64位App,可用内存已经突破天际(所以开发64位app将会减少大量Native崩溃)…… 几种主流内存占用类型,可以文末会给出一个总结。 需要注意,这里提到的内存,均为 虚拟内存(可以回忆回忆学校学的操作系统知识,网上搜索瞅瞅)。 2.2 定位解决 这里需要用到的工具为应用性能监控全链路版(APMPlus),APMPlus是字节跳动应用开发套件MARS下的性能监控产品,通过先进的数据采集与监控技术,为企业提供全链路的应用性能监控服务,解决企业对各端监控的需求。具备非侵入式监控、丰富的异常现场还原能力,助力企业提升异常问题排查与解决的效率、优化应用品质,以降低成本提高收入。 经过多年技术积累、亿级用户验证,APMPlus 集崩溃监控、上报、分析、归因于一体,可以轻松定位各种线上疑难杂症,更有超详细 性能、卡顿、打点等全流程监控处理工具,覆盖近乎一切线上问题的处理。并拥有多个外部客户的实践,如:虎扑、作业帮、甄云科技等,为企业和开发者提供 一站式APM服务。 我们直接在 A PMPlus 平台中查看崩溃,点击“ Native 信息 -> Maps详情”,查看 虚拟内存占用。 一眼看出,这个内存占用明显接近上一节中提到的 内存占满的阈值(32App在64位设备上最多使用4G内存)!此时基本可以确认,该崩溃为内存占满导致的Native崩溃,即Native OOM。 知道是Native OOM就完了? 再点一个按钮,直接告诉你怎么解决:“ Native 信息 -> Maps智能归类”,查看 虚拟内存占用分布。 我们可以看到,这里直接提示出三个地方占用的虚拟内存最多,分别是Java runtime、Thread、Files;其中 Thread占用最多,高达2.59GB! 直接根据提示,逐级展开内存占用最多的条目: 立即破案:doTestThread 线程过多导致虚拟内存占满!接下来只需要去代码里看,哪里创建的这个线程,便可进行问题解决。 类似的,一旦在崩溃中发现Maps智能归类中给出的任意一个条目过高,都可以确认出Native OOM的原因;假如发现Files条目占用内存达到了2G,那么只需根据内存名即可确认什么文件占用内存多,从而进行问题定位解决。 其中由于 “ Java runtime”条目占用起点较高,其内包含Java堆内存等虚拟机自用区域,基本上固定占用1G上下,且一般情况下其占用不会受我们的代码控制,所以需要注意不要被它混淆了视线, 优先关注其他条目即可。 另外,Thread内存占用过多且需要查看线程的详细信息时,可以在“Native信息 -> 线程状态”中查看。 注:不同App下,虚拟内存分布的结果都有不同,具体分析需联系自身App正常情况下的内存分布来确认问题。 2.3 内存类型简要解释 ApmInsight平台当前的内存分类方式: Java runtime:安卓系统Java虚拟机占用,一般App默认会占用1G以上,可降低关注优先级 Native Heap:C代码使用的堆内存大小,如malloc调用分配的内存等,都会在这里体现; Thread:线程使用的内存大小,默认情况下每个线程启动后(Java、Native均如此)便会占用1M内存 Files:映射入内存中的文件,一般由C代码中调用mmap直接加载文件到内存里,Java中使用FileInputStream不会在这里体现 Devices:设备相关内存使用 nameless:部分没有名字的未知内存使用 Other:其他未识别内存 三、FD溢出 如下case: Signal6(SIGABRT),Code-1(SI_QUEUE) abortmessage:'Couldnotmakewakeeventfd:Toomanyopenfiles' #00pc000604de/apex/com.android.runtime/lib/bionic/libc.so(abort+165) #01pc00005a95/system/lib/liblog.so(__android_log_assert+176) #02pc000100bf/system/lib/libutils.so(_ZN7android6LooperC2Eb+218) #03pc000d3c51/system/lib/libandroid_runtime.so(_ZN7android18NativeMessageQueueC1Ev+112) #04pc000d424d/system/lib/libandroid_runtime.so(_ZN7androidL34android_os_MessageQueue_nativeInitEP7_JNIEnvP7_jclass+12) #05pc002920e7/system/framework/arm/boot-framework.oat(art_jni_trampoline+94) #06pc000d7bc5/apex/com.android.runtime/lib/libart.so(art_quick_invoke_stub_internal+68) ...... #30pc003afb7f/apex/com.android.runtime/lib/libart.so(_ZN3art6Thread14CreateCallbackEPv+1018) #31pc000ab3dd/apex/com.android.runtime/lib/bionic/libc.so(_ZL15__pthread_startPv+20) #32pc00061989/apex/com.android.runtime/lib/bionic/libc.so(__start_thread+30) 同样的,堆栈基本无意义,但有一句看起来能看懂的“Too many open files”。 3.1 崩溃原因 FD即文件描述符(File Descriptor),打开一个文件就占用一个。 看起来没什么的,大家读写文件都是常规操作,一个App产生千八百个文件不过分吧。 但是,系统会 限制单个App打开的 FD 个数! 该数字在部分低版本安卓机上 一般为1024,也就是你打开1024个FD后,就不能再打开了,有时候就会因此 产生Native崩溃。 3.2 定位解决 直接点开“ Native 信息 -> FD 归类”,来确认是不是 FD 过多导致的崩溃。 很明显,确实可以看到使用的FD过多,达到了3万以上。向下滚动可以直接看到App在运行时到底打开了哪些文件,只要找到打开的文件名,便能轻松解决此类崩溃。 四、总结 本文提到的两种崩溃类型,本质上都是 系统、应用资源不足下产生的。 资源不足实际上并不会直接导致崩溃,但是会 使某些系统调用返回出错,如open打开文件失败返回无效值、malloc分配内存失败返回无效值等。这些返回的无效值如果在使用时未做合理容错判断,则会 引起如空指针等这样的代码错误。 更多的崩溃问题归类及解析,将在应用性能监控全链路版(APMPlus)上及后续的文章中进行补充。 如果还未接入使用 应用性能监控全链路版(APMPlus),也可以立刻开始进行免费试用,目前 APMPlus面向新用户提供 试用30 天的限时免费服务。其中包含 App 监控、Web 监控、Server 监控、小程序 监 控,App 监控和 Web 监控各500 万条事件量, Server 与小程序监控限时不限量。

优秀的个人博客,低调大师

Python——Web.py詳解

ubuntu安裝Web.py 1 sudo pip install web.py 測試代碼: 1 import web 2 3 urls = ( 4 '/(.*)','hello' 5 ) 6 app = web.application(urls,globals()) 7 8 class hello: 9 def GET(self,name): 10 if not name: 11 name = 'world' 12 return 'hello,'+name+'!' 13 14 if __name__=='__main__': 15 app.run() 类似于flask模板,这里也可以使用文件读写返回html内容: 1 import web 2 3 urls = ( 4 '/(.*)','hello' 5 ) 6 app = web.application(urls,globals()) 7 8 class hello: 9 def GET(self,name): 10 return open(r'index.html','r').read() 11 12 if __name__=='__main__': 13 app.run() URL映射: 完全匹配: /index 模糊匹配 /post/\d+ 带组匹配 /post/(\d+) 1 import web 2 3 urls = ( 4 '/blog/\d+','blog', 5 '/index','index', 6 '/(.*)','hello', 7 ) 8 app = web.application(urls,globals()) 9 10 class index: 11 def GET(self): 12 return 'index' 13 14 class blog: 15 def GET(self): 16 return 'blog' 17 18 class hello: 19 def GET(self,name): 20 return 'hello' 21 22 if __name__=='__main__': 23 app.run() 请求处理: 请求参数获取 : web.input() hello.html源码: 1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>User Login</title> 7 </head> 8 9 <body> 10 <div> 11 <h6>User Login</h6> 12 </div> 13 <form action="/blog/123" method="POST"> 14 <h6>用戶名:</h6> 15 <input type="text" name="username"><br> 16 <h6>密碼:</h6> 17 <input type="password" name="password"><br> 18 <input type="submit" name="submit"><br> 19 </form> 20 </body> 21 </html> app.py源码: 1 import web 2 3 urls = ( 4 '/blog/\d+','blog', 5 '/index','index', 6 '/(.*)','hello', 7 ) 8 app = web.application(urls,globals()) 9 10 class index: 11 def GET(self): 12 r = web.input() 13 return r 14 15 class blog: 16 def POST(self): 17 r = web.input() 18 return r 19 20 class hello: 21 def GET(self,name): 22 return open(r'hello.html','r').read() 23 24 if __name__=='__main__': 25 app.run() 运行结果: 请求头获取 : web.ctx.env app.py代码如下,hello.html代码如上相同 1 import web 2 3 urls = ( 4 '/blog/\d+','blog', 5 '/index','index', 6 '/(.*)','hello', 7 ) 8 app = web.application(urls,globals()) 9 10 class index: 11 def GET(self): 12 return web.ctx.env 13 14 class blog: 15 def POST(self): 16 r = web.input() 17 return r 18 19 class hello: 20 def GET(self,name): 21 return open(r'hello.html','r').read() 22 23 if __name__=='__main__': 24 app.run() 运行结果: 响应处理: 模板文件读取:render.index("参数") 在py文件同目录下要创建templates文件夹,存放模板。 index是指定模板的名称,参数是html中是所需参数。 1 import web 2 3 render = web.template.render("templates") 4 5 urls = ( 6 '/blog/\d+','blog', 7 '/index','index', 8 '/(.*)','hello', 9 ) 10 app = web.application(urls,globals()) 11 12 class hello: 13 def GET(self,name): 14 return render.hello() 15 16 if __name__=='__main__': 17 app.run() 采用模板就可以不用之前所用的open(r'hello.html','r').read()了。 结果数据获取:model.select("sql") 参考我之前的文章http://www.cnblogs.com/LexMoon/p/Flask_6.html URL跳转 :web.seeother("/") 1 import web 2 3 urls = ( 4 '/(.*)','hello', 5 ) 6 app = web.application(urls,globals()) 7 8 class hello: 9 def GET(self,name): 10 return web.seeother('http://www.cnblogs.com/LexMoon/') 11 12 if __name__=='__main__': 13 app.run()

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册