Android几种定时任务实现方式汇总

2023-10-30

目录

前言

方式一:AlarmManager

API19之前AlarmManager常用的一些方法

参数说明

使用举例

AlarmManager实例Demo讲解(包含版本适配以及高版本设置重复闹钟)

AlarmManager总结

方式二:Handler实现方式

 采用Handle与线程的sleep(long)方法

采用Handler的postDelayed(Runnable, long)方法

采用Handler与timer及TimerTask结合的方法

方式三:ScheduledExecutorService

延迟不循环任务schedule方法

方式三:RXjava实现

方式四:WorkManager实现定时任务

总结


前言

    Android开发当中,定时器的场景太多太多,比如过多久轮询一次业务需求,或者轮询网络以及多少秒的倒计时,记录一下给需要的人一些帮助

  Android中的定时任务一般有两种实现方式,一种是使用 Java API 里提供的 Timer 类,一种是使用 Android 的 Alarm 机制。这两种方式在多数情况下都能实现类似的效果,但 Timer 有一个明显的短板,它并不适用于那些需要长期在后台运行的定时任务。我们都知道,为了能让电池更加耐用,每种手机都会有自己的休眠策略,Android 手机就会在长时间不操作的情况下自动让 CPU 进入到睡眠状态,这就有可能导致 Timer 中的定时任务无法正常运行。而 Alarm 则具有唤醒 CPU 的功能,它可以在需要执行定时任务的时候大吼一声:“小UU,不要跟我 bbll ,赶紧给我起来干活,不然你看我扎不扎你就完了。” 需要注意,这里唤醒 CPU 和唤醒屏幕完全不是一个概念,千万不要混淆。

方式一:AlarmManager

API19之前AlarmManager常用的一些方法

  • set(int type,long startTime,PendingIntent pi) //该方法用于设置一次性定时器,到达时间执行完就GG了
  • setRepeating(int type,long startTime,long intervalTime,PendingIntent pi)//该方法用于设置可重复执行的定时器
  • setInexactRepeating(int type,long startTime,long intervalTime,PendingIntent pi)//该方法用于设置可重复执行的定时器。与setRepeating相比,这个方法更加考虑系统电量,比如系统在低电量情况下可能不会严格按照设定的间隔时间执行闹钟,因为系统可以调整报警的交付时间,使其同时触发,避免超过必要的唤醒设备。

参数说明

int type:闹钟类型,常用有几个类型,说明如下: | | | |--|--| | AlarmManager.ELAPSED_REALTIME |表示闹钟在手机睡眠状态下不可用,就是睡眠状态下不具备唤醒CPU的能力(跟普通Timer差不多了),该状态下闹钟使用相对时间,相对于系统启动开始。 | |AlarmManager.ELAPSED_REALTIME_WAKEUP|表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间| |AlarmManager.RTC|表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间| |AlarmManager.RTC_WAKEUP|表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间|

long startTime: 定时任务的出发时间,以毫秒为单位。

PendingIntent pi: 到时间后的执行意图。PendingIntent是Intent的封装类。需要注意的是,如果是通过启动服务来实现闹钟提 示的话,PendingIntent对象的获取就应该采用Pending.getService(Context c,int i,Intent intent,int j)方法;如果是通过广播来实现闹钟提示的话,PendingIntent对象的获取就应该采用 PendingIntent.getBroadcast(Context c,int i,Intent intent,int j)方法;如果是采用Activity的方式来实现闹钟提示的话,PendingIntent对象的获取就应该采用 PendingIntent.getActivity(Context c,int i,Intent intent,int j)方法。关于PendingInten不是本文重点,请自行查阅使用方法。

使用举例

需求:定义一个在CPU休眠情况下也能执行的闹钟,到==指定时间==发送一次广播,代码如下:

AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,21);
        calendar.set(Calendar.MINUTE,14);
        calendar.set(Calendar.SECOND,00);//这里代表 21.14.00
        Intent intent = new Intent("Li_ALI");  
        intent.putExtra("msg","阿力起床了啊");     
        PendingIntent pi = PendingIntent.getBroadcast(this,0,intent,0);   
        // 到了 21点14分00秒 后通过PendingIntent pi对象发送广播  
        am.set(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),pi);

AlarmManager实例Demo讲解(包含版本适配以及高版本设置重复闹钟)

好了经过上面讲解,我相信你是似懂非懂的,因为没看到具体代码啊,简单,一个小Demo就全都明白了。 实现功能:在CPU休眠情况下依然可以设定时间启动一次服务,在服务中执行相应逻辑(Demo中只是打印Log),适配各个版本。

先看一下最核心的AlarmManagerUtils类(AlarmManager工具类):

package com.shanya.testalarm;

import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

import java.util.Calendar;

public class AlarmManagerUtils {

    private static final long TIME_INTERVAL = 10 * 1000;//闹钟执行任务的时间间隔
    private Context context;
    public static AlarmManager am;
    public static PendingIntent pendingIntent;

    private Calendar calendar;
    //
    private AlarmManagerUtils(Context aContext) {
        this.context = aContext;
    }

    //singleton
    private static AlarmManagerUtils instance = null;

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

    public void createGetUpAlarmManager() {
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, MyService.class);
        pendingIntent = PendingIntent.getService(context, 0, intent, 0);//每隔10秒启动一次服务
    }

    @SuppressLint("NewApi")
    public void getUpAlarmManagerStartWork() {

        calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY,23);
        calendar.set(Calendar.MINUTE,50);
        calendar.set(Calendar.SECOND,00);

        //版本适配 System.currentTimeMillis()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 4.4及以上
            am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                    pendingIntent);
        } else {
            am.setRepeating(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), TIME_INTERVAL, pendingIntent);
        }
    }

    @SuppressLint("NewApi")
    public void getUpAlarmManagerWorkOnOthers() {
        //高版本重复设置闹钟达到低版本中setRepeating相同效果
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上
            am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
                    System.currentTimeMillis() + TIME_INTERVAL, pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 4.4及以上
            am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
                    + TIME_INTERVAL, pendingIntent);
        }
    }
}

AlarmManagerUtils就是将与AlarmManager有关的操作都封装起来了,方便解耦。很简单,主要就是版本适配了,上面已经讲解够仔细了,这里就是判断不同版本调用不同API了。

MainActivity代码:

package com.shanya.testalarm;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    private AlarmManagerUtils alarmManagerUtils;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        alarmManagerUtils = AlarmManagerUtils.getInstance(this);
        alarmManagerUtils.createGetUpAlarmManager();

        Button button = findViewById(R.id.am);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                alarmManagerUtils.getUpAlarmManagerStartWork();
                Toast.makeText(getApplicationContext(),"设置成功",Toast.LENGTH_SHORT).show();
            }
        });

    }
}

MainActivity中就是调用AlarmManagerUtils中已经封装好的代码进行初始化以及点击Button的时候调用getUpAlarmManagerStartWork方法完成第一次触发AlarmManager。

最后看下服务类中具体做了什么。 MyService类:

package com.shanya.testalarm;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.RequiresApi;

public class MyService extends Service {
    private static final String TAG = "MyService";
    public MyService() {
    }

    @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.M)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d(TAG, "run: ");
            }
        }).start();
        AlarmManagerUtils.getInstance(getApplicationContext()).getUpAlarmManagerWorkOnOthers();
        return super.onStartCommand(intent, flags, startId);
    }
}

AlarmManager总结

好了,本文到此就该结束了,相信经过以上讲述你对AlarmManager有了更进一步全面了解,在我们使用的时候请不要滥用,考虑一下用户电量,尽量优化自己APP。

方式二:Handler实现方式

 采用Handle与线程的sleep(long)方法

Handler主要用来处理接受到的消息。这只是最主要的方法,当然Handler里还有其他的方法供实现,有兴趣的可以去查API,这里不过多解释。
 1. 定义一个Handler类,用于处理接受到的Message。


    Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            // 要做的事情  
            return false;
        }
    });

2. 新建一个实现Runnable接口的线程类,如下:

 
public class MyThread implements Runnable {  
    @Override  
    public void run() {  
        // TODO Auto-generated method stub  
        while (true) {  
            try {  
                Thread.sleep(10000);// 线程暂停10秒,单位毫秒  
                Message message = new Message();  
                message.what = 1;  
                handler.sendMessage(message);// 发送消息  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  
}  

3. 在需要启动线程的地方加入下面语句:

new Thread(new MyThread()).start();  

4. 启动线程后,线程每10s发送一次消息。

5.最后记得在onDestroy回收清空

采用Handler的postDelayed(Runnable, long)方法

1. 定义一个Handler类

Handler handler=new Handler();  
Runnable runnable=new Runnable() {  
    @Override  
    public void run() {  
        // TODO Auto-generated method stub  
        //要做的事情  
        handler.postDelayed(this, 2000);  
    }  
};  

2. 启动计时器

handler.postDelayed(runnable, 2000);//每两秒执行一次runnable.  

3. 停止计时器

handler.removeCallbacks(runnable);   

采用Handler与timer及TimerTask结合的方法

1. 定义定时器、定时器任务及Handler句柄

private final Timer timer = new Timer();  
private TimerTask task;  
Handler handler = new Handler() {  
    @Override  
    public void handleMessage(Message msg) {  
        // TODO Auto-generated method stub  
        // 要做的事情  
        super.handleMessage(msg);  
    }  
}; 

2. 初始化计时器任务

task = new TimerTask() {  
    @Override  
    public void run() {  
        // TODO Auto-generated method stub  
        Message message = new Message();  
        message.what = 1;  
        handler.sendMessage(message);  
    }  
};   

3. 启动定时器

timer.schedule(task, 2000, 2000);  

4. 停止计时器

timer.cancel();  

方式三:ScheduledExecutorService

介绍:

ScheduledExecutorService有线程池的特性,也可以实现任务循环执行,可以看作是一个简单地定时任务组件,因为有线程池特性,所以任务之间可以多线程并发执行,互不影响,当任务来的时候,才会真正创建线程去执行
我们在做一些普通定时循环任务时可以用它,比如定时刷新字典常量,只需要不断重复执行即可,这篇文章讲解一下它的用法以及注意事项,不涉及底层原理

注意:我们都知道,在使用线程池的时候,如果我们的任务出现异常没有捕获,那么线程会销毁被回收,不会影响其他任务继续提交并执行,但是在这里,如果你的任务出现异常没有捕获,会导致后续的任务不再执行,所以一定要try...catch

延迟不循环任务schedule方法

schedule(Runnable command, long delay, TimeUnit unit)
参数1:任务
参数2:方法第一次执行的延迟时间
参数3:延迟单位
说明:延迟任务,只执行一次(不会再次执行),参数2为延迟时间

案例说明:

private ScheduledExecutorService mScheduledExecutorService;

    mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        mScheduledExecutorService.scheduleAtFixedRate(() -> {
            try {
                //有网络的情况下合成播放
                if (ConfigApp.isExtranet()) {
                    syntheticConsumption();
                }
            } catch (Exception e) {
                Timber.i("合成mScheduledExecutorService出错了!");
            }
        }, 0, 1, TimeUnit.SECONDS);

方式三:RXjava实现

rxjava实现方式需要导包,根据需要导入对应的包即可

    //rxjava2
    api 'io.reactivex.rxjava2:rxjava:2.2.19'
    api 'io.reactivex.rxjava2:rxandroid:2.1.1'
    api 'com.jakewharton.rxbinding2:rxbinding:2.2.0'

工具类封装:

package com.maxvision.fyj.common.utils;

import android.content.Context;

import androidx.annotation.NonNull;

import com.maxvision.fyj.common.aop.CheckNetAspect;

import org.jetbrains.annotations.NotNull;

import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * name:cl
 * date:2022/8/16
 * desc:
 */
public class RxTimer {

    private Disposable mDisposable;
    private Disposable mDisposable2;


    /**
     * milliseconds毫秒后执行指定动作
     *
     * @param milliSeconds
     * @param rxAction
     */
    public void timer(long milliSeconds, final RxAction rxAction) {
        Observable.timer(milliSeconds, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable disposable) {
                        mDisposable = disposable;
                    }

                    @Override
                    public void onNext(@NonNull Long number) {
                        if (rxAction != null) {
                            rxAction.action(number);
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        //取消订阅
                        cancel();
                    }

                    @Override
                    public void onComplete() {
                        //取消订阅
                        cancel();
                    }
                });
    }

    /**
     * 每隔milliseconds毫秒后执行指定动作
     *
     * @param milliSeconds
     * @param rxAction
     */
    public void interval(long milliSeconds, final RxAction rxAction) {
        Observable.interval(milliSeconds, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(@NonNull Disposable disposable) {
                        mDisposable = disposable;
                    }

                    @Override
                    public void onNext(@NonNull Long number) {
                        if (rxAction != null) {
                            rxAction.action(number);
                        }
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    /**
     * 网络判断
     */
    public void networkCallback(Context context, RxNetwork rxNetwork) {
        Observable.just(CheckNetAspect.pingFactory(context))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Boolean>() {
                    @Override
                    public void onSubscribe(@NotNull Disposable d) {
                        mDisposable2 = d;
                    }

                    @Override
                    public void onNext(@NotNull Boolean aBoolean) {
                        rxNetwork.networkCB(aBoolean);
                    }

                    @Override
                    public void onError(@NotNull Throwable e) {
                        rxNetwork.networkCB(false);
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    /**
     * 取消订阅
     */
    public void cancel() {
        if (mDisposable != null && !mDisposable.isDisposed()) {
            mDisposable.dispose();
        }
        if (mDisposable2 != null && !mDisposable2.isDisposed()) {
            mDisposable2.dispose();
        }
    }

    public interface RxAction {
        /**
         * 让调用者指定指定动作
         *
         * @param number 时间
         */
        void action(long number);
    }

    public interface RxNetwork {
        void networkCB(boolean network);
    }
}

使用方法:

        rxTimer = new RxTimer();
        rxTimer.interval(5000, number -> {
            LogUtils.e("MQTT","startMqtt===========");      
        });
       //取消
       rxTimer.cancel();

方式四:WorkManager实现定时任务

同样的如果不清楚WorkManager的基础使用,推荐大家看看教程

Android架构组件WorkManager详解

WorkManager的使用相对来说也比较简单, WorkManager组件库里面提供了一个专门做周期性任务的类PeriodicWorkRequest。但是PeriodicWorkRequest类有一个限制条件最小的周期时间是15分钟。

WorkManager 比较适合一些比较长时间的任务。还能设置一些约束条件,比如我们每24小时,在设备充电的时候我们就上传这一整天的Log文件到服务器,比如我们每隔12小时就检查应用是否需要更新,如果需要更新则自动下载安装(需要指定Root设备)。

场景如下,还是那个放在公司前台常亮并且一直运行在前台的平板,我们每12小时就检查自动更新,并自动安装,由于之前写了 AlarmManager 所以安装成功之后App会自动打开。

Data inputData2 = new Data.Builder().putString("version", "1.0.0").build();
        PeriodicWorkRequest checkVersionRequest =
                new PeriodicWorkRequest.Builder(CheckVersionWork.class, 12, TimeUnit.HOURS)
                        .setInputData(inputData2).build();

        WorkManager.getInstance().enqueue(checkVersionRequest);
        WorkManager.getInstance().getWorkInfoByIdLiveData(checkVersionRequest.getId()).observe(this, workInfo -> {
            assert workInfo != null;
            WorkInfo.State state = workInfo.getState();
          
            Data data = workInfo.getOutputData();
            String url = data.getString("download_url", "");
             //去下载并静默安装Apk    
            downLoadingApkInstall(url)
        });
/**
 * 间隔12个小时的定时任务,检测版本的更新
 */
public class CheckVersionWork extends Worker {

    public CheckVersionWork(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @Override
    public void onStopped() {
        super.onStopped();
    }

    @NonNull
    @Override
    public Result doWork() {
        Data inputData = getInputData();
        String version = inputData.getString("version");
   
        //接口获取当前最新的信息
       
       //对比当前版本与服务器版本,是否需要更新

       //如果需要更新,返回更新下载的url
   
       Data outputData = new Data.Builder().putString("key_name", inputData.getString("download_url", "xxxxxx")).build();
      //设置输出数据
        setOutputData(outputData);

        return Result.success();
    }
}

这个时间太长了不好测试,不过是我之前自用的代码,没什么问题,哪天有时间做个Demo把日志文件导出来看看才能看出效果。

那除此之外我们一些Log的上传,图片的更新,资源或插件的下载等,我们都可以通过WorkManager来实现一些后台的操作,使用起来也是很简单。

总结

这里我直接给出了一些特定的场景应该使用哪一种定时任务,如果大家的应用场景适合App内部的定时任务,应该优先选择内部的定时任务。

App外的定时任务,都是系统服务的定时任务,不一定保险,毕竟是和厂商(特别是国内的厂商)作对,厂商会想方设法杀死我们的定时任务,毕竟有风险。

关于系统服务的定时任务我感觉自己讲的不是很好,好在给出了一些方案和一些文章,大家如果对一些基础的使用或者底层原理感兴趣,可以自行了解一下。

关于系统服务的周期任务的使用如果有错误,或者版本兼容的问题,又或者有更多或更好的方法,也可以在评论区交流讨论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android几种定时任务实现方式汇总 的相关文章

  • 如何从一个活动中完成一系列开放的子活动?

    我正在尝试为我的应用程序制作一个退出按钮 无论如何 我能够跟踪我的应用程序中的所有活动实例 然后完成它们 但在某些情况下 仍有一些活动仍然存在 不知道怎么办 有没有什么方法可以杀死android中的特定应用程序 或者我可以通过任何其他方式退
  • Google Play 商店中基于服务的 Android 应用程序

    我正在开发一个应用程序 该应用程序仅包含一些服务 没有任何活动 即没有 UI 基本上 当用户在他 她的设备上安装应用程序时 我希望有 2 到 3 个服务在后台运行 对此我有几个疑问 应用程序安装后我的服务将如何启动 我的BroadcastR
  • OPENGL ES 不工作:无当前上下文

    我尝试了 OpenGL ES2 for Android 一书中所示的程序 但它不起作用 我已经在Odroid E 三星s3 三星y 三星star上进行了测试 the gl version suported returns 2 but i g
  • Android 中的 java.util.Observable 是线程安全的吗?

    Android 中的 java util Observable 是线程安全的吗 这文档 http developer android com reference java util Observable html说只有deleteObser
  • 如何在 Android 应用程序中单击按钮时打开 Gmail Compose?

    当我的 Android 应用程序中单击按钮时 我尝试打开 Gmail 撰写屏幕 我需要 Google 提供的 API 密钥吗 或者我需要在按钮 onClickListener 中做什么 任何形式的见解都非常值得赞赏 正如 JeffC 指出的
  • FLAG_ACTIVITY_REORDER_TO_FRONT 被忽略

    我有一个包含项目列表的 FragmentActivity 当应用程序处于后台时 可以推送该项目列表 发生这种情况时 我想创建一个状态栏通知并提醒用户更新 当用户单击通知时 活动应重新排序到前面并显示在屏幕上 同时在列表底部显示新项目 所以我
  • 在浏览器中打开 URL,即使我的应用程序为其注册了意图过滤器

    我的应用程序为某些 URL 注册了一个意图过滤器 因为它可以处理来自这些 URL 的数据 但是 在应用程序内部 我想提供一个按钮来在浏览器中打开这样的 URL 也就是说 如果设置了默认浏览器 则在默认浏览器中打开它 否则提供一个选择器 就像
  • 我的 Android 设备需要安装哪个驱动程序才能运行我的应用程序?

    我购买了 intex mobile 来在真实设备中测试我的 Android 应用程序 然而 该设备不存在于 OEM USB 驱动程序列表中 android 提供的设备列表中 我检查了 intex 官方网站 但不确定到底需要安装哪个驱动程序
  • 在 Android 中始终以横向模式打开相机

    在我的 Android 应用程序中 单击按钮后我希望相机以横向模式打开 即使我将手机旋转为纵向模式 相机也应始终处于横向模式或纵向模式 使用此代码在横向模式下打开相机 Intent cameraIntent new Intent Media
  • 无法在云控制台中启用 Maps SDK for Android

    我在云控制台中启用适用于 Android 的 Maps SDK 时遇到此问题 https console cloud google com https console cloud google com 它会抛出以下错误 附截图 我收到错误消
  • Android BLE 扫描在后台几分钟后停止

    当我为公司开发新冠肺炎接触者追踪应用程序时 我在后台遇到了 Android 扫描停止问题 这是我尝试过的 添加前台服务 禁用手机中所有与电池相关的优化选项 启用后台运行的应用程序 测试设备 搭载 Android 10 的 Galaxy S2
  • 更改 Android 中的媒体音量?

    我可以更改媒体音量吗 如何 到目前为止我用过这个 setVolumeControlStream AudioManager STREAM MUSIC 但有一个搜索栏并且想要更改媒体音量 而不是铃声音量 那么有人可以告诉我如何更改媒体音量onC
  • 如何知道用户是否在 Android 应用程序中输入了错误的密码(锁定屏幕)

    我正在开发一个 Android 应用程序 如果用户在 Android 锁定屏幕中输入错误的密码 则必须完成其中一项活动 例如 如果用户输入错误的密码 则会发送电子邮件 我将不胜感激任何帮助 提前致谢 Kshitij 锁屏在完全沙箱环境中运行
  • 如何在Android网格视图中设置单元格大小?

    我正在尝试为应用程序制作一个带有大图标的网格视图 但我找不到任何有关修改 Android 上网格布局上的单元格大小的教程 有人可以给我一个例子或相关链接吗 Thanks 就像另一个一样适配器视图 http developer android
  • Android Drawable 绘图性能?

    在我看来 我有一个简单的 ARGB 可绘制对象 大约需要 2 毫秒才能绘制 但我可以在 0 5 毫秒内绘制与位图相同的文件 只是一些快速代码 我真的不能认为它是一个选项 优化可绘制对象的绘制速度的最佳方法是什么 这取决于可绘制的数量以及每个
  • 如何更改 Android 12 启动屏幕中的图标形状?

    我想要矩形形状的启动屏幕图标 而不是 android 12 中的圆形形状 我不相信你可以 如果你看这里的第 3 点 https developer android com about versions 12 features splash
  • Android 4.2 - Environment.getExternalStorageDirectory().getPath() 行为

    我一直在开发一个android应用程序 在上次更新到4 2之前 我使用 Environment getExternalStorageDirectory getPath 它返回了我 storage sdcard0 但自从更新后我现在得到了 s
  • 使用 RecyclerView.Adapter 在 onBindViewHolder() 内设置 onItemClickListener

    我有一个自定义对象 学生班 public class Student private String name private String age public String getName return name public void
  • 如何从灰度字节缓冲区图像创建位图?

    我正在尝试使用新的 Android 人脸检测移动视觉 API 来处理帧图像 所以我创建了自定义检测器来获取帧并尝试调用 getBitmap 方法 但它为空 所以我访问了帧的灰度数据 有没有办法从它或类似的图像持有者类创建位图 public
  • 查询联系人 - 有时返回空游标

    我正在尝试查询联系人的显示名称 Override public void onActivityResult int requestCode int resultCode Intent data switch requestCode case

随机推荐

  • 蓝桥杯 算法训练 印章

    蓝桥杯 算法训练 印章 共有n种图案的印章 每种图案的出现概率相同 小A买了m张印章 求小A集齐n种印章的概率 输入输出 一行两个正整数n和m 一个实数P表示答案 保留4位小数 样例 2 3 0 7500 这是个dp问题 存在两个变量 印章
  • springboot基础篇—SpringBoot 配置

    1 配置文件 SpringBoot 使用一个全局配置文件 application yml application properties 配置文件放在 src main resources 目录或者 类路径 config 下 yml 是 YA
  • 【Spring Boot丨(11 )】json的集成

    集成JSON 概述 Jackson Gson JSON B 主页传送门 传送 概述 Spring boot 提供了三种json库的集成 Gson Jackson JSON B 上述三种库提供了将Java对象转换为JSON字符串以及将JSON
  • c语言全局变量fork,使用fork进行C语言编程()

    好吧我做错了什么 我在Ubuntu上这样做 我想让系统命令 ls 和一个参数如 a 然后让孩子执行它 然后父母只是打印出来 我不明白为什么我一直让 父母 返回两次 有任何想法吗 使用fork进行C语言编程 include include i
  • 内存四区(代码区 静态区 栈区 堆区)

    参考 内存四区 代码区 静态区 栈区 堆区 作者 今天天气眞好 发布时间 2021 04 01 18 09 13 网址 https blog csdn net qq 51118175 article details 115379779 sp
  • C#文件重命名工具

    文章目录 工具背景 4个文件介绍 RenamesSpecificPrefixFile exe config DataSave txt 工具介绍 重命名的存储方式 文件夹介绍 源文件夹 结果 使用 PDF 视频 重名时坚持拷贝 可能的报错 工
  • json数据一次读取多条数据(数组形式,数组前面没有字符和有字符)的操作方法

    适用于读取的数据如图所示的数组格式 public static List
  • 田忌赛马

    题目描述 我国历史上有个著名的故事 那是在2300年以前 齐国的大将军田忌喜欢赛马 他经常和齐王赛马 他和齐王都有三匹马 常规马 上级马 超级马 一共赛三局 每局的胜者可以从负者这里取得200银币 每匹马只能用一次 齐王的马好 同等级的马
  • Linux(CentOS6.5_X86.64)编译libjpeg出现“checking host system type... Invalid configuration `x86_64-unknow...

    本文地址http comexchan cnblogs com 作者Comex Chan 尊重知识产权 转载请注明出处 谢谢 今天在编译libjpeg 的时候 遇到下面的报错 checking host system type Invalid
  • 实现以太坊的数据结构----状态树

    状态树 实现账户地址 addr 到账户状态 state 的映射 在以太坊中账户地址用160位 bits 表示 即40个16进制的数 1 为什么不能使用哈希表实现 用哈希表实现 就是系统中的全节点维护一个哈希表 在不考虑哈希碰撞的情况下 每次
  • Flask - 实现数据分页

    目录 一 Flask SQLAlchemy 直接获取分页后的数据 1 0 基于 flsk sqlalchemy 的批量数据插入 add all list 1 1 Pagination对象的常用属性 1 2 Pagination对象的常用方法
  • nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件,很全

    文章目录 前言 一 nginx简介 1 什么是 nginx 和可以做什么事情 2 Nginx 作为 web 服务器 3 正向代理 4 反向代理 5 负载均衡 6 动静分离 二 Nginx 的安装 Linux centos为例 1 准备工作
  • 吃透Spring源码(九):Spring实例化(createBeanInstance)源码解析

    一 createBeanInstance 方法概述 createBeanInstance 是Spring实例化的核心代码 它根据不同的情况会调用四种实例化方法 obtainFromSupplier 通过Supplier实例化 instant
  • 2021年最新IT职业技能全套图谱

    2021年最新IT职业技能图谱出炉 如图 包含各个方向 各个专业 按照以上技能图谱学习 保证你拿高薪
  • 解决ROS系统 rosdep update超时问题的新方法

    由于近期国内Github Raw的可用IP越来越少 通过修改hosts文件解决rosdep update超时问题的方法已经不太好用 本文通过修改rosdep源码中下载资源的函数来解决这一问题 网站https ghproxy com 支持gi
  • c++时间戳获取和转换

    1 使用api 可以使用windows下和linux下api函数来获取 比较简单 如下所示 int64 t getTimeStamp 毫秒数 int mSecond 0 if defined WIN32 SYSTEMTIME sys Get
  • video-player实现hls播放全过程

    安装依赖 npm install vue video player save 引入样式 第一个是videoJs的样式 后一个是vue video player的样式 因为考虑到我其他业务组件可能也会用到视频播放 所以就放在了main js内
  • typeScript--[数据定义]

    一 安装ts 1 命令行运行如下命令 全局安装 TypeScript npm install g typescript 2 安装完成后 在控制台运行如下命令 检查安装是否成功 tsc V 二 创建ts文件 1 创建一个day01 ts文件
  • Linux·C/C++主线程对子线程的影响

    这篇文章主要介绍了简单了解C语言中主线程退出对子线程的影响 文中通过示例代码介绍的非常详细 对大家的学习或者工作具有一定的参考学习价值 需要的朋友可以参考下 对于程序来说 如果主进程在子进程还未结束时就已经退出 那么Linux内核会将子进程
  • Android几种定时任务实现方式汇总

    目录 前言 方式一 AlarmManager API19之前AlarmManager常用的一些方法 参数说明 使用举例 AlarmManager实例Demo讲解 包含版本适配以及高版本设置重复闹钟 AlarmManager总结 方式二 Ha