精华内容
下载资源
问答
  • foreground
    千次阅读
    2021-09-25 20:19:33

    Android 10 在使用MediaProjectionManager录音的时候报错:

    java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION

    错误详细信息如下,看了网上很多相关的解决介绍,但是都没有太多的用处要么就是不是太清楚。这里说下解决的几个关键点,希望对大家有所帮助

    2021-09-25 20:17:32.007 4032-4032/? E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.demo.audiocapture, PID: 4032
        java.lang.RuntimeException: Unable to start service com.demo.audiocapture.AudioCaptureService@403e26c with Intent { cmp=com.demo.audiocapture/.AudioCaptureService (has extras) }: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
            at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4971)
            at android.app.ActivityThread.access$3300(ActivityThread.java:260)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2486)
            at android.os.Handler.dispatchMessage(Handler.java:110)
            at android.os.Looper.loop(Looper.java:219)
            at android.app.ActivityThread.main(ActivityThread.java:8668)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)
         Caused by: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
            at android.os.Parcel.createException(Parcel.java:2090)
            at android.os.Parcel.readException(Parcel.java:2058)
            at android.os.Parcel.readException(Parcel.java:2006)
            at android.media.projection.IMediaProjection$Stub$Proxy.start(IMediaProjection.java:231)
            at android.media.projection.MediaProjection.<init>(MediaProjection.java:75)
            at android.media.projection.MediaProjectionManager.getMediaProjection(MediaProjectionManager.java:104)
            at com.demo.audiocapture.AudioCaptureService.onStartCommand(AudioCaptureService.java:86)
            at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4951)
            at android.app.ActivityThread.access$3300(ActivityThread.java:260) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2486) 
            at android.os.Handler.dispatchMessage(Handler.java:110) 
            at android.os.Looper.loop(Looper.java:219) 
            at android.app.ActivityThread.main(ActivityThread.java:8668) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109) 
         Caused by: android.os.RemoteException: Remote stack trace:
            at com.android.server.media.projection.MediaProjectionManagerService$MediaProjection.start(libmapleservices.so:16625028)
            at android.media.projection.IMediaProjection$Stub.onTransact(libmapleframework.so:26979796)
            at android.os.Binder.execTransactInternal(libmapleframework.so:6246476)
            at android.os.Binder.execTransact(libmapleframework.so:6248272)
        callee: null 2075/4123
    

    尝试了一下,最终解决。关键点如下(录音的代码不再这里黏贴了): 关键点1我在网上没找到有说明的,或者介绍的也不太清楚,关键点2网上有介绍说明

    关键点1:关于通知的调用时机及调用方法

    1> NotificationManager 显示通知时,必须要在getMediaProjection方法之前调用

    @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            throw new UnsupportedOperationException("Not yet implemented");
        }
    
        @RequiresApi(api = Build.VERSION_CODES.Q)
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            resultCode = intent.getIntExtra("resultCode", -1);
            resultData = intent.getParcelableExtra("data");
            Log.i(TAG, "onStartCommand: " + resultCode);
            Log.i(TAG, "onStartCommand: " + resultData);
            notification();//通知显示可以写到onCreate中。不管是写到onCreate里面还是onStartCommand中,都要写到getMediaProjection方法调用之前
            mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
            mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);//必须在通知显示之后调用
            Log.i(TAG, "onStartCommand: " + mediaProjection);
            AudioPlaybackCaptureConfiguration.Builder builder = new AudioPlaybackCaptureConfiguration.Builder(mediaProjection);
            builder.addMatchingUsage(AudioAttributes.USAGE_MEDIA);//多媒体
            builder.addMatchingUsage(AudioAttributes.USAGE_ALARM);//闹铃
            builder.addMatchingUsage(AudioAttributes.USAGE_GAME);//游戏
            audioPlaybackCaptureConfiguration = builder.build();
            generateAudioRecord();
            return super.onStartCommand(intent, flags, startId);
        }
    

    2> 必须使用 startForeground(NOTIFICATION_ID, notification) 显示通知,不能使用notificationManager.notify(NOTIFICATION_ID, notification);

    public void notification() {
            Log.i(TAG, "notification: " + Build.VERSION.SDK_INT);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                //Call Start foreground with notification
                Intent notificationIntent = new Intent(this, AudioCaptureService.class);
                PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
                NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
                        .setSmallIcon(R.drawable.ic_launcher_foreground)
                        .setContentTitle("Starting Service")
                        .setContentText("Starting monitoring service")
                        .setTicker(NOTIFICATION_TICKER)
                        .setContentIntent(pendingIntent);
                Notification notification = notificationBuilder.build();
                NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
                channel.setDescription(NOTIFICATION_CHANNEL_DESC);
                NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                notificationManager.createNotificationChannel(channel);
                //notificationManager.notify(NOTIFICATION_ID, notification);
                startForeground(NOTIFICATION_ID, notification); //必须使用此方法显示通知,不能使用notificationManager.notify,否则还是会报上面的错误
            }
        }
    

    关键点2 权限

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    

    关键点3 service配置

    清单文件(即AndroidManifest.xml)中配置服务的时候,需要加上 android:foregroundServiceType=“mediaProjection”

    <service
                android:name=".AudioCaptureService"
                android:enabled="true"
                android:exported="true"
                android:foregroundServiceType="mediaProjection"
                />
    

    代码git地址

    参考
    https://stackoverflow.com/questions/62004735/media-projections-require-a-foreground-service-of-type-serviceinfo-foreground-se
    Android notification 适配不同版本
    https://developer.android.google.cn/reference/kotlin/android/media/AudioPlaybackCaptureConfiguration
    Android require a foreground service of type ServiceInfo.FOREGROUND_SERVICE
    MediaProjections in Android Q(Media projections require a foreground service)
    AudioPlaybackCapture(Android 10)无法正常工作并录制空声音

    更多相关内容
  • foreground

    2016-10-14 14:50:01
    foreground,便捷简单
  • foreground_service

    2021-05-15 23:28:24
    Android foreground services require a notification to be displayed, and notifications require an icon. For this plugin to work, the icon needs to be in this specific location: res/drawable/org_thebus...
  • foreground_app_info 是否曾经想获得有关前台应用程序的一些详细信息,例如它是哪个,当前打开了哪个 URL 等? 这个项目适合你。 通过sleep 3; ./demo.py演示sleep 3; ./demo.py sleep 3; ./demo.py 。 例子: $ ./...
  • android-ForegroundService 这是我如何维护在前台运行的绑定服务的生命周期的实践项目。 如果服务在前台启动,应用程序可以打招呼。 到目前为止它的工作,但还没有尝试服务中是否有正在运行的线程。导入方法: 打开...
  • foreground_detection.m

    2020-08-11 23:13:07
    运动目标检测,混合高斯背景建模,背景建模,matlab 运动目标检测的混合高斯背景建模matlab代码 背景差分 混合高斯 matlab
  • android-foreground-service-master.zip,android-foreground-service-master,ForegroundService,gradlew.bat,gradlew,gradle.properties,foregroundservice,proguard-rules.pro,src,test,java,lt,setkus,foreground...
  • opencv GrabCut -Interactive Foreground Extraction using Iterated Graph Cuts ,图像分割算法原文
  • Real-time vehicle detection with foreground-based cascade classifier
  • app-foreground-background-listen-master.zip,app-foreground-background-listen-master,gradlew.bat,gradlew,gradle.properties,App-foregroung-background-Listen.iml,.gitignore.orig,screen.png,LICENSE,app,...
  • open-link-in-a-foreground-tab-extension-master.zip,open-link-in-a-foreground-tab-extension-master,manifest.json,icon48.png,background.js,icon16.png,icon128.png,README.md
  • 前景分离很不错的书籍,一本书几乎可以全面了解该方向的所有论文,很适合初学者认真学习!!
  • Android Foreground Service adb shell ps | grep com.karl.android.foregroundservice cat proc/31291/oom_adj frameworks/base/services/java/...

    一、簡述

    我們都知道,在Android8.0之前,我們可以使用StartService啓動另一個獨立運行的Service程序,這個Service程序不會因爲調起它的程序停止了而停止運行,而是會一直運行著,除非用戶主動將其關閉或是被系統kill掉才會停止運行。

    然而在Android8.0開始,Google對這一行爲做了限制,這個限制叫做Background Execution Limits,詳細的可以看Google官方文檔(https://developer.android.com/about/versions/oreo/background)

    簡單來説,Google禁止了直接使用StartService來啓動另一個未在前臺運行的Service程序,而需要采用StartForegroundService方法來啓動,并且在該啓動方式中,增加了各種限制來增加Service的啓動和運行的控制。所以針對這些限制,我們在開發中需要做出什麽應對措施呢?我這邊做了一些測試,以下的示例均是在Android 9.0上做的測試,這邊和大家一起分析學習一下。

    二、Android LowMemoryKiller 介紹

    在分析這一問題之前,我們需要瞭解一個知識點,即Android的内存管理機制。
    可以參考這篇文章的内容:Android LowMemoryKiller 介紹.裏面有關於ADJ值得説明,以及如何查看ADJ值得方法,如果你不瞭解,建議可以先看一下。

    三、startService方法的調用行爲測試

    我們先來研究一下startService的啓動方式

    我們首先創建一個Project,然後創建一個Foreground Service,代碼如下

    class ForegroundService: Service() {
        override fun onBind(p0: Intent?): IBinder? {
            Log.d("ForegroundService", "onBind")
            return null
        }
    
        override fun onCreate() {
            super.onCreate()
            Log.d("ForegroundService", "onCreate")
    
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.d("ForegroundService", "onDestroy")
        }
    }
    

    給Service增加獨立進程

    別忘了在Manifest裏面注冊

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
        <service android:name=".ForegroundService"
        	android:process=":remote"
            >
            <intent-filter>
                <action android:name="com.karl.android.foregroundService" />
            </intent-filter>
        </service>
    
    </application>
    

    這裏我們給service加一個單獨進程來運行,增加process參數,至於爲什麽增加該參數,因爲如果不加的話,其實service運行的進程和APP運行的進程是一樣的,所以查看ADJ的話,就是APP的ADJ,區分不出來service自己獨有的ADJ,大家可以自己嘗試一下。

    然後我們在MainActivity裏面去調用該Service

    class MainActivity : AppCompatActivity() {
        internal var myBinder: IBinder? = null
        private lateinit var conn : ServiceConnection
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            btnStartService.setOnClickListener {
                    val intent = Intent()
                    intent.setClassName("com.karl.android.foregroundservice", "com.castles.android.foregroundservice.ForegroundService")
                    startService(intent)
            }
            btnBindService.setOnClickListener{
                if(!::conn.isInitialized){
                    conn = MyConnection()
                    val intent = Intent()
                    intent.action = "com.karl.android.foregroundService"
                    intent.setClassName("com.karl.android.foregroundservice", "com.castles.android.foregroundservice.ForegroundService")
                    bindService(intent, conn, Context.BIND_AUTO_CREATE)
                }
            }
        }
        
        override fun onDestroy() {
            super.onDestroy()
            Log.d("MainActivity", "onDestroy")
            if(::conn.isInitialized){
                unbindService(conn)
            }
        }
    
        //自定义ServiceConnection
        internal inner class MyConnection : ServiceConnection {
            override fun onServiceDisconnected(p0: ComponentName?) {
            }
    
    
            override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
                myBinder = p1
            }
        }
    }
    

    這裏我們會分別使用bind和start的方式來調起Service

    先使用startService的方式
    這時候,從日志可以看到,我們的service起來了
    在这里插入图片描述

    查看ADJ為500,即屬於Service分類,很符合,這時候我們將我們的程序按返回或者按Home,再次查看ADJ,你會發現,現在變爲了800
    在这里插入图片描述
    即B_Service,解釋爲老舊的service,從ADJ看就知道,這個service的優先級不高哈
    ,而如果我們再等待一會時間,你會發現,ADJ最終會變爲900,不幸的進入了Cache,這時候就算你重新進入你的APP,Service的ADJ也不會提升了,除非你再次調用StartService,大家不妨試一下。

    接下來我們使用bind Service的方式,查看ADJ,你會發現ADJ變爲了100
    在这里插入图片描述
    這簡直就是突飛猛進了啊,這樣的ADJ也説明了,我們這個Service變爲了一個很重要的存在,LMK殺手不會把它當成目標了,這也很好理解,畢竟我們有一個交互界面和用戶正在交互著,而托這個交互界面的福,我們bind的Service自然而然地也需要始終保持存在才對。接下來我們把我們的APP再次按Home按鈕,讓其回到launcher界面,你會發現,ADJ變爲了700
    在这里插入图片描述
    再次進入APP,又變爲了100,而按下返回按鈕,你會發現其ADJ直接變成了900,這也説得通,因爲我們與用戶交互的Activity都退出了,自然而然地,也就不需要我們的Service了,所以直接進入了Cache,雖然Service還沒有退出,不過也半死不活了。
    有些朋友其實可以發現,就是我們上面的程序中,變爲ADJ 900后,就算你再次進入你的APP,與用戶有了交互的Activity,我們的Service的ADJ也不會變低了,除非你再次調用bind或startService,才能再次激活它。

    獨立Service APP的調用行爲分析

    分析到這裏,你可能會問:“你説的這種情況屬於從自己的APP中調起Service,我在實際中可能會有另一個單獨的Service 程序來處理任務,而不是在自身APP中注冊的Service”,沒錯,現在我們就來分析一下這種情況。想要實現這種方式的調用的話:

    首先我們需要將上述APP的Manifest中,唯一的Launcher Activity去掉,使他變爲沒有入口Activity,注意,這樣安裝的時候,有些手機上還會提示有風險哦。修改后的AndroidManifest為這樣:

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service android:name=".ForegroundService"
        	android:process=":remote"
            >
            <intent-filter>
                <action android:name="com.karl.android.foregroundService" />
            </intent-filter>
        </service>
    
    </application>
    

    這時候我們新建一個TestForeground APP,在MainActivity中調用這個Service,一樣我們會用start和bind的方式來調用,代碼如下:

    class MainActivity : AppCompatActivity() {
        internal var myBinder: IBinder? = null
        private lateinit var conn : ServiceConnection
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            btnStartService.setOnClickListener {
    			Intent().apply {
                       component = ComponentName("com.karl.android.foregroundservice", "com.castles.android.foregroundservice.ForegroundService")
                       startService(this)
                }
            }
            btnBindService.setOnClickListener{
                if(!::conn.isInitialized){
                    Log.d("MainActivity", "btnBindService")
                    conn = MyConnection()
                    Intent().apply {
                        component = ComponentName("com.karl.android.foregroundservice", "com.castles.android.foregroundservice.ForegroundService")
                        bindService(this, conn, Context.BIND_AUTO_CREATE)
                    }
                }
            }
        }
    
        override fun onDestroy() {
            super.onDestroy()
            Log.d("MainActivity", "onDestroy")
            if(::conn.isInitialized){
                unbindService(conn)
            }
        }
    
        internal inner class MyConnection : ServiceConnection {
            override fun onServiceDisconnected(p0: ComponentName?) {
            }
    
    
            override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
                myBinder = p1
            }
        }
    }
    

    注意,這裏我們先還是使用startService來啓動,這時候,你會發現,程序直接crash了,查看日志
    在这里插入图片描述
    意思貌似是説,我們的程序沒有運行在後臺的權限,這就是Android的一個限制。
    在解決這個問題之前,我們先要瞭解到一個知識點,就是如何區分是前臺的APP還是後臺的APP呢?

    區分以限制Service為目的的後臺APP和前臺APP

    那麽,説到前臺APP和後臺APP,大家應該都有一定的瞭解,但是這裏提到的前臺和後臺其實是專指在當下環境下用來限制Service行爲的一種區分,和正常我們理解的前臺後臺還是有區別的,Google文檔上的解釋為:
    在这里插入图片描述
    大家可以把它理解爲我在需要啓動Service的時候才需要知道的一種APP的前後臺狀態,它與我們經常理解的内存管理中的前臺後臺概念是不一樣的。

    那麽哪些情況下會屬於在前臺的情況呢?Google同樣給出了判斷依據,其中只要有一條滿足,則為前臺APP:
    在这里插入图片描述
    從這裏可以看出來,第一條中説明了,只要有一個可見APP,不管是started還是paused狀態,其都代表是一個前臺APP,然而我們的Service程序第一條和第二條我們都沒有滿足,所以我們想要直接調起我們的Service是行不通的,所以這邊報錯是理所應當的,那麽在這裏,你自己可以試一下,如果我們將ForegroundService先使用帶Activity的方式讓其起來后,你再調用startService,你會發現,不會報錯了,這也印證了這一限制。

    那麽爲了想要測試該行為,我們還能夠繼續進行下去嗎?辦法還是有的,你可以去APP裏面先把它的後臺運行權限以及自動啓動和允許自動啓動打開。

    我這邊試了二種手機的打開方式:
    華爲手機需要在應用管理-》應用耗電管理裏面,點選應用設置為手動管理,打開允許自動啓動和允許關聯啓動和後臺運行。

    OPPO手機需要在應用管理中點選應用,打開允許其他應用自動啓動和允許自動啓動,耗電保護-》打開允許後臺運行。

    (注意:如果你使用的是Google系統的手機,比如我在模擬器Nexus5X上試驗下來的結果顯示,默認其對於background程序的限制是放開的,但是你直接調用的話,也是會報錯的,所以在Google系統上還是不能這樣來處理,大家也可以試一下看看。)

    這樣,我們的程序就可以在某些手機上正常的使用startService進行調用了,這時候再次調用,你會發現正常運行了,然而這種方式還有一個巨大問題,就是在你按返回鍵或者是按Home建回到Launcher的時候,service直接就停止了,畢竟人家骨子裏還是不支持你在後臺運行的,再次試了一下bindService的方式,也是半斤八兩,雖然按Home鍵會堅挺一會,查看ADJ為900而已啦。。。

    四、startForegroundService方法的調用行爲測試

    所以上述的做法,在Android 8.0以後是行不通的了,那麽我們要怎麽辦呢?對了,這時候該使用startForegroundService了,修改一下調用代碼:

    Intent().apply {
       component = ComponentName("com.karl.android.foregroundservice", "com.castles.android.foregroundservice.ForegroundService")
    //                   startService(this)
       ContextCompat.startForegroundService(this@MainActivity, this)
    }
    

    這次再試一下,你會發現,我們的service也可以起來,但是好景不長,等了5秒後,竟然又給我報錯了,
    在这里插入图片描述
    錯誤消息很明顯,告訴我們,你調用了startForegroundService方法來啓動Service,但是你沒有調用startForeground方法來設置service,這就是Android最新版本對前臺Service做的限制,所以我們來解決一下這個問題,在ForgroundService APP的Service的onCreate方法中,加入一個startForeground方法的調用

    override fun onCreate() {
            super.onCreate()
            Log.d("ForegroundService", "onCreate")
            val notification = createNotification()
            startForeground(1, notification)
        }
    

    注意,這裏的Id和notification為必須的,這也是Android的規定,如果使用startForeground,必須給他設置一個id和Notification對象,并且,如果是Android O版本,還需要給這個Notification對象設置一個Channel,這就設置一下:

    private fun createNotification(): Notification {
            val notificationChannelId = "FOREGROUND SERVICE CHANNEL"
            // depending on the Android API that we're dealing with we will have
            // to use a specific method to create the notification
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    
                val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                val channel = NotificationChannel(
                    notificationChannelId,
                    "Endless Service notifications channel",
                    NotificationManager.IMPORTANCE_HIGH
                ).apply {
    //                description = "Endless Service channel"
    //                enableLights(true)
    //                lightColor = Color.RED
    //                enableVibration(true)
    //                vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
                }
                notificationManager.createNotificationChannel(channel)
            }
    
    //        val pendingIntent: PendingIntent = Intent(this, MainActivity::class.java).let { notificationIntent ->
    //            PendingIntent.getActivity(this, 0, notificationIntent, 0)
    //        }
    
            val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) Notification.Builder(
                this,
                notificationChannelId
            ) else Notification.Builder(this)
    
            return builder
    //            .setContentTitle("Endless Service")
    //            .setContentText("This is your favorite endless service working")
    //            .setContentIntent(pendingIntent)
    //            .setSmallIcon(R.mipmap.ic_launcher)
    //            .setTicker("Ticker text")
    //            .setPriority(Notification.PRIORITY_HIGH) // for under android 26 compatibility
                .build()
        }
    

    其中注釋部分大家可以自行嘗試,沒有的話,也是沒關係的。

    注意:這邊存在一個比較坑的問題,在實踐過程中發現,在我的Android 6.0的設備上,如果我的app只有一個service的情況下,Manifest裏面我可以選擇不給這個app設置icon,這時候,在將這個service設置為前臺service的時候,是會報錯的,所以要格外注意。

    好了,再讓我們來試一下,調用startForegroundService,service正常啓動,等待數分鐘后,一切正常,查看ADJ為200
    在这里插入图片描述
    這時候,我們不管是返回還是回到桌面亦或者把TestForeground APP進程手動幹掉,都能保持200的ADJ,簡直炸裂,而且我有測試過,這個程序基本上你不主動去使用内存優化的程序,一個晚上你看電影玩游戲,都是不會被Kill掉的。我們再試一下bind的方式,你會發現,他的ADJ為100,這和我們之前測試的情況很像,只不過,在你按Home回到桌面的時候,這時候其ADJ為200,而不是之前的700,按返回還是會變回900,而直接幹掉TestForeground APP進程,也會停止ForegroundService。但是如果你使用過startForegroundService調啓過,那在上述行爲下,ADJ再變也只會變爲200,不會變爲700或是900了。

    所以,必定對該行爲進行限制,試想一下,如果大家都能使用獨立的前臺Service APP來運行自己的任務,而且這個任務又這麽頑固的話,那會造成手機的内存消耗過快而得不到合理的清理的情況發生,所以我們得用另外一種方式去做才行。

    將Service再次回歸到APP中

    那麽我們既然不能獨立的一個service運行,但我們還是可以像我在第三章節的演示程序中那樣,在APP中注冊一個Service去執行一些後臺任務,這樣我們甚至都不需要去打開自動啓動以及關聯啓動選項,我們在第三章節中的前半部分也沒有去在意這個地方不是嗎?

    將Manifest中的Activity先釋放,我們現在只運行這個Foreground Service程序,並將MainActivity中調用的方法改爲使用startForegroundService的方式:

    btnStartService.setOnClickListener {
       val intent = Intent()
       intent.setClassName("com.karl.android.foregroundservice", "com.castles.android.foregroundservice.ForegroundService")
    // startService(intent)
       ContextCompat.startForegroundService(this@MainActivity, intent)
    }
    

    這樣,儅你的主程序退出的時候,service也會一起跟著退出,如果主程序在後臺運行的話,能保證service被系統殺掉的概率變低,因爲查看service的ADJ為200。

    我們還能將之前在service注冊的地方的process屬性去除,即把service與主程序的進程運行在一起,我們來試一下

    按下Start按鈕,service啓動,查看ADJ為0,將程序切到launcher,再次查看,你會發現,ADJ為50?
    在这里插入图片描述
    你肯定會説:“這個50我對應不到上面說的分類啊?”,是的,這個沒有完全對應,但是我想,他應該表示的是一種範圍值,其分類應該也是FOREGROUND_APP_ADJ一層的吧,可以使用dumpsys meminfo進行查看。

    我們過一會兒再次查看,這個時候ADJ變爲了200,注意,是200,而不是之前第三章節中的700啊,那麽它竟然改變了我們原有APP這一行爲下的ADJ,太棒了,這樣一來,其實也間接的降低了我們程序的被殺死的概率咯。
    這時候不管你是退出程序還是返回Home,都不會停止該service了,除非你把整個進程手動幹掉,那麽他還是會和service一起死掉的。

    另外,還記不記得在第三章節的示例中,我們有提到,如果你使用的是Google的原生系統,你可以嘗試一下使用startForegroundService來調起另一個未在運行的Service APP的這種方式,你會發現,其實是可以調起的,所以如果你像我們公司一樣,使用的是自己Build的Firmware,那麽可以采納使用這種調起方式去做,這樣可以保證能夠調起你的Service,并且可以使Service不會因爲你的調起的APP的進程的關閉而被關閉。

    五、總結

    最後,通過上面的試驗,我們知道了startService在新的Firmware中是如何被限制的,并且知道了startForegroundService的用法,以及一些特殊場景下的使用手法,這些限制都説明了Google越來越看重用戶體驗了,畢竟用戶至上!!!

    展开全文
  • gmm matlab源码 动态背景下的前景提取 本项目为本科毕业设计的题目。 本实验旨在提出一种动态背景下的快速前景算法。首先,利用帧差、GMM[1]等方法计算出...Foreground。 在“Preprocess/import_data.m”文件中设置数
  • Location_Updates-Background_Foreground 今天(20/03/2019)。 经过2个月的搜索,我找到了适用于Oreo及以上设备的最佳代码。 本守则的特点1.此代码效果很好,在这里您可以在应用程序处于后台或前台时更新位置。 2....
  • 您应该将其解压缩到skins目录中名为foreground的文件夹中。 另外,您可以使用git来克隆存储库,这使得使用以下命令非常容易地更新代码: git clone https://github.com/thingles/foreground.git 之后,您可以随时...
  • this code uses to convert a video into frames and contains a code for draw a bounding box around foreground objects
  • This package contains m-files for the segmentation of a moving foreground from video with a static background
  • Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE报错原因报错解决 (这里默认你已经写好了正常的录制屏幕流程)Android技术生活交流 更多其他页面-自定义View-实用功能合集...

    android 10.0录制屏幕报错. Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE

    • 更多其他页面-自定义View-实用功能合集:点击查看


    报错原因

    1. Android10.0以上的录制屏幕需要获取到FOREGROUND_SERVICE权限

    2. Android10.0以上实例化mediaProjection需要在service里进行

    3. Android10.0以上录制屏幕需要添加notification,提醒用户该app正在录制屏幕

    Caused by: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION


    报错解决 (这里默认你已经写好了正常的录制屏幕流程)

    1. AndroidManifest.xml内的service添加foregroundServiceType
    android:foregroundServiceType="mediaProjection"
    
    1. 在申请录屏幕权限后的返回数据onActivityResult内对sdk进行判断,并实现startForegroundService,这样我们就可以在service内进行初始化mediaProjection
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    screenRecordService.setMediaProject(MediaProject); //将MediaProject传进service里,这个medieaProject应该为 null
                    screenRecordService.setMediaProjectionManager(MediaProjectionManager); //将mediaProjectionManager传进service里,这个manager你应该已经在`ServiceConnection`时实例化好了()
                 
                    Intent service = new Intent(this, ScreenRecordService.class);
                    service.putExtra("code", resultCode);
                    service.putExtra("data", data);
                    startForegroundService(service);
                }
            }
    

    调过startForegroundService会执行service的 onStartCommand 方法,之后我们就可以在service里进行实例化 MediaProject

     @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                createNotificationChannel(); //创建通知栏,你正在录屏
                Bundle bundle = intent.getExtras();
                MediaProject = mediaProjectionManager.getMediaProjection( bundle.getInt("code",-1), Objects.requireNonNull(intent.getParcelableExtra("data")));
                if(null != mOnStartCommandListener){
                    mOnStartCommandListener.finished(mediaProjection!=null);
                }
            }
    
            return START_STICKY;
        }
    
    1. 构造Notification
        private void createNotificationChannel() {
            Notification.Builder builder = new Notification.Builder(this.getApplicationContext()); //获取一个Notification构造器
            Intent nfIntent = new Intent(this, TutorW2Activity.class); //点击后跳转的界面,可以设置跳转数据
    
            builder.setContentIntent(PendingIntent.getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
                    .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
                    //.setContentTitle("SMI InstantView") // 设置下拉列表里的标题
                    .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
                    .setContentText("is running......") // 设置上下文内容
                    .setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
    
            /*以下是对Android 8.0的适配*/
            //普通notification适配
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                builder.setChannelId("notification_id");
            }
            //前台服务notification适配
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
                NotificationChannel channel = new NotificationChannel("notification_id", "notification_name", NotificationManager.IMPORTANCE_LOW);
                notificationManager.createNotificationChannel(channel);
            }
    
            Notification notification = builder.build(); // 获取构建好的Notification
            notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
            startForeground(110, notification);
    
        }
    

    Android技术生活交流

    微信 ----- qq群



    展开全文
  • 设置点击效果foreground

    2021-06-04 01:24:02
    android:foreground最近一段时间研究了一下plaid,想学习一下material design。这里记录一下view的background和foreground。平时的话我们设置点击效果,为了简便,大多数人应该都会使用android:background="@...

    android:foreground

    最近一段时间研究了一下plaid,想学习一下material design。

    这里记录一下view的background和foreground。

    平时的话我们设置点击效果,为了简便,大多数人应该都会使用

    android:background="@drawable/selecterDrawable"

    //或是

    android:background="?selectableItemBackground"

    但是相信都注意到一个情况,就是在有ImageView的时候,点击效果无法渲染在ImageView上面

    在看plaid和medium app的时候发现它们的点击效果就很好,看了plaid的xml发现是设置了foreground

    所以奥秘就在这里,以后设置点击效果的话可以设置foreground

    android:layout_width="match_parent"

    android:layout_height="200dp"

    android:layout_gravity="center"

    android:clickable="true"

    android:foreground="@drawable/middle_grey"

    android:padding="@dimen/activity_vertical_margin"

    android:stateListAnimator="@animator/anim_rise">

    android:layout_width="100dp"

    android:layout_height="100dp"

    android:layout_gravity="center_vertical"

    android:layout_marginLeft="50dp"

    android:scaleType="fitCenter"

    app:srcCompat="@drawable/image"/>

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:layout_gravity="center_vertical|right"

    android:text="foreground"/>

    因为api 21以上才能使用ripple效果,所以最好区分以下

    api21以下的middle_grey及效果

    5d278262517aa4578f457cb45c1b6825.gif

    api21以上的middle_grey

    android:color="@color/mid_grey">

    356731449f97e7dd1b1921c726ef8767.gif

    可以看出效果要好很多

    限制: 想要确保有以上效果需要注意几点

    确保view是可以点击的(即有click事件或者clickable="true")

    确保安卓版本在6.0(M)及以上或者以FrameLayout本身及其子类作为容器

    附赠:

    AS快速导航menu的icon

    a906a509e53e41521435dc0c0e71bfa6.png

    其实平时一直都没注意过,后来fork了这个项目的时候突然出现了一个小图标,才发现这一点,感觉挺不错的。

    讲一下怎么简单设置icon

    9a24096cc9b7845c3efa73c511be28df.png

    在项目上右键,点击Change Icon选择自己的icon就好了

    GIPHY CAPTURE : 录屏 GIF

    推荐一下这款mac上的录屏软件,非常实用而且颜值蛮高,个人觉得比LICEcap更好。

    650ca0c4922b751ccd384782d250c9ee.png

    录制完成之后,你能简单剪辑 GIF 和选择播放速度、大小。

    d08da5290edd5504b1be0e7ba85732b5.png

    可以看这里了解更多

    展开全文
  • We present a method to extract foreground object regions efficiently from image sequences. Scale-invariant feature transform algorithm is adopted to estimate the descriptor firstly by matching between...
  • android Foreground Service 前台服务/notification全局通知 前言 要素简介 前台服务(Foreground Service) 全局通知(notification) 功能实现 声明服务与获取权限 Notification 初始化 NotificationChannel 通知...
  • 通过读取视频流,基于OpenCV建立背景模型,以用于前景检测
  • android启动Service和ForegroundService

    千次阅读 2022-03-22 22:00:16
    最近有个需求需求"开启前台服务,然后进行持续定位"没有用过特此记录一下 之前用的都是Service class SocketService : Service() { private var webSockethandler: WebSocketHandler? = null ...
  • -----------------------------------------------前言君--------------------------------------------------正好碰到了这个foreground属性平时没怎么用到过。这次用到,就特意的去看了下。在这里记录一下。-------...
  • 资源分类:Python库 所属语言:Python 资源全名:foreground-0.1.1.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
  • Android Foreground Service

    千次阅读 2019-06-27 13:43:18
    为了防止后台服务被系统干掉,我们需要将服务提升为前台服务。...uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> FOREGROUND_SERVICE Added in API level 28 Android...
  • to detect foreground objects in single images1Institute of Mathematics of the Ro
  • 前景提取四种方法,好学易懂,适合初学者!
  • foreground extracting

    2013-03-28 12:01:47
    前景提取技术研究

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 87,699
精华内容 35,079
关键字:

foreground