首页
网站首页
公司简介
资讯中心
推荐内容
返回顶部
Android自定义控件之局部图片放大镜【澳门葡京赌场】,数据库升级
发布时间:2020-03-30 20:47
浏览次数:

一、Jobscheduler诞生

Android 5.0系统以前,在处理一些特定情况下的任务,或者是为了应用的保活,我们通常是使用了Service常驻后台来满足我们的需求。当达到某个条件时触发该Service来进行相应任务的处理。或者仅仅是为了我们自己的应用不被系统回收销毁。这样做在满足了自己应用的需求的同时也消耗了部分硬件性能。对用户的体验上,和Android系统环境上都有不利的影响。而且在其它地方,大多数开发者都认为在后台永驻进程,是获取用户的隐私,是不合法的。然而在我国也许是因为开发商的需求大,迫切想要达到自己的目标,使用永驻的进程可以完成用户行为分析和推送等其它后台的业务。因此在开发的模式上采取了极端的个体主义思想。

Android 5.0系统以后,Google为了优化Android系统,提高使用流畅度以及延长电池续航,加入了在应用后台/锁屏时,系统会回收应用,同时自动销毁应用拉起的Service的机制。同时为了满足在特定条件下需要执行某些任务的需求,google在全新一代操作系统上,采取了Job (jobservice & JobInfo)的方式,即每个需要后台的业务处理为一个job,通过系统管理job,来提高资源的利用率,从而提高性能,节省电源。这样又能满足APP开发商的要求,又能满足系统性能的要求。Jobscheduler由此应运而生。

如需下载源码,请访问https://github.com/fengchuanfang/Rxjava2Tutorial*文章原创,转载请注明出处:*Rxjava2入门教程四:Scheduler线程调度器Scheduler赋予RxJava简洁明了的异步操作,可以说是RxJava中最值得称道的地方。在之前的代码中,Observable发射数据流,Observer接收响应数据流,以及Operators加工数据流均是在同一个线程中,实现出来的是一个同步的函数响应式。但是函数响应式的实际应用却不是这样的,大部分都是后台处理,前台响应的一个过程。Observable生成发射数据流,以及Operators加工数据流都是在后台线程中进行,而Observer在前台线程中接受并相应数据。Scheduler可以让RxJava的线程切换变得简单明了,即使程序逻辑变得十分复杂,他依然能够保持简单明了。

Sqlite作为android重要的数据存储库,原生类SQLiteOpenHelper使用起来繁琐容易出错,本着"偷懒"的目的,来学习一下当下十分受欢迎的ORM 框架: greenDAO;网上教程不少,但是大都是针对3.0之前的版本,greenDAO2和3的构建和生成代码的方式区别很大,最终还是通过greenDAO github上的介绍,慢慢摸索,成功的在项目中构建了greenDAO,本文章写于greenDao更新至3.2版本

官方介绍:greenDAO github greenDAO 官网 网友的博客:Android ORM框架 GreenDao3.0的使用

greenDAO3开始使用注解的方式定义实体类,并且是通过安装gradle插件来生成代码。

构建之前,不熟悉gradle概念的同学建议学习Gradle for android 系列教程

先来看下greenDAO 官方构建介绍:

澳门葡京游戏 1greenDAO github 构建介绍

本文的知识点一览

1.自定义控件及自定义属性的写法,你也将对onMesure有更深的认识2.关于bitmap的简单处理,及canvas区域裁剪 3.本文会实现两个自定义控件:FitImageViewBiggerView,前者为后者作为铺垫。4.最后会介绍如何从guihub生成自己的依赖库,这样一个完整的自定义控件库便ok了。5.本项目源码见文尾捷文规范第一条

二、Jobscheduler特性&适用

1、支持在一个任务上组合多个条件2、内置条件:设备待机、设备充电和连接网络3、支持持续的job,这意味着设备重启后,之前被中断的job可以继续执行4、支持设置job的最后执行期限5、根据你的配置,可以设置job在后台运行还是在主线程中运行

需要在Android设备满足某种场合才需要去执行处理数据:1、应用具有可以推迟的非面向用户的工作(定期数据库数据更新)2、应用具有当插入设备时希望优先执行的工作(充电时才希望执行的工作备份数据)3、需要访问网络Wi-Fi 连接时需要进行的任务(如向服务器拉取内置数据)4、希望作为一个批次定期运行的许多任务

1、Job Scheduler只有在Api21或以上的系统支持。2、Job Scheduler是将多个任务打包在一个场景下执行。3、在系统重启以后,任务会依然保留在Job Scheduler当中,因此不需要监听系统启动状态重复设定。4、如果在一定期限内还没有满足特定执行所需情况,Job Scheduler会将这些任务加入队列,并且随后会进行执行。

使用Job Scheduler,应用需要做的事情就是判断哪些任务是不紧急的,可以交给Job Scheduler来处理,Job Scheduler集中处理收到的任务,选择合适的时间,合适的网络,再一起进行执行。把时效性不强的工作丢给它做

subscribeOn

Observable<T> subscribeOn(Scheduler scheduler) 

subscribeOn通过接收一个Scheduler参数,来指定对数据的处理运行在特定的线程调度器Scheduler上。若多次设定,则只有一次起作用。

开始搭建:
  • github中提示添加maven仓库,但是android studio 项目已经默认包含了jcenter仓库,而jcenter仓库就是maven仓库的一个分支,因此我们不要再添加仓库,直接添加classPath即可

澳门葡京游戏 2项目下build.gradle

  • 在moudle下的build.gradle文件中 申明插件以及配置greendao

澳门葡京游戏 3moudle 下build.gradle脚本中配置

  • 并在dependencies中添加compile
 /* green dao */ compile 'org.greenrobot:greendao:3.2.0
  1. 编写实体类:User类,使用greendao @Entity注解
import org.greenrobot.greendao.annotation.Entity;import org.greenrobot.greendao.annotation.Id;@Entitypublic class User { @Id private long id; private String userName; private int age; private String gender;}
  1. 编译项目,build后greendao插件会为所有带有该注解的实体生成Dao文件,以及DaoManager与DaoSession,默认生成目录为build/generated/source ,如果我们在gradle脚本中配置了,则会生成在我们的配置目录

澳门葡京游戏 4在配置目录中生成dao文件

不配置,则在默认目录(build/generated/source/greendao)下生成dao文件:

澳门葡京游戏 5不配置,则在默认目录下生成dao文件

1.实体@Entity注解schema:告知GreenDao当前实体属于哪个schemaactive:标记一个实体处于活动状态,活动实体有更新、删除和刷新方法nameInDb:在数据中使用的别名,默认使用的是实体的类名indexes:定义索引,可以跨越多个列createInDb:标记创建数据库表2.基础属性注解@Id :主键 Long型,可以通过@Id(autoincrement = true)设置自增长@Property:设置一个非默认关系映射所对应的列名,默认是的使用字段名 举例:@Property (nameInDb="name")@NotNul:设置数据库表当前列不能为空@Transient :添加次标记之后不会生成数据库表的列3.索引注解@Index:使用@Index作为一个属性来创建一个索引,通过name设置索引别名,也可以通过unique给索引添加约束@Unique:向数据库列添加了一个唯一的约束4.关系注解@ToOne:定义与另一个实体的关系@ToMany:定义与多个实体对象的关系
  • 使用单例模式构建一个工具类来获取daoSession等对象
public class GreenDaoHelper { private static DaoMaster.DevOpenHelper devOpenHelper; private static SQLiteDatabase database; private static DaoMaster daoMaster; private static DaoSession daoSession; /** * 初始化greenDao * 建议放在Application 中进行 */ public static void initDatabase(){ // 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。 // 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。 // 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。 // 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。 devOpenHelper = new DaoMaster.DevOpenHelper(AppContext.getInstance(),"cache-db",null);//数据库名 database = devOpenHelper.getWritableDatabase(); // 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。 daoMaster = new DaoMaster; daoSession = daoMaster.newSession(); } public static DaoSession getDaoSession() { return daoSession; } public static SQLiteDatabase getDb() { return database; }}
  • 增加数据(直接inser一个对象即可,十分简便)
 private UserDao mUserDao = GreenDaoHelper.getDaoSession().getUserDao(); public void insertData(){ //数据库的增删改查我们都将通过UserDao来进行,插入操作如下: mUserDao.insert(new User(null,"david",23,"male"));//id传null 即自增。==> 这里是Long类型而不是long }

小手一抖,一口气插了6条数据:

澳门葡京游戏 6小手一抖,一口气插了6条数据

  • 删除数据(删除数据和修改数据的思路一样,都是要先查找到数据),操作很简单,效果就不一一截图了
//查询id等于3的所有行并删除User user = mUserDao.queryBuilder().where(UserDao.Properties.Id.eq.build().unique();if (user == null) ToastUtils.show, "用户不存在!");else mUserDao.deleteByKey(user.getId;

//查询id小于5的集合并删除List<User> userList = (List<User>) mUserDao.queryBuilder().where(UserDao.Properties.Id.le.build;for (User user : userList) { mUserDao.delete;}

//删除所有数据mUserDao.deleteAll();
  • 修改数据
//修改id为2的行 User user = new User 2, "Nancy", 23, "female");mUserDao.update;

//查询id>= 3 且like ("%david%")User user = mUserDao.queryBuilder() .where(UserDao.Properties.Id.ge, UserDao.Properties.UserName.like("%david%")).build().unique();if (user == null) { ToastUtils.show, "用户不存在!");} else { user.setUserName; mUserDao.update;}
  • 查询数据
//查出所有数据List<User> users = mUserDao.loadAll(); //查询id为1~4之间的数,查出前2个List<User> users = mUserDao.queryBuilder() .where(UserDao.Properties.Id.between.limit.build;
实现效果一览:

1.放大镜效果1:

澳门葡京游戏 7放大镜效果1.gif

2.放大镜效果2:(使用了clipOutPath需要API26)

澳门葡京游戏 8放大镜效果2.gif

三、Jobscheduler初识

通俗的来讲,Jobscheduler也是通过Jobservice服务和JobInfo搭配来完成我们特定情况下需要完成的各种工作的一个新组件。和传统的service相比较,(从上边也可以了解到)它具有更优秀的表现,不仅对系统环境的友好,同时在特定的预置条件下进行我们期望的工作也节省了手机的电量,节约了系统资源。下边先了解下Jobservice和JobInfo。之后在完成搭配使用的用例以及应用衍生。

Jobscheduler的使用流程大概分为以下四个部分:

  • 派生JobService 子类,定义需要执行的任务
  • 从Context 中获取JobScheduler 实例
  • 构建JobInfo 实例,指定 JobService任务实现类及其执行条件
  • 通过JobScheduler 实例加入到任务队列

java.lang.Object ↳ android.content.Context ↳ android.content.ContextWrapper ↳ android.app.Service ↳ android.app.job.JobService

JobService是从Service中扩展出来的一个新类,继承Service。但是有两个重要的新接口。

下边使用用例代码来讲解:

public class JobSchedulerService extends JobService { @Override public boolean onStartJob(JobParameters params) { // 返回true,表示该工作耗时,同时工作处理完成后需要调用onStopJob销毁(jobFinished) // 返回false,任务运行不需要很长时间,到return时已完成任务处理 return false; } @Override public boolean onStopJob(JobParameters params) { // 有且仅有onStartJob返回值为true时,才会调用onStopJob来销毁job // 返回false来销毁这个工作 return false; }} 

1、OnStartJob(JobParameters params)

当开始一个任务时,onstartjob(jobparameters params) 是必须使用的方法,因为它是系统用来触发已经安排的工作的。从上边的用例代码可以看到,该方法返回一个布尔值。不同的返回值对应了不同的处理方式。

  • 如果返回值是false,该系统假定任何任务运行不需要很长时间并且到方法返回时已经完成。
  • 如果返回值是true,那么系统假设任务是需要一些时间并且是需要在我们自己应用执行的。当给定的任务完成时需要通过调用jobFinished(JobParameters params, boolean needsRescheduled)告知系统,该任务已经处理完成。如果返回值为true,我们需要手动调用jobFinished来停止该任务

2、JobFinished

void jobFinished (JobParameters params, boolean needsReschedule)

Callback to inform the JobManager you've finished executing. This can be called from any thread, as it will ultimately be run on your application's main thread. When the system receives this message it will release the wakelock being held.回调通知已完成执行的JobManager。这可以从任何线程调用,因为它最终将在应用程序的主线程上运行。当系统收到该消息时,它将释放正在保存的唤醒。

  • JobParameters params-- onStartJob(JobParameters).传入的param需要和onStartJob中的param一致

  • boolean needsReschedule-- True if this job should be rescheduled according to the back-off criteria specified at schedule-time. False otherwise.如果这项工作应按照计划时间指定的停止条件进行重新安排,则传入true。 否则的话传入false。说人话就是让系统知道这个任务是否应该在最初的条件下被重复执行(稍后会介绍这个布尔值的用处)

3、OnStopJob(JobParameters params)

当收到取消请求时,onStopJob(JobParameters params)是系统用来取消挂起的任务的。重要的是要注意到,如果onStartJob(JobParameters params)返回 false,当取消请求被接收时,该系统假定没有目前运行的工作。换句话说,它根本就不调用onStopJob(JobParameters params)。那此时就需要我们手动调用jobFinished (JobParameters params, boolean needsReschedule)方法了。

要注意的是,工作服务需要在应用程序的主线程上运行。这意味着,须使用另一个线程处理程序,或运行时间更长的任务异步任务用以不阻塞主线程。

简单的来讲,我们可以在上面JobSchedulerService类中创建一个Handler或者AsyncTask来处理需要进行的Job。

public class JobSchedulerService extends JobService { @Override public boolean onStartJob(JobParameters params) { // 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁 mJobHandler.sendMessage(Message.obtain(mJobHandler, 1, params)); return true; } @Override public boolean onStopJob(JobParameters params) { mJobHandler.removeMessages; return false; } // 创建一个handler来处理对应的job private Handler mJobHandler = new Handler(new Handler.Callback() { // 在Handler中,需要实现handleMessage(Message msg)方法来处理任务逻辑。 @Override public boolean handleMessage(Message msg) { Toast.makeText(getApplicationContext(), "JobService task running", Toast.LENGTH_SHORT).show(); // 调用jobFinished jobFinished((JobParameters) msg.obj, false); return true; } });} 

当然一个异步任务也是可行的:

public class JobSchedulerService extends JobService { private JobParameters mJobParameters @Override public boolean onStartJob(JobParameters params) { // 返回true,表示该工作耗时,同时工作处理完成后需要调用jobFinished销毁 mJobParameters = params; mTask.execute(); return true; } @Override public boolean onStopJob(JobParameters params) { return false; } private AsyncTask<Void, Void, Void> mTask = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... params) { // TODO Auto-generated method stub return null; } @Override protected void onPostExecute(Void result) { // TODO Auto-generated method stub Toast.makeText(wenfengService.this, "finish job", 1000).show(); jobFinished(mJobParameters, true); super.onPostExecute; } } }

当任务完成时,需要调用jobFinished(JobParameters params, boolean needsRescheduled)让系统知道完成了哪项任务,它可以开始排队接下来的操作。如果不这样做,工作将只运行一次,应用程序将不被允许执行额外的工作

代码片段中,因为handler中的操作可能比onStartJob(JobParameters params)方法它可能需要更长的时间来完成。通过设置true返回值,让程序了解将手动调用jobFinished(JobParameters params, boolean needsRescheduled)方法标记完成任务。

同时从上边的用例也可发现jobFinished(JobParameters params, boolean needsRescheduled)布尔值是false,它让系统知道是否需要根据工作的最初要求重新编排工作。同时这个布尔值是非常有用,可以帮助我们解决如何处理由于其他问题(如一个失败的网络电话)而导致任务无法完成的情况。设置为true我们就可以重复的进行这个任务。

任务失败的情况有很多,例如下载失败了,例如下载过程wifi断掉了。例如如果下载过程中,wifi断掉了,JobService会回调onStopJob函数,这是只需要把函数的返回值设置为true就可以了。当wifi重新连接后,JobService会重新回调onStartJob函数。而如果下载失败了,例如上面的例子中的mJobHandler执行失败,怎么办呢?我们只需要在Handler的handleMessage中执行jobFinished(mJobParameters, true),这里的true代表任务要在wifi条件重新满足情况下重新调度。

4、 绑定服务:

澳门葡京赌场,在简单的完成以上发送toast的Java代码之后,同Service一样,需要在AndroidManifest.xml中添加一个Service节点让应用拥有绑定和使用这个JobService的权限。

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

在完成JobSchedulerService的构建以及绑定Service节点之后,接下来进行的是如何与JobScheduler API交互。

1、创建一个JobScheduler

在Activity中我们通过getSystemService(Context.JOB_SCHEDULER_SERVICE)实例化一个mJobScheduler的JobScheduler对象。

JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);

创建定时任务时,可以使用JobInfo.Builder来构建一个JobInfo对象,然后传递给JobService。

JobInfo.Builder builder = new JobInfo.Builder(jobId, new ComponentName(getPackageName(), JobSchedulerService.class.getName;

JobInfo

Constants

  • BACKOFF_POLICY_普金娱乐,EXPONENTIAL 、BACKOFF_POLICY_新澳门葡京赌场手机版,LINEAR与setBackoffCriteria (long initialBackoffMillis, int backoffPolicy)中backoffPolicy对应

  • DEFAULT_INITIAL_BACKOFF_MILLIS默认的执行延迟时间

  • MAX_澳门葡京游戏,BACKOFF_DELAY_MILLIS最大的执行延迟时间

  • NETWORK_TYPE_ANY网络状态联网就行setRequiredNetworkType

  • NETWORK_TYPE_NONE默认的网络连接状态setRequiredNetworkType

  • NETWORK_TYPE_NOT_ROAMING移动网络连接情况setRequiredNetworkType

  • NETWORK_TYPE_UNMETEREDWIFI连接情况setRequiredNetworkType

▼▼▼▼重点来了!!!▼▼▼▼

JobInfo.Builder

JobInfo.Builder(int jobId, ComponentName jobService)Initialize a new Builder to construct a JobInfo.

JobInfo.Builder接收两个参数

  • jobId : 要运行的任务的标识符
  • jobService : Service组件的类名。

下面简要的介绍部分builder中的方法:(截止Android API 25)

  • addTriggerContentUri(JobInfo.TriggerContentUri uri):添加一个TriggerContentUri,该Uri将利用ContentObserver来监控一个Content Uri,当且仅当其发生变化时将触发任务的执行。为了持续监控content的变化,你需要在最近的任务触发后再调度一个新的任务(需要注意的是触发器URI不能与setPeriodicsetPersisted组合使用。要持续监控内容更改,需要在完成JobService处理最近的更改之前,调度新的JobInfo,观察相同的URI。因为设置此属性与定期或持久化Job不兼容,这样做会在调用build()时抛出IllegalArgumentException异常。)

  • setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)特殊:设置回退/重试的策略,详细的可以参阅Google API。类似网络原理中的冲突退避,当一个任务的调度失败时需要重试,所采取的策略。第一个参数时第一次尝试重试的等待间隔,单位为毫秒,预设的参数有:DEFAULT_INITIAL_BACKOFF_MILLIS 30000MAX_BACKOFF_DELAY_MILLIS 18000000。第二个参数是对应的退避策略,预设的参数有:BACKOFF_POLICY_EXPONENTIAL 二进制退避。等待间隔呈指数增长 BACKOFF_POLICY_LINEAR

  • setExtras(PersistableBundle extras):设置可选附件。这是持久的,所以只允许原始类型。

  • setMinimumLatency(long minLatencyMillis): 这个函数能用以设置任务的延迟执行时间,相当于post delay。

  • setOverrideDeadline(long maxExecutionDelayMillis): 这个方法让用以设置任务最晚的延迟时间。如果到了规定的时间时其他条件还未满足,任务也会被启动。

  • setPeriodic(long time):设置任务运行的周期(每X毫秒,运行一次)。衍生在运行一些权限检查的时候如果可以使用job scheduler的话,可以这样来循环检查权限的开启,但是目前的触发条件没有权限部分的触发

  • setPeriodic(long intervalMillis, long flexMillis):设置在Job周期末的一个flex长度的窗口,任务都有可能被执行 require API LEVEL 24

  • setPersisted(boolean isPersisted): 这个方法告诉系统当设备重启之后任务是否还要继续执行

  • setRequiredNetworkType(int networkType): 这个方法让这个任务只有在满足指定的网络条件时才会被执行。默认条件是JobInfo.NETWORK_TYPE_NONE,这意味着不管是否有网络这个任务都会被执行。另外两个可选类型,一种是JobInfo.NETWORK_TYPE_ANY,它表明需要任意一种网络才使得任务可以执行。另一种是JobInfo.NETWORK_TYPE_UNMETERED,它表示设备不是蜂窝网络( 比如在WIFI连接时 )时任务才会被执行。

  • setRequiresCharging(boolean requiresCharging): 只有当设备在充电时这个任务才会被执行。这个也并非只是插入充电器,而且还要在电池处于健康状态的情况下才会触发,一般来说是手机电量>15%

  • setRequiresDeviceIdle(boolean requiresDeviceIdle):指定Job在空闲状态才能运行。设备处于屏幕关闭或dreaming状态(类似window的休眠动画状态)71分钟后,执行工作

  • setTransientExtras(Bundle extras):设置可选的临时附加功能。(Android O Developer Preview)这里指定了需要Android O 系统,可以看出,Google还是在继续完善job scheduler的,期待能够代替传统的service组件成为主流。

  • setTriggerContentMaxDelay(long durationMs):设置从第一次检测到内容更改到Job之前允许的最大总延迟。说人话就是设置从content变化到任务被执行,中间的最大延迟同样的require API 24

  • setTriggerContentUpdateDelay(long durationMs):设置从content变化到任务被执行中间的延迟。如果在延迟期间content发生了变化,延迟会重新计算

  • setExtras(PersistableBundle extra):Bundle

要注意的是:1、*设置延迟时间 setMinimumLatency(long minLatencyMillis)设置最终期限时间* setOverrideDeadline(long maxExecutionDelayMillis)的两个方法不能同时与setPeriodic(long time)同时设置,也就是说,在设置延迟和最终期限时间时是不能设置重复周期时间的。还有在具体开发过程中需要注意各个方法的API兼容情况。

2、setRequiredNetworkType(int networkType), setRequiresCharging(boolean requireCharging)setRequiresDeviceIdle(boolean requireIdle)这几个方法可能会使得任务无法执行,除非调用setOverrideDeadline(long time)设置了最大延迟时间,使得任务在为满足条件的情况下也会被执行。

构建一个JobInfo对象设置预置的条件,然后通过如下所示的代码将它发送到的JobScheduler中。

开启一个JobScheduler任务:

mJobScheduler.schedule(JobInfo job)

在schedule时,会返回一个int类型的值来标记这次任务是否执行成功,如果返回小于0的错误码,这表示该次任务执行失败,反之则成功(成功会返回该任务的id,这里可以使用这个id来判断哪些任务成功了)。所以在返回值小于0的时候就需要我们手动去处理一些事情了。

if(mJobScheduler.schedule(JobInfo job) < 0){ // do something when schedule goes wrong}

最后如果需要停止一个任务,就通过JobScheduler中,cancel(int jobId)来实现(所以之前在Builer中的指定id又有了重要作用);如果想取消所有的任务,可以调用JobScheduler对象的cancelAll()来实现。

observeOn

Observable<T> observeOn(Scheduler scheduler)

observeOn同样接收一个Scheduler参数,来指定下游操作运行在特定的线程调度器Scheduler上。若多次设定,每次均起作用。

数据库升级的意义:

如果我们再项目中使用了数据库。而数据库的结构在第一版的时候定下来,之后发布功能更新,或增加业务逻辑,原来的数据库结构可能就不适用了。而如果数据库的结构与之前版本的结构不同,新版本的应用读取旧数据库肯定会出问题。解决办法只有两种:1.让用户卸载老版本再安装新的程序;2.软件自行更新数据库结构。第一种办法很明显不具备可操作性,而且用户一旦卸载软件,数据就丢失了,这是不能容忍的事情。因此,作为开发者必须妥善处理数据库的升级问题。

  • 修改gradle文件

首先在module的gradle文件中修改版本号:

//改为最新的版本号 schemaVersion 2 

如果只是做了上面的步骤则会默认清除所有数据,一看源码便知,当检测到version变化的时候便执行dropAllTables()操作,再重新建库

 /** WARNING: Drops all table on Upgrade! Use only during development. */ public static class DevOpenHelper extends OpenHelper { public DevOpenHelper(Context context, String name) { super(context, name); } public DevOpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); dropAllTables; onCreate; } }

数据迁移的核心思想:1 把旧表改为临时表2 建立新表3 临时表数据写入新表,删除临时表

站在巨人的肩膀上,这里直接使用了开源的库,配合greenDAO来做数据迁移,亲测稳定:GreenDaoUpgradeHelper

schemaVersion 版本加1,User类新增属性:major

不做迁移处理前,重新安装后数据库已经被重置,数据为空。迁移配置后,旧表数据已经被存到新表中,major字段为null;

澳门葡京游戏 9数据迁移成功

使用方式该git上面介绍的很详细了,不再赘述。感谢前辈们的无私分享。

3.该控件已做成类库,使用:
 allprojects { repositories { ... maven { url 'https://jitpack.io' } } } dependencies { implementation 'com.github.toly1994328:BiggerView:v1.01' }

一开始想做放大镜效果,没多想就继承ImageView了,后来越做越困难,bitmap的裁剪模式会影响视图中显示图片的大小。而View自己的的大小不变,会导致图片显示宽高捕捉困难,和图片左上角捕捉困难。这就会导致绘制放大图片时的定位适配困难,那么多裁剪模式,想想都崩溃。于是我想到,自己定义图像显示的view算了,需求是宽高按比例适应,并且View的尺寸即图片的尺寸,将蓝色作为背景,结果如下,你应该明白是什么意思了吧,就是既想要图片不变形,又想不要超出的背景区域:

澳门葡京游戏 10宽大于高.png澳门葡京游戏 11高大于宽.png

四、Jobscheduler使用

在上边初略的讲解了job scheduler的一些使用方法,下边通过一个用例来加深一下理解。

创建我们Job依附的Activity:

public class SchedulerAcitvity extends Activity { private static final String TAG = "SchedulerAcitvity"; public static final String MESSENGER_INTENT_KEY = TAG + ".MESSENGER_INTENT_KEY"; public static final String WORK_DURATION_KEY = TAG + ".WORK_DURATION_KEY"; public static final int MSG_JOB_START = 0; public static final int MSG_JOB_STOP = 1; public static final int MSG_ONJOB_START = 2; public static final int MSG_ONJOB_STOP = 3; private int mJobId = 0;// 执行的JobId ComponentName mServieComponent;// 这就是我们的jobservice组件了 private IncomingMessageHandler mHandler;// 用于来自服务的传入消息的处理程序。 // UI private EditText mEt_Delay;// 设置delay时间 private EditText mEt_Deadline;// 设置最长的截止时间 private EditText mEt_DurationTime;// setPeriodic周期 private RadioButton mRb_WiFiConnectivity;// 设置builder中的是否有WiFi连接 private RadioButton mRb_AnyConnectivity;// 设置builder中的是否有网络即可 private CheckBox mCb_RequiresCharging;// 设置builder中的是否需要充电 private CheckBox mCb_RequiresIdle;// 设置builder中的是否设备空闲 private Button mBtn_StartJob;// 点击开始任务的按钮 private Button mBtn_StopAllJob;// 点击结束所有任务的按钮 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.durian_main); mHandler = new IncomingMessageHandler; mServieComponent = new ComponentName(this, MyJobService.class);// 获取到我们自己的jobservice,同时启动该service // 设置UI mEt_Delay =  findViewById(R.id.delay_time); mEt_DurationTime =  findViewById(R.id.duration_time); mEt_Deadline =  findViewById(R.id.deadline_time); mRb_WiFiConnectivity = (RadioButton) findViewById(R.id.checkbox_unmetered); mRb_AnyConnectivity = (RadioButton) findViewById(R.id.checkbox_any); mCb_RequiresCharging =  findViewById(R.id.checkbox_charging); mCb_RequiresIdle =  findViewById(R.id.checkbox_idle); mBtn_StartJob = findViewById(R.id.button_start_job); mBtn_StopAllJob = findViewById(R.id.button_start_job); mBtn_StartJob.setOnClickListener(new View.OnClickListener() { @Override public void onClick { scheduleJob; mBtn_StopAllJob.setOnClickListener(new View.OnClickListener() { @Override public void onClick { cancelAllJobs; } @Override protected void onStart() { super.onStart(); // 启动服务并提供一种与此类通信的方法。 Intent startServiceIntent = new Intent(this, MyJobService.class); Messenger messengerIncoming = new Messenger; startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming); startService(startServiceIntent); } @Override protected void onStop() { // 服务可以是“开始”和/或“绑定”。 在这种情况下,它由此Activity“启动” // 和“绑定”到JobScheduler(也被JobScheduler称为“Scheduled”)。 // 对stopService()的调用不会阻止处理预定作业。 // 然而,调用stopService()失败将使它一直存活。 stopService(new Intent(this, MyJobService.class)); super.onStop(); } // 当用户单击SCHEDULE JOB时执行。 public void scheduleJob() { //开始配置JobInfo JobInfo.Builder builder = new JobInfo.Builder(mJobId++, mServiceComponent); //设置任务的延迟执行时间 String delay = mEt_Delay.getText().toString(); if (!TextUtils.isEmpty { builder.setMinimumLatency(Long.valueOf * 1000); } //设置任务最晚的延迟时间。如果到了规定的时间时其他条件还未满足,你的任务也会被启动。 String deadline = mEt_Deadline.getText().toString(); if (!TextUtils.isEmpty) { builder.setOverrideDeadline(Long.valueOf * 1000); } boolean requiresUnmetered = mRb_WiFiConnectivity.isChecked(); boolean requiresAnyConnectivity = mRb_AnyConnectivity.isChecked(); //让你这个任务只有在满足指定的网络条件时才会被执行 if (requiresUnmetered) { builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); } else if (requiresAnyConnectivity) { builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); } //你的任务只有当用户没有在使用该设备且有一段时间没有使用时才会启动该任务。 builder.setRequiresDeviceIdle(mCb_RequiresIdle.isChecked; //告诉你的应用,只有当设备在充电时这个任务才会被执行。 builder.setRequiresCharging(mCb_RequiresCharging.isChecked; // Extras, work duration. PersistableBundle extras = new PersistableBundle(); String workDuration = mEt_DurationTime.getText().toString(); if (TextUtils.isEmpty(workDuration)) { workDuration = "1"; } extras.putLong(WORK_DURATION_KEY, Long.valueOf(workDuration) * 1000); builder.setExtras; // Schedule job Log.d(TAG, "Scheduling job"); JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); // 这里就将开始在service里边处理我们配置好的job mJobScheduler.schedule(builder.build; //mJobScheduler.schedule(builder.build会返回一个int类型的数据 //如果schedule方法失败了,它会返回一个小于0的错误码。否则它会返回我们在JobInfo.Builder中定义的标识id。 } // 当用户点击取消所有时执行 public void cancelAllJobs() { JobScheduler mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); mJobScheduler.cancelAll(); Toast.makeText(MainActivity.this, R.string.all_jobs_cancelled, Toast.LENGTH_SHORT).show(); } /** * {@link Handler}允许您发送与线程相关联的消息。 * {@link Messenger}使用此处理程序从{@link MyJobService}进行通信。 * 它也用于使开始和停止视图在短时间内闪烁。 */ private static class IncomingMessageHandler extends Handler { // 使用弱引用防止内存泄露 private WeakReference<SchedulerAcitvity> mActivity; IncomingMessageHandler(SchedulerAcitvity activity) { super(/* default looper */); this.mActivity = new WeakReference<>; } @Override public void handleMessage(Message msg) { SchedulerAcitvity mSchedulerAcitvity = mActivity.get(); if (mSchedulerAcitvity == null) { // 活动不再可用,退出。 return; } // 获取到两个View,用于之后根据Job运行状态显示不同的运行状态 View showStartView = mSchedulerAcitvity.findViewById(R.id.onstart_textview); View showStopView = mSchedulerAcitvity.findViewById(R.id.onstop_textview); Message m; switch  { // 当作业登录到应用程序时,从服务接收回调。 打开指示灯并发送一条消息,在一秒钟后将其关闭。 case MSG_JOB_START: // Start received, turn on the indicator and show text. // 开始接收,打开指示灯并显示文字。 showStartView.setBackgroundColor(getColor(R.color.start_received)); updateParamsTextView(msg.obj, "started"); // Send message to turn it off after a second. // 发送消息,一秒钟后关闭它。 m = Message.obtain(this, MSG_ONJOB_START); sendMessageDelayed; break; // 当先前执行在应用程序中的作业必须停止执行时, // 从服务接收回调。 打开指示灯并发送一条消息, // 在两秒钟后将其关闭。 case MSG_JOB_STOP: // Stop received, turn on the indicator and show text. // 停止接收,打开指示灯并显示文本。 showStopView.setBackgroundColor(getColor(R.color.stop_received)); updateParamsTextView(msg.obj, "stopped"); // Send message to turn it off after a second. // 发送消息,一秒钟后关闭它。 m = obtainMessage(MSG_ONJOB_STOP); sendMessageDelayed; break; case MSG_ONJOB_START: showStartView.setBackgroundColor(getColor(R.color.none_received)); updateParamsTextView(null, "job had started"); break; case MSG_ONJOB_STOP: showStopView.setBackgroundColor(getColor(R.color.none_received)); updateParamsTextView(null, "job had stoped"); break; } } // 更新UI显示 // @param jobId jobId // @param action 消息 private void updateParamsTextView(@Nullable Object jobId, String action) { TextView paramsTextView =  mActivity.get().findViewById(R.id.task_params); if (jobId == null) { paramsTextView.setText; return; } String jobIdText = String.valueOf; paramsTextView.setText(String.format("Job ID %s %s", jobIdText, action)); } private int getColor(@ColorRes int color) { return mActivity.get().getResources().getColor; } }} 

在activity中我们使用一个按钮来开启JobScheduler,同时简单的配置了相应Builder的配置。在点击button的时候,此项scheduler就开始schedule(我们配置的jobInfo)。那当我们的条件匹配我们配置的JobInfo的时候会开始怎样的处理呢?这里就需要我们在service里边做作业了。

满足配置条件时候启动任务的Service:

public class MyJobService extends JobService { private static final String TAG = MyJobService.class.getSimpleName(); private Messenger mActivityMessenger; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "Service created"); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "Service destroyed"); } // 当应用程序的MainActivity被创建时,它启动这个服务。 // 这是为了使活动和此服务可以来回通信。 请参见“setUiCallback()” @Override public int onStartCommand(Intent intent, int flags, int startId) { mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY); return START_NOT_STICKY; } @Override public boolean onStartJob(final JobParameters params) { // The work that this service "does" is simply wait for a certain duration and finish // the job (on another thread). // 该服务做的工作只是等待一定的持续时间并完成作业。 sendMessage(MSG_JOB_START, params.getJobId; // 当然这里可以处理其他的一些任务 // TODO something else // 获取在activity里边设置的每个任务的周期,其实可以使用setPeriodic() long duration = params.getExtras().getLong(WORK_DURATION_KEY); // 使用一个handler处理程序来延迟jobFinished()的执行。 Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { sendMessage(MSG_JOB_STOP, params.getJobId; jobFinished(params, false); } }, duration); Log.i(TAG, "on start job: " + params.getJobId; // 返回true,很多工作都会执行这个地方,我们手动结束这个任务 return true; } @Override public boolean onStopJob(JobParameters params) { // 停止跟踪这些作业参数,因为我们已经完成工作。 sendMessage(MSG_JOB_STOP, params.getJobId; Log.i(TAG, "on stop job: " + params.getJobId; // 返回false来销毁这个工作 return false; } private void sendMessage(int messageID, @Nullable Object params) { // 如果此服务由JobScheduler启动,则没有回调Messenger。 // 它仅在MainActivity在Intent中使用回调函数调用startService()时存在。 if (mActivityMessenger == null) { Log.d(TAG, "Service is bound, not started. There's no callback to send a message to."); return; } Message m = Message.obtain(); m.what = messageID; m.obj = params; try { mActivityMessenger.send; } catch (RemoteException e) { Log.e(TAG, "Error passing service object back to activity."); } }}

虽然在service里边只是简单地进行了一个我们设置的耗时操作,但是通过以上的例子应该很容易理解JobScheduler的使用了。在某些条件下(充电,网络连接【可以指定特定的状态】,设备空闲)JobScheduler可以更优秀的完成我们的触发型任务,虽然目前来讲匹配的条件很是很少,但使用JobScheduler可以更优雅的处理这些触发事件,也是值得使用的。同时随着Android的发展,我相信JobScheduler的应用场景会越来越多。

Schedulers.io:

用于IO密集型的操作,例如读写SD卡文件,查询数据库,访问网络等,具有线程缓存机制,在此调度器接收到任务后,先检查线程缓存池中,是否有空闲的线程,如果有,则复用,如果没有则创建新的线程,并加入到线程池中,如果每次都没有空闲线程使用,可以无上限的创建新线程。

1.自定义属性:
 <!--图片放大镜--> <declare-styleable name="FitImageView"> <!--图片资源--> <attr name="z_fit_src" format="reference"/> </declare-styleable>

五、衍生:

似乎都很热衷于应用的保活,很多地方都是将jobScheduler应用于Service杀不死,进一步拉应用的状态使用JobScheduler进行开机自启动Android服务保活-JobScheduler拉活Android进程保活的一般套路

JobScheduler省电:Android L 的 JobScheduler API 是怎么让设备省电的

除了JobScheduler ,还有其他一些类似的APIs去帮助安排你的工作计划,它们包括:AlarmManagerFirebase JobDispatcherGCM NETwork ManagerSyncAdapterAdditional Facilities

自启动问题

按照官方文档的定义,在原生的Android系统上,当设定了一个Job之后,哪怕该App的进程已经结束或者被杀掉,对应的JobService也是可以启动的。然而Android已经被国内的各大厂商重新定制过,导致的一个问题就是当前App的进程被杀掉之后,JobService无法启动。例如在MIUI系统中,第三方App如果没有被用户设置到允许自启动的名单中,在启动Service的时候会被拦截掉。这恐怕会导致很多第三方应用无法使用这个东西,正如之前的AlarmManager一样。还有一个比较坑的地方在于,当Job正在执行时,如果使用类似MIUI上的一键清除所有进程,JobService会被强制停止,也不会执行对应的onStopJob方法。

兼容性问题JobScheduler支持Android 5.0以上的系统,但对于运行Android 5.0以下系统的手机是否有办法呢?

Github上有一个开源的项目也许能帮得上忙:项目地址这个项目在介绍中也宣称不在进行维护了,所以,仅供参考吧。

Android省电的秘密之JobSchedulerAndroid省电的秘密之adb解读JobScheduler

本文整合部分网上相关文章,感谢所有文章的作者。

Schedulers.newThread:

在每执行一个任务时创建一个新的线程,不具有线程缓存机制,因为创建一个新的线程比复用一个线程更耗时耗力,虽然使用Schedulers.io的地方,都可以使用Schedulers.newThread,但是,Schedulers.newThread的效率没有Schedulers.io高。

2.自定义控件初始代码
/** * 作者:张风捷特烈<br/> * 时间:2018/11/19 0019:0:14<br/> * 邮箱:1981462002@qq.com<br/> * 说明:宽高自适应图片视图 */public class FitImageView extends View { private Paint mPaint;//主画笔 private Drawable mFitSrc;//自定义属性获取的Drawable private Bitmap mBitmapSrc;//源图片 protected Bitmap mFitBitmap;//适应宽高的缩放图片 protected float scaleRateW2fit = 1;//宽度缩放适应比率 protected float scaleRateH2fit = 1;//高度缩放适应比率 protected int mImageW, mImageH;//图片显示的宽高 public FitImageView(Context context) { this(context, null); } public FitImageView(Context context, @Nullable AttributeSet attrs) { this(context, attrs,0); } public FitImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FitImageView); mFitSrc = a.getDrawable(R.styleable.FitImageView_z_fit_src); a.recycle;//初始化 } private void init() { //初始化主画笔 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBitmapSrc = ((BitmapDrawable) mFitSrc).getBitmap();//获取图片 } @Override protected void onDraw(Canvas canvas) { super.onDraw; //TODO draw }
Schedulers.computation():

用于CPU 密集型计算任务,即不会被 I/O 等操作限制性能的耗时操作,例如xml,json文件的解析,Bitmap图片的压缩取样等,具有固定的线程池,大小为CPU的核数。不可以用于I/O操作,因为I/O操作的等待时间会浪费CPU。

3.测量及摆放:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mImageW = dealWidth(widthMeasureSpec);//显示图片宽 mImageH = dealHeight(heightMeasureSpec);//显示图片高 float bitmapWHRate = mBitmapSrc.getHeight() * 1.f / mBitmapSrc.getWidth();//图片宽高比 if (mImageH >= mImageW) { mImageH =  (mImageW * bitmapWHRate);//宽小,以宽为基准 } else { mImageW =  (mImageH / bitmapWHRate);//高小,以高为基准 } setMeasuredDimension(mImageW, mImageH);}/** * @param heightMeasureSpec * @return */private int dealHeight(int heightMeasureSpec) { int result = 0; int mode = MeasureSpec.getMode(heightMeasureSpec); int size = MeasureSpec.getSize(heightMeasureSpec); if (mode == MeasureSpec.EXACTLY) { //控件尺寸已经确定:如: // android:layout_height="40dp"或"match_parent" scaleRateH2fit = size * 1.f / mBitmapSrc.getHeight() * 1.f; result = size; } else { result = mBitmapSrc.getHeight(); if (mode == MeasureSpec.AT_MOST) {//最多不超过 result = Math.min(result, size); } } return result;}/** * @param widthMeasureSpec */private int dealWidth(int widthMeasureSpec) { int result = 0; int mode = MeasureSpec.getMode(widthMeasureSpec); int size = MeasureSpec.getSize(widthMeasureSpec); if (mode == MeasureSpec.EXACTLY) { //控件尺寸已经确定:如: // android:layout_XXX="40dp"或"match_parent" scaleRateW2fit = size * 1.f / mBitmapSrc.getWidth(); result = size; } else { result = mBitmapSrc.getWidth(); if (mode == MeasureSpec.AT_MOST) {//最多不超过 result = Math.min(result, size); } } return result;}
Schedulers.trampoline():

在当前线程立即执行任务,如果当前线程有任务在执行,则会将其暂停,等插入进来的任务执行完之后,再将未完成的任务接着执行。

4.创建缩放后的bitmap及绘制

创建的时机选择在onLayout里,因为要先测量后才能知道缩放比

@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mFitBitmap = createBigBitmap(Math.min(scaleRateW2fit, scaleRateH2fit), mBitmapSrc); mBitmapSrc = null;//原图已无用将原图置空}/** * 创建一个rate倍的图片 * * @param rate 缩放比率 * @param src 图片源 * @return 缩放后的图片 */protected Bitmap createBigBitmap(float rate, Bitmap src) { Matrix matrix = new Matrix(); //设置变换矩阵:扩大3倍 matrix.setValues(new float[]{ rate, 0, 0, 0, rate, 0, 0, 0, 1 }); return Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);}@Overrideprotected void onDraw(Canvas canvas) { super.onDraw; canvas.drawBitmap(mFitBitmap, 0, 0, mPaint);}
友情链接: 网站地图
Copyright © 2015-2019 http://www.nflfreepicks.net. 新葡萄京娱乐场网址有限公司 版权所有