Android后台操作

来源:互联网 发布:java bigdecimal用法 编辑:程序博客网 时间:2024/05/16 14:21

一.service

1.创建service要创建一个Service需要创建一个继承Service的类,并重写onCreate和onBind方法。

创建完成后,需要在application标签内注册此Service,可加入permission属性来确保Service只能由自己的程序启动和停止。
2.执行Service当一个Service通过startService启动时,就会调用onStartCommand方法。
重写OnStartCommand方法以执行由Service封装的任务。
应在onStartCommand方法中运行一个新线程在后台执行处理。
onStartCommand的返回值可以控制重新启动的行为。
3.启动和停止启动类似Activity的启动,通过调用startService(Intent)方法来启动一个service,停止时调用stopService(Intent)
当Service完成操作后,应调用stopSelf终止它
4.将Service绑定到Activity将Service绑定到Activity后后者会维持一个对前者实例的引用,这样能对正在运行的Service进行方法调用
要让一个Service支持绑定需要实现onBind方法,并返回被绑定Service的当前实例,当被绑定时,可以通过onServiceConnected
获得的serviceBinder对象来使用Service所有的共有方法和属性。
5.创建前台Service前台Service可以有更高的优先级,可以有效地避免运行时在释放资源时终止这个Service。
通过调用Service的startForeground方法来实现在前台运行,在调用该方法时要指定一个持续工作的Notification。

当不需要在前台时可调用stopForeground,并可选择是否移除通知。


服务类的示例代码


package com.whathecode.servicedemo;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
 
public class ExtendsionService extends Service
{
 
    /**
     * 当服务第一次被创建的时候调用此方法
     */
    @Override
    public void onCreate()
    {
         
        super.onCreate();
        Toast.makeText(getBaseContext(), "服务被创建了", Toast.LENGTH_SHORT).show();
    }
     
    /**
     * 当通过startService方法启动服务时此方法被调用
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Toast.makeText(getBaseContext(), "服务被启动了", Toast.LENGTH_SHORT).show();
        return super.onStartCommand(intent, flags, startId);
    }
 
    @Override
    public IBinder onBind(Intent intent)
    {
        return null;
    }
 
    @Override
    public void onDestroy()
    {
        super.onDestroy();
    }
 
}
 

使用startService启动服务

 
注意:

         当我们第一次点击启动服务按钮的时候,onCreate方法和onStartCommand方法依次执行,

         但在第二次点击启动服务按钮的时候,这个服务已经处于运行状态,onCreate方法不再执行第二次,

         而onStartCommand方法总是在调用startService启动服务的时候执行。

         onDestroy方法在stopService方法被调用后执行。

 

除了使用startService方法启动服务外,我们还可以使用Bind的方式启动服务。

前者和后者的不同在于:

      服务的生命周期不依赖于启动者。服务被启动后一直在后台运行直到调用stopService被停止。

       使用Bind方式启动服务,服务的生命周期依赖于启动者。即服务在启动者退出后自动销毁。

 

使用Bind方式启动服务

主要使用bindService方法启动服务,unbindService方法销毁服务。

使用此方式启动服务的好处是,bind定成功的时候服务返回一个IBinder对象,

我们可以通过在服务类里面实现这个对象,从而访问服务里面的具体方法。

从而达到和服务沟通的目的。

 

注意:

        我们不能以直接实例化服务的方式调用里面的方法。

 

另外:

       如果在退出服务的时候没有解绑,那么程序就会抛出异常IllegalArgumentException异常。

       因此,每次调用bindService启动服务,完成后需要退出服务都需要使用unbindService 解绑。  

 

示例代码:


package com.whathecode.servicedemo;
 
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
 
public class MainActivity extends Activity
{
 
    Intent service = null;
    IReceptionist rpst;
 
    /**
     * bindService和unbindService方法使用的参数
     */
    private ServiceConnection conn = new ServiceConnection()
    {
         
        //此方法在服务连接被意外终止的时候被调用,
        @Override
        public void onServiceDisconnected(ComponentName name)
        {
         
        }
         
        //当服务被连接上的时候自动调用这个方法,第二个参数是服务类onBind方法中返回的对象
        @Override
        public void onServiceConnected(ComponentName name, IBinder service)
        {
            rpst = (IReceptionist) service;
            rpst.callExtendsionNumber();
        }
    };
     
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         
        //创建服务
        service = new Intent(this, ExtendsionService.class);
    }
 
    public void start(View view)
    {
        //启动服务
        startService(service);
    }
     
    public void stop(View view)
    {
        //停止服务
        stopService(service);
    }
     
    public void bind(View view)
    {
        //绑定服务
        bindService(service, conn , BIND_AUTO_CREATE);
    }
     
    public void unBind(View view)
    {
        //解绑服务
        unbindService(conn);
    }
}
 

继承ServiceBinder对象,并实现自定义接口:

通过这个方式,我们可以暴露只需要的方法,实现代码的保护。

 

IReceptionist接口代码:

package com.whathecode.servicedemo;
 
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
 
public class ExtendsionService extends Service
{
 
    private static final String TAG = "ExtendsionService";
 
    /**
     * 当服务第一次被创建的时候调用此方法
     */
    @Override
    public void onCreate()
    {
         
        super.onCreate();
        Toast.makeText(getBaseContext(), "服务被创建了", Toast.LENGTH_SHORT).show();
    }
     
    /**
     * 当通过startService方法启动服务时此方法被调用
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        Toast.makeText(getBaseContext(), "服务被启动了", Toast.LENGTH_SHORT).show();
        return super.onStartCommand(intent, flags, startId);
    }
 
    @Override
    public IBinder onBind(Intent intent)
    {
        return new ServiceBinder();
    }
 
    @Override
    public void onDestroy()
    {
        Toast.makeText(getBaseContext(), "服务被销毁了", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "服务被销毁了");
        super.onDestroy();
    }
     
    public void putExtendsion()
    {
        Toast.makeText(getBaseContext(), "正在帮你转接分机", Toast.LENGTH_SHORT).show();
    }
     
    /**
     *
     * 继承Binder并实现IReceptionist接口
     * 继承Binder类的目的是onBind方法中需要返回这个类型的对象
     *
     * 实现IReceptionist接口是暴露方法的需要
     */
    private class ServiceBinder extends Binder implements IReceptionist
    {
        public void callExtendsionNumber()
        {
            putExtendsion();
        }
         
        private void otherMethod()
        {
             
        }
    }
}
 

注册服务:

 
<service android:name="com.whathecode.servicedemo.BackgroundService"></service>


二.AsyncTask

1.使用AsyncTask运行异步任务AsyncTask 处理线程创建管理和同步等全部工作,可以用来创建一个异步任务,由在后台的处理以及处理后的UI

更新组成。
AsyncTask 适合生命周期较短且需要在UI上显示进度结果的后台操作,但在Activity被销毁时会被取消,所以生命周期较长的后台操作应使用Service组件
应重写的方法:
doInBackground:这个方法在后台线程上执行,所以应把运行时间较长的代码放到这里,不能与UI交互。可以调用publishProgress方法传递参数给onProgressUpdate方法后台任务完成后返回的最终结果作为参数传递给onPostExecute
onProgressUpdate:当中间进度变化时更新UI,此方法与GUI线程同步
onPostExecute:当异步任务完成后,doInBackground方法的返回值传入这个方法,用此方法更新UI

运行方法:创建新实例并调用execute方法,可以传入很多参数(每个AsyncTask实例只能执行一次)。

代码如下:

class MyAsyTask extends AsyncTask<String, Integer, String>
    {
        @Override
        protected String doInBackground(byte[]... arg0) {
            // TODO Auto-generated method stub

            File picture = new File("/sdcard/mhc.jpg");
            try {
                FileOutputStream fos = new FileOutputStream(picture.getPath());
                fos.write(params[0]);
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.e(tag, "照片保存完成");
            CameraActivity.this.finish();
            return null;
          
        }
        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
        }
        @Override
        protected void onPostExecute(String result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
        }
    }

2.IntentService作用:根据需求执行一组任务,将收到的所有请求Intent放到队列中,并在异步后台线程中逐个处理他们,是Service类的子类,用来处理异步请求。
当处理完每个收到的Intent后,IntentService 会终止自己。

用法:继承IntentService并重写onHandleIntent(Intent)方法

代码:

public class IntentServiceSub extends IntentService {

    private static final String TAG = "IntentServiceSub";

    public IntentServiceSub() {
        super("IntentServiceSub");
        Log.i(TAG, "=>IntentServiceSub");
    }

    /* (non-Javadoc)
     * @see android.app.IntentService#onCreate()
     */
    @Override
    public void onCreate() {
        Log.i(TAG, "=>onCreate");
        super.onCreate();
    }

    /* (non-Javadoc)
     * @see android.app.IntentService#onDestroy()
     */
    @Override
    public void onDestroy() {
        Log.i(TAG, "=>onDestroy");
        super.onDestroy();
    }
    
    @Override
    protected void onHandleIntent(Intent arg0) {
        Log.i(TAG, "IntentService 线程:"+Thread.currentThread.getId());
        Thread.sleep(2000);
}


private OnClickListener listener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            
            case R.id.btnStartIntentService:
                Log.i(TAG, "主线程ID:"+Thread.currentThread.getId());
                if (mServiceIntent == null)
                    mServiceIntent = new Intent(AndroidServiceActivity.this,IntentServiceSub.class);
                startService(mServiceIntent);
                break;
            case R.id.btnStopIntentService:
                Log.i(TAG, "btnStopIntentService");
                if (mServiceIntent != null) {
                    stopService(mServiceIntent);
                    mServiceIntent = null;
                }
                break;
            }

        }
    };

三.手动创建线程和GUI同步的方法

1.直接创建子线程即使用Thread或Runnable创建,比较简单。

2.可以使用runOnUiThread方法强制一个方法在UI线程中执行。3.使用Handler使用Handler的post方法可以将更新从后台线程发布到UI线程(创建Handler的线程)上,postDelayed方法延迟发布。

//这个方法在主GUI线程调用
    private void backgroundExecution()
    {
        
        Thread thread = new Thread(null, dobackgroundThreadProcessing, "Background");
        thread.start();
    }
    //执行后台处理方法Runnable
    private Runnable dobackgroundThreadProcessing = new Runnable() {
        
        @Override
        public void run() {
            // TODO Auto-generated method stub
            backgroundThreadProcessing();
        }
    };
    private Handler handler = new Handler();
    //在后台执行一些处理方法
    private void backgroundThreadProcessing() {

        handler.post(doUpdateGUI);
        //延迟发布更新
        handler.postDelayed(doUpdateGUI, SystemClock.uptimeMillis());
    }

    private Runnable doUpdateGUI = new Runnable() {
        public void run() {
            updateGUI();
        }
    };

    private void updateGUI() {
        //打开对话框或者修改GUI元素
        }


四.用Alarm实现后台操作1.Alarm 是一种在某一时间点或一段时间间隔内激活Intent的方式。
2.Alarm 在应用程序之外操作,即使程序关闭也依然能够用来激活程序事件或操作。
3.用set方法指定一个类型,触发时间和一个要激活的PendingIntent可以创建一个只激活一次的Alarm。
4.使用setRepeating 或setInexactRepeating 方法设置重复的Alarm。
5.应用:自动刷新等

五.其它技巧

1.与 Service 通信并且让它持续运行

  如果我们想保持和 Service 的通信,又不想让 Service 随着 Activity 退出而退出呢?你可以先 startService() 然后再 bindService()  。当你不需要绑定的时候就执行 unbindService() 方法,执行这个方法只会触发 Service 的 onUnbind() 而不会把这个 Service 销毁。这样就可以既保持和 Service 的通信,也不会随着 Activity 销毁而销毁了。

2.提高 Service 优先级

  Android 系统对于内存管理有自己的一套方法,为了保障系统有序稳定的运信,系统内部会自动分配,控制程序的内存使用。当系统觉得当前的资源非常有限的时候,为了保证一些优先级高的程序能运行,就会杀掉一些他认为不重要的程序或者服务来释放内存。这样就能保证真正对用户有用的程序仍然再运行。如果你的 Service 碰上了这种情况,多半会先被杀掉。但如果你增加 Service 的优先级就能让他多留一会,我们可以用 setForeground(true) 来设置 Service 的优先级。

  为什么是 foreground ? 默认启动的 Service 是被标记为 background,当前运行的 Activity 一般被标记为 foreground,也就是说你给 Service 设置了 foreground 那么他就和正在运行的 Activity 类似优先级得到了一定的提高。当让这并不能保证你得 Service 永远不被杀掉,只是提高了他的优先级。

  有一个方法可以给你更清晰的演示,进入 $SDK/tools 运行命令复制内容到剪贴板代码:
# adb shell dumpsys activity|grep oom_adj  
 
Running Norm Proc # 6: oom_adj=  0 ProcessRecord{43635cf0 12689:com.roiding.netraffic/10028}  
Running Norm Proc # 5: oom_adj=  7 ProcessRecord{436feda0 12729:com.android.browser/10006}  
Running Norm Proc # 4: oom_adj=  8 ProcessRecord{4367e838 12761:android.process.acore/10016}  
Running Norm Proc # 3: oom_adj=  8 ProcessRecord{43691cd8 12754:com.google.process.gapps/10000}  
Running PERS Proc # 1: oom_adj=-12 ProcessRecord{43506750 5941:com.android.phone/1001}  
Running PERS Proc # 0: oom_adj=-100 ProcessRecord{4348fde0 5908:system/1000}  返回的一大堆东西,观察 oom_adj 的值,如果是大于 8 一般就是属于 backgroud 随时可能被干掉,数值越小证明优先级越高,被干掉的时间越晚。你看phone的程序是 -12 说明电话就是电话,其他什么都干了了,也的能接电话对吧。另外还有一个 -100 的,更邪乎因为是 system 如果他也完蛋了,你得系统也就挂了,嘿嘿。

3.用其他方式启动 Service

  其实不光能从 Activity 中启动 Service ,还有一个很有用的方法是接收系统的广播,这就要用到 Receiver 。在 Mainfest 文件中配置你得 Receiver 能接收什么样的广播消息,那么即使你得程序没有显示给用户,你的 Service 也能启动。你要做的就是继承 android.content.BroadcastReceiver ,然后实现 onReceive(Context context, Intent intent) 方法,就可以启动你得 Service 了。这里不能 bindService 因为一个 Receiver 是一个短暂存在的对象,所以 bind 是没有什么意义的。

4.资源消耗

  大家都说 G1 的电池太不抗用,这个问题其实我看来跟多是软件的问题。1150毫安的电池不算大,但也不算小了,考虑到 500mhz 的 CPU 还是非常耗电的。因为一个 Service 要长时间后台运行,所以如果你得 Service 太过于消耗资源那电池更用不了多久了。

  对于这个问题我有一点点考虑,和大家分享一下。因为一般 Service 都会启动另外的线程不断循环作一些操作,循环频率不易太高。也不要做太过于耗费资源的操作,特别是CPU资源,因为后台 Service 用户看不到,会比较莫名奇妙。具体可以结合 top 以及 logcat 监测使用情况。LOG中如果虚拟机频繁的 GC 应该也说明程序还有很大改进的余地。因为GC 也是很耗费CPU的。可能这些不光 Service 应该注意,只要是移动设备都应该考虑,才能给你的用户最佳的体验。


0 0
原创粉丝点击