Android实例剖析笔记(三)

简介:   Activity的生命周期Activity类中有许多onXXX形式的函数可以重载,比如onCreate,onStart,onStop,onPause,那么它们的调用顺序到底是如何的呢?下面就通过一个实验来进行分析。

  Activity的生命周期

Activity类中有许多onXXX形式的函数可以重载,比如onCreate,onStart,onStop,onPause,那么它们的调用顺序到底是如何的呢?下面就通过一个实验来进行分析。在做这个实验之前,我们先得知道如何在Android中进行Log输出的。我们要使用的是android.util.log类,这个类相当的简单易用,因为它提供的全是一些静态方法:

Log.v(String tag, String msg);         // VERBOSE
Log.d(String tag, String msg);        // DEBUG    
Log.i(String tag, String msg);         // INFO
Log.w(String tag, String msg);      // WARN
Log.e(String tag, String msg);       // ERROR

  前面的tag是由我们定义的一个标识,一般可以用“类名_方法名“来定义。要在Eclipse中查看输出的log信息,需要打开Logcat(WindowàShow ViewàotheràAndroidàLogCat即可打开)

  实验一

  我们要做的实验非常简单,就是有两个Activity(我这里分别叫做frmLogin和hello2),t它们各自有一个button,可以从第一个跳到第二个,也可以从第二个跳回到第一个。配置文件AndroidManifest.xml非常简单,第二个activity并没有多余的信息需要指定。

     < application  android:icon ="@drawable/icon"  android:label ="@string/app_name" >
        
< activity  android:name =".frmLogin"
                  android:label
="@string/app_name" >
            
< intent-filter >
                
< action  android:name ="android.intent.action.MAIN"   />
                
< category  android:name ="android.intent.category.LAUNCHER"   />
            
</ intent-filter >
        
</ activity >
         
< activity  android:name ="hello2"  android:label ="@string/app_name" >
        
</ activity >
</ application >

  第一个activity的代码如下:

public   class  frmLogin  extends  Activity 
{
    
private   final   static  String TAG  =   " FrmLogin " ;

    
/**  Called when the activity is first created.  */
    @Override
    
public   void  onCreate(Bundle savedInstanceState)
    {
        
super .onCreate(savedInstanceState);
        Log.v(TAG,
" onCreate " );
        setContentView(R.layout.main);
        
this .setViewOneCommand();
    }

    
public   void  setViewOneCommand()
    {
            Button btn 
=  (Button)findViewById(R.id.btnGo);
            btn.setOnClickListener(
new  View.OnClickListener()
            {
                
public   void  onClick(View v)
                {
                    Intent intent 
=   new  Intent();
                    intent.setClass(frmLogin.
this , hello2. class );
                    startActivity(intent);
                    finish();            
                }
            });       
            Button btnExit
= (Button)findViewById(R.id.btnExit);
            btnExit.setOnClickListener(
new  View.OnClickListener()
            {
                
public   void  onClick(View v)
                {
                    frmLogin.
this .finish();
                }
            });    
        } 
    
    @Override
    
protected   void  onDestroy() 
    {
        
super .onDestroy();
        Log.v(TAG,
" onDestroy " );
    }

    @Override
    
protected   void  onPause()
    {
        
super .onPause();
        Log.v(TAG,
" onPause " );

    }

    @Override
    
protected   void  onRestart() 
    {
        
super .onRestart();
        Log.v(TAG,
" onRestart " );
    }

    @Override
    
protected   void  onResume() 
    {
        
super .onResume();
        Log.v(TAG,
" onResume " );
    }

    @Override
    
protected   void  onStart() 
    {
        
super .onStart();
        Log.v(TAG,
" onStart " );
    }

    @Override
    
protected   void  onStop() 
    {
        
super .onStop();
        Log.v(TAG,
" onStop " );
    } 
}

  我在每个onXXX方法中都加入了log方法,值得注意的一点是按钮单击事件处理函数中,在最后我调用了finish();待会我会将此行注释掉进行对比实验。第二个activity的代码和第一个完全一样,只是将setClass的两个参数反一下,这样就可以简单地实现在两个Activity界面中来回切换的功能了。下面开始实验,第一个实验室从第一个activity跳到第二个activity(此时第一个关闭),然后从第二个跳回第一个(此时第二个关闭)。运行后观察LogCat,得到如下画面:

  然后来进行第二个实验,对代码进行调整,我们把第一个activity中的finish()注释掉,从第一个activity跳到第二个(此时第一个没有关闭),然后第二个直接关闭(则第一个会重新来到前端),结果如图所示,可以看出调用了FrmLoginonRestart而不是onStart,因为第一个activity只是stop,而并没有被destory掉。

  前面两个实验都很好理解,可第三个实验就让我不明白了,过程如下:从第一个activity跳到第二个activity(此时第一个不关闭),然后第二个跳回第一个(此时第二个也不关闭),然后第一个再跳回第二个(此时第一个不关闭),照上面来推断,应该还是会调用onRestart才对,可实际上它调用的却是onStart,why???


  这里先不讨论例子了,来看看官方文档对Activity生命周期的介绍。

  1. Android用Activity Stack来管理多个Activity,所以呢,同一时刻只会有最顶上的那个Activity是处于active或者running状态。其它的Activity都被压在下面了。

  2. 如果非活动的Activity仍是可见的(即如果上面压着的是一个非全屏的Activity或透明的Activity),它是处于paused状态的。在系统内存不足的情况下,paused状态的Activity是有可被系统杀掉的。只是不明白,如果它被干掉了,界面上的显示又会变成什么模样?看来下回有必要研究一下这种情况了。

  3. 几个事件的配对可以比较清楚地理解它们的关系。Create与Destroy配成一对,叫entrie lifetime,在创建时分配资源,则在销毁时释放资源;往上一点还有Start与Stop一对,叫visible lifetime,表达的是可见与非可见这么一个过程;最顶上的就是Resume和Pause这一对了,叫foreground lifetime,表达的了是否处于激活状态的过程。

  4. 因此,我们实现的Activity派生类,要重载两个重要的方法:onCreate()进行初始化操作,onPause()保存当前操作的结果。除了Activity Lifecycle以外,Android还有一个Process Lifecycle的说明:

在内存不足的时候,Android是会主动清理门户的,那它又是如何判断哪个process是可以清掉的呢?文档中也提到了它的重要性排序:

  1. 最容易被清掉的是empty process,空进程是指那些没有Activity与之绑定,也没有任何应用程序组件(如Services或者IntentReceiver)与之绑定的进程,也就是说在这个process中没有任何activity或者service之类的东西,它们仅仅是作为一个cache,在启动新的Activity时可以提高速度。它们是会被优先清掉的。因此建议,我们的后台操作,最好是作成Service的形式,也就是说应该在Activity中启动一个Service去执行这些操作。

  2. 接下来就是background activity了,也就是被stop掉了那些activity所处的process,那些不可见的Activity被清掉的确是安全的,系统维持着一个LRU列表,多个处于background的activity都在这里面,系统可以根据LRU列表判断哪些activity是可以被清掉的,以及其中哪一个应该是最先被清掉。不过,文档中提到在这个已被清掉的Activity又被重新创建的时候,它的onCreate会被调用,参数就是onFreeze时的那个Bundle。不过这里有一点不明白的是,难道这个Activity被killed时,Android会帮它保留着这个Bundle吗?

  3. 然后就轮到service process了,这是一个与Service绑定的进程,由startService方法启动。虽然它们不为用户所见,但一般是在处理一些长时间的操作(例如MP3的播放),系统会保护它,除非真的没有内存可用了。

  4. 接着又轮到那些visible activity了,或者说visible process。前面也谈到这个情况,被Paused的Activity也是有可能会被系统清掉,不过相对来说,它已经是处于一个比较安全的位置了。

  5. 最安全应该就是那个foreground activity了,不到迫不得已它是不会被清掉的。这种process不仅包括resume之后的activity,也包括那些onReceiveIntent之后的IntentReceiver实例。在Android Application的生命周期的讨论中,文档也提到了一些需要注意的事项:因为Android应用程序的生存期并不是由应用本身直接控制的,而是由Android系统平台进行管理的,所以,对于我们开发者而言,需要了解不同的组件Activity、Service和IntentReceiver的生命,切记的是:如果组件的选择不当,很有可能系统会杀掉一个正在进行重要工作的进程。

  自定义控件

      这里主要介绍下“编辑日志”中使用的一个自定义EditText控件,它的效果如下图:

  主要功能就是在文本语句之间绘制分割线。

   public   static   class  LinedEditText  extends  EditText 
    {
        
private  Rect mRect;
        
private  Paint mPaint;
        
//  we need this constructor for LayoutInflater
         public  LinedEditText(Context context, AttributeSet attrs) 
        {
            
super (context, attrs);           
            mRect 
=   new  Rect();
            mPaint 
=   new  Paint();
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(
0x800000FF );
        }    
        @Override
        
protected   void  onDraw(Canvas canvas)
        {
            
int  count  =  getLineCount();
      Rect r 
=  mRect;
            Paint paint 
=  mPaint;
            
for  ( int  i  =   0 ; i  <  count; i ++
            {
                
int  baseline  =  getLineBounds(i, r);
                canvas.drawLine(r.left, baseline 
+   1 , r.right, baseline  +   1 , paint);
            }
            
super .onDraw(canvas);
        }
    }

  主要工作就是重载onDraw方法,利用从TextView继承下来的getLineCount函数获取文本所占的行数,以及getLineBounds来获取特定行的基准高度值,而且这个函数第二个参数会返回此行的“外包装”值。再利用这些值绘制这一行的线条。为了让界面的View使用自定义的EditText类,必须在配置文件中进行设置

< view  xmlns:android ="http://schemas.android.com/apk/res/android"
    class
="com.example.android.notepad.NoteEditor$LinedEditText"
    android:id
="@+id/note"
    android:layout_width
="fill_parent"
    android:layout_height
="fill_parent"
    android:background
="@android:color/transparent"
    android:padding
="5dip"
    android:scrollbars
="vertical"
    android:fadingEdge
="vertical"
    android:gravity
="top"
    android:textSize
="22sp"
    android:capitalize
="sentences"
/>

  这里class="com.example.android.notepad.NoteEditor$LinedEditText"就指明了应当使用自定义的LinedEditText类。

目录
相关文章
|
1月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
60 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
22天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
73 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
6月前
|
Android开发
Android应用实例(一)之---有道辞典VZ.0
Android应用实例(一)之---有道辞典VZ.0
42 2
|
3月前
|
JavaScript 前端开发 Java
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
59 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
|
3月前
|
编解码 安全 Ubuntu
Android Selinux 问题处理笔记
这篇文章是关于处理Android系统中SELinux权限问题的笔记,介绍了如何通过分析SELinux拒绝的日志、修改SELinux策略文件,并重新编译部署来解决权限问题,同时提供了一些SELinux的背景知识和实用工具。
60 0
|
4月前
|
API Android开发
Android 监听Notification 被清除实例代码
Android 监听Notification 被清除实例代码
|
5月前
|
安全 Java Android开发
使用Unidbg进行安卓逆向实例讲解
使用Unidbg进行安卓逆向实例讲解
128 2
|
6月前
|
安全 Linux Android开发
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
该文介绍了如何在Linux服务器上交叉编译Android的FFmpeg库以支持HTTPS视频播放。首先,从GitHub下载openssl源码,解压后通过编译脚本`build_openssl.sh`生成64位静态库。接着,更新环境变量加载openssl,并编辑FFmpeg配置脚本`config_ffmpeg_openssl.sh`启用openssl支持。然后,编译安装FFmpeg。最后,将编译好的库文件导入App工程的相应目录,修改视频链接为HTTPS,App即可播放HTTPS在线视频。
107 3
FFmpeg开发笔记(十六)Linux交叉编译Android的OpenSSL库
|
5月前
|
Java API Android开发
技术经验分享:Android源码笔记——Camera系统架构
技术经验分享:Android源码笔记——Camera系统架构
54 0
|
6月前
|
Java 测试技术 开发工具
Android 笔记:AndroidTrain , Lint , build(1),只需一篇文章吃透Android多线程技术
Android 笔记:AndroidTrain , Lint , build(1),只需一篇文章吃透Android多线程技术
下一篇
无影云桌面