Android学习笔记_02 – Service的测试应用

Android学习笔记_02 – Service的测试应用

 

在我的项目里,有些工作需要在后台进行,比如:手机采集到的地理位置信息需要定时上传到远程Web服务器;百度地图离线数据的下载管理等。此类后台应用基本都依赖网络连接质量、长时间运行的特点,所以不能对前台造成影响,比如卡顿、死机等等。所以这几天对Android的Service应用做了一些了解。

在下面的测试工程里,我会以示例来进行相关知识的梳理。测试工程的大体思路为:建立一个前台Activity、一个后台Service;该Service主要作用是提供一个大概1秒运行一次的定时器,为我实战项目的其他定时操作定时器服务。此用法我是参考以前用过的Delphi的TTimer控件的OnTimer事件来处理的。定时器使用的轮训线程采用“Handler +Runnable接口”方式来实现,Runnable接口也是常用的线程处理方法之一。

 

1、 很简单地创建一个基于Service应用。

1.1、       创建工程,生成一个默认的MainActivity类

应用程序名称:TimerService

包名:test.timerservice

Activity名称为:MainActivity

1.2、       修改布局文件

在手机的MainActivity的布局文件activity_main.xml里放置两个按钮,一个为启动服务,一个为停止服务。

                   文件“activity_main.xml”内容为:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/LinearLayout1"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:paddingBottom="@dimen/activity_vertical_margin"

    android:paddingLeft="@dimen/activity_horizontal_margin"

    android:paddingRight="@dimen/activity_horizontal_margin"

    android:paddingTop="@dimen/activity_vertical_margin"

    tools:context=".MainActivity">

 

   <Button

        android:id="@+id/btnStartService"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:onClick="StartService"

        android:text="启动服务"/>

 

   <Button

        android:id="@+id/btnSopService"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:onClick="StopService"

        android:text="停止服务"/>

 

</LinearLayout>

注意文件中的两行:

android:onClick="StartService"

android:onClick="StopService"

在布局文件里定义了按钮的点击事件函数名称,需要在MainActivity.java里定义好该函数后才能起作用。现在可以启动程序,会出现以下界面,当然,现在点击按钮会出现异常的。

 

1.3、       初步编写MainActivity的代码,实现按钮接口,也好避免运行时按钮弹出错误。

文件“”内容为:

package test.timerservice;

 

import android.os.Bundle;

import android.app.Activity;

import android.util.Log;

import android.view.Menu;

import android.view.View;

import android.widget.Toast;

 

public classMainActivity extends Activity {

 

    @Override

    protected void onCreate(BundlesavedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

    }

 

    @Override

    public booleanonCreateOptionsMenu(Menu menu) {

        // Inflate the menu; this adds items to the action bar ifit is present.

        getMenuInflater().inflate(R.menu.main, menu);

        return true;

    }

 

    public void StartService(Viewview){

        Toast.makeText(this,"点击了启动服务按钮!", Toast.LENGTH_SHORT).show();

    }

   

    public void StopService(View view){

        Toast.makeText(this,"点击了停止服务按钮!", Toast.LENGTH_SHORT).show();

    }

 

}

 

        在模拟器里调试运行后,点击按钮,会弹出相应的提示信息。


1.4、       编写服务类代码

下面的代码是一个比较标准的Service的格式,我基本上将基类所有的函数复写入口都预留出来了,便于读者理解和修改。

文件“TimerService.java”内容为:

package test.timerservice;

 

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

 

public classTimerService extends Service {

    private static String TAG = "TimerService";

   

    @Override   

    public void onCreate() {

        super.onCreate(); 

        Log.d(TAG,"onCreate()");

    }        

   

    @Override   

    public void onStart(Intent intent, int startId) {            

        super.onStart(intent,startId);

        Log.d(TAG,"onStart(intent, startId)");

    }   

 

    @Override   

    public void onDestroy() {          

        super.onDestroy();   

        Log.d(TAG,"onDestroy()");

    }   

 

    @Override   

    public IBinder onBind(Intentintent) {               

        Log.e(TAG,"onBind(Intent intent)");                        

        return null;   

    }   

   

    @Override   

    public boolean onUnbind(Intent intent){             

        Log.e(TAG,"onUnbind(Intent intent)");                        

        return super.onUnbind(intent);

    }

 

}

 

1.5、       实现在MainActivity里启动TimerService

在启动一个新建的Service之前,需要对该Service进行注册,注册的方法是在AndroidManifest.xml里加上Service标签,如下图:


现在可以修改MainActivity.java文件里的两个按钮执行函数了:

public voidStartService(View view){

        Toast.makeText(this,"点击了启动服务按钮!", Toast.LENGTH_SHORT).show();

        Intentintent = newIntent(this,TimerService.class);

        startService(intent);

    }

   

    public void StopService(View view){

        Toast.makeText(this,"点击了停止服务按钮!", Toast.LENGTH_SHORT).show();

        Intentintent = newIntent(this,TimerService.class);

        stopService(intent);

    }

运行程序,点击模拟器界面上的“启动服务”、“停止服务”按钮,在LogCat里输出了在代码里实现的几个和生命周期有关的输出信息。说明服务生成、启动、销毁都成功了。

到此就实现了Service的基本功能

 

2.          在服务TimerService里采用“Handler + Runnable接口”方式实现线程

2.1、       修改TimerService.java文件

此处的代码都在TimerService的类里面编写。

1)先定义和实例化一个Handler类对象handler:

private Handler handler = new Handler();

 

2)定义和生成Runnable线程timerThread:

    RunnabletimerThread= newRunnable(){

        public void run(){

            Log.d(TAG,"TimerOnTimer");

            handler.postDelayed(timerThread, 1000);

        } 

    };

上面代码中handler.postDelayed(timerThread, 1000); 实现延时1秒后再运行下一个线程。

 

3)首次使用Handler调用timerThead线程

大家都知道Handler启动线程的语句为:

handler.post(timerThread);

但该代码放到哪里很关键,我们可以看看尝试多次点击“启动服务”按钮,看LogCat输出如下图:


从图中可以看到,程序刚开始运行第一次启动TimerService时,会触发顺序onCreate事件和onStart事件,但从第二次以后,startService只触发onStart事件;

尝试点击一下“停止服务”,再点击一下“启动服务”,看下图:


注意红框里的内容,服务停止时,触发onDestroy事件,服务重新启动时,又顺序触发onCreate和onStart事件。

由于启动线程语句handler.post(timerThread); 每运行一次,会启动一个新线程,所以该语句必须写到TimerService的onCreate事件里,而不能放到onStart事件里,因为startService可能会多次运行,会多次触发onStart事件。

 

4)、停止Runnable线程

语句 handler.removeCallbacks(timerThread);可以从Handler列表里去除timerThread线程,就不会再次运行该线程了。

线程的停止语句,当然是放到onDestroy事件里了,确保服务停止时,会同时停止线程的运行。

 

5)程序测试

在模拟器里运行程序,输出结果如下图:


如图所示

启动服务后,启动Service的onCreate和onStart事件,并且在onCreate里启动了定时线程,由于第一次是采用handler.post方法,所以不延时直接启动线程,就是上图第三行的TimerOnTimer输出;

随后以接近1秒钟的时间间隔启动线程,就是上图中后面的几个TimerOnTimer输出;

最后我停止了Service服务,所以触发Service的onDestroy事件,在onDestroy用handler.removeCallbacks里取消了线程的下次执行;

说明:关于定时1秒,由于线程里有一些操作,这种写法间隔一般都大于1000毫秒,但这并不影响“定时器”的使用效果;

3.          此方法的不足

我发现在timerThread里执行较长时间的耗时循环时,程序界面会出现“假死”现象,时间如果太长,会导致程序出现异常。

3.1、给程序添加一个按钮和文本显示框,activity_main.xml文件的修改内容如下:

   <Button

        android:id="@+id/btnClickMe"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:onClick="ClickMe"

        android:text="点我"/>

 

   <TextView

        android:id="@+id/textView1"

        android:layout_width="fill_parent"

        android:layout_height="wrap_content"

        android:text="计数:0"/>

 

3.2、修改MainActivity.java文件

1)、在类里定义一个类内的公共变量:

int count = 0;

 

2)、定义一个按钮的点击函数:

    public void ClickMe(View view){

        TextViewtextview = (TextView)findViewById(R.id.textView1);

        count = count + 1;

        textview.setText("计数:"+count);

    }

    //当每次点击一下按钮,计数自动加1,病显示在文本显示框里

 

    3)、为了便于测试,重写onCreate和onDestroy方法

    @Override

    protected void onCreate(BundlesavedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Intentintent = newIntent(this,TimerService.class);

        startService(intent);

    }

// 说明:在onCreate里加入启动服务的操作,这样当程序启动时,就可以自动启动服务了;

   

    @Override

    protected void onDestroy() {

        Intentintent = newIntent(this,TimerService.class);

        stopService(intent);

        super.onDestroy();

    }

    // 说明:在onDestroy里加入停止服务的操作,这样当程序退出时,就可以自动停止服务了;

 

4)、测试一下

模拟器里运行程序,从LogCat里可以看到服务启动正常,并1秒钟执行一次线程,点击“点我”按钮,看到计数加1变化,且界面运行速度正常。

 

3.3、修改TimerService.java

         1)、在Service类里写一个延时函数

    private void delay(int intVal){

        int i = 0;

        int j = 0;

        int k = 0;

        while (i < intVal){

            while (j < intVal){

                while (k < intVal){

                    k= k + 1;

                }

                k= 0;

                j= j + 1;

            }

            j= 0;

            i= i + 1;

        }

        i= 0;

    }

   

2)、在runnable里调用延时函数

    RunnabletimerThread= newRunnable(){

        public void run(){

            Log.d(TAG,"TimerOnTimer");

            delay(500);   // 此处调用延时函数

            handler.postDelayed(timerThread, 1000);

        } 

    };

//说明:delay(500);里面的500是一个延时经验数值,各个系统可能效果不一样,

//这个数值越大,延时越长,可以根据自己电脑来进行调整

//我的系统里,设为500时,大概延时5秒

 

3)、运行测试


从上图可以看出达到了延时效果,此次狂点“点我”按钮,界面已经出现反应迟钝了,说明当线程里出现耗时较长的任务比如循环时,主界面会假死。

继续加大延时,我的测试里出现了程序崩溃的错误:

 


4)、这是代码问题吗?

不知道是不是对线程使用的不对?还是在线程里用循环来做测试是否合理?我确实不得而知,希望高手指教。

不过我用在TimerService里套用Thread线程的方法达到了我的项目使用要求,这部分留待下次再整理记录吧。



 示例源码:Android学习笔记_02 – Service的测试应用



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值