Android 后台运行那些事

/ Android / 没有评论 / 1864浏览

Android 后台运行那些事

这里我告诉大家一些最快最实用的解决方法,这些都是放在我项目中成功实现的。

Service 保活

服务相互保活是一个很流氓的方式 而且随着sdk升级 这种方式已经不能在高版本使用了; 解决方式:

1. 一种比较文明的方式,这也是大多数注重用户体验app使用的

下面这张是我常用的app,因为常常把它放在后台记录行程,所以有一天突然app跳出提示说被意外杀死,要我怎么做怎么做. 其实说的很明白了。根据不同的手机对后台运行app的容忍度不同。一般都会在3-5分钟左右杀死。这个时候即使你依然在运行但是收不到任何通知网络也会被断掉,也就是常说的休眠。 这种机制很好的保护了你的电池续航。 1

2. 提升服务到前台,starService并bindService

//onStartCommand只会调用一次  
//START_STICKY类似的一共有3个  
//想了解的可以去查一下 这个意思是当服务被杀死后开启后不走onCreate直接启动;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    play();
    return START_STICKY;
}

//这个就是将服务提到前台的代码
private void play() {
    if (!isPlay) {
        isPlay = true;
        //和上一笔记中创建通知的步骤一样,只是不需要通过通知管理器进行触发,而是用startForeground(ID,notify)来处理
        //步骤1:和上一笔记一样,通过Notification.Builder( )来创建通知
        //FakePlayer就是两个大button的activity,也即服务的界面,见最左图
        Intent i = new Intent(this, MainActivity.class);
        //注意Intent的flag设置:FLAG_ACTIVITY_CLEAR_TOP: 如果activity已在当前任务中运行,在它前端的activity都会被关闭,它就成了最前端的activity。FLAG_ACTIVITY_SINGLE_TOP: 如果activity已经在最前端运行,则不需要再加载。设置这两个flag,就是让一个且唯一的一个activity(服务界面)运行在最前端。
        i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
        Notification myNotify = new Notification.Builder(this)
            .setSmallIcon(R.drawable.logo)
            .setContentTitle("共享代驾")
            .setContentText("共享代驾正在工作中")
            .setContentIntent(null)
            .getNotification();
        //设置notification的flag,表明在点击通知后,通知并不会消失,也在最右图上仍在通知栏显示图标。这是确保在activity中退出后,状态栏仍有图标可提下拉、点击,再次进入activity。
        myNotify.flags |= Notification.FLAG_NO_CLEAR;
        // 步骤 2:startForeground( int, Notification)将服务设置为foreground状态,使系统知道该服务是用户关注,低内存情况下不会killed,并提供通知向用户表明处于foreground状态。
        startForeground(968, myNotify);
    }
}
private void stop() {
    if (isPlay) {
        isPlay = false;
        //将服务从forefround状态中移走,使得系统可以在低内存的情况下清除它。
        stopForeground(true);
    }
}

starService并bindService 先开启在绑定服务 这样做即使所有绑定服务的Activity走了onDestory了 服务依然可以在后台运行 还有一种方式在Manifest中给Service配置 android:priority =1000;

Service于Activity通讯

描述问题 : 当你切出去到桌面的时候 如何在回到当前界面而不都是数据 常常会发现页面上的数据不见了甚至崩溃的情况 解决方法:

1. 使用movebackTask结合HomeWatcherReceiver使用

这种方式只是让桌面挡住了当前的Activity而没有正真的走onDestory方法; 监听到home键被按下就执行该方法 点击app图标会很快回到当前界面而且数据不会丢失

public class HomeWatcherReceiver extends BroadcastReceiver {
    private static final String LOG_TAG = "HomeReceiver";
    private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
    private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Log.i(LOG_TAG, "onReceive: action: " + action);
        if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
            String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
            if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
                ((LocationActivity) context).moveTaskToBack(true);
            }
        }
    }
}

2. 当我们点击通知的到Activity界面数据消失问题

解决方法:

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);

这个flags的意思是如果该Activity存在于栈中就将他置顶否则重新创建; 一般你切换到后的时候不应该销毁这个Activity的 但是事实是过不了30秒他就会被销毁 结合上面的HomeWatcherReceiver 使用就可以防止这个数据丢失的问题;

/**
 * 创建通知
 */
private void setUpNotification() {
    if (currentTencentLocation != null) {
        int id = new Random(System.nanoTime()).nextInt();
        // 放置在"正在运行"栏目中
        Intent intent = new Intent(this, MovingActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        PendingIntent contentIntent = PendingIntent.getActivity(this, UUID.randomUUID().hashCode(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
        //intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        //intent.putExtra(MovingActivity.IntentResult, getIntentResult(currentTencentLocation));
        Notification mNotification = new Notification.Builder(ServingPassengerDownloadService.this)
            .setAutoCancel(true)
            .setContentTitle("车辆行驶中")
            .setSmallIcon(R.drawable.logo)
            .setContentIntent(contentIntent)
            .setWhen(System.currentTimeMillis()).getNotification();
        mNotification.flags = Notification.FLAG_ONGOING_EVENT;
        mNotificationManager.notify(id, mNotification);
    }
}

有一种情况 当你开着app不使用半个小时间1个小时后会怎样?

大多数不会去考虑这个问题,是怎样就怎样吧~,但是烦人的客户不买账怎么办? 以上的三种方式可以就做到app在后台长期运行 但是app在长时间后台将会进入休眠(在上面就有说过)。 解决方法:

//当请求中断后  注意是你框架中的回调突然走了Error的时候,这个时候你应该去判断并排除网络断开的原因
//你发现原来是代码依然在运行,可就是不走onNext方法,发个通知他也收不到··,这就是上面说到的休眠状态。
//以下是做一个闹钟唤醒app这种方式还挺好用的。但是现在手机品牌很多 我测试的小米note1只能唤醒一次,再次
//唤醒将无效。。。。无效。。。
boolean b = !cheNetWork();
Log.i("cheNetWork", "===========cheNetWork=============" + b);
if (b) return;
AlarmManager aManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent();
intent.setData(Uri.parse("Start://Now"));
PendingIntent pi = PendingIntent.getActivity(MainActivity.this, 0, intent, 0);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
aManager.set(AlarmManager.RTC_WAKEUP, Calendar.getInstance().getTimeInMillis(), pi);

其他

1 屏幕常亮

android:keepScreenOn=”true”
  1. 在Activity的oncreate中加入以下代码 结合上面的闹钟 可以唤醒屏幕
getWindow().addFlags(
    WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
    WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
  1. 有的朋友会问:那APP就是被杀死了呢?怎么办?
  2. 好吧 推荐大家使用一款数据库
//之所以推荐这个是因为他可以动态更新,网上对它评价褒贬不一,请自行选择
public class RealmHelper {
    public static final String DB_NAME = "drive.realm";
    private Realm mRealm;
    private static RealmHelper instance;
    private int maxSize;

    private RealmHelper() {
        maxSize = 10;
    }

    public static RealmHelper getInstance() {
        if (instance == null) {
            synchronized (RealmHelper.class) {
                if (instance == null)
                    instance = new RealmHelper();
            }
        }
        return instance;
    }

    public Realm getRealm() {
        if (mRealm == null || mRealm.isClosed())
            mRealm = Realm.getDefaultInstance();
        return mRealm;
    }