android_问题总结

dalvik

       dalvik与jvm的区别: dalvik基于寄存器,jvm基于栈(stack,执行.class);一个应用对应一个dalvik,每一个 Dalvik应用作为一个独立的Linux进程执行执行dex字节码文件,独立的进程空间。

dalvik作用:完成对象生命周期管理、堆栈管理、线程管理、安全异常管理、垃圾回收

libdvm.so(核心内容实现库)

优势:  1、在编译时提前优化代码而不是等到运行时

    2、 虚拟机很小,使用的空间也小;被设计来满足可高效运行多种虚拟机实例。

    3、常量池已被修改为只使用32位的索引,以简化解释器

dex整合多个.class文件减少整体文件尺寸(多class冗余),减少I/O,提高类的查找速度

多文件对应多常量池,dex文件一个常量池管理

Zygote是虚拟机实例的孵化器(fork())(复制,减少创建,加载系统库,资源的时间)

Zygote进程是在系统启动时产生的,它会完成虚拟机的初始化,库的加载,预置类库的加载和初始化等等操作

Dalvik两件事:处理JNI,提供Thread

在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器转换为机器码,这会拖慢应用的运行效率

JIT(Just-In-Time),用来在运行时动态地将执行频率很高的dex字节码翻译成本地机器码,然后再执行。通过JIT,就可以有效地提高Dalvik虚拟机的执行效率重新运行的时候,都要做重做这个翻译工作

安装时dex文件生成odex文件。这个过程由安装服务PackageManagerService请求守护进程installd来执行的

ART

ART(android runtime) 应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。

ART优点:

1、系统性能的显著提升。

2、应用启动更快、运行更快、体验更流畅、触感反馈更及时。

3、更长的电池续航能力。

4、支持更低的硬件。

ART缺点:

1、更大的存储空间占用,可能会增加10%-20%。

2、更长的应用安装时间,总的来说ART的功效就是“空间换时间”。

art与dalvik区别

1:Dalvik主要是完成对象生命周期管理,堆栈管理,线程管理,安全和异常管理,以及垃圾回收等等重要功能。

   2:Dalvik负责进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。

   3:不同于Java虚拟机运行java字节码,Dalvik虚拟机运行的是其专有的文件格式Dex

   4:dex文件格式可以减少整体文件尺寸,提高I/o操作的类查找速度。

   5:odex是为了在运行过程中进一步提高性能,对dex文件的进一步优化。

   6:所有的Android应用的线程都对应一个Linux线程,虚拟机因而可以更多的依赖操作系统的线程调度和管理机制

   7:有一个特殊的虚拟机进程Zygote,他是虚拟机实例的孵化器。它在系统启动的时候就会产生,它会完成虚拟机的初始化,库的加载,预制类库和初始化的操作。如果系统需要一个新的虚拟机实例,它会迅速复制自身,以最快的数据提供给系统。对于一些只读的系统库,所有虚拟机实例都和Zygote共享一块内存区域。

Android中mvc

android鼓励弱耦合和组件的重用,在android中mvc的具体体现如下:

    1.视图层(view):一般采用xml文件进行界面的描述,使用的时候可以非常方便的引入,当然,如何你对android了解的比较的多了话,就一定 可以想到在android中也可以使用javascript+html等的方式作为view层,当然这里需要进行java和javascript之间的通 信,幸运的是,android提供了它们之间非常方便的通信实现。

 2.控制层(controller):android的控制层的重 任通常落在了众多的acitvity的肩上,这句话也就暗含了不要在acitivity中写代码,要通过activity交割model业务逻辑层处理, 这样做的另外一个原因是android中的acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。

 3.模型层(model):对数据库的操作、对网络等的操作都应该在model里面处理,当然对业务计算等操作也是必须放在的该层的。

Activity
生命周期

    onCreate: 在这里创建界面,做一些数据 的初始化工作

 onStart: 到这一步变成用户可见不可交互的

    onResume:变成和用户可交互 的,(在activity 栈系统通过栈的方式管理这些个Activity的最上面,运行完弹出栈,则回到上一个Activity)

 onPause: 到这一步是可见但不可交互的,系统会停止动画 等消耗CPU 的事情从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候你的程序的优先级降低,有可能被系统收回。在这里保存的数据,应该在

 onstop: 变得不可见,被下一个activity覆盖了

   onDestroy: 这是activity被干掉前最后一个被调用方法了,可能是外面类调用finish方法或者是系统为了节省空间将它暂时性的干掉

Activity变窗口

清单文件activity中android :theme="@android :style/Theme.Dialog"

android:theme="@android :style/Theme.Translucent"//如果是作半透明的效果:

<item name="android:windowBackground">@android  :color/transparent</item>窗口背景色

<item name="android:windowFrame">@null</item>DialogwindowFrame框为无

<item name="android:windowIsFloating">true</item>是否浮现在activity之上

<item name="android:windowIsTranslucent">true</item>窗口是否半透明——是(与第一条配合使用)

<item name="android:windowAnimationStyle">@android  :style/Animation.Dialog</item>窗口弹出效果

<item name="android:backgroundDimEnabled">true</item> 是否允许背景模糊

<item name="android:windowContentOverlay">@null</item>这个不设置的话,可能会出现边框黑线

横竖屏切换生命周期问题

    1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次

    2、设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次

   3、设置Activity的android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

Activity四种启动模式

1.Standared(默认)激活该Activity,则会向任务栈中加入新创建的实例,退出Activity则会在任务栈中销毁该实例

       2.SingleTop这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶,如果处于栈顶则无需重新创建新的实例,会重用已存在的实例,否则会在任务栈中创建新的实例。

       3.SingleTask如果任务栈中存在该模式的Activity实例,则把栈中该实例以上的Activity实例全部移除,调用该实例的newInstance()方法重用该Activity,使该实例处於栈顶位置,否则就重新创建一个新的Activity实例。

       4.SingleInstance当该模式Activity实例在任务栈中创建后,只要该实例还在任务栈中,即只要激活的是该类型的Activity,都会通过调用实例的newInstance()方法重用该Activity,此时使用的都是同一个Activity实例,它都会处于任务栈的栈顶。此模式一般用于加载较慢的,比较耗性能且不需要每次都重新创建的Activity

Activty启动流程

启动新的Activity需要借助于应用程序框架层的ActivityManagerService服务进程  ActivityManagerService和ActivityStack位于同一个进程中,ApplicationThread和ActivityThread位于另一个进程中,  ActivityManagerService是负责管理Activity的生命周期的,ActivityManagerService还借助ActivityStack是来把所有的 Activity按照后进先出的顺序放在一个堆栈中,ActivityThread来表示应用程序的主进程,而每一个ActivityThread都包含有一 个ApplicationThread实例,它是一个Binder对象,负责和其它进程进行通信

     1. 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口; 

     2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;

     3. ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;

     4. ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;

    5. 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;

    6. ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;

    7. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。

onSaveInstanceState() 和 onRestoreInstanceState()在什么时候被调用:

    Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState()才会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
    另外,当屏幕的方向发生了改变, Activity会被摧毁并且被重新创建,如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建后恢复缓存的数据。可以重写Activity的 onSaveInstanceState() 和 onRestoreInstanceState()方法。

android view的刷新:

    Android中对View的更新有很多种方式,使用时要区分不同的应用场合。我感觉最要紧的是分清:多线程和双缓冲的使用情况。
    1.不使用多线程和双缓冲
     这种情况最简单了,一般只是希望在View发生改变时对UI进行重绘。你只需在Activity中显式地调用View对象中的invalidate()方法即可。系统会自动调用 View的onDraw()方法。
    2.使用多线程和不使用双缓冲
    这种情况需要开启新的线程,新开的线程就不好访问View对象了。强行访问的话会报:android.view.ViewRoot$CalledFromWrongThreadException:Only the originalthread that created a view hierarchy can touch its views.
    这时候你需要创建一个继承了android.os.Handler的子类,并重写handleMessage(Messagemsg)方法。android.os.Handler是能发送和处理消息的,你需要在Activity中发出更新UI的消息,然后再你的Handler(可以使用匿名内部类)中处理消息(因为匿名内部类可以访问父类变量,你可以直接调用View对象中的invalidate()方法 )。也就是说:在新线程创建并发送一个Message,然后再主线程中捕获、处理该消息。
    3.使用多线程和双缓冲
    Android中SurfaceView是View的子类,她同时也实现了双缓冲。你可以定义一个她的子类并实现SurfaceHolder.Callback接口。由于实现SurfaceHolder.Callback接口,新线程就不需要android.os.Handler帮忙了。SurfaceHolder中lockCanvas()方法可以锁定画布,绘制玩新的图像后调用unlockCanvasAndPost(canvas)解锁(显示),还是比较方便得。

Application类的作用:

    如果想在整个应用中使用全局变量,在java中一般是使用静态变量,public类型;而在android中如果使用这样的全局变量就不符合Android的框架架构,但是可以使用一种更优雅的方式就是使用Application context。 
    首先需要重写Application,主要重写里面的onCreate方法,就是创建的时候,初始化变量的值。然后在整个应用中的各个文件中就可以对该变量进行操作了。 
    启动Application时,系统会创建一个PID,即进程ID,所有的Activity就会在此进程上运行。那么我们在Application创建的时候初始化全局变量,同一个应用的所有Activity都可以取到这些全局变量的值,换句话说,我们在某一个Activity中改变了这些全局变量的值,那么在同一个应用的其他Activity中值就会改变

android view,surfaceview,glsurfaceview的区别:

    SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放以及Camera摄像头一般均使用SurfaceView
SurfaceView和View最本质的区别在于,surfaceView是在一个新起的单独线程中可以重新绘制画面而View必须在UI的主线程中更新画面。 
    那么在UI的主线程中更新画面 可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。 
当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要surfaceView中thread处理,一般就需要有一个event queue的设计来保存touch event,这会稍稍复杂一点,因为涉及到线程同步。 


1.      listview 优化方式

a)        重用convertView

b)       viewholder

c)        static class viewHolder

d)       在列表里面有图片的情况下,监听滑动不加载图片

e)        多个不同布局,可以创建不同的viewHolder和convertView进行重用

 

2.      listview 展示数据的形式

a)        sqlite数据库取

b)       xml中解析

c)        网络取

 

3.      ipc

a)        进程间通信主要包括管道,系统IPC(Inter-Process Communication, 进程间通信)(包括消息队列,信号,共享存储),套接字(Socket)

b)       目的包括:数据传输:一个进程需要将他的数据发送给另一个进程,发送的数据量在一个字节到几兆字节之间

c)        共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到

d)       通知时间:一个进程需要向另一个或一组进程发送消息,通知他们发生了某种事件(如进程终止时要通知父进程)

e)        资源共享:多个进程之间共享同样的资源,内核提供锁和同步机制

f)        进程控制:有些进程希望完全控制另一个进程的执行(Debug进程),此时控制进程希望能够拦截另一个进程的所有缺陷和异常,并能够及时知道他得状态改变

g)       进程通过与内核及其它进程之间的互相通信来协调他们的行为Linux支持多种进程间通信

 

4.      Parcel的机制

a)        整个读写全是在内存中进行,效率比java序列化中使用外部存储器会高很多

b)       读写时是4字节对齐

c)        如果预分配的空间不够时,会一次多分配50%

d)       对于普通数据,使用的是mData内存地址,对于IBinder类型的数据以及FileDescriptor使用的是mObjects内存地址。后者是通过flatten_binder()和unflatten_binder()实现的,目的是反序列化时读出的对象就是原对象而不用重新new一个新对象

e)        代码

mBundle.putParcelable(key,mPolice);

mIntent.putExtras(mBundle);

f)        实体类

public classPolice implements Parcelable {
    private String name;
    private int workTime;
    public String getName() {
        returnname;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getWorkTime() {
        returnworkTime;
    }
    public void setWorkTime(int workTime) {
        this.workTime =workTime;
    }
    public static final Parcelable.Creator<Police>CREATOR =newCreator<Police>() {
   
        @Override
        public PolicecreateFromParcel(Parcel source) {
            Policepolice =newPolice();
            police.name =source.readString();
            police.workTime= source.readInt();
            returnpolice;
        }
        @Override
        public Police[] newArray(intsize) {
            returnnewPolice[size];
        }
    };
    @Override
    public int describeContents() {
        return0;
    }
    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(name);
        parcel.writeInt(workTime);
    }
}

 

5.      内存优化

1、使用优化过的数据容器。 在Android framework下,建议使用优化过的数据容器比如:SparseArray,SparseBooleanArray,LongSparseArray。通用的HashMap实现的内存使用率非常的低,因为他需要为每一个mapping创建一个分离的entry object。另外,SparseArray类避免了系统对有些key的自动装箱,因而带来了更高的效率。

2、注意内存的开销。[size=12.800000190734863px]  注意你使用的语言和第三方库的成本和开销,要自始至终的将这些因素考虑在你的程序设计中。通常,有些事情表面上看着没什么问题但实际上的开销让人惊叹。比如:

   ·枚举相对于静态常量来说,需要两倍甚至更多的内存。你应该完全避免在Android中使用枚举。

   [size=12.666666984558105px]·每一个在java中的类(包括匿名内部类)使用大约500 bytes的代码量。    ·每一个类的实例拥有12-16 bytes的RAM消耗。    ·放置一个单独的实体到HashMap中,一个额外加的实体对象分配需要花费32 bytes。

[size=12.666666984558105px]

3、关于代码的抽象  抽象是一个好的编程的基础,因为抽象可以提高代码的灵活性和可维护性。然而抽象也带来了一定的花销,一般情况下,他们有更多的代码需要执行,需要更多的时间和更多RAM来将这些代码映射到内存中。因此,如果你的抽象不能带来巨大的好处,你就应该割掉你的赘肉。

4、避免依赖注入框架  虽然注入框架给我们带来了很多方便,但是在使用这些框架的时候,框架需要花费很多时间来扫描我们自己写的代码,甚至会将哪些你根本不需要的东西也加载到内存中。

5、小心的使用扩展库  很多扩展库的代码不是针对手机环境开发的,可能在用到移动客户端的时候会导致很低的效率。因此在使用之前,需要评估一下其占用内存的大小。     即使库针对手机开发,也会有潜在的危险,因为每一个Library做的事情不尽相同。比如,一个Library使用nano protobufs而另一个使用micro protobufs。现在,在你的app中就有两个protobuf。类似情况经常发生。

6、使用混淆器移除不必要的代码  ProGuard工具通过移除无用代码,使用语意模糊来保留类,字段和方法来压缩,优化和混淆代码。可以使你的代码更加完整,更少的RAM 映射页。

7、使用多个进程(注意是process 不是 thread ok?) 如果这适合你的app,可能帮助你管理你的app的内存就是将你的app多个部分分配到多个进程中。该技术必须小心使用并且大多数应用不应该运行在多个进程下。这个技术的主要应用是后台工作跟天台工作一样重要的情况下。典型应用就是:当音乐播放器从服务器下载并长时间播放音乐,一般将其分为两个进程:一个是UI,另一个位置后台服务的运行。like this:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值