针对AndroidO后台服务限制这个问题,本篇文章进行了详细的解答,同时本文还将给你拓展Android10蓝牙断开后台服务、Android:App客户端与后台服务的AIDL通信以及后台服务的JNI接口
针对Android O后台服务限制这个问题,本篇文章进行了详细的解答,同时本文还将给你拓展Android 10蓝牙断开后台服务、Android : App 客户端与后台服务的 AIDL 通信以及后台服务的 JNI 接口实现、Android Oreo的后台服务、Android O后台服务限制背后的逻辑等相关知识,希望可以帮助到你。
本文目录一览:- Android O后台服务限制
- Android 10蓝牙断开后台服务
- Android : App 客户端与后台服务的 AIDL 通信以及后台服务的 JNI 接口实现
- Android Oreo的后台服务
- Android O后台服务限制背后的逻辑
Android O后台服务限制
但我在NotificationManager
中看不到任何startServiceInForeground()方法.我错过了什么?
解决方法
您应该使用在26.0.0-beta1支持库版本中添加的@RobCo建议的以下方法
ContextCompat.startForegroundService()
是的,迁移指南是错误的,即使在API 26中,notificationmanager也没有名为startForegroundService()的方法.
PS:迁移指南已更正,现在它正确链接到ContextCompat.startForegroundService()方法.
Android 10蓝牙断开后台服务
原来,这还取决于电话类型本身。一些电话公司试图延长某些类型电话的电池寿命,因此它关闭了所有未使用的后台服务。
一种变通方法,可以通过创建一个循环来完成,该循环每隔一小段时间发送一条消息以维持连接。
Android : App 客户端与后台服务的 AIDL 通信以及后台服务的 JNI 接口实现
一、APP 客户端进程与后台服务进程的 AIDL 通信
AIDL(Android Interface definition language-“接口定义语言”) 是 Android 提供的一种进程间通信 (IPC:Inter-Process Communication) 机制,支持的数据类型:
1. Java 的原生类型;
2. String 和 CharSequence;
3. List 和 Map ,List 和 Map 对象的元素必须是 AIDL 支持的数据类型; 以上三种类型都不需要导入 (import);
4. AIDL 自动生成的接口 需要导入 (import);
5. 实现 android.os.Parcelable 接口的类. 需要导入 (import)。
Android studio 工程建立如下:
app 和 remoteserver 按常规应用建立,remoteservicecontract 通过新建 Android Library 生成:
也可以将原本的应用模块改成库模块:
然后在 remoteservicecontract 建立 aidl 目录并新建 AIDL 文件:
建立如下三个 AIDL 接口:
aidl 文件的声明和 java 实现如下:
(1)Entity.aidl 是声明本地实现的 android.os.Parcelable 接口的类
// Entity.aidl
package com.example.remoteserver;
parcelable Entity;
java 实现:
package com.example.remoteserver;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
public class Entity implements Parcelable {
private int age;
private String name;
private final String TAG = "Engity";
public Entity() {
}
public Entity(int age, String name) {
Log.i(TAG,"new age="+age+",name="+name);
this.age = age;
this.name = name;
}
protected Entity(Parcel in) {
age = in.readInt();
name = in.readString();
}
public static final Creator<Entity> CREATOR = new Creator<Entity>() {
@Override
public Entity createFromParcel(Parcel in) {
return new Entity(in);
}
@Override
public Entity[] newArray(int size) {
return new Entity[size];
}
};
public int getAge() {
Log.i(TAG,"get age="+age);
return this.age;
}
public void setAge(int age) {
Log.i(TAG,"set age="+age);
this.age = age;
}
public String getName() {
Log.i(TAG,"get name="+name);
return this.name;
}
public void setName(String name) {
Log.i(TAG,"set name="+name);
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
}
@Override
public String toString() {
return String.format("age=%s, name=%s", getAge(), getName());
}
}
(2)IRemoteService.aidl声明服务端供客户端调用的接口:
// IRemoteService.aidl
package com.example.remoteserver;
import com.example.remoteserver.Entity;
import com.example.remoteserver.ITVCallback;
// Declare any non-default types here with import statements
interface IRemoteService {
void doSomeThing(int anInt,String aString);
void addEntity(in Entity entity);
void setEntity(int index,in Entity entity);
List<Entity> getEntity();
void asyncCallSomeone( String para, ITVCallback callback);
}
java实现:
package com.example.remoteserver;
import android.Manifest;
import android.app.Service;import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Toast;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class RemoteService extends Service {
public static final String TAG = "RemoteService";
private List<Entity> data = new ArrayList<Entity>();
int mStartMode; // indicates how to behave if the service is killed
final RemoteCallbackList<ITVCallback> remoteCallbackList = new RemoteCallbackList<>();
public void onCreate() {
// Used to load the ''native-lib'' library on application startup.
System.loadLibrary("RemoteServiceJNI"); //加载native接口的c库
pthreadState = true;
DataThread datathread = new DataThread();
datathread.start();
Nano_Printf("service onCreate");
Nano_Printf(String.format("<%s>",stringFromJNI())); //调用JNI接口
}
public int onStartCommand(Intent intent, int flags, int startId) {
Nano_Printf("service onStartCommand");
return mStartMode;
}
/*返回Binder对象实例*/
public IBinder onBind(Intent intent) {
Nano_Printf("service on bind,intent = %s",intent.toString());
return binder;
}
public void onDestroy() {
Nano_Printf("service onDestroy");
pthreadState = false;
// 取消掉所有的回调
remoteCallbackList.kill();
}
private void Nano_Printf(String...args) {
String str = "";
for(int i = 0; i < args.length; i++){
str += args[i];
if( i != args.length - 1){
str += ", ";
}
}
Log.d(TAG, str);
}
/*生成的 Binder 对象实例,实现接口定义的方法*/
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override
public void doSomeThing(int anInt, String aString) throws RemoteException {
Log.i(TAG, String.format("rcv:%s, %s", anInt, aString));
}
@Override
public void addEntity(Entity entity) throws RemoteException {
Log.i(TAG, String.format("rcv:entity = %s", entity));
data.add(entity);
}
@Override
public List<Entity> getEntity() throws RemoteException {
Log.i(TAG, String.format("get:List<Entity> = %s", data));
return data;
}
public void setEntity(int index, Entity entity) throws RemoteException {
Log.i(TAG, String.format("set:entity[%d] = %s", index, entity));
data.set(index, entity);
}
@Override
/*客户端调用asyncCallSomeone接口并传过来callback实例,服务端注册callback并回调修改结果*/
public void asyncCallSomeone(String para, ITVCallback callback) throws RemoteException {
Log.i(TAG, String.format("asyncCallSomeone..."));
remoteCallbackList.register(callback);
final int len = remoteCallbackList.beginBroadcast();
for (int i = 0; i < len; i++) {
remoteCallbackList.getBroadcastItem(i).onSuccess(para + "_callbck");
}
remoteCallbackList.finishBroadcast();
}
};
/*native interface*/
public native String stringFromJNI();
}
(3)ITVCallback.aidl声明客户端向服务端注册的回调接口:
// Callback.aidl
package com.example.remoteserver;
// Declare any non-default types here with import statements
interface ITVCallback {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onSuccess(String aString);
}
app 客户端 Java 实现:
package com.example.administrator.sheldon_aidl;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.nfc.Tag;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
/*导入资源库中AIDL定义的类*/
import com.example.remoteserver.Entity;
import com.example.remoteserver.ITVCallback;
import com.example.remoteserver.IRemoteService;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private boolean mBound = false;
private IRemoteService iRemoteService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mBound) {
alert("未连接到远程服务");
return;
}
try {
Entity entity = new Entity(100, "sheldon");
if (iRemoteService != null){
iRemoteService.addEntity(entity); //调用服务端的接口添加成员变量
iRemoteService.registerCallBack(mCallback);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
findViewById(R.id.modify).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mBound) {
alert("未连接到远程服务");
return;
}
if (iRemoteService != null) {
try {
List<Entity> entityList = iRemoteService.getEntity();
int pos = 1;
if(entityList.size()>pos){
entityList.get(pos).setAge(1314);
entityList.get(pos).setName("li");
iRemoteService.setEntity(pos,entityList.get(pos)); //调用服务端的接口修改成员变量
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
findViewById(R.id.callback).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!mBound) {
alert("未连接到远程服务");
return;
}
if (iRemoteService != null) {
try {
final String para = "canshu";
iRemoteService.asyncCallSomeone(para, mCallback); //调用服务端的接口并传入回调
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
private void alert(String str) {
//解决在子线程中调用Toast的异常情况处理(还是有异常)
//Looper.prepare();
Toast.makeText(this, str, 0).show();
//Looper.loop();
}
@Override
protected void onStart() {
super.onStart();
if (!mBound) {
attemptToBindService(); // 尝试绑定服务
}
}
@Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mServiceConnection); // 解绑服务
mBound = false;
}
}
/**
* 尝试与服务端建立连接
*/
private void attemptToBindService() {
Intent intent = new Intent();
intent.setAction("com.example.REMOTE.myserver"); //这里的action由..\remoteserver\src\main\AndroidManifest.xml中指定
intent.setPackage("com.example.remoteserver"); //这里即为服务端进程包名
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
/*实现 ServiceConnection 接口,在其中拿到IRemoteService AIDL类*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(getLocalClassName(), "service connected");
iRemoteService = IRemoteService.Stub.asInterface(service);
mBound = true;
if (iRemoteService != null) {
try {
iRemoteService.doSomeThing(0, "anything string");
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(getLocalClassName(), "service disconnected");
mBound = false;
}
};
/*实现callback接口*/
private ITVCallback mCallback = new ITVCallback.Stub() {
@Override
public void onSuccess(String aString) throws RemoteException { //回调接口被服务端调用,获得结果并用Toast显示
Log.d("nano-client ", String.format("service arrived %s",aString));
alert(String.format("回调: %s", aString));
}
};
}
客户端和服务端的通信AIDL接口定义在remoteservicecontract库中,需要在各模块导入使用,
如果各模块在同一个Android Studio工程开发,可通过修改build.gradle直接应用:
也可以将生成的 aar, 提供给另一个工程导入使用:
二、后台服务的JNI接口实现:
目录结构如下:

1.声明native方法,如 RemoteService.java 中声明的:
/*native interface*/
public native String stringFromJNI();
2.通过javah生成native格式的头文件 com_example_remoteserver_RemoteService.h:
![]()
javah -d 【头文件生成路径】 -classpath 【java 文件路径】-jni 【包名。类名】
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_remoteserver_RemoteService */
#ifndef _Included_com_example_remoteserver_RemoteService
#define _Included_com_example_remoteserver_RemoteService
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_remoteserver_RemoteService
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_remoteserver_RemoteService_stringFromJNI
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
3. 根据生成的 jni 头文件建立 RemoteServiceJNI.c 文件实现其接口:
#include <jni.h>
#include <string.h>
#include <android/log.h>
#define TAG "nano-jni"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_remoteserver_RemoteService
* Method: stringFromJNI
* Signature: ()Ljava/lang/String;
参数说明:
Native的对应函数名要以“Java_”开头,后面依次跟上Java的“package名”、“class名”、“函数名”,中间以下划线“_” 分割,在package名中的“.”也要改为“_”。
关于函数的参数和返回值也有相应的规则。对于 Java 中的基本类型如 int 、double 、char 等,
在 Native 端都有相对应的类型来表示,如 jint 、jdouble 、jchar 等;其他的对象类型则统统由 jobject 来表示,
(String 是个例外,由于其使用广泛,故在 Native 代码中有 jstring 这个类型来表示)。
而对于 Java 中的数组,在 Native 中由 jarray 对应,具体到基本类型和一般对象类型的数组则有 jintArray 等
和 jobjectArray 分别对应(String 数组在这里没有例外,同样用 jobjectArray 表示)。
另外在 JNI 的 Native 函数中,其前两个参数 JNIEnv * 和 jobject 是必需的,前者是一个 JNIEnv 结构体的指针,这个结构体中定义了很多 JNI 的接口函数指针,
使开发者可以使用 JNI 所定义的接口功能;后者指代的是调用这个 JNI 函数的 Java 对象,有点类似于 C++ 中的 this 指针。
在上述两个参数之后,还需要根据 Java 端的函数声明依次对应添加参数,如下 Java 中声明的 JNI 函数没有参数,则 Native 的对应函数只有类型为 JNIEnv * 和 jobject 的两个参数。
*/
JNIEXPORT jstring JNICALL Java_com_example_remoteserver_RemoteService_stringFromJNI
(JNIEnv *env, jobject thiz){
return (*env)->NewStringUTF(env, "Hi! Sheldon, I`m JNI ~");
}
#ifdef __cplusplus
}
#endif
4. 编译 c 文件生成 so:
Android studio 的gradle3.0版本以下可以配置NDK编译c/cpp文件:
修改对应模块的build.gradle在defaultConfig中添加:
//gradle3.0以上已经不支持该方式
ndk {
moduleName "libRemoteServiceJNI" //指定生成的so文件名
ldLibs "log", "z", "m" //添加log库
abiFilters "armeabi", "armeabi-v7a", "x86" //支持cpu的类型
}
而 gradle3.0 以上版本需要用 CMake 工具编译:
首先 Android studio 安装 CMake 工具:
然后同样在 defaultConfig {} 中添加编译参数:
// 使用Cmake工具
externalNativeBuild {
cmake {
cppFlags ""
//生成多个版本的so文件
abiFilters ''armeabi-v7a'' //,''arm64-v8a'',''x86'',''x86_64''
}
}
另外在 defaultConfig {} 的外一层即 android {} 中配置编译脚本的路径:
// 配置CMakeLists.txt路径
externalNativeBuild {
cmake {
path "CMakeLists.txt" //编译脚本
}
}
比较关键的是 CMakeLists.txt 编译脚本,具体内容如下:
###############################
#1.cmake verson,指定cmake版本
cmake_minimum_required(VERSION 3.4.1)
#2.C++ 的编译选项是 CMAKE_CXX_FLAGS
# 指定编译参数,可选
#SET(CMAKE_C_FLAGS "-Wno-error=format-security -Wno-error=pointer-sign")
#3.设置cmake生成so输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
#4.包含头文件,导入第三方动态库
include_directories(
${CMAKE_SOURCE_DIR}/src/main/jni/include
)
#5.指定源文件和编译生成so名及类型
# 生成在intermediates/cmake/和以上指定的目录下(指定的话build.gradle设置pickFirst避免冲突)
add_library(RemoteServiceJNI SHARED
${CMAKE_SOURCE_DIR}/src/main/jni/RemoteServiceJNI.c)
#6.设置需要生成so的第三方链接库
target_link_libraries(
RemoteServiceJNI
log
android
)
#添加子目录,将会调用子目录中的CMakeLists.txt
#ADD_SUBDIRECTORY(one)
#ADD_SUBDIRECTORY(two)
###############################
配置好编译环境后,点击 make project 生成 so 在 remoteserver\build\intermediates\cmake\debug\obj\armeabi-v7a\libRemoteServiceJNI.so
在 java 中加载调用即可:
如果遇到: More than one file was found with OS independent path ''lib/armeabi-v7a/xxx.so'' 的报错,则在 build.gradle 中的 android {} 里添加:
packagingOptions { //For Error: More than one file was found with OS independent path
pickFirst ''lib/armeabi-v7a/libnano_socket.so''
pickFirst ''lib/armeabi-v7a/libRemoteServiceJNI.so''
}
完整工程已上传到 GitHub: https://github.com/dragonforgithub/sheldon_aidl.git
Android Oreo的后台服务
如何在不显示通知点的情况下继续在Android Oreo中进行后台服务?我使用通知继续后台服务,但我不想显示正在运行的服务的通知。
答案1
小编典典如果您可以在此处的某处正确阅读
Android Oreo 8.0
文档,则可能未在此处发布此问题。
步骤1: 确保您将服务作为前台启动,
Service
如以下代码所示
ContextCompat.startForegroundService(mainActivity, new Intent(getContext(), GpsServices.class));ContextCompat.startForegroundService(mainActivity, new Intent(getContext(), BluetoothService.class));ContextCompat.startForegroundService(mainActivity, new Intent(getContext(), BackgroundApiService.class));
步骤2: 使用通知显示您的服务正在运行。在的
onCreate
方法中的代码下面添加一行Service
。
@Overridepublic void onCreate() { ... if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForeground(NOTIFICATION_ID, notification); } ...}
步骤3:
notification
在服务停止或销毁时删除。
@Overridepublic void onDestroy() { ... if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { stopForeground(true); //true will remove notification } ...}
该解决方案的一个问题是,它将一直显示,notification
直到您Service
在 Android Oreo 8.0
上运行的所有设备上运行为止。
我确信即使该应用程序处于后台或处于终止状态,该解决方案也能正常工作。
重要说明: 在ANDROID OREO 8.0中必须显示在后台运行服务的通知(在后台或处于杀死状态的APP)。您 不能逃避 它。因此,
建议 您对应用程序进行必要的更改,以按照ANDROID遵循或要求的 最佳 做法正确地使它正常工作。
我希望这可能有助于解决您的问题。
Android O后台服务限制背后的逻辑
这种新行为显然是为了阻止应用程序在后台执行大量操作而用户甚至不知道,这是公平的.
但是,建议的解决方法之一是使用预定作业.但这不是也可能导致应用程序“在没有用户意识到的情况下在后台做很多事情”吗?唯一的区别是Android决定何时完成这些工作,而不是应用程序.
那么,新限制究竟是什么意思呢?或许我错过了一些基本的东西.
编辑:这不是this question的副本…一个是关于使用startServiceInForeground()作为替代(或关于相关的文档),而这个问题是关于使用预定作业作为替代(以及是否失败的目的)完全新的限制).这些是完全不同的选择.
解决方法
在我看来(通过阅读文档后)这是我们使用新JobScheduler的唯一专业人士.
但是如果你看一下Job Scheduler Improvements in O
Scheduled jobs Now support several new constraints:
JobInfo.isRequireStorageNotLow()
Job does not run if the device’s available storage is low.JobInfo.isRequireBatteryNotLow()
Job does not run if the battery level is at or below the criticial threshold; this is the level at which the device shows the Low battery warning system dialog.NETWORK_TYPE_METERED
Job requires a metered network connection,like most cellular data plans.
您会注意到,对于每个JobInfo,您可以指定需要为作业启动哪些约束.
Once the job has been started,it is then free to use as many resources as it wants for however long it wants.
这似乎是正确的,因为没有来自文档的指示,如果/何时再次需要资源会发生什么.但是,您可以将大任务分解为较小的任务,然后使用[JobScheduler.enque()](https://developer.android.com/reference/android/app/job/JobScheduler.html#enqueue(android.app.job.JobInfo,android.app.job.JobWorkItem))方法确保JobScheduler在队列中启动下一个作业之前停止否则需要资源.但是,如果JobScheduler为Job暂停/停止提供方法或回调可能没有必要(由于文档没有进入,因此可疑)我自己从未尝试过.
结论:
总而言之,虽然新API并不完全限制后台服务在所需资源可用时运行,但它确实为编码人员提供了一个平台,以便仅在满足上述条件时限制它们启动,同时还限制最小行代码需要.在大多数情况下,应该最大限度地减少电池使用并为用户提供更流畅的体验.
今天关于Android O后台服务限制的讲解已经结束,谢谢您的阅读,如果想了解更多关于Android 10蓝牙断开后台服务、Android : App 客户端与后台服务的 AIDL 通信以及后台服务的 JNI 接口实现、Android Oreo的后台服务、Android O后台服务限制背后的逻辑的相关知识,请在本站搜索。
本文标签: