GVKun编程网logo

为什么与Android的兼容性不是Linux发行版中的常见功能?(为什么与android的兼容性不是linux发行版中的常见功能)

2

以上就是给各位分享为什么与Android的兼容性不是Linux发行版中的常见功能?,其中也会对为什么与android的兼容性不是linux发行版中的常见功能进行解释,同时本文还将给你拓展Android

以上就是给各位分享为什么与Android的兼容性不是Linux发行版中的常见功能?,其中也会对为什么与android的兼容性不是linux发行版中的常见功能进行解释,同时本文还将给你拓展Android 13 虚拟化让 Pixel 6 可以运行 Windows 11、Linux 发行版、Android 4.4 closest 的兼容性问题、Android 插件化的兼容性(上):Android O 的适配、Android兼容性优化-8.0之后禁止在后台启动服务的兼容性优化等相关知识,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

本文目录一览:

为什么与Android的兼容性不是Linux发行版中的常见功能?(为什么与android的兼容性不是linux发行版中的常见功能)

为什么与Android的兼容性不是Linux发行版中的常见功能?(为什么与android的兼容性不是linux发行版中的常见功能)

由于Android基于Linux(我理解的只是Linux上的Java层),我想知道为什么Linux通常不会运行Android应用程序.

为什么Android兼容层有自己的桌面或X,而不是现代Linux发行版的标准功能?

解决方法:

Android基于Linux内核.那个,以及一个非常精简的BusyBox.所有其余的GNU/X11/Apache/Linux/TeX/Perl/Python/FreeCiv都不在Android上.问为什么Linux不能模仿Android就像问为什么卡车不能效仿飞机 – 毕竟它们都是底部有轮子的大型车辆.

大多数Android应用程序专门设计用于处理便携式设备的局限性:计算资源有限,能耗最高,屏幕小,无需外部输入设备. PC风格的计算机通常有类似的应用程序,但位置相关的应用程序除外,这些应用程序通常在移动设备之外无用.

您可以在Google提供的emulator中运行Android应用程序.这是一个开发人员工具,因为在PC风格的计算机上运行Android应用程序的主要应用是测试它们.

在Linux和Android结合的系统上有一些工作(例如Ubuntu for Android,但它已被放弃),主要在中等格式设备(平板电脑)上运行,但也在较小的设备(电话)上运行,以允许移动设备的用户运行现有的来自大格式世界的应用程序.由于这两个操作系统主要是兼容的内核,因此可以并排运行其余的操作系统(这比重写Android库在Linux / X11上工作更容易,反之亦然).然而,存在重大的技术困难.可能最大的一个是GUI在完全不同的软件上运行:Linux使用the X Window System和其他unix变体一样,而Android有its own stack.

Android 13 虚拟化让 Pixel 6 可以运行 Windows 11、Linux 发行版

Android 13 虚拟化让 Pixel 6 可以运行 Windows 11、Linux 发行版

第一个Android 13 开发者预览版可能让人感觉有点平淡无奇,但在 Google Pixel 6 智能手机等硬件上可以实现完全虚拟化的隐藏宝石。

这意味着现在可以在谷歌 Tensor 驱动的手机上运行几乎任何操作系统,包括 Windows 11、Ubuntu 或 Arch Linux Arm 等 Linux 发行版,并以接近原生的速度运行。

Android 13 虚拟化 Pixel 6 Linux

Android 和 Web 开发人员“ kdrag0n ”使用“Pixel 6 + Android 13 DP1 上的 KVM 管理程序”在 Pixel 6 上使用 Ubuntu 21.10、Arch Linux Arm、Void Linux 和 Alpine Linux 测试了几个为 Aarch64 编译的 Linux 发行版。他/她进一步解释:

据我所知,我们现在几乎可以在生产设备上获得完整的 EL2。受保护的 KVM 是可选的,可以在每个 VM 的基础上启用,但对于未受保护的 VM,似乎可以使用完整的 KVM 功能。

EL2 是指 Arm 异常级别,如Arm 开发人员网站上所述。kdrag0n 并没有就此止步,而是通过相同的 Android 13 虚拟化设法让 Windows 11 在 Pixel 6 上运行。

像素 6 Windows 11Pixel 6 上的 Windows 11

Windows 手机又回来了!更严重的是,我们必须看看一切是否按预期工作,但这看起来很有希望。

但为什么 Google 会在 Android 中启用虚拟化呢?他们不太可能只是想让用户在手机上安装 Linux 或 Windows。大约两个月前,Mishaal Rahman 谈到了这个问题:

......这是因为虚拟机管理程序可能存在也可能不存在于设备上,当它们存在时,它们通常甚至没有用于其预期目的,即在虚拟机中运行操作系统!相反,它们用于增强内核的安全性(或至少尝试)以及在 Android 操作系统之外运行杂项代码(例如 DRM、密码学和其他闭源二进制文件的第三方代码) .

因此,这主要用于安全性和 DRM 等二进制文件。Mishaal 的文章还指出了虚拟化模块源代码和说明如何开始使用受保护虚拟机的指南。

Ref: https://www.cnx-software.com/2022/02/14/android-13-virtualization-lets-pixel-6-run-windows-11-linux-distributions/

Android 4.4 closest 的兼容性问题

Android 4.4 closest 的兼容性问题

 

原文链接: Android 4.4 closest 的兼容性问题

上一篇: mathjax 导出svg和png

下一篇: vue cli3 打包兼容Android 4.4

https://developer.mozilla.org/en-US/docs/Web/API/Element/closest

基本上参考mdn即可

 

这个函数也是这次才发现的... 以前没用过, 主要作用是从自身开始向根元素查找, 返回第一个满足比较器的元素, 如果没有满足的, 返回null

 

polyfill

首先从自己开始, 然后进行比较器的判断, 如果为真则表示找到了返回元素, 否则向上级查找, 注意只会向上级查找, 不会查找兄弟节点!

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.msMatchesSelector || 
    Element.prototype.webkitMatchesSelector;
}

if (!Element.prototype.closest) {
  Element.prototype.closest = function(s) {
    var el = this;

    do {
      if (Element.prototype.matches.call(el, s)) return el;
      el = el.parentElement || el.parentNode;
    } while (el !== null && el.nodeType === 1);
    return null;
  };
}

 

Android 插件化的兼容性(上):Android O 的适配

Android 插件化的兼容性(上):Android O 的适配

      首先声明,《Android 插件化开发指南》这本书所介绍的 Android 底层是基于 Android6.0(API level 23)的,而本书介绍的各种插件化解决方案,以及配套的 70 多个例子,在 Android7.0(API level 24)手机上测试都是能正常工作的。

     如果读者您的手机是 Android 26、27,甚至 28(也就是 Android P),那么会有 30 个插件化的例子不能正常工作,这是因为 Android 系统底层的源码改动导致的。

     本篇文章,专门介绍 Android O 的改动对插件化产生的影响,以及相应的插件化解决方案。

 

(一)从 ActivityManagerNative 的重构谈起

     首先是 ActivityManagerNative 这个类的 gDefault 字段,这个字段在 API 25 以及之前的版本,定义如下:

public abstract class ActivityManagerNative extends Binder implements IActivityManager {
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
}

 

     所以,我们可以通过反射获取 ActivityManagerNative 的 gDefault 字段,执行它的 create 方法,得到 IActivityManager 接口类型的对象。

     看到这个接口类型,我们眼前一亮,可以通过 Proxy.newProxyInstance 方法,hook 掉这个 IActivityManager 对象,拦截它的 startActivity 方法,把要启动的、没有在 Manifest 中声明的 Activity,替换成占坑 StubActivity,代码如下所示:

public static void hookAMN() throws ClassNotFoundException,
            NoSuchMethodException, InvocationTargetException,
            IllegalAccessException, NoSuchFieldException {

        //获取AMN的gDefault单例gDefault,gDefault是final静态的
        Object gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative", "gDefault");
        // gDefault是一个 android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
        Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", gDefault, "mInstance");

        // 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
        Class<?> classB2Interface = Class.forName("android.app.IActivityManager");
        Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class<?>[] { classB2Interface },
                new MockClass1(mInstance));

        //把gDefault的mInstance字段,修改为proxy
        Class class1 = gDefault.getClass();
        RefInvoke.setFieldObject("android.util.Singleton", gDefault, "mInstance", proxy);
    }

 

     我们在书中的第 5 章详细讲解过上述这些代码。但不幸的是,这些代码在 Android O(API level 26)以上的系统版本中就不能运行了,在运行到这句话的时候,gDefault 的值为空:

Object gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative", "gDefault");

 

     这是因为 Google 在 Android O 中,把 ActivityManagerNative 中的这个 gDefault 字段删除了,转移到了 ActivityManager 类中,但此时,这个字段改名为 IActivityManagerSingleton,所以在 Android P 中,要把这句话改为:

Object gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManager", "IActivityManagerSingleton");

    

     但这又不兼容于 Android O 以下的版本了,所以写一个 if-else 条件语句,根据 Android 系统的版本,来做不同的处理,如下所示:

Object gDefault = null;
        if (android.os.Build.VERSION.SDK_INT <= 25) {
            //获取AMN的gDefault单例gDefault,gDefault是静态的
            gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative", "gDefault");
        } else {
            //获取ActivityManager的单例IActivityManagerSingleton,他其实就是之前的gDefault
            gDefault = RefInvoke.getStaticFieldObject("android.app.ActivityManager", "IActivityManagerSingleton");
        }

 

(二)Element 和 DexFile 的兴衰史

     接下来我们把目光转移到插件类的加载。我们在书中介绍了 3 种加载方式:

     1. 为每一个插件创建一个 ClassLoader,用插件 ClassLoader 去加载插件中的类。

     2. 把所有插件中的 dex,都合并到宿主 App 的 dex 数组中。

     3. 把宿主 App 所使用的 ClassLoader,替换成我们自己创建的 ClassLoader,在这个新的 ClassLoader 中,有一个容器变量,承载所有插件的 ClassLoader,用来加载插件中的类。

     这其中,第 2 种方式的实现是最简单的,也就是合并所有插件的 dex 到一个数组中,具体代码实现如下所示:

public final class BaseDexClassLoaderHookHelper {

    public static void patchClassLoader(ClassLoader cl, File apkFile, File optDexFile)
            throws IllegalAccessException, NoSuchMethodException, IOException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        // 获取 BaseDexClassLoader : pathList
        Object pathListObj = RefInvoke.getFieldObject(DexClassLoader.class.getSuperclass(), cl, "pathList");

        // 获取 PathList: Element[] dexElements
        Object[] dexElements = (Object[]) RefInvoke.getFieldObject(pathListObj, "dexElements");

        // Element 类型
        Class<?> elementClass = dexElements.getClass().getComponentType();

        // 创建一个数组, 用来替换原始的数组
        Object[] newElements = (Object[]) Array.newInstance(elementClass, dexElements.length + 1);

        // 构造插件Element(File file, boolean isDirectory, File zip, DexFile dexFile) 这个构造函数
        Class[] p1 = {File.class, boolean.class, File.class, DexFile.class};
        Object[] v1 = {apkFile, false, apkFile, DexFile.loadDex(apkFile.getCanonicalPath(), optDexFile.getAbsolutePath(), 0)};
        Object o = RefInvoke.createObject(elementClass, p1, v1);

        Object[] toAddElementArray = new Object[] { o };
        // 把原始的elements复制进去
        System.arraycopy(dexElements, 0, newElements, 0, dexElements.length);
        // 插件的那个element复制进去
        System.arraycopy(toAddElementArray, 0, newElements, dexElements.length, toAddElementArray.length);

        // 替换
        RefInvoke.setFieldObject(pathListObj, "dexElements", newElements);
    }
}

 

     这个思路没问题。注意其中的这么几句话:

Class[] p1 = {File.class, boolean.class, File.class, DexFile.class};
Object[] v1 = {apkFile, false, apkFile, DexFile.loadDex(apkFile.getCanonicalPath(), optDexFile.getAbsolutePath(), 0)};
Object o = RefInvoke.createObject(elementClass, p1, v1);
Object[] toAddElementArray = new Object[] { o };

 

     这几句话中,通过反射执行了 Element 的带有 4 个参数的构造函数,但不幸的是,在 Android O 以及之后的版本,这个带有 4 个参数的构造函数就被废弃了。

     此外,在这个构造函数中使用到的 DexFile 这个类,也被废弃了,对此 Google 给出的解释是,只有 Android 系统可以使用 DexFile,App 层面不能使用它。

     于是,我们不得不另辟蹊径,通过执行 DexPathList 类的 makeDexElements 方法,来生成插件中的 dex:

List<File> legalFiles = new ArrayList<>();
legalFiles.add(apkFile);

List<IOException> suppressedExceptions = new ArrayList<IOException>();

Class[] p1 = {List.class, File.class, List.class, ClassLoader.class};
Object[] v1 = {legalFiles, optDexFile, suppressedExceptions, cl};
Object[] toAddElementArray = (Object[])
RefInvoke.invokeStaticMethod("dalvik.system.DexPathList", "makeDexElements", p1, v1);

 

     这段代码,在 Android O 之前的版本也是适用的。所以,我们找到了比 DexFile 更好用的 makeDexElements 方法,进行 Hook。

Android兼容性优化-8.0之后禁止在后台启动服务的兼容性优化

Android兼容性优化-8.0之后禁止在后台启动服务的兼容性优化

前言

==本次主要内容包括:==

1、Android8.0之后IntentService启动异常跟踪

2、JobIntentService替代IntentService方案

一、Android8.0之后IntentService启动异常跟踪

项目中在做启动优化时,在Application 通过IntentService启动第三方组件时,bugly时常会上报如下问题:

*android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()*


# main(2)
android.app.RemoteServiceException
Context.startForegroundService() did not then call Service.startForeground()

1 android.app.ActivityThread$H.handleMessage(ActivityThread.java:2056)
2 android.os.Handler.dispatchMessage(Handler.java:106)
3 android.os.Looper.loop(Looper.java:192)
4 android.app.ActivityThread.main(ActivityThread.java:6959)
5 java.lang.reflect.Method.invoke(Native Method)
6 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:557)
7 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:875)
1、Service和IntentService使用场景以及区别

Service的使用场景:

a、Service是运行在主线程的,如果我们需要执行耗时操作,也是在Service创建Thread执行。

b、如果耗时操作无法在当前Activity生命周期执行完成的任务就需要在Service执行,比如异步下载等。

c、需要长时间在后台运行,跟随APP生命周期的任务需要在Service执行,比如Socket长连接。

d、Service是所有服务的基类,我们通常都是继承该类实现服务,如果使用该类,我们需要对Service的生命周期进行管理,在合适的地方停止Service。

IntentService特点

a、IntentService继承于Service,通过源码可以看到,是在Service的基础上增加了Handler、Looper、HandlerThread的支持;

b、只需要重写onHandleIntent(Intentintent)实现异步任务,这个方法已经是非UI线程,可以执行耗时操作;

c、一旦这个方法执行完毕,就会立刻执行stopSelf()停止服务,无需手动停止服务。

==Android 8.0新增了startForegroundService方法,用于启动前台服务,前台服务是指带有通知栏的服务,如果我们使用startForegroundService启动服务,那么必须在5秒内调用startForeground()显示一个通知栏,否则就会报错==

2、明明调用startforeground了为什么还会报Context.startForegroundService() did not then call Service.startForeground()

首先看下启动IntentService的调用:

private void startInitService() {
    Intent intent = new Intent(this, InitIntentService.class);
    intent.setAction("com.pxwx.student.action");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(intent);
    } else {
        startService(intent);
    }
}

在IntentService中的处理:

在onCreate方法中调用了startForeground方法

public class InitIntentService extends IntentService {

    public InitIntentService() {
        super("InitIntentService");
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private Notification getNotification() {
        NotificationChannel channel = new NotificationChannel("init", "", NotificationManager.IMPORTANCE_LOW);
        NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (manager != null) {
            manager.createNotificationChannel(channel);
        }
        return new Notification.Builder(this, "init")
                .setContentTitle("")
                .setContentText("")
                .setAutoCancel(true)
                .setSmallIcon(com.pxwx.student.core.R.mipmap.small_icon)
                .build();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //notification ID must not be 0 
            startForeground(1,getNotification());
        }
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        initThirdSDK();
    }

onCreate方法中明明调用startforeground了为什么还会报Context.startForegroundService() did not then call Service.startForeground()?

分析:

1、启动IntentService服务在Application中执行了多次,IntentService在第二次启动时还未停止的话不知执行onCreate方法,但会走onStart()方法,所以在onStart()方法中也执行startforeground

@Override
public void onStart(@Nullable Intent intent, int startId) {
    super.onStart(intent, startId);
    //主要是针对后台保活的服务,如果在服务A运行期间,保活机制又startForegroundService启动了一次服务A,那么这样不会调用服务A的onCreate方法,只会调用onStart方法
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForeground(1,getNotification());
    }
}

2、但是问题依旧存在,只是减少了该问题的发生,然后我又尝试了讲starttForeground方法放在了onHandleIntent中执行

@Override
protected void onHandleIntent(@Nullable Intent intent) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForeground(1,getNotification());
    }
    initThirdSDK();
}

3、但是问题依旧存在,只是又减少了该问题的发生,不能完全杜绝该问题的发生,具体分析下IntentService的特性:

a、IntentService如果第一次启动后,onhandleIntent没处理完,继续startService,不会再重新实例化这个Service了,而是将请求放到请求队列里,等待第一个处理完再处理第二个。这种情况下,只有一个线程在运行

b、、IntentService处理任务时是按请求顺序处理的,也就是一个接一个处理

3、 IntentService源码分析

1、首先看下IntentService的继承关系等声明信息:

public abstract class IntentService extends Service {
    ...
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

可以看出IntentService是继承自Service的抽象类,有个抽象方法onHandleIntent需要子类覆写,通过注解我们知道该方法的执行是在子线程中的。

2、其次看下IntentService中声明的字段

//volatile修饰,保证其可见性

//Service中子线程中的Looper对象
private volatile Looper mServiceLooper;
//与子线程中Looper关联的Hander对象
private volatile ServiceHandler mServiceHandler;
//与子线程HandlerThread相关的一个标识
private String mName;
//设置Service的标志位,根据它的值来设置onStartCommand的返回值
private boolean mRedelivery;

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

==mRedelivery是来处理onStartCommand返回值的一个标志位参数,着重看下onStartCommand的返回值在Service中定义的几个类型:==

public static final int START_CONTINUATION_MASK = 0xf;
public static final int START_STICKY_COMPATIBILITY = 0;
public static final int START_STICKY = 1;
public static final int START_NOT_STICKY = 2;
public static final int START_REDELIVER_INTENT = 3;

根据这几个类型的注释,可以翻译解释:

START_STICKY_COMPATIBILITY:兼容模式,如果Service在创建后,被系统杀死,此时不能保证onStartCommand方法会被执行(可能会被执行,也可能不会被执行);

START_STICKY:如果Service进程被kill掉,保留Service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建Service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到Service,那么参数Intent将为null;

START_NOT_STICKY:如果Service在启动后(从onStartCommand返回了)被系统杀掉了,在下一次调用Context.startService()之前,不会再创建Service。期间,也不接受空Intent参数的onStartCommand方法调用,因为空的Intent无法进行Service的创建;

START_REDELIVER_INTENT:在Service启动后,被系统杀掉了,将会重传最近传入的Intent到onStartCommand方法中对Service进行重建;

3、上边关于mRedelivery值控制onStartCommand的返回值的问题:

1、如果为true,则返回START_REDELIVER_INTENT,表示如果Service被系统杀死,可以进行重建并重传最近传入的Intent;

2、如果为false,则返回START_NOT_STICKY,表示如果Service被系统杀死,除非再次调用Context.startService(),不会对Servcie进行重建;

3、在Service被销毁的时候,会停止子线程的消息队列;

4、ntentService中还有一个设置mRedelivery的setter方法

4、总结分析:

==从IntentService的源码分析看,导致android.app.RemoteServiceException Context.startForegroundService() did not then call Service.startForeground():异常发生的原因:==

1、onStartCommand返回值的一个标志位参数默认是START_NOT_STICKY

2、如果Service在启动后(从onStartCommand返回了)被系统杀掉了,在下一次调用Context.startService()之前,不会再创建Service。期间,也不接受空Intent参数的onStartCommand方法调用,因为空的Intent无法进行Service的创建;

3、导致Context.startForegroundService() did not then call Service.startForeground()的原因可能是被系统杀掉了,未执行IntentService的onCreate、onStart、onHandleIntent方法中的startForeground方法

二、JobIntentService替代IntentService方案

综合上面的分析,没有能完全解决上面的异常情况,该如何解决呢?

通过IntentService源码中针对IntentService的部分注释如下:

 * <p><b>Note:</b> IntentService is subject to all the
 * <a href="/preview/features/background.html">background execution limits</a>
 * imposed with Android 8.0 (API level 26). In most cases, you are better off
 * using {@link android.support.v4.app.JobIntentService}, which uses jobs
 * instead of services when running on Android 8.0 or higher.
 * </p>

翻译一下:

IntentService受Android 8.0(API级别26)的所有后台执行限制的约束。在大多数情况下,在Android 8.0或更高版本上运行时您最好使用android.support.v4.app.JobIntentService而不是服务。
1、JobIntentService介绍

JobIntentService是Android 8.0 新加入的类,它也是继承自Service,根据官方的解释:

Helper for processing work that has been enqueued for a job/service. When running on Android O or later, the work will be dispatched as a job via JobScheduler.enqueue. When running on older versions of the platform, it will use Context.startService.

大概翻译一下:

JobIntentService用于执行加入到队列中的任务。对Android 8.0及以上的系统,JobIntentService的任务将被分发到JobScheduler.enqueue执行,对于8.0以下的系统,任务仍旧会使用Context.startService执行。
2、JobIntentService使用

1、在Manifest中声名Permission:

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

2、在Manifest中声名Service:

<service android:name=".InitIntentService" android:permission="android.permission.BIND_JOB_SERVICE" />

3、实现JobIntentService类:

public class InitIntentService extends JobIntentService {

    public static final int JOB_ID = 1;

    public static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, InitIntentService.class, JOB_ID, work);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // 具体逻辑
    }

}

4、调用启动实现JobIntentService

InitIntentService.enqueueWork(context, new Intent());

JobIntentService不需要关心JobIntentService的生命周期,不需要startService()方法,也就避免了开头中的crash问题,通过静态方法就可以启动,还是非常不错的。


关注我的技术公众号

image

我们今天的关于为什么与Android的兼容性不是Linux发行版中的常见功能?为什么与android的兼容性不是linux发行版中的常见功能的分享就到这里,谢谢您的阅读,如果想了解更多关于Android 13 虚拟化让 Pixel 6 可以运行 Windows 11、Linux 发行版、Android 4.4 closest 的兼容性问题、Android 插件化的兼容性(上):Android O 的适配、Android兼容性优化-8.0之后禁止在后台启动服务的兼容性优化的相关信息,可以在本站进行搜索。

本文标签: