server前台服务及后台切前台广告页

关于前台后台切换

最近接触到一个地方就是app的前后台,及前后服务,有点生疏,,话不多说进入正题

  1. service 前台保活
  2. service 后台换前台广告页面

1

关于保活这里可以说是漫天都是了,很多方案,通知栏保活,屏幕1像素啊,播放无声音乐,两个service相互监听啊。很多,大家可以去查一下,有很多方案

参考 原博 https://blog.csdn.net/andrexpert/article/details/75045678

将Service置为前台,目的时提高进程Service的oom_adj值,以降低其被系统回收的几率。该方案的原理是,通过使用 startForeground()方法将当前Service置于前台来提高Service的优先级。需要注意的是,对API大于18而言 startForeground()方法需要弹出一个可见通知,如果你觉得不爽,可以开启另一个Service将通知栏移除,其oom_adj值还是没变的。实现代码如下:
a) DaemonService.java

/**前台Service,使用startForeground
 * 这个Service尽量要轻,不要占用过多的系统资源,否则
 * 系统在资源紧张时,照样会将其杀死
 *
 * Created by jianddongguo on 2017/7/7.
 * http://blog.csdn.net/andrexpert
 */
public class DaemonService extends Service {
    private static final String TAG = "DaemonService";
    public static final int NOTICE_ID = 100;
 
 
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
 
 
    @Override
    public void onCreate() {
        super.onCreate();
        if(Contants.DEBUG)
            Log.d(TAG,"DaemonService---->onCreate被调用,启动前台service");
        //如果API大于18,需要弹出一个可见通知
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){
            
            boolean isLollipop1 = Build.VERSION.SDK_INT >= 21 ;
            int smallIcon1 = getApplicationContext().getResources().getIdentifier(
                    "notification_small_icon", "drawable",
                    getApplicationContext().getPackageName());
            if(smallIcon1 <= 0 || !isLollipop1) {
                smallIcon1 = getApplicationContext().getApplicationInfo().icon;
            }
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(smallIcon1);
            builder.setContentText("一条通知");
            builder.setContentTitle("Title");

            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                builder.setChannelId("holoview_im_notification_id");
            }

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                NotificationChannel channel = new NotificationChannel("holoview_im_notification_id", "holoview_im_notification", NotificationManager.IMPORTANCE_LOW);
                notificationManager.createNotificationChannel(channel);
            }
            startForeground(CommonUtil.NOTIFICATION_ID, builder.build());

        }else {
            startForeground(CommonUtil.NOTIFICATION_ID, new Notification());
            Intent intent = new Intent(getApplicationContext(), DeskService.class);
            startService(intent);
        }
    }
 
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 如果Service被终止
        // 当资源允许情况下,重启service
        return START_STICKY;
    }
 
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        // 如果Service被杀死,干掉通知
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){
            NotificationManager mManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
            mManager.cancel(NOTICE_ID);
        }
        if(Contants.DEBUG)
            Log.d(TAG,"DaemonService---->onDestroy,前台service被杀死");
        // 重启自己
        Intent intent = new Intent(getApplicationContext(),DaemonService.class);
        startService(intent);
    }
}

讲解一下:
这里还用到了两个技巧:一是在onStartCommand方法中返回START_STICKY,其作用是当Service进程被kill后,系统会尝试重新创建这个Service,且会保留Service的状态为开始状态,但不保留传递的Intent对象,onStartCommand方法一定会被重新调用。其二在onDestory方法中重新启动自己,也就是说,只要Service在被销毁时走到了onDestory这里我们就重新启动它。
b) CancelNoticeService.java

/** 移除前台Service通知栏标志,这个Service选择性使用
 *
 * Created by jianddongguo on 2017/7/7.
 * http://blog.csdn.net/andrexpert
 */
 
 
public class CancelNoticeService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
 
 
    @Override
    public void onCreate() {
        super.onCreate();
    }
 
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2){
            Notification.Builder builder = new Notification.Builder(this);
            builder.setSmallIcon(R.mipmap.ic_launcher);
            startForeground(DaemonService.NOTICE_ID,builder.build());
            // 开启一条线程,去移除DaemonService弹出的通知
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 延迟1s
                    SystemClock.sleep(1000);
                    // 取消CancelNoticeService的前台
                    stopForeground(true);
                    // 移除DaemonService弹出的通知
                    NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                    manager.cancel(DaemonService.NOTICE_ID);
                    // 任务完成,终止自己
                    stopSelf();
                }
            }).start();
        }
        return super.onStartCommand(intent, flags, startId);
    }
 
 
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
c) AndroidManifest.xml
<service android:name=".service.DaemonService"
			android:enabled="true"
            android:exported="true"
            android:process=":service"/>
 
 
<service android:name=".service.CancelNoticeService"
			android:enabled="true"
            android:exported="true"
            android:process=":service"/>

讲解一下:
总所周知,一个Service没有自己独立的进程,它一般是作为一个线程运行于它所在的应用进程中,且应用进程名称与包名一致。如果希望指定的组件和应用运行在指定的进程中,就需要通过android:process属性来为其创建一个进程,因此android:process=":daemon_service"就是让DaemonService运行在名为“com.jiangdg.keepappalive:daemon_service”进程中;android:enabled属性的作用是Android系统是否实例化应用程序中的组件;android:exported属性的作用是当前组件(Service)是否可以被包含本身以外的应用中的组件启动。

接下来就是启动service了,这个亲身测试没有什么问题。还有一些其他的方案,大家可以去参考原博

2

接下来就是关于后台切前台广告页面,这个其实思路很简单,用到android的原生方法ActivityLifecycleCallbacks去监听前后台切换,通过计算时间超过10秒的话进行广告,否则的话就是直接进入主页,这样一说就明确了很多

registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                //前台
                Log.i("aaa", "onActivityStarted: "+"在前台");
            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {
                //后台
                Log.i("aaa", "onActivityStarted: "+"在后台");
               
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });

当知道这个方法后整个编写就简单多了。就是监听台前操作时监听时间

            if ( canShowAdvertisement(activity.getClass())) {
                //如果是PhotoActivity时,在开启相机或者打开相册返回时不显示广告
                //onActivityResult(int requestCode, int resultCode, Intent data)在onActivityStarted(Activity activity)方法前调用,
                //所以不能在onActivityResult(int requestCode, int resultCode, Intent data)中设置enableAdvertisement = true
                if (activity instanceof PhotoActivity && !((PhotoActivity) activity).enableAdvertisement){
                    ((PhotoActivity) activity).enableAdvertisement = true;
                    return;
                }

                //说明从后台回到了前台
                long lastShowAdvertisementTimeStamp = SharePreferencesUtils.getInstance().getLong(Configration.SP_ADVERTISEMENT_LAST_SHOW_TIME, 0L);
                long curTime = System.currentTimeMillis();
                if ((curTime - lastShowAdvertisementTimeStamp > ADVERTISEMENT_INTERNAL_TIME)) {
                    SharePreferencesUtils.getInstance().saveLong(Configration.SP_ADVERTISEMENT_LAST_SHOW_TIME, curTime);
                    activity.startActivity(new Intent(activity, AdvertisementViewActivity.class));
                }
            }

意思就是说呢,返回到台前时,如果是从相册或是相机的话就没有广告,否则其他的话就有广告,然后通过sp存入切入后台时间,在切换到前台时进行比较,如果在相差时间内就可以出广告,这个时间就可以自定义了。贴入完整代码

public class MyApplication extends Application {

    public static final long ADVERTISEMENT_INTERNAL_TIME = 2 * 60 * 60 * 1000;//2小时
    private final String TAG = getClass().getSimpleName();
    private int mFinalCount;
    private ArrayMap<String, String> advertisementFilter = new ArrayMap<>();
    private ActivityLifecycleCallbacks activityLifecycleCallbacks = new ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

        }

        @Override
        public void onActivityStarted(Activity activity) {
            mFinalCount++;
            Log.e(TAG, "onActivityStarted: " + activity.getClass().getSimpleName() + ">>>" + mFinalCount);
            if (mFinalCount == 1 && canShowAdvertisement(activity.getClass())) {
                //如果是PhotoActivity时,在开启相机或者打开相册返回时不显示广告
                //onActivityResult(int requestCode, int resultCode, Intent data)在onActivityStarted(Activity activity)方法前调用,
                //所以不能在onActivityResult(int requestCode, int resultCode, Intent data)中设置enableAdvertisement = true
                if (activity instanceof PhotoActivity && !((PhotoActivity) activity).enableAdvertisement){
                    ((PhotoActivity) activity).enableAdvertisement = true;
                    return;
                }

                //说明从后台回到了前台
                long lastShowAdvertisementTimeStamp = SharePreferencesUtils.getInstance().getLong(Configration.SP_ADVERTISEMENT_LAST_SHOW_TIME, 0L);
                long curTime = System.currentTimeMillis();
                if ((curTime - lastShowAdvertisementTimeStamp > ADVERTISEMENT_INTERNAL_TIME)) {
                    SharePreferencesUtils.getInstance().saveLong(Configration.SP_ADVERTISEMENT_LAST_SHOW_TIME, curTime);
                    activity.startActivity(new Intent(activity, AdvertisementViewActivity.class));
                }
            }
        }

        @Override
        public void onActivityResumed(Activity activity) {

        }

        @Override
        public void onActivityPaused(Activity activity) {

        }

        @Override
        public void onActivityStopped(Activity activity) {
            mFinalCount--;
//            Log.e(TAG, "onActivityStopped: " + activity.getClass().getSimpleName() + ">>>" + mFinalCount);
            if (mFinalCount == 0) {
                //说明从前台回到了后台
            }
        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

        }

        @Override
        public void onActivityDestroyed(Activity activity) {

        }
    };

    @Override
    public void onCreate() {
        Log.e(TAG, "onCreate()");
        super.onCreate();
        InstanceManager.getInstance().init(this);
        //初始化SharedPreferences工具
        SharePreferencesUtils.getInstance().init(this, "share_data");
        //注册activity生命周期监听
        if (BuildConfig.enableAdvertisementFeature) {
            registerActivityLifecycleCallbacks(activityLifecycleCallbacks);
            //启动界面不显示广告页
            addToAdvertisementFilter(LaunchActivity.class);
            addToAdvertisementFilter(AdvertisementViewActivity.class);
        }
    }

    /**
     * Add activity to advertisement filter.
     *
     * @param clazz clazz
     */
    public void addToAdvertisementFilter(Class<? extends Activity> clazz) {
        String key = clazz.getName();
        String activityName = clazz.getSimpleName();
        if (!advertisementFilter.containsKey(key))
            advertisementFilter.put(key, activityName);
    }

    /**
     * Remove activity from advertisement filter.
     *
     * @param clazz clazz
     */
    public void removeFromAdvertisementFilter(Class<? extends Activity> clazz) {
        String key = clazz.getName();
        if (advertisementFilter.containsKey(key))
            advertisementFilter.remove(key);
    }

    private boolean canShowAdvertisement(Class<? extends Activity> clazz){
        String key = clazz.getName();
        return !advertisementFilter.containsKey(key);
    }
}

参考博客https://www.jianshu.com/p/b6b0f3c4efb1

其实这个广告的话拓展性很大,也不是局限这一种方式。希望大家一起努力吧

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值